This source PFPE generates EuroAssembler output executable file in program format PE. as described in [MS_PECOFF].
pfpe PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32 INCLUDEHEAD "euroasm.htm" ; Interface (structures, symbols and macros) of other modules.
pfpe HEAD ; Start module interface.
PFPE_DATA_DIRECTORY STRUC .VirtualAddress D DWORD ; RVA of the table (relative to ImageBase). .Size D DWORD ; Number of bytes in the directory table. ENDSTRUC PFPE_DATA_DIRECTORY
PFPE_OPTIONAL_HEADER STRUC ; Size# = 224 = 0E0h. .Magic D WORD ; 0x010B=PE executable, 0x0107=ROMimage. .MajorLinkerVersion D BYTE .MinorLinkerVersion D BYTE .SizeOfCode D DWORD ; Total size of all sections with PURPOSE=CODE. .SizeOfInitializedData D DWORD ; Total size of all sections with PURPOSE=DATA. .SizeOfUninitializedData D DWORD ; Total size of all sections with PURPOSE=BSS. .AddressOfEntryPoint D DWORD ; Entry offset relative to ImageBase, or 0. .BaseOfCode D DWORD ; Code section offset relative to ImageBase. .BaseOfData D DWORD ; Data section offset relative to ImageBase. .ImageBase D DWORD ; Preferred VA of the image, 64K aligned. NT additional fields. .SectionAlignment D DWORD ; Power of 2, must be > .FileAlignment, default=4K. .FileAlignment D DWORD ; Power of 2, 512..64K, default=512. .MajorOperatingSystemVersion D WORD ; Required version of required OS. .MinorOperatingSystemVersion D WORD .MajorImageVersion D WORD ; Version of this image. .MinorImageVersion D WORD .MajorSubsystemVersion D WORD ; Required version of subsystem. .MinorSubsystemVersion D WORD .Win32VersionValue D DWORD ; Reserved, must be 0. .SizeOfImage D DWORD ; Size of image loaded in memory, rounded up to .SectionAlignment. .SizeOfHeaders D DWORD ; Size of stub+PE header+section headers, rounded up to .FileAlignment. .CheckSum D DWORD .Subsystem D WORD ; DictProgramSubsystems.Data value. .DllCharacteristics D WORD .SizeOfStackReserve D DWORD .SizeOfStackCommit D DWORD .SizeOfHeapReserve D DWORD .SizeOfHeapCommit D DWORD .LoaderFlags D DWORD ; Reserved, must be 0. .NumberOfRvaAndSizes D DWORD ; Number of following .DataDirectory entries. .DataDirectory DS 16*PFPE_DATA_DIRECTORY ; See indexes 0..15 in PFPE_encodings below. ENDSTRUC PFPE_OPTIONAL_HEADER
PFPE_OPTIONAL_HEADER64 STRUC ; Size# = 240 = 0F0h. .Magic D WORD ; 0x20B=PE32+ (64bit executable). .MajorLinkerVersion D BYTE ; Standard members have identical offsets in 32bit and 64bit versions. .MinorLinkerVersion D BYTE .SizeOfCode D DWORD .SizeOfInitializedData D DWORD .SizeOfUninitializedData D DWORD .AddressOfEntryPoint D DWORD .BaseOfCode D DWORD ; .BaseOfData DWORD ; Omitted in 64bit version in favour of qword .ImageBase. .ImageBase D QWORD .SectionAlignment D DWORD .FileAlignment D DWORD .MajorOperatingSystemVersion D WORD .MinorOperatingSystemVersion D WORD .MajorImageVersion D WORD .MinorImageVersion D WORD .MajorSubsystemVersion D WORD .MinorSubsystemVersion D WORD .Win32VersionValue D DWORD .SizeOfImage D DWORD .SizeOfHeaders D DWORD .CheckSum D DWORD .Subsystem D WORD .DllCharacteristics D WORD .SizeOfStackReserve D QWORD ; This and following members have different offsets in 32 and 64bit version. .SizeOfStackCommit D QWORD .SizeOfHeapReserve D QWORD .SizeOfHeapCommit D QWORD .LoaderFlags D DWORD .NumberOfRvaAndSizes D DWORD .DataDirectory DS 16*PFPE_DATA_DIRECTORY ; See indexes 0..15 in PFPE_encodings below. ENDSTRUC PFPE_OPTIONAL_HEADER64
[.reloc]
section of PE and DLL executable files.
PFPE_BASERELOC STRUC .PageRVA D DWORD ; RVA of raw data which may need base-relocation by this block. .BlockSize D DWORD ; Size of this block of relocations including .PageRVA and .BlockSize. .TypeOffset D 0 * WORD ; Variable number of words which combine type and offset in 4+12 bits. ENDSTRUC PFPE_BASERELOC
[.idata]
created by PfpeImportCreate.
.Offset*
member specifies offset of the table within the emitted contents of section
[.idata]
.PFPE_IDATA_MAP STRUC .IdataPtr D DWORD ; Pointer to SSS section[.idata]
. .OffsetIAT D DWORD ; Offset of Import Address Table. Always 0. .OffsetIDT D DWORD ; Offset of Import Descriptor Table. .OffsetILT D DWORD ; Offset of Import Lookup Table. .OffsetONT D DWORD ; Offset of Ordinal+Name Table. .OffsetDNT D DWORD ; Offset of Dll Names Table. .OffsetSPT D DWORD ; Offset of Stub Proxy Table. .OffsetEND D DWORD ; Offset of end-of tables, i.e. the size of section[.idata]
. ENDSTRUC PFPE_IDATA_MAP
PFPE_EXPORT_DIRECTORY STRUC .Characteristics D DWORD ; Reserved, must be 0. .TimeDateStamp D DWORD ; Time when the export data was created. .MajorVersion D WORD ; User-defined version number of the export. .MinorVersion D WORD ; Identical with ImageVersion in optional PE header. .Name D DWORD ; RVA of DNT (ASCIIZ DLL name). .Base D DWORD ; Starting ordinal number, always 1. .NumberOfFunctions D DWORD ; Number of DWORD entries in EAT (Export Address Table). .NumberOfNames D DWORD ; Number of DWORD entries in NPT and WORD entries in OPT. .AddressOfFunctions D DWORD ; RVA of EAT (Export Address Table). .AddressOfNames D DWORD ; RVA of NPT (Name Pointer Table). .AddressOfOrdinals D DWORD ; RVA of ONT (Ordinal Numbers Table). ENDSTRUC PFPE_EXPORT_DIRECTORY
PFPE_IMPORT_DESCRIPTOR STRUC .OriginalFirstThunk D DWORD ; RVA of the table with name/ordinal for each import. 0 to terminate chain. .TimeDateStamp D DWORD ; Time of the DLL, or 0 until the image is bound. .ForwarderChain D DWORD ; Index of 1st forwarder reference or -1 if no forwarders. .Name D DWORD ; RVA of ASCIIZ DLL name. .FirstThunk D DWORD ; RVA of IAT. If bound, this IAT has actual addresses. ENDSTRUC PFPE_IMPORT_DESCRIPTOR
WINNT.h.
; PFPE_OPTIONAL_HEADER.DataDirectory and PFPE_OPTIONAL_HEADER64.DataDirectory indexes: pfpeDIRECTORY_ENTRY_EXPORT = 0 ; Export Directory. pfpeDIRECTORY_ENTRY_IMPORT = 1 ; Import Directory. pfpeDIRECTORY_ENTRY_RESOURCE = 2 ; Resource Directory. pfpeDIRECTORY_ENTRY_EXCEPTION = 3 ; Exception Directory. pfpeDIRECTORY_ENTRY_SECURITY = 4 ; Security (certificate) Directory. pfpeDIRECTORY_ENTRY_BASERELOC = 5 ; Base Relocation Table. pfpeDIRECTORY_ENTRY_DEBUG = 6 ; Debug Directory. pfpeDIRECTORY_ENTRY_COPYRIGHT = 7 ; (X86 usage) pfpeDIRECTORY_ENTRY_ARCHITECTURE = 7 ; Architecture Specific Data. Must be 0. pfpeDIRECTORY_ENTRY_GLOBALPTR = 8 ; RVA of the value in GPTR register. pfpeDIRECTORY_ENTRY_TLS = 9 ; TLS Directory. pfpeDIRECTORY_ENTRY_LOAD_CONFIG = 10 ; Load Configuration Directory. pfpeDIRECTORY_ENTRY_BOUND_IMPORT = 11 ; Bound Import Directory in headers. pfpeDIRECTORY_ENTRY_IAT = 12 ; Import Address Table. pfpeDIRECTORY_ENTRY_DELAY_IMPORT = 13 ; Delay Load Import Descriptors. pfpeDIRECTORY_ENTRY_CLR = 14 ; CLR Runtime descriptor. ; PFPE_BASERELOC.TypeOffset base-relocation width specifiers: pfpeREL_BASED_ABSOLUTE EQU 0 << 12 ; Empty relocation used to pad a block. ;>> pfpeREL_BASED_LOW EQU 2 << 12 ; Relocate the word at Offset. ;>> pfpeREL_BASED_HIGHLOW EQU 3 << 12 ; Relocate the dword at Offset. ;>> pfpeREL_BASED_DIR64 EQU 10 << 12 ; Relocate the qword at Offset. ;>>
ENDHEAD pfpe ; End of module interface.
.Magic
, .AddressOfEntryPoint
,
ImageBase
, SectionAlignment
,
FileAlignment
, Subsystem
, *Version, SizeOfStack*
,
SizeOfHeap*, NumberOfRvaAndSizes
.
PfpeOptionalHeader Procedure OptionalHeader,PgmPtr
MOV EDI,[%OptionalHeader]
MOV EBX,[%PgmPtr]
Clear EDI,Size=SIZE#PFPE_OPTIONAL_HEADER64 ; Clear the longer variant.
MOV CX,0x020B ; 64bit executable.
JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.10:
MOV CX,0x010B ; 32bit executable.
.10:MOV [EDI+PFPE_OPTIONAL_HEADER.Magic],CX
; Standard members specified in program options.
LEA ESI,[EBX+PGM.EntryExp]
MOV EAX,[ESI+EXP.Low]
MOV EDX,[ESI+EXP.High]
CMPB [ESI+EXP.Status+0],0
JNZ .20:
; Program entry was not specified. This is OK if FORMAT=DLL.
MOV EAX,pgmoptFormatMask
AND EAX,[EBX+PGM.Pgmopt.Status]
CMP AL,pgmoptDLL
JE .55: ; No entry in DLL, leave .AddressOfEntryPoint at zero.
Msg '7710' ; Missing program entry point.
JMP .25:
.20:MOV ECX,[ESI+EXP.Seg] ; Entry was specified.
JECXZ .23:
MOV EAX,[ESI+EXP.Low]
MOV EDX,[ESI+EXP.High]
MOV ECX,[ECX+SSS.SegmPtr] ; For the case when ECX specifies a section instead of segment.
TEST ECX
JNZ .45:
.23:LEA ECX,[EBX+PGM.Pgmopt.EntryPtr] ; Msg parameter.
Msg '2921',ECX ; Nonrelocable entry point "!1S" is not supported by linker.
.25:ListGetFirst [EBX+PGM.SssList] ; Use [1stCodeSegment]:0
instead.
JZ .55:
.30:JNSt [EAX+SSS.Status],sssSegment,.35:
JSt [EAX+SSS.Purpose],sssPurposeCODE,.40:
.35:ListGetNext EAX
JNZ .30:
JMP .55:
.40:MOV ECX,EAX
SUB EAX,EAX
.45 ADD EAX,[ECX+SSS.BottomLow] ; VA of the entry segment, usually [.text].
ADC EDX,[ECX+SSS.BottomHigh]
SUB EAX,[EBX+PGM.Pgmopt.ImageBaseLow] ; Convert VA to RVA.
SBB EDX,[EBX+PGM.Pgmopt.ImageBaseHigh]
Msg cc=NZ,'7711',[ESI+EXP.Sym] ; Invalid program entry point "!1S".
.50:MOV [EDI+PFPE_OPTIONAL_HEADER.AddressOfEntryPoint],EAX
.55:MOV EAX,[EBX+PGM.Pgmopt.SectionAlign]
MOV EDX,[EBX+PGM.Pgmopt.FileAlign]
MOV [EDI+PFPE_OPTIONAL_HEADER.SectionAlignment],EAX
MOV [EDI+PFPE_OPTIONAL_HEADER.FileAlignment],EDX
MOV EAX,[EBX+PGM.Pgmopt.MajorOsVersion]
MOV EDX,[EBX+PGM.Pgmopt.MinorOsVersion]
MOV [EDI+PFPE_OPTIONAL_HEADER.MajorOperatingSystemVersion],AX
MOV [EDI+PFPE_OPTIONAL_HEADER.MinorOperatingSystemVersion],DX
MOV EAX,[EBX+PGM.Pgmopt.MajorImageVersion]
MOV EDX,[EBX+PGM.Pgmopt.MinorImageVersion]
MOV [EDI+PFPE_OPTIONAL_HEADER.MajorImageVersion],AX
MOV [EDI+PFPE_OPTIONAL_HEADER.MinorImageVersion],DX
MOV EAX,[EBX+PGM.Pgmopt.MajorSubsystemVersion]
MOV EDX,[EBX+PGM.Pgmopt.MinorSubsystemVersion]
MOV [EDI+PFPE_OPTIONAL_HEADER.MajorSubsystemVersion],AX
MOV [EDI+PFPE_OPTIONAL_HEADER.MinorSubsystemVersion],DX
MOV EAX,[EBX+PGM.Pgmopt.MajorLinkerVersion]
MOV EDX,[EBX+PGM.Pgmopt.MinorLinkerVersion]
MOV [EDI+PFPE_OPTIONAL_HEADER.MajorLinkerVersion],AL
MOV [EDI+PFPE_OPTIONAL_HEADER.MinorLinkerVersion],DL
MOV ECX,[EBX+PGM.Pgmopt.Win32VersionValue]
MOV EAX,[EBX+PGM.Pgmopt.Subsystem]
MOV EDX,[EBX+PGM.Pgmopt.DllCharacteristics]
MOV [EDI+PFPE_OPTIONAL_HEADER.Win32VersionValue],ECX
MOV [EDI+PFPE_OPTIONAL_HEADER.Subsystem],AX
MOV [EDI+PFPE_OPTIONAL_HEADER.DllCharacteristics],DX
; Version specific members.
MOV EAX,[EBX+PGM.Pgmopt.ImageBaseLow]
MOV EDX,[EBX+PGM.Pgmopt.ImageBaseHigh]
MOV ECX,16 ; Number of data directory records in optional header.
JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.60:
MOV [EDI+PFPE_OPTIONAL_HEADER.ImageBase],EAX
MOV [EDI+PFPE_OPTIONAL_HEADER.NumberOfRvaAndSizes],ECX
JMP .65:
.60:MOV [EDI+PFPE_OPTIONAL_HEADER64.ImageBase+0],EAX
MOV [EDI+PFPE_OPTIONAL_HEADER64.ImageBase+4],EDX
MOV [EDI+PFPE_OPTIONAL_HEADER64.NumberOfRvaAndSizes],ECX
.65:MOV EAX,[EBX+PGM.Pgmopt.SizeOfHeapCommitLow]
MOV EDX,[EBX+PGM.Pgmopt.SizeOfHeapCommitHigh]
MOV ECX,[EBX+PGM.Pgmopt.SizeOfHeapReserveLow]
MOV ESI,[EBX+PGM.Pgmopt.SizeOfHeapReserveHigh]
JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.70:
MOV [EDI+PFPE_OPTIONAL_HEADER.SizeOfHeapCommit],EAX
MOV [EDI+PFPE_OPTIONAL_HEADER.SizeOfHeapReserve],ECX
JMP .75:
.70:MOV [EDI+PFPE_OPTIONAL_HEADER64.SizeOfHeapCommit+0],EAX
MOV [EDI+PFPE_OPTIONAL_HEADER64.SizeOfHeapCommit+4],EDX
MOV [EDI+PFPE_OPTIONAL_HEADER64.SizeOfHeapReserve+0],ECX
MOV [EDI+PFPE_OPTIONAL_HEADER64.SizeOfHeapReserve+4],ESI
.75:MOV EAX,[EBX+PGM.Pgmopt.SizeOfStackCommitLow]
MOV EDX,[EBX+PGM.Pgmopt.SizeOfStackCommitHigh]
MOV ECX,[EBX+PGM.Pgmopt.SizeOfStackReserveLow]
MOV ESI,[EBX+PGM.Pgmopt.SizeOfStackReserveHigh]
JSt [EBX+PGM.Pgmopt+PGMOPT.Status],pgmoptWidth64,.80:
MOV [EDI+PFPE_OPTIONAL_HEADER.SizeOfStackCommit],EAX
MOV [EDI+PFPE_OPTIONAL_HEADER.SizeOfStackReserve],ECX
JMP .90:
.80:MOV [EDI+PFPE_OPTIONAL_HEADER64.SizeOfStackCommit+0],EAX
MOV [EDI+PFPE_OPTIONAL_HEADER64.SizeOfStackCommit+4],EDX
MOV [EDI+PFPE_OPTIONAL_HEADER64.SizeOfStackReserve+0],ECX
MOV [EDI+PFPE_OPTIONAL_HEADER64.SizeOfStackReserve+4],ESI
.90:EndProcedure PfpeOptionalHeader
PfpeCompile Procedure OutputStream, Pgm
StubFileBuf LocalVar ; Pointer to BUFFER which keeps DOS stub file contents.
SectionHeaderBuf LocalVar ; Pointer to BUFFER which keeps PFCOFF_SECTION_HEADER objects, one for each segment.
RawBuf LocalVar ; Pointer to BUFFER for emitted contents and relocations of all segments.
SymbolTableBuf LocalVar ; Pointer to BUFFER for PFCOFF_SYMBOL records.
StringTableBuf LocalVar ; Pointer to BUFFER for longname strings.
SectionHeaderPtr LocalVar ; Pointer to the current PFCOFF_SECTION_HEADER object on SectionHeaderBuf.
FileAddr LocalVar ; File offset in PE file, updated on-the-fly.
ExportSection LocalVar ; Pointer to SSS with [.edata]
section.
ENToffset LocalVar ; Offset of ENT within export section contents.
FWDoffset LocalVar ; Offset of FWD within export section contents.
MaxTopHigh LocalVar ; Maximal SSS.Top of all sections.
MaxTopLow LocalVar
InputFile LocalVar Size=SIZE# FILE ; File object for reading stub and icon file.
CoffFileHeader LocalVar Size=SIZE#PFCOFF_FILE_HEADER ; Room for PFCOFF_FILE_HEADER object.
PeOptionalHeader LocalVar Size=SIZE#PFPE_OPTIONAL_HEADER64 ; Room for PFPE_OPTIONAL_HEADER or PFPE_OPTIONAL_HEADER64.
IdataMap LocalVar Size=SIZE# PFPE_IDATA_MAP ; Room for PFPE_IDATA_MAP.
SUB EAX,EAX
MOV [%MaxTopLow],EAX
MOV [%MaxTopHigh],EAX
; Reserve temporary buffers.
Invoke EaBufferReserve::,PfpeCompile
MOV [%StubFileBuf],EAX
Invoke EaBufferReserve::,PfpeCompile
MOV [%SectionHeaderBuf],EAX
Invoke EaBufferReserve::,PfpeCompile
MOV [%RawBuf],EAX ; Raw data buffer.
Invoke EaBufferReserve::,PfpeCompile
MOV [%SymbolTableBuf],EAX ; Symbols buffer.
Invoke EaBufferReserve::,PfpeCompile
MOV [%StringTableBuf],EAX ; String table buffer.
BufferStoreDword EAX,4 ; Initialize DD StringTableSize in the buffer.
; DOS MZ stub.
Invoke PfpeLoadStubFile, [%StubFileBuf], EBX
Invoke EaBufferAlign::,[%StubFileBuf],8 ; Following PE signature should be QWORD aligned.
; Virtually link program segments.
MOV EBX,[%Pgm]
Invoke RelocSort::,EBX
LEA EDX,[%IdataMap]
Invoke PfpeImportCreate,EBX,EDX
Invoke PfpeExportCreate,EBX
MOV [%ExportSection],EAX
MOV [%ENToffset],ECX
MOV [%FWDoffset],EDX
Invoke PfpeBaserelocCreate,EBX
Invoke PfrsrcLoadIconFile::,EBX
Invoke PgmOrderSegments::,EBX
BufferRetrieve [%StubFileBuf]
Invoke PgmLinkImage::,EBX,ECX
Invoke PgmEvalEntry::,EBX
LEA EDI,[EBX+PGM.EntryExp]
MOV ESI,[EDI+EXP.Seg]
TEST ESI
JZ .10:
JNSt [ESI+SSS.Status],sssExtern,.10:
MOV ECX,[ESI+SSS.SegmPtr]
MOV [EDI+EXP.Seg],ECX ; Replace external pseudosegment in PGM.EntryExp with code segment.
.10:BufferRetrieve [%StubFileBuf] ; ECX is now aligned DOS stub size.
MOV [ESI+PFMZ_DOS_HEADER.e_lfanew],ECX ; Update the pointer to PE signature.
MOV [%FileAddr],ECX
; Dword PE signature may be skipped for now.
; Initialize COFF file header.
LEA EDI,[%CoffFileHeader]
Invoke PfcoffFileHeader::,EDI,EBX
MOVZXW ECX,[EDI+PFCOFF_FILE_HEADER.SizeOfOptionalHeader]
; Initialize PE optional header.
LEA EDI,[%PeOptionalHeader]
Invoke PfpeOptionalHeader,EDI,EBX
ADD ECX,4+ SIZE# PFCOFF_FILE_HEADER
ADDD [%FileAddr],ECX ; Now it is FA of the 1st section header.
LEA EAX,[%IdataMap]
Invoke PfpeImportFixup,EBX,EDI,EAX
Invoke PfpeExportFixup,EBX,[%ExportSection],[%ENToffset],[%FWDoffset]
; Create symbol record ".file".
LEA EAX,[Ea::+EA.SrcFile]
Invoke PfcoffSymFile::, EAX, [%SymbolTableBuf]
; First segments pass .30: .. .33: updates PFCOFF_FILE_HEADER.NrOfSections,
; stores empty IMAGE_SECTION_HEADER object to [%SectionHeaderBuf] and
; creates segment's symbol record + one auxilliary record to [%SymbolTableBuf]
BufferRetrieve [EBX+PGM.SssPtrBuf] ; Array of pointers to segments, sorted order.
SHR ECX,2 ; Number of pointers.
JZ .37:
LEA EDX,[%CoffFileHeader]
.30:LODSD
JNSt [EAX+SSS.Status],sssSegment,.33: ; Skip if EAX is not a segment ("section" in MS terminology}.
Invoke PfcoffSegmCreate::, EAX, [%Pgm],[%SectionHeaderBuf],EDX ; Create one section header.
Invoke PfcoffSymSegment::,EAX,[%SymbolTableBuf],[%StringTableBuf] ; Create symbol of the section EAX.
.33:LOOP .30:
.37:BufferRetrieve [%SectionHeaderBuf]
MOV [%SectionHeaderPtr],ESI
ADD [%FileAddr],ECX ; Now it is FA of raw data, i.e. .SizeOfHeaders in optional header.
MOV EAX,[%FileAddr]
LEA EDI,[%PeOptionalHeader]
MOV [EDI+PFPE_OPTIONAL_HEADER.SizeOfHeaders],EAX
Invoke PfpeBaserelocFixup,EBX,[%SectionHeaderBuf]
; Symbol list pass at .40: creates PFCOFF_SYMBOL_TABLE records.
ListGetFirst [EBX+PGM.SymList]
JZ .43:
.40:Invoke PfcoffSymSymbol::,EAX,[%SymbolTableBuf],[%StringTableBuf] ; Create symbol EAX.
ListGetNext EAX
JNZ .40:
.43:; Second segments pass .43: .. .50: copies data from segments to COFF buffers.
BufferRetrieve [EBX+PGM.SssPtrBuf]
SHR ECX,2
JZ .55:
.47:LODSD
MOV EDX,EAX ; ^SSS.
JNSt [EDX+SSS.Status],sssSegment,.50: ; If not a segment.
Invoke PfcoffSegmRawData::,EDX,EBX,[%FileAddr],[%SectionHeaderPtr],[%RawBuf],[%StringTableBuf]
MOV [%FileAddr],EAX
ADDD [%SectionHeaderPtr],SIZE#PFCOFF_SECTION_HEADER
.50:LOOP .47: ; The next segment.
.55:; Update file header.
JNSt [Ea::+EA.Eaopt.Status],eaoptDEBUG,.60: ; Skip symbol table if not debug version.
MOV EAX,[%FileAddr] ; Raw emitted data and relocations has been just buffered.
LEA EDI,[%CoffFileHeader]
MOV [EDI+PFCOFF_FILE_HEADER.PointerToSymbolTable],EAX
; Count the number of symbols.
BufferRetrieve [%SymbolTableBuf]
MOV EAX,ECX
SUB EDX,EDX
MOV ECX,SIZE#PFCOFF_SYMBOL
DIV ECX
MOV [EDI+PFCOFF_FILE_HEADER.NumberOfSymbols],EAX
.60: ; Third segment pass .65: .. .77: to update optional header.
LEA EDI,[%PeOptionalHeader]
BufferRetrieve [EBX+PGM.SssPtrBuf]
SHR ECX,2
JZ .80:
.65:LODSD
PUSH ECX,ESI,EDI
MOV ESI,EAX ; ^SSS.
JNSt [ESI+SSS.Status],sssSegment,.77: ; Skip if not a segment.
MOV EAX,[ESI+SSS.TopLow]
MOV EDX,[ESI+SSS.TopHigh]
CMP EDX,[%MaxTopHigh]
JB .73:
JA .70:
CMP EAX,[%MaxTopLow]
JBE .73:
.70: MOV [%MaxTopLow],EAX
MOV [%MaxTopHigh],EDX
.73: SUB EAX,[ESI+SSS.BottomLow]
SBB EDX,[ESI+SSS.BottomHigh] ; EDX:EAX is now unaligned section size.
Invoke ExpAlign::,EAX,[EBX+PGM.Pgmopt.SectionAlign],0
ADD ECX,EAX
ADC EDX,0
; EDX:ECX is now aligned section size.
TEST EDX
Msg cc=NZ,'8525',ESI ; Size of segment [!1S] exceeded 4 GB.
MOV EDX,[ESI+SSS.BottomLow] ; EDX is now section VA.
SUB EDX,[EBX+PGM.Pgmopt.ImageBaseLow] ; Convert VA to RVA (Borland linkers don't do this).
MOV EAX,[ESI+SSS.Purpose]
RstSt EAX,sssPurposeLITERAL+sssPurposeDRECTVE ; Get rid of pseudopurposes.
Dispatch EAX,sssPurposeCODE,sssPurposeDATA,sssPurposeBSS
TEST EAX,sssPurposeOptionalMask
JZ .77: ; Ignore other purposes.
; RvaAndSizes of DataDirectories in optional header will be specified here.
JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.74:
LEA EDI,[EDI+PFPE_OPTIONAL_HEADER.DataDirectory]
JMP .75:
.74: LEA EDI,[EDI+PFPE_OPTIONAL_HEADER64.DataDirectory]
.75: MOV ECX,[ESI+SSS.TopLow]
SUB ECX,[ESI+SSS.BottomLow]
; EDI is now pointer to 0-th member of PFPE_DATA_DIRECTORY in optional header (32 or 64bit).
; EAX is special PE purpose.
; EDX is RVA of the segment ESI.
; ECX is unaligned section data size.
JNSt EAX,sssPurposeEXPORT,.NotEXPORT:
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_EXPORT+PFPE_DATA_DIRECTORY.VirtualAddress],EDX
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_EXPORT+PFPE_DATA_DIRECTORY.Size],ECX
.NotEXPORT: ; Skip directory entry IMPORT and IAT, which were set in PfpeImportFixup.
JNSt EAX,sssPurposeRESOURCE,.NotRESOURCE:
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_RESOURCE+PFPE_DATA_DIRECTORY.VirtualAddress],EDX
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_RESOURCE+PFPE_DATA_DIRECTORY.Size],ECX
.NotRESOURCE:
JNSt EAX,sssPurposeEXCEPTION,.NotEXCEPTION:
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_EXCEPTION+PFPE_DATA_DIRECTORY.VirtualAddress],EDX
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_EXCEPTION+PFPE_DATA_DIRECTORY.Size],ECX
.NotEXCEPTION:
JNSt EAX,sssPurposeSECURITY,.NotSECURITY:
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_SECURITY+PFPE_DATA_DIRECTORY.VirtualAddress],EDX
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_SECURITY+PFPE_DATA_DIRECTORY.Size],ECX
.NotSECURITY:
JNSt EAX,sssPurposeBASERELOC,.NotBASERELOC:
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_BASERELOC+PFPE_DATA_DIRECTORY.VirtualAddress],EDX
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_BASERELOC+PFPE_DATA_DIRECTORY.Size],ECX
.NotBASERELOC:
JNSt EAX,sssPurposeDEBUG,.NotDEBUG:
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_DEBUG+PFPE_DATA_DIRECTORY.VirtualAddress],EDX
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_DEBUG+PFPE_DATA_DIRECTORY.Size],ECX
.NotDEBUG:
JNSt EAX,sssPurposeCOPYRIGHT,.NotCOPYRIGHT:
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_COPYRIGHT+PFPE_DATA_DIRECTORY.VirtualAddress],EDX
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_COPYRIGHT+PFPE_DATA_DIRECTORY.Size],ECX
.NotCOPYRIGHT:
JNSt EAX,sssPurposeGLOBALPTR,.NotGLOBALPTR:
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_GLOBALPTR+PFPE_DATA_DIRECTORY.VirtualAddress],EDX
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_GLOBALPTR+PFPE_DATA_DIRECTORY.Size],ECX
.NotGLOBALPTR:
JNSt EAX,sssPurposeTLS,.NotTLS:
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_TLS+PFPE_DATA_DIRECTORY.VirtualAddress],EDX
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_TLS+PFPE_DATA_DIRECTORY.Size],ECX
.NotTLS:
JNSt EAX,sssPurposeLOAD_CONFIG,.NotLOAD_CONFIG:
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_LOAD_CONFIG+PFPE_DATA_DIRECTORY.VirtualAddress],EDX
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_LOAD_CONFIG+PFPE_DATA_DIRECTORY.Size],ECX
.NotLOAD_CONFIG:
JNSt EAX,sssPurposeBOUND_IMPORT,.NotBOUND_IMPORT:
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_BOUND_IMPORT+PFPE_DATA_DIRECTORY.VirtualAddress],EDX
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_BOUND_IMPORT+PFPE_DATA_DIRECTORY.Size],ECX
.NotBOUND_IMPORT:
JNSt EAX,sssPurposeDELAY_IMPORT,.NotDELAY_IMPORT:
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_DELAY_IMPORT+PFPE_DATA_DIRECTORY.VirtualAddress],EDX
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_DELAY_IMPORT+PFPE_DATA_DIRECTORY.Size],ECX
.NotDELAY_IMPORT:
JNSt EAX,sssPurposeCLR,.NotCLR:
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_CLR+PFPE_DATA_DIRECTORY.VirtualAddress],EDX
MOV [EDI+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_CLR+PFPE_DATA_DIRECTORY.Size],ECX
.NotCLR:
JMP .77:
; EDI is pointer to PFPE_OPTIONAL_HEADER when updating ordinary CODE,DATA or BSS section.
; EAX is ordinary PE purpose (CODE,DATA or BSS).
; EDX is RVA of the segment ESI.
; ECX is section-aligned section size.
.sssPurposeCODE:
ADD [EDI+PFPE_OPTIONAL_HEADER.SizeOfCode],ECX
CMPD [EDI+PFPE_OPTIONAL_HEADER.BaseOfCode],0
JNZ .77: ; If the BaseOfCode was already specified by another section.
MOV [EDI+PFPE_OPTIONAL_HEADER.BaseOfCode],EDX
JMP .77:
.sssPurposeDATA:
ADD [EDI+PFPE_OPTIONAL_HEADER.SizeOfInitializedData],ECX
JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.77: ; 64bit version doesn't have BaseOfData.
CMPD [EDI+PFPE_OPTIONAL_HEADER.BaseOfData],0
JNZ .77: ; If the BaseOfData was already specified by another section.
MOV [EDI+PFPE_OPTIONAL_HEADER.BaseOfData],EDX
JMP .77:
.sssPurposeBSS:
ADD [EDI+PFPE_OPTIONAL_HEADER.SizeOfUninitializedData],ECX
.77:POP EDI,ESI,ECX
DEC ECX
JNZ .65: ; The next segment.
.80:; Update image size.
MOV EAX,[%MaxTopLow]
MOV EDX,[%MaxTopHigh]
SUB EAX,[EBX+PGM.Pgmopt.ImageBaseLow]
SBB EDX,[EBX+PGM.Pgmopt.ImageBaseHigh]
JNZ .85:
Invoke ExpAlign::,EAX,[EBX+PGM.Pgmopt.SectionAlign],0
ADD EAX,ECX
MOV [EDI+PFPE_OPTIONAL_HEADER.SizeOfImage],EAX
.85:; Flush all COFF components to the output stream.
BufferRetrieve [%StubFileBuf]
StreamStore [%OutputStream],ESI,ECX ; DOS stub, size QWORD aligned.
StreamStoreDword [%OutputStream],'PE' ; Signature.
LEA EDI,[%CoffFileHeader]
StreamStore [%OutputStream],EDI,SIZE#PFCOFF_FILE_HEADER ; COFF file header.
LEA EDX,[%PeOptionalHeader]
MOV ECX,SIZE#PFPE_OPTIONAL_HEADER64
JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.87:
MOV ECX,SIZE#PFPE_OPTIONAL_HEADER
.87:StreamStore [%OutputStream],EDX,ECX ; PE Optional header.
BufferRetrieve [%SectionHeaderBuf]
StreamStore [%OutputStream],ESI,ECX ; Section headers.
BufferRetrieve [%RawBuf]
StreamStore [%OutputStream],ESI,ECX ; Section data + relocations of all segments.
JNSt [Ea::+EA.Eaopt.Status],eaoptDEBUG,.90: ; Skip symbol table if not debug version.
BufferRetrieve [%SymbolTableBuf]
StreamStore [%OutputStream],ESI,ECX ; Symbol table.
BufferRetrieve [%StringTableBuf]
MOV [ESI],ECX ; Update StringTable size.
StreamStore [%OutputStream],ESI,ECX ; String table.
.90:Invoke EaBufferRelease::,[%RawBuf]
Invoke EaBufferRelease::,[%SymbolTableBuf]
Invoke EaBufferRelease::,[%StringTableBuf]
Invoke EaBufferRelease::,[%SectionHeaderBuf]
Invoke EaBufferRelease::,[%StubFileBuf]
EndProcedure PfpeCompile
PfpeLoadPgm Procedure BasePgm, ObjBegin, ObjSize, FileNamePtr Msg '8534',Dict_FormatPE::,[%FileNamePtr] ; Format !1S of file "!2$" is not linkable. EndProcedure PfpeLoadPgm
[.idata]
and PURPOSE=IMPORT+IAT
in the main Program.
[.idata]
section.Type of pointers in ILT and IAT (DWORD|QWORD) is determined by the program width (32|64).
When the section is created, its RVA is not known ( PgmLinkImage wasn't invoked yet) and pointers in ILT, IAT, IDT are related to RVA=0. Hence PfpeImportFixup must be invoked later to fixup the RVAs.
Each import creates a new public symbol with offset pointing to proxy jumps in SPT.
Offsets returned in IdataMapPtr can be used to calculate IMPORT and IAT data directory entries in PE optional header.
Section [.idata]
will not be created when no symbol with symImport && symReferenced
is found in program.
[.idata]
is created and appended on Program.SssList.
PfpeImportCreate Procedure Program, IdataMapPtr SssPtr LocalVar ; Pointer to SSS[.idata]
. SymImpBuf LocalVar ; BUFFER for pointers to imported symbols. SymPubBuf LocalVar ; Buffer for pointers to new created public symbols. DllPtrBuf LocalVar ; Buffer with Ptr+Size of DLL names used in imports. ILTbuffer LocalVar ; Buffer with thunks (dword or qword RVA pointers into ONT). IDTbuffer LocalVar ; Buffer with PFPE_IMPORT_DESCRIPTOR records. ONTbuffer LocalVar ; Buffer with 16bit ordinal + ASCIIZ symbol name strings. DNTbuffer LocalVar ; Buffer with ASCCIZ DLL name strings. SPTbuffer LocalVar ; Buffer with JMPN instructions to offsets from IAT. IDTmember LocalVar Size=SIZE# PFPE_IMPORT_DESCRIPTOR ; Temporary record of IDT. Invoke EaBufferReserve::,PfpeImportCreate MOV [%SymImpBuf],EAX Invoke EaBufferReserve::,PfpeImportCreate MOV [%SymPubBuf],EAX Clear [%IdataMapPtr],Size=SIZE#PFPE_IDATA_MAP MOV EBX,[%Program] ; Collect import symbols actually used in this program into %SymImpBuf. SUB ECX,ECX ListGetFirst [EBX+PGM.SymList] JZ .25: ; Loop .10: .. .20: collecting referenced imports. .10:JNSt [EAX+SYM.Status],symImport,.20: JNSt [EAX+SYM.Status],symUsed|symReferenced,.20: BufferStoreDword [%SymImpBuf],EAX CMP [EAX+SYM.DllNameSize],ECX JNE .20: ; If the DLL is already specified. MOVD [EAX+SYM.DllNamePtr],=B"%EaDefaultDllName" MOVD [EAX+SYM.DllNameSize],%EaDefaultDllNameSize .20:ListGetNext EAX JNZ .10: .25:BufferRetrieve [%SymImpBuf] SHR ECX,2 ; Number of imports. JZ .90: ; If no import is used in this program, do not create section [.idata]. ; Prepare to construct import data. Invoke EaBufferReserve::,PfpeImportCreate MOV [%DllPtrBuf],EAX Invoke EaBufferReserve::,PfpeImportCreate MOV [%ILTbuffer],EAX Invoke EaBufferReserve::,PfpeImportCreate MOV [%IDTbuffer],EAX Invoke EaBufferReserve::,PfpeImportCreate MOV [%ONTbuffer],EAX Invoke EaBufferReserve::,PfpeImportCreate MOV [%DNTbuffer],EAX Invoke EaBufferReserve::,PfpeImportCreate MOV [%SPTbuffer],EAX ; Collect used dynamic libraries into DllPtrBuf in a loop .31: .. .39:. ; ESI points to an array of ECX pointers to import symbol. .31:LODSD ; EAX is now pointer to referenced import symbol. MOV EDI,[EAX+SYM.DllNamePtr] MOV EDX,[EAX+SYM.DllNameSize] ; Check if DLL EDI,EDX already is on DllPtrBuf. PUSH ECX,ESI BufferRetrieve [%DllPtrBuf] JECXZ .35: ; If empty, go store EDI,EDX. .32: Compare EDI,EDX,[ESI+0],[ESI+4] JE .39: ; Skip when DLL EDI,EDX was already stored. ADD ESI,8 SUB ECX,8 JNZ .32: ; The next DLL. .35: BufferStoreDword [%DllPtrBuf],EDI BufferStoreDword [%DllPtrBuf],EDX .39:POP ESI,ECX DEC ECX JNZ .31: ; Collect DllName from the next used import. ; Create a new section [idata]. MOV ECX,pgmoptWidthMask AND ECX,[EBX+PGM.Pgmopt.Status] OR ECX,sssSegment+sssNotBSS+sssPrivate+sssUsed Invoke SssCreate::,[EBX+PGM.CurrentStm],0,=B".idata",6, \ ECX,sssPurposeIMPORT+sssPurposeIAT,8 MsgUnexpected cc=C MOV [%SssPtr],EAX ; Initialize static fields in %IDTmember, based on DLL names collected in %DllPtrBuf. LEA EDI,[%IDTmember] XOR EAX,EAX MOV [EDI+PFPE_IMPORT_DESCRIPTOR.TimeDateStamp],EAX ; Always 0. DEC EAX MOV [EDI+PFPE_IMPORT_DESCRIPTOR.ForwarderChain],EAX ; Always -1. ; Create tables for each DLL which is now in %DllPtrBuf. BufferRetrieve [%DllPtrBuf] TEST ECX JZ .80: ; This should never happen. ; Outer loop .41: .. .59: through all dynamic libraries. .41:PUSH ECX,ESI ; ESI points to QWORD (Ptr,Size) of DLL name. LEA EDI,[%IDTmember] PUSH ESI BufferRetrieve [%DNTbuffer] MOV [EDI+PFPE_IMPORT_DESCRIPTOR.Name],ECX ; Future offset of DllName in DNT. BufferRetrieve [%ILTbuffer] MOV [EDI+PFPE_IMPORT_DESCRIPTOR.OriginalFirstThunk],ECX MOV [EDI+PFPE_IMPORT_DESCRIPTOR.FirstThunk],ECX BufferStore [%IDTbuffer],EDI,SIZE# PFPE_IMPORT_DESCRIPTOR POP ESI MOV EDI,[ESI+0] MOV EDX,[ESI+4] BufferStore [%DNTbuffer],EDI,EDX ; DLL name. BufferStoreByte [%DNTbuffer],0 ; ASCIIZ string terminator. ; Handle each symbol imported from current DLL named EDI,EDX. BufferRetrieve [%SymImpBuf] SHR ECX,2 .42: LODSD ; Inner loop .42: .. .58: through all symbols from one DLL (EDI,EDX). PUSH ECX,EDX,ESI,EDI Compare [EAX+SYM.DllNamePtr],[EAX+SYM.DllNameSize],EDI,EDX JNE .58: ; Skip symbol EAX when it is not from the current DLL named EDI,EDX. ; Create record in ONT (hint-name table). BufferRetrieve [%ONTbuffer] ; ECX is now offset of record within ONT. TEST CL,1 JZ .43: BufferStoreByte [%ONTbuffer],0 ; Word alignment of %ONTbuffer. INC ECX .43: MOV EDX,[EAX+SYM.OrdinalNr] BufferStoreWord [%ONTbuffer],EDX ; 16bit hint. BufferStore [%ONTbuffer],[EAX+SYM.NamePtr],[EAX+SYM.NameSize] BufferStoreByte [%ONTbuffer],0 ; ASCIIZ string terminator. ; Store dword or qword thunk to ILT (Import Lookup Table). JSt [EAX+SYM.Status],symImportedByOrd,.45: ; Import by name pointed to by ECX. PUSH ECX BufferRetrieve [%ILTbuffer] MOV EDI,ECX ; Store pointer to thunk to EDI. POP ECX BufferStoreDword [%ILTbuffer],ECX ; Relative offset in ONT. JNSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.50: BufferStoreDword [%ILTbuffer],0 ; PE64 uses qword thunks. JMP .50: .45: ; Import by ordinal EDX. MSbit must be set in thunk. JNSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.46: BufferStoreDword [%ONTbuffer],EDX ; 64bit mode. BufferStoreDword [%ONTbuffer],0x8000_0000 ; Signalize import by ordinal with MSbit. JMP .50: .46: OR EDX,0x8000_0000 BufferStoreDword [%ONTbuffer],EDX ; 32bit mode. .50: ; Create public symbol as a copy of the imported symbol. It represents a proxy JMPN. ListStore [EBX+PGM.SymList],EAX RstSt [EAX+SYM.Status],symScopeMask ; EAX is now the copy of EXTERN symbol. SetSt [EAX+SYM.Status],symPublic MOV EDX,[%SssPtr] MOV [EAX+SYM.Section],EDX BufferStoreDword [%SymPubBuf],EAX ; Remember the just created public symbols in SymPubBuf. BufferRetrieve [%SPTbuffer] ; ECX is now offset of proxy jump, relative to SPT. MOV [EAX+SYM.OffsetLow],ECX ; After relocation this will be the pointer to proxy JMPN. ; Store proxy jump instruction to SPT. ; Target of JMPN is specified by EDI (not relocated yet). BufferStore [%SPTbuffer],=I"JMPN [0],ADDR=ABS,SCALE=VERBATIM",7 ; Assembled asFF2425[00000000]
in both 32bit and 64bit mode. BufferRetrieve [%SPTbuffer] LEA ECX,[ESI+ECX-4] ; ECX is now pointer to relocable DWORD in SPT. ; EDI is stored index to DWORD|QWORD thunk in ILT (0,4,8,,, or 0,8,16,,, in 64bit mode). MOV [ECX],EDI ; Rewrite the jump target in the last 4 bytes with ILT index. ; Create relocation of target IAT in the body of proxy jump instruction. MOV EDX,[%SssPtr] ; ^SSS [idata]. BufferNew [EDX+SSS.RelocBuffer],SIZE# RELOC Clear EAX,Size=SIZE# RELOC SetSt [EAX+RELOC.Status],relocAbsVA+relocWidth32 SUB ECX,ESI ; Make the pointer to relocated DWORD relative to the start of SPT. MOV [EAX+RELOC.OrgLow],ECX ; ECX is now 3,10,17,,, MOV [EAX+RELOC.Section],EDX MOV [EAX+RELOC.Target],EDX .58: POP EDI,ESI,EDX,ECX LOOP .42: ; The next symbol. ; Terminate ILT of this DLL with NULL thunk. BufferStoreDword [%ILTbuffer],0 JNSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.59: BufferStoreDword [%ILTbuffer],0 .59:POP ESI,ECX ADD ESI,8 SUB ECX,8 JNZ .41: ; The next DLL. LEA EDI,[%IDTmember] MOV ECX,SIZE# PFPE_IMPORT_DESCRIPTOR Clear EDI,Size=ECX BufferStore [%IDTbuffer],EDI,ECX ; Use NULL directory entry to terminate IDT. Invoke EaBufferAlign::,[%IDTbuffer],8 ; The following ILT should be qword aligned. MOV EAX,[%SssPtr] ; Populate [.idata] section EAX.EmitBuffer with its tables. MOV EDI,[%IdataMapPtr] MOV [EDI+PFPE_IDATA_MAP.IdataPtr],EAX ; Return pointer to just created section [.idata]. SUB EDX,EDX ; EDX keeps offsets of the tables relative to the start of section. MOV [EDI+PFPE_IDATA_MAP.OffsetIAT],EDX ; Section starts with IAT at offset 0. BufferRetrieve [%ILTbuffer] ; IAT is identical with ILT at link time. ADD EDX,ECX BufferStore [EAX+SSS.EmitBuffer],ESI,ECX ; Store IAT. MOV [EDI+PFPE_IDATA_MAP.OffsetIDT],EDX BufferRetrieve [%IDTbuffer] ADD EDX,ECX BufferStore [EAX+SSS.EmitBuffer],ESI,ECX ; Store IDT. MOV [EDI+PFPE_IDATA_MAP.OffsetILT],EDX BufferRetrieve [%ILTbuffer] ADD EDX,ECX BufferStore [EAX+SSS.EmitBuffer],ESI,ECX ; Store ILT. MOV [EDI+PFPE_IDATA_MAP.OffsetONT],EDX BufferRetrieve [%ONTbuffer] ADD EDX,ECX BufferStore [EAX+SSS.EmitBuffer],ESI,ECX ; Store ONT. MOV [EDI+PFPE_IDATA_MAP.OffsetDNT],EDX BufferRetrieve [%DNTbuffer] ADD EDX,ECX BufferStore [EAX+SSS.EmitBuffer],ESI,ECX ; Store DNT. MOV [EDI+PFPE_IDATA_MAP.OffsetSPT],EDX BufferRetrieve [%SPTbuffer] ADD EDX,ECX BufferStore [EAX+SSS.EmitBuffer],ESI,ECX ; Store SPT. MOV [EDI+PFPE_IDATA_MAP.OffsetEND],EDX ; EDX is now the raw size of [.idata] contents. MOV [EAX+SSS.TopLow],EDX ; Fixup relocations of dwords in the body of proxy jumps. MOV EDX,[EDI+PFPE_IDATA_MAP.IdataPtr] ; ^SSS[.idata]
. BufferRetrieve [EDX+SSS.RelocBuffer] JECXZ .70: MOV EAX,[EDI+PFPE_IDATA_MAP.OffsetSPT] SUB EDX,EDX ; EDX:EAX is now the fixup delta. .62:ADD [ESI+RELOC.OrgLow],EAX ADC [ESI+RELOC.OrgHigh],EDX ADD ESI,SIZE# RELOC SUB ECX,SIZE# RELOC JNZ .62: ; The next relocation. .70:; Fixup offset of created public symbols by SPT offset. MOV EDX,[EDI+PFPE_IDATA_MAP.OffsetSPT] SUB EDI,EDI ; Fixup delta is EDI:EDX. BufferRetrieve [%SymPubBuf] SHR ECX,2 JZ .80: .71:LODSD ADD [EAX+SYM.OffsetLow],EDX ADC [EAX+SYM.OffsetHigh],EDI .79:LOOP .71: .80:Invoke EaBufferRelease::,[%SPTbuffer] Invoke EaBufferRelease::,[%DNTbuffer] Invoke EaBufferRelease::,[%ONTbuffer] Invoke EaBufferRelease::,[%IDTbuffer] Invoke EaBufferRelease::,[%ILTbuffer] Invoke EaBufferRelease::,[%DllPtrBuf] .90:Invoke EaBufferRelease::,[%SymPubBuf] Invoke EaBufferRelease::,[%SymImpBuf] EndProcedure PfpeImportCreate
[.idata]
. It is invoked when PE image was linked by
PgmLinkImage and section bottoms fixed.
PfpeImportFixup Procedure Program, OptionalHeaderPtr, IdataMapPtr IdataRVA LocalVar ; RVA of[.idata]
section. IdataPtr LocalVar ; Pointer to the emitted contents of[.idata]
section. MOV EBX,[%Program] MOV EDX,[%IdataMapPtr] MOV ESI,[EDX+PFPE_IDATA_MAP.IdataPtr] TEST ESI JZ .90: ; If section [.idata] wasn't created (no imports in image). MOV EAX,[ESI+SSS.BottomLow] ; VA of [.idata] in memory at run time. SUB EAX,[EBX+PGM.Pgmopt.ImageBaseLow] ; Convert VA to RVA of IAT. MOV [%IdataRVA],EAX ; Update data directory entries IAT and IMPORT in optional header. MOV EDI,[%OptionalHeaderPtr] MOV ECX,[EDX+PFPE_IDATA_MAP.OffsetIDT] ; ECX is now the size of IAT. JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.10: ; 32bit program. MOV [EDI+PFPE_OPTIONAL_HEADER.DataDirectory+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_IAT+PFPE_DATA_DIRECTORY.VirtualAddress],EAX MOV [EDI+PFPE_OPTIONAL_HEADER.DataDirectory+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_IAT+PFPE_DATA_DIRECTORY.Size],ECX ADD EAX,ECX ; EAX is now RVA of IDT. MOV ECX,[EDX+PFPE_IDATA_MAP.OffsetEND] SUB ECX,[EDX+PFPE_IDATA_MAP.OffsetIDT] ; ECX is now size of IDT+ILT+ONT+DNT+SPT. MOV [EDI+PFPE_OPTIONAL_HEADER.DataDirectory+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_IMPORT+PFPE_DATA_DIRECTORY.VirtualAddress],EAX MOV [EDI+PFPE_OPTIONAL_HEADER.DataDirectory+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_IMPORT+PFPE_DATA_DIRECTORY.Size],ECX JMP .20: .10:; 64bit program. MOV [EDI+PFPE_OPTIONAL_HEADER64.DataDirectory+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_IAT+PFPE_DATA_DIRECTORY.VirtualAddress],EAX MOV [EDI+PFPE_OPTIONAL_HEADER64.DataDirectory+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_IAT+PFPE_DATA_DIRECTORY.Size],ECX ADD EAX,ECX ; EAX is now RVA of IDT. MOV ECX,[EDX+PFPE_IDATA_MAP.OffsetEND] SUB ECX,[EDX+PFPE_IDATA_MAP.OffsetIDT] ; ECX is now size of IDT+ILT+ONT+DNT+SPT. MOV [EDI+PFPE_OPTIONAL_HEADER64.DataDirectory+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_IMPORT+PFPE_DATA_DIRECTORY.VirtualAddress],EAX MOV [EDI+PFPE_OPTIONAL_HEADER64.DataDirectory+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_IMPORT+PFPE_DATA_DIRECTORY.Size],ECX .20:BufferRetrieve [ESI+SSS.EmitBuffer] ; ESI,ECX is now contents of IAT+IDT+ILT+ONT+DNT+SPT. MOV [%IdataPtr],ESI ; Fixup Import Address Table. MOV ECX,[EDX+PFPE_IDATA_MAP.OffsetIDT] ADD ECX,ESI ; ESI..ECX is now contents of IAT. MOV EDI,[EDX+PFPE_IDATA_MAP.OffsetONT] ADD EDI,[%IdataRVA] ; EDI is now delta fixup of dwords/qwords in IAT. JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.40: ; 32bit program. MOV EAX,[ESI+0] TEST EAX JS .28: ; Do not fixup when imported by ordinal (MSbit is set). ADD [ESI],EDI ; Do not check thunk at the 1st position in IAT on NULL as it legally refers to 0-offset in ONT. .28:LODSD ; Skip the first thunk. .30:CMP ESI,ECX JNB .60: LODSD TEST EAX JNG .30: ; Skip fixup when EAX=0 or when imported by ordinal (MSbit is set). ADD [ESI-4],EDI ; Perform fixup. JMP .30: .40:; 64bit program. MOV EAX,[ESI+0] MOV EDX,[ESI+4] TEST EDX JS .48: ; Do not fixup when imported by ordinal (MSbit is set). ADD [ESI+0],EDI ; Do not check thunk at the 1st position in IAT on NULL ADCD [ESI+4],0 ; as it legally refers to 0-offset in ONT. .48:ADD ESI,8 .50:CMP ESI,ECX JNB .60: LODSD XCHG EAX,EDX LODSD TEST EAX JS .50: ; Skip fixup when imported by ordinal (MSbit is set). OR EAX,EDX JZ .50: ; Skip fixup when NULL. ADD [ESI-8],EDI ADCD [ESI-4],0 JMP .50: .60: ; Copy fixed IAT to ILT. MOV EDX,[%IdataMapPtr] MOV ESI,[EDX+PFPE_IDATA_MAP.OffsetIAT] MOV ECX,[EDX+PFPE_IDATA_MAP.OffsetIDT] MOV EDI,[EDX+PFPE_IDATA_MAP.OffsetILT] SUB ECX,ESI SHR ECX,2 ADD ESI,[%IdataPtr] ADD EDI,[%IdataPtr] REP MOVSD ; Fixup Import Descriptor Table. MOV ESI,[%IdataPtr] MOV EDI,[%IdataRVA] ; Fixup delta to IAT. MOV ECX,[EDX+PFPE_IDATA_MAP.OffsetILT] MOV EAX,[EDX+PFPE_IDATA_MAP.OffsetDNT] ADD ECX,EDI ; Fixup delta to ILT. ADD EAX,EDI ; Fixup delta to DNT. ADD ESI,[EDX+PFPE_IDATA_MAP.OffsetIDT] .70:CMPD [ESI+PFPE_IMPORT_DESCRIPTOR.ForwarderChain],-1 JNE .90: ; Empty record signalizes end of IDT. ADD [ESI+PFPE_IMPORT_DESCRIPTOR.OriginalFirstThunk],ECX ADD [ESI+PFPE_IMPORT_DESCRIPTOR.Name],EAX ADD [ESI+PFPE_IMPORT_DESCRIPTOR.FirstThunk],EDI ADD ESI,SIZE# PFPE_IMPORT_DESCRIPTOR JMP .70: ; The next import descriptor (one for each imported DLL). .90:EndProcedure PfpeImportFixup
[.edata]
and PURPOSE=EXPORT
in the main Program.
NTDLL.RtlAllocateHeap
.[.edata]
created and appended on Program.SssList.
PfpeExportCreate Procedure Program
SymExpBuf LocalVar ; BUFFER for pointers to exported symbols.
Ordinal LocalVar ; Ordinal number of the current exported symbol.
EATbuffer LocalVar ; Buffer with RVA of exported symbols.
NPTbuffer LocalVar ; Buffer with RVA of symbol names.
ONTbuffer LocalVar ; Buffer with word ordinal numbers.
DNTbuffer LocalVar ; Buffer with RVA of DLL ASCIIZ names.
ENTbuffer LocalVar ; Buffer with RVA of exported symbol ASCIIZ names.
FWDbuffer LocalVar ; Buffer with RVA of forward-exported Dll.symbol ASCIIZ names.
EDT LocalVar Size=SIZE#PFPE_EXPORT_DIRECTORY
XOR EAX,EAX
MOV [%Ordinal],EAX
MOV [%ReturnEAX],EAX
MOV [%ReturnECX],EAX
MOV [%ReturnEDX],EAX
Invoke EaBufferReserve::,PfpeExportCreate
MOV [%SymExpBuf],EAX
MOV EDX,EAX
; Collect exported symbols actually used in this program into buffer EDX.
MOV EBX,[%Program]
ListGetFirst [EBX+PGM.SymList]
JZ .25: ; Loop .10: .. .20: collecting exports.
.10:JNSt [EAX+SYM.Status],symExport,.20:
BufferStoreDword EDX,EAX
.20:ListGetNext EAX
JNZ .10:
.25:Invoke EaBufferSort::,EDX ; Alphabetically by symbol names.
BufferRetrieve EDX
SHR ECX,2 ; Number of exports.
JZ .90: ; If no export is used in this program, do not create section.
; Prepare to construct export data.
Invoke EaBufferReserve::,PfpeExportCreate
MOV [%EATbuffer],EAX
Invoke EaBufferReserve::,PfpeExportCreate
MOV [%NPTbuffer],EAX
Invoke EaBufferReserve::,PfpeExportCreate
MOV [%ONTbuffer],EAX
Invoke EaBufferReserve::,PfpeExportCreate
MOV [%DNTbuffer],EAX
Invoke EaBufferReserve::,PfpeExportCreate
MOV [%ENTbuffer],EAX
Invoke EaBufferReserve::,PfpeExportCreate
MOV [%FWDbuffer],EAX
LEA EDI,[%EDT]
Clear EDI,Size=SIZE#PFPE_EXPORT_DIRECTORY
MOV EAX,[Ea.Eaopt.TimeStamp::]
MOV [EDI+PFPE_EXPORT_DIRECTORY.TimeDateStamp],EAX
MOV EDX,[EBX+PGM.Pgmopt.MajorImageVersion]
MOV EAX,[EBX+PGM.Pgmopt.MinorImageVersion]
MOV [EDI+PFPE_EXPORT_DIRECTORY.MajorVersion],DX
MOV [EDI+PFPE_EXPORT_DIRECTORY.MinorVersion],AX
INCD [EDI+PFPE_EXPORT_DIRECTORY.Base] ; Always start with ordinal 1.
BufferStore [%DNTbuffer],[EBX+PGM.Pgmopt.OutFilePtr],[EBX+PGM.Pgmopt.OutFileSize] ; Compiled DLL name.
BufferStoreByte [%DNTbuffer],0 ; Zero-terminate the DLL name.
; ESI points to ECX DWORD pointers to alphabetically sorted SYM objects.
.30:LODSD
PUSH ECX,ESI
MOV ESI,EAX ; ESI is now pointer to exported symbol.
MOV EAX,[%Ordinal]
BufferStoreWord [%ONTbuffer],EAX
INC EAX
MOV [%Ordinal],EAX
MOV [EDI+PFPE_EXPORT_DIRECTORY.NumberOfFunctions],EAX
MOV [EDI+PFPE_EXPORT_DIRECTORY.NumberOfNames],EAX
MOV [ESI+SYM.OrdinalNr],EAX
BufferStoreDword [%EATbuffer],ESI ; Temporarily store pointer to exported SYM
; instead of pointer to the function or instead of pointer to forwarded "DllName.SymbolName".
; This will be fixed later in PfpeExportFixup after PgmLinkImage.
JNSt [ESI+SYM.Status],symForwarded,.36:
PUSH ESI ; Forwarded DLL.SymbolName will be stored in FWD.
BufferRetrieve [%FWDbuffer]
POP ESI
MOV [ESI+SYM.NameIndex],ECX ; Temporary store forwarder name offset relative to the bottom of FWD
; instead of RVA. This will be fixed later in PfpeExportFixup after PgmLinkImage.
Invoke SymStoreForwarderName::,ESI,[%FWDbuffer]
.36: PUSH ESI ; Symbol name will be stored in ENT.
BufferRetrieve [%ENTbuffer]
POP ESI
BufferStoreDword [%NPTbuffer],ECX ; Temporarily store name offset relative to the bottom of ENT
; instead of RVA. This will be fixed later in PfpeExportFixup after PgmLinkImage.
BufferStore [%ENTbuffer],[ESI+SYM.NamePtr],[ESI+SYM.NameSize]
BufferStoreByte [%ENTbuffer],0 ; Zero-terminate the name string.
.40:POP ESI,ECX
LOOP .30:
; Create export section [.edata]
.
Invoke SssCreate::,[EBX+PGM.CurrentStm],0,=B'.edata',6, \
sssSegment+sssNotBSS+sssImplicit+sssPrivate,sssPurposeEXPORT,8
JC .80:
MOV [%ReturnEAX],EAX
; Populate [.edata] section EAX.EmitBuffer with its divisions.
MOV EDX,SIZE#PFPE_EXPORT_DIRECTORY
BufferStore [EAX+SSS.EmitBuffer],EDI,EDX
MOV EDI,EAX ; EDI is now the section [.edata].
BufferRetrieve [EDI+SSS.EmitBuffer]
MOV [ESI+PFPE_EXPORT_DIRECTORY.AddressOfFunctions],EDX ; Temporary offset of EAT in section.
BufferRetrieve [%EATbuffer]
BufferStore [EDI+SSS.EmitBuffer],ESI,ECX
BufferRetrieve [EDI+SSS.EmitBuffer]
MOV [ESI+PFPE_EXPORT_DIRECTORY.AddressOfNames],ECX ; Temporary offset of NPT in section.
BufferRetrieve [%NPTbuffer]
BufferStore [EDI+SSS.EmitBuffer],ESI,ECX
BufferRetrieve [EDI+SSS.EmitBuffer]
MOV [ESI+PFPE_EXPORT_DIRECTORY.AddressOfOrdinals],ECX ; Temporary offset of ONT in section.
BufferRetrieve [%ONTbuffer]
BufferStore [EDI+SSS.EmitBuffer],ESI,ECX
BufferRetrieve [EDI+SSS.EmitBuffer]
MOV [ESI+PFPE_EXPORT_DIRECTORY.Name],ECX
BufferRetrieve [%DNTbuffer]
BufferStore [EDI+SSS.EmitBuffer],ESI,ECX
BufferRetrieve [EDI+SSS.EmitBuffer]
MOV [%ReturnECX],ECX
BufferRetrieve [%ENTbuffer]
BufferStore [EDI+SSS.EmitBuffer],ESI,ECX
BufferRetrieve [EDI+SSS.EmitBuffer]
MOV [%ReturnEDX],ECX
BufferRetrieve [%FWDbuffer]
BufferStore [EDI+SSS.EmitBuffer],ESI,ECX
BufferRetrieve [EDI+SSS.EmitBuffer]
MOV [EDI+SSS.TopLow],ECX
.80:Invoke EaBufferRelease::,[%FWDbuffer]
Invoke EaBufferRelease::,[%ENTbuffer]
Invoke EaBufferRelease::,[%DNTbuffer]
Invoke EaBufferRelease::,[%ONTbuffer]
Invoke EaBufferRelease::,[%NPTbuffer]
Invoke EaBufferRelease::,[%EATbuffer]
.90:Invoke EaBufferRelease::,[%SymExpBuf]
EndProcedure PfpeExportCreate
[.edata]
. It is invoked when PE image was linked by
PgmLinkImage and section bottoms fixed.[.edata]
.
[.edata]
emit contents.
[.edata]
emit contents.PfpeExportFixup Procedure Program, edataPtr, ENToffset, FWDoffset edataRVA LocalVar ; RVA of section[.edata]
(low byte). MOV EDI,[%edataPtr] MOV EBX,[%Program] TEST EDI JZ .90: ; Do nothing when [.edata] was not created. MOV EAX,[EDI+SSS.BottomLow] ; VA of[.edata]
. MOV EDX,[EDI+SSS.BottomHigh] SUB EAX,[EBX+PGM.Pgmopt.ImageBaseLow] ; Convert VA to RVA. SBB EDX,[EBX+PGM.Pgmopt.ImageBaseHigh] Msg cc=NZ,'8525',EDI ; Size of segment [!1S] exceeded 4 GB. MOV [%edataRVA],EAX ; EAX is now fixup delta which will update pointers in export dictionary ESI. ; Fixup RVAs in export directory. BufferRetrieve [EDI+SSS.EmitBuffer] ; Get contents of [.edata] to ESI,ECX. MOV EDX,[ESI+PFPE_EXPORT_DIRECTORY.AddressOfNames] ; Offset of NPT within EmitBuffer. MOV EBX,[ESI+PFPE_EXPORT_DIRECTORY.AddressOfFunctions] ; Offset of EAT within EmitBuffer. ADD EDX,ESI ; EDX now points to NPT with temporary offsets to ENT. ADD [ESI+PFPE_EXPORT_DIRECTORY.Name],EAX ADD [ESI+PFPE_EXPORT_DIRECTORY.AddressOfFunctions],EAX ADD [ESI+PFPE_EXPORT_DIRECTORY.AddressOfNames],EAX ADD [ESI+PFPE_EXPORT_DIRECTORY.AddressOfOrdinals],EAX ; Fixup RVAs of function names stored in NPT. ; Each pointer in NPT temporarily contains an offset of ASCIIZ string ; related to the start of NPT. So the 1st dword contains 0. Required delta is RVA of ENT. MOV EAX,[ESI+PFPE_EXPORT_DIRECTORY.AddressOfFunctions] SUB EAX,SIZE#PFPE_EXPORT_DIRECTORY ADD EAX,[%ENToffset] ; Compute fixup delta value. MOV ECX,[ESI+PFPE_EXPORT_DIRECTORY.NumberOfNames] JECXZ .30: .20:ADD [EDX],EAX ADD EDX,4 ; The next name pointer. LOOP .20: .30:; Fixup function addresses in EAT. ; EAT was temporarily populated in PfpeExportCreate with pointers to symbols. ; Now they will be replaced with RVA of the symbol (exported function entry). MOV EDI,[%Program] MOV ECX,[ESI+PFPE_EXPORT_DIRECTORY.NumberOfFunctions] JECXZ .90: ; EBX is temporary offset of EAT within [.edata]. ESI is pointer to [.edata] contents. ADD EBX,ESI ; Pointer to EAT in memory, i.e. table of pointers to exported symbols. .40:PUSH ECX MOV ESI,[EBX] ; ESI now points to the exported symbol. MOV ECX,[ESI+SYM.Section] JSt [ESI+SYM.Status],symForwarded,.50: MOV EAX,[ESI+SYM.OffsetLow] MOV EDX,[ESI+SYM.OffsetHigh] JECXZ .70: ; If a scalar was exported. ADD EAX,[ECX+SSS.BottomLow] ADC EDX,[ECX+SSS.BottomHigh] ; EDX:EAX is now VA of the symbol. SUB EAX,[EDI+PGM.Pgmopt.ImageBaseLow] SBB EDX,[EDI+PGM.Pgmopt.ImageBaseHigh] ; VA is converted to RVA. Msg cc=NZ,'8525',ECX ; Size of segment [!1S] exceeded 4 GB. JMP .70: .50: MOV EAX,[ESI+SYM.NameIndex] ; Temporary offset within FWD table. ADD EAX,[%FWDoffset] ; EAX is now the offset of "DLL.FwdSymbol" in [.edata]. ADD EAX,[%edataRVA] .70: MOV [EBX],EAX ; Update one pointer in EAT. .80: ADD EBX,4 ; Point to the next symbol in EAT. POP ECX LOOP .40: .90:EndProcedure PfpeExportFixup
[.reloc]
with base relocations in the main Program. Size of its emitted contents is only
estimated here into SSS.Top. The actual base relocation blocks will be written
to section [.reloc]
later by PfpeBaserelocFixup
, after bottoms of all sections will have been fixed by PgmLinkImage.PfpeBaserelocCreate Procedure Program RelocCnt LocalVar ; Total number of BASERELOC records in Program. SegmentCnt LocalVar ; Total number of relocable sections in Program. EmitSize LocalVar ; Total emitted size of segments with relocations. ClearLocalVar XOR EAX,EAX MOV EBX,[%Program] ListGetFirst [EBX+PGM.SssList] JZ .90: .10:JNSt [EAX+SSS.Status],sssSegment,.50: BufferRetrieve [EAX+SSS.RelocBuffer] JECXZ .50: ; Skip if this segment does not contribute to BASERELOC. INCD [%SegmentCnt] LEA EDX,[ESI+ECX] ; End of RELOC array. PUSH ESI BufferRetrieve [EAX+SSS.EmitBuffer] POP ESI ADD [%EmitSize],ECX .20:CMP ESI,EDX JNB .50: JNSt [ESI+RELOC.Status],relocAbsVA,.30: INCD [%RelocCnt] ; Absolute relocation will have a BASERELOC record. .30:ADD ESI,SIZE#RELOC JMP .20: .50:ListGetNext EAX ; The next segment. JNZ .10: MOV ECX,[%RelocCnt] SAL ECX,1 ; Each relocation takes 2 bytes. JZ .90: ; [.reloc] section will be created. Estimate its raw size. ; Each PFPE_BASERELOC header takes 8 bytes and it manages 4KB of emitted data. MOV EDI,4K MOV EAX,[%EmitSize] SUB EDX,EDX DIV EDI ; EAX is now the maximal possible number of base relocation blocks. INC EAX ; Round up. ADD EAX,[%SegmentCnt] ; Add one more block per each section change. LEA EDI,[8*EAX] ; Each block takes 8-byte header (SIZE#PFPE_BASERELOC). LEA EDI,[EDI+2*EAX] ; Add word alignment for each header. LEA EDI,[EDI+ECX+32] ; Add a small reserve to be safe. ; EDI is now estimated [.reloc] section size. Invoke SssCreate::,[EBX+PGM.CurrentStm],0,=B".reloc",6, \ sssSegment+sssWidth32+sssNotBSS+sssImplicit+sssPublic,sssPurposeBASERELOC,4 MOV [EAX+SSS.TopLow],EDI ; Now PgmLinkImage knows the sizes of all linked sections. .90:EndProcedure PfpeBaserelocCreate
[reloc]
with base relocations in the main Program.
Program.SssPtrBuf
.PURPOSE=BASERELOC
(if found) is filled with
PFPE_BASERELOC records.
PfpeBaserelocFixup Procedure Program, SectionHeaderBuffer SssReloc LocalVar ; Pointer to SSS [.reloc]. TypeOffsetBuf LocalVar ; Pointer to BUFFER with WORDs of one PFPE_BASERELOC block. SectionRVA LocalVar ; RVA of the section bottom. PageOffset LocalVar ; Offset of currently processed block relative to section bottom. Invoke EaBufferReserve::,PfpeBaserelocFixup MOV [%TypeOffsetBuf],EAX MOV EBX,[%Program] CALL .FlushBlock: ; Initialize TypeOffsetBuf and BlockRVA. .FlushBlock: PROC1 ; Write one PFPE_BASERELOC block to section[.reloc]
. PUSHAD Invoke EaBufferAlign::,[%TypeOffsetBuf],4 ; Pad the block with an empty word if they weren't even. BufferRetrieve [%TypeOffsetBuf] JECXZ .F9: ; If no base relocation appeared in this segment. MOV EDI,[%SssReloc] MOV EAX,[%PageOffset] ADD EAX,[%SectionRVA] BufferStoreDword [EDI+SSS.EmitBuffer],EAX LEA EAX,[ECX+SIZE# PFPE_BASERELOC] BufferStoreDword [EDI+SSS.EmitBuffer],EAX ; BlockSize. BufferStore [EDI+SSS.EmitBuffer],ESI,ECX ; The block is complete now. BufferClear [%TypeOffsetBuf] .F9: XOR ECX,ECX DEC ECX MOV [%PageOffset],ECX ; Initialize PageOffset with nonexisting address. POPAD RET ENDP1 .FlushBlock: ; First segment pass .10: .. .20: searches for [.reloc] section created by PfpeBaserelocCreate. BufferRetrieve [EBX+PGM.SssPtrBuf] SAR ECX,2 JZ .90: ; If no segments are linked. .10:LODSD JSt [EAX+SSS.Purpose],sssPurposeBASERELOC,.20: LOOP .10: JMP .90: ; No BASERELOC section exists in PE program (e.g. pure resource DLL). .20:MOV [%SssReloc],EAX ; [.reloc] section was found. ; Second segment pass .30: .. .80: handles base relocation from all segments. BufferRetrieve [EBX+PGM.SssPtrBuf] SAR ECX,2 .30:LODSD ; Get the next segment. JNSt [EAX+SSS.Status],sssSegment,.80: PUSHAD MOV ECX,[EAX+SSS.BottomLow] ; Linked VA of the segment (section). MOV EDX,[EAX+SSS.BottomHigh] SUB ECX,[EBX+PGM.Pgmopt.ImageBaseLow] SBB EDX,[EBX+PGM.Pgmopt.ImageBaseHigh] Msg cc=NZ,'7922',EAX,ECX ; Fixup increment out of 4GB range at [!1S]:!2Hh. MOV [%SectionRVA],ECX BufferRetrieve [EAX+SSS.RelocBuffer] LEA EDX,[ESI+ECX] ; End of RELOC array. .40: CMP ESI,EDX JNB .70: JNSt [ESI+RELOC.Status],relocAbsVA,.60: ; ESI is (resolved) absolute relocation. MOV ECX,~(4K-1) ; Mask of PFPE_BASERELOC.PageOffset (0xFFFF_F000). AND ECX,[ESI+RELOC.OrgLow] CMP ECX,[%PageOffset] ; Check if the RELOC ESI is in the same block. JE .50: CALL .FlushBlock: ; Flush the previous block and start a new one. MOV [%PageOffset],ECX .50: MOV ECX,4K-1 ; Mask of PFPE_BASERELOC.TypeOffset (0x0000_0FFF). AND ECX,[ESI+RELOC.OrgLow] MOV EAX,pfpeREL_BASED_HIGHLOW JSt [ESI+RELOC.Status],relocWidth32,.55: MOV EAX,pfpeREL_BASED_DIR64 JSt [ESI+RELOC.Status],relocWidth64,.55: MOV EAX,pfpeREL_BASED_LOW JSt [ESI+RELOC.Status],relocWidth16,.55: Msg '7737',[ESI+RELOC.Section],[ESI+RELOC.OrgLow] ; Invalid base relocation at [!1S]:!2H. JMP .60: .55: OR ECX,EAX ; Merge PFPE_BASERELOC.TypeOffset. BufferStoreWord [%TypeOffsetBuf],ECX .60: ADD ESI,SIZE# RELOC JMP .40: ; The next RELOC record. .70: CALL .FlushBlock: POPAD .80:LOOP .30: ; The next segment. MOV EDI,[%SssReloc] ; ^SSS[.reloc]
. ; Base relocation blocks are now stored in [EDI+SSS.RelocBuffer]. MOV EAX,[EDI+SSS.SegmIndex] MOV ESI,SIZE# PFCOFF_SECTION_HEADER DEC EAX ; Segment index is 1-based. MsgUnexpected cc=S MUL ESI BufferRetrieve [%SectionHeaderBuffer] ADD ESI,EAX ; ESI is now pointer to section header[.reloc]
. PUSH ESI BufferRetrieve [EDI+SSS.EmitBuffer] ; The final[.reloc]
contents. POP ESI ; Update section header with the final size ECX. MOV [ESI+PFCOFF_SECTION_HEADER.SizeOfRawData],ECX ; Update SSS virtual size. ADD ECX,[EDI+SSS.BottomLow] MOV [EDI+SSS.TopLow],ECX .90:Invoke EaBufferRelease::,[%TypeOffsetBuf] EndProcedure PfpeBaserelocFixup
PfpeLoadStubFile Procedure StubBuffer, Pgm PathNr LocalVar ; Ordinal number of LINKPATH. StubFile LocalVar Size=SIZE# FILE ; FILE object of the external stub file. ClearLocalVar ; First assume that default stub (built inside euroasm.exe) will be used. MOV ESI,PfcoffStub:: MOV EAX,PfcoffStubEnd:: SUB EAX,ESI BufferStore [%StubBuffer],ESI,EAX ; Check if stub file is explicitly specified. MOV EBX,[%Pgm] MOV EDX,[EBX+PGM.Pgmopt.StubFileSize] MOV ESI,[EBX+PGM.Pgmopt.StubFilePtr] TEST EDX JZ .90: ; If not, we're done. ; Query if Filemask ESI,EDX was specified with path (it contains slash or colon). PfQueryChar '\' JE .50: PfQueryChar '/' JE .50: PfQueryChar ':' JE .50: ; When there is no path in STUBFILE=, try all pathes from LINKPATH=. SUB EAX,EAX MOV [%PathNr],EAX .20:Invoke EaoptGetOnePath::,[Ea::+EA.Eaopt.LinkPathPtr],[Ea::+EA.Eaopt.LinkPathSize],[%PathNr] JC .E7742: ; StubFile=!1$ was no found in LinkPath="!2S". INCD [%PathNr] ; Prepare for the next path. MOV EDX,[EBX+PGM.Pgmopt.StubFileSize] ; One path is in ESI,ECX. LEA EAX,[EDX+ECX+1] CMP EAX,MAX_PATH_SIZE JA .E6953: ; Size of LinkPath "!1_" + size of filename exceeded 256 characters. LEA EDI,[%StubFile+FILE.Name] ; Assign path+stubfile name to %StubFile. REP MOVSB MOV AX,'\/' CMPB [Ea.EuroasmOS::],'W' ; Choose slash or backslash. JE .30: XCHG AL,AH .30:CMP AL,[EDI-1] JE .40: CMP AH,[EDI-1] JE .40: STOSB ; If the path was not terminated with (back)slash , append it. .40:MOV ESI,[EBX+PGM.Pgmopt.StubFilePtr] MOV ECX,EDX REP MOVSB SUB EAX,EAX STOSB ; Zero terminate the filename. LEA EDI,[%StubFile] ; %StubFile is now assigned with path and filemask. LEA EDX,[EDI+FILE.Name] SysOpenFileMap EDI,EDX JC .20: ; If not found, try the next IncludePath. JMP .60: .50:LEA EDI,[%StubFile] SysOpenFileMap EDI,ESI,FileNameSize=EDX ; Path was present in filename ESI, use as is. JC .E7741: ; StubFile=!1$ was no found. .60:MOV ECX,EAX ; Contents of the file is now mapped at ESI,EAX. CMP ECX,SIZE#PFMZ_DOS_HEADER JNA .E7743: ; Invalid format (file is too short). CMPW [ESI],'MZ' JNE .E7743: ; Invalid format. BufferClear [%StubBuffer] BufferStore [%StubBuffer],ESI,ECX SysCloseFile EDI JMP .90: .E6953:Msg '6953',ESI ; Size of LinkPath "!1_" + size of filename exceeded 256 characters. JMP .Error: .E7741:MOV ESI,[EBX+PGM.Pgmopt.StubFilePtr] Msg '7741',ESI ; StubFile=!1$ was no found. JMP .Error: .E7742:MOV ESI,[EBX+PGM.Pgmopt.StubFilePtr] LEA EAX,[Ea::+EA.Eaopt.LinkPathPtr] Msg '7742',ESI,EAX ; StubFile=!1$ was no found in LinkPath="!2S". JMP .Error: .E7743:MOV ESI,[EBX+PGM.Pgmopt.StubFilePtr] Msg '7743',ESI ; Invalid format of StubFile="!1$". SysCloseFile EDI .Error:STC .90:EndProcedure PfpeLoadStubFile
ENDPROGRAM pfpe