This source PFPE generates EuroAssembler output executable file in program format PE. as described in [MS_PECOFF].
EUROASM NOWARN=2101 pfpe PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32 INCLUDEHEAD "euroasm.htm" ; Interface (structures, symbols and macros) of other modules. INCLUDEHEAD \ ; Include headers of another modules used in this module. ea.htm, \ eaopt.htm, \ exp.htm, \ msg.htm, \ pf.htm, \ pfcoff.htm, \ pfmz.htm, \ pgm.htm, \ pgmopt.htm, \ reloc.htm, \ sss.htm, \ stm.htm, \ sym.htm, \ syswin.htm, \ ;;
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_HEADER32 STRUC .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 ; See DLL_CHARACTERSTICS below. .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_HEADER32 ; Size# = 224 = 0E0h.
PFPE_OPTIONAL_HEADER64 STRUC .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 ; See DLL_CHARACTERSTICS below. .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 ; Size# = 240 = 0F0h.
[.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 ; Size# = 32 = 020h.
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 ; Size# = 40 = 028h.
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_HEADER32.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_OPTIONAL_HEADER32.DllCharacteristic. pfpeDLLCHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020 ; ASLR with 64 bit address space. pfpeDLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040 ; The DLL can be relocated at load time. pfpeDLLCHARACTERISTICS_NO_SEH = 0x0400 ; The image does not use structured exception handling (SEH). pfpeDLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000 ; The image is terminal server aware. ; 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. ; PFPE_OPTIONAL_HEADER32.Magic. MOV CX,0x020B ; 64bit execu`table. JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.10: MOV CX,0x010B ; 32bit executable. .10:MOV [EDI+PFPE_OPTIONAL_HEADER32.Magic],CX ; PFPE_OPTIONAL_HEADER32.AddressOfEntryPoint. MOV ECX,[EBX+PGM.Pgmopt.EntrySize] MOV ESI,[EBX+PGM.Pgmopt.EntryPtr] TEST ECX JNZ .12: JSt [EBX+PGM.Pgmopt.Status],pgmoptExportable,.19: ; PROGRAM ENTRY is optional in DLL format. Msg '7710' ; "PROGRAM ENTRY=" is mandatory in this program format. .11:; Find the surrogate ENTRY as the bottom of the first CODE section. This will make PE valid in spite of E7710. Invoke EaBufferReserve::,%^PROC Invoke SssFindByPurpose::,sssSegment,sssPurposeCODE,sssNotBSS,0,EBX,EAX BufferRetrieve EAX Invoke EaBufferRelease::,EAX JECXZ .19: MOV ECX,[ESI] ; ECX is the first CODE section. XOR EAX,EAX XOR EDX,EDX JMP .17: .12:StripColons ESI,ECX ; ESI,ECX is the raw ENTRY= value. Invoke SymFindByName::, symPublic,ESI,ECX,EBX JNC .15: Invoke SymFindByName::, symWeak,ESI,ECX,EBX JNC .15: LEA EAX,[EBX+PGM.Pgmopt.EntryPtr] Msg '7711',EAX ; "PROGRAM ENTRY=!1S" symbol was not found. JMP .11: ; Use the bottom of CODE section instead. .15:MOV ECX,[EAX+SYM.Section] ; Entry symbol EAX was found. MOV EDX,[EAX+SYM.OffsetHigh] MOV EAX,[EAX+SYM.OffsetLow] .17:JECXZ .18: ADD EAX,[ECX+SSS.BottomLow] ADC EDX,[ECX+SSS.BottomHigh] ; EDX:EAX is now VA or entry point. SUB EAX,[EBX+PGM.Pgmopt.ImageBaseLow] ; Convert to RVA. ; SBB EDX,[EBX+PGM.Pgmopt.ImageBaseHigh] ; JZ .18: LEA EAX,[EBX+PGM.Pgmopt.EntryPtr] Msg '7712',EAX ; "PROGRAM ENTRY=!1S" is above 4 GB. Ignored. SUB EAX,EAX .18:MOV [EDI+PFPE_OPTIONAL_HEADER32.AddressOfEntryPoint],EAX .19:; PFPE_OPTIONAL_HEADER32 | PFPE_OPTIONAL_HEADER64 standard members. MOV EAX,[EBX+PGM.Pgmopt.SectionAlign] MOV EDX,[EBX+PGM.Pgmopt.FileAlign] MOV [EDI+PFPE_OPTIONAL_HEADER32.SectionAlignment],EAX MOV [EDI+PFPE_OPTIONAL_HEADER32.FileAlignment],EDX MOV EAX,[EBX+PGM.Pgmopt.MinorOsVersion] MOV EDX,[EBX+PGM.Pgmopt.MajorOsVersion] MOV [EDI+PFPE_OPTIONAL_HEADER32.MinorOperatingSystemVersion],AX MOV [EDI+PFPE_OPTIONAL_HEADER32.MajorOperatingSystemVersion],DX MOV EAX,[EBX+PGM.Pgmopt.MinorImageVersion] MOV EDX,[EBX+PGM.Pgmopt.MajorImageVersion] MOV [EDI+PFPE_OPTIONAL_HEADER32.MinorImageVersion],AX MOV [EDI+PFPE_OPTIONAL_HEADER32.MajorImageVersion],DX MOV EAX,[EBX+PGM.Pgmopt.MinorSubsystemVersion] MOV EDX,[EBX+PGM.Pgmopt.MajorSubsystemVersion] MOV [EDI+PFPE_OPTIONAL_HEADER32.MinorSubsystemVersion],AX MOV [EDI+PFPE_OPTIONAL_HEADER32.MajorSubsystemVersion],DX MOV EAX,[EBX+PGM.Pgmopt.MinorLinkerVersion] MOV EDX,[EBX+PGM.Pgmopt.MajorLinkerVersion] MOV [EDI+PFPE_OPTIONAL_HEADER32.MinorLinkerVersion],AL MOV [EDI+PFPE_OPTIONAL_HEADER32.MajorLinkerVersion],DL MOV ECX,[EBX+PGM.Pgmopt.Win32VersionValue] MOV EAX,[EBX+PGM.Pgmopt.Subsystem] MOV EDX,[EBX+PGM.Pgmopt.DllCharacteristics] MOV [EDI+PFPE_OPTIONAL_HEADER32.Win32VersionValue],ECX MOV [EDI+PFPE_OPTIONAL_HEADER32.Subsystem],AX MOV [EDI+PFPE_OPTIONAL_HEADER32.DllCharacteristics],DX ; PFPE_OPTIONAL_HEADER32 | PFPE_OPTIONAL_HEADER64 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_HEADER32.ImageBase],EAX MOV [EDI+PFPE_OPTIONAL_HEADER32.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_HEADER32.SizeOfHeapCommit],EAX MOV [EDI+PFPE_OPTIONAL_HEADER32.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_HEADER32.SizeOfStackCommit],EAX MOV [EDI+PFPE_OPTIONAL_HEADER32.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
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 ( PgmLink 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: collects referenced import symbols. .10:JNSt [EAX+SYM.Status],symImport,.20: BufferStoreDword [%SymImpBuf],EAX CMP [EAX+SYM.DllNameSize],ECX JNE .20: ; Skip default if the DLL is already specified. MOVD [EAX+SYM.DllNamePtr],=B"%EuroasmDefaultDllName" MOVD [EAX+SYM.DllNameSize],%EuroasmDefaultDllNameSize .20:ListGetNext EAX ; The next symbol. JNZ .10: .25:BufferRetrieve [%SymImpBuf] SHR ECX,2 ; ECX=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 names 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 a referenced import symbol. Invoke SymLowcaseDll::,EAX MOV EDI,[EAX+SYM.DllNamePtr] MOV EDX,[EAX+SYM.DllNameSize] PUSH ECX,ESI ; Check if DLL name EDI,EDX already is on DllPtrBuf. BufferRetrieve [%DllPtrBuf] JECXZ .35: ; If empty, go and 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: ; The next import. ; Create a new section [idata]. MOV ECX,pgmoptWidthMask AND ECX,[EBX+PGM.Pgmopt.Status] OR ECX,sssSegment+sssNotBSS+sssPrivate+sssUsed Invoke SssCreateSe::,[EBX+PGM.CurrentStm],0,=B".idata",6, \ ECX,sssPurposeIMPORT+sssPurposeIAT,8 MsgUnexpected cc=C MOV [%SssPtr],EAX ; EAX=^SSS - the new [.idata] segment. ; Initialize static fields in %IDTmember, based on DLL names collected in %DllPtrBuf. LEA EDI,[%IDTmember] ; One temporary record of IDT. 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: ; 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 offset of thunk to EDI (0,4,8,,, in PE32 or 0,8,16,24,, in PE64). 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 EAX. It represents the proxy jump. MOV EDX,EAX ; Imported symbol. ListStore [EBX+PGM.SymList],EAX ; Save copy of the imported SYM EAX. The new copy is returned in EAX. MOV [EDX+SYM.SymbPtr],EAX ; Let the imported symbol refer to the new public symbol EAX SetSt [EDX+SYM.Status],symResolved ; and mark it as resolved. SetSt [EAX+SYM.Status],symPublic+symImport+symReferenced MOV EDX,[%SssPtr] ; ^SSS [.idata]. MOV [EAX+SYM.Section],EDX BufferStoreDword [%SymPubBuf],EAX ; Collect 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 the relocable DWORD[00000000]
in SPT. ; EDI is stored offset of 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 of JMPN encoding with ILT index. ; Create relocation of target IAT in the body of proxy jump instruction. MOV EDI,EAX ; ^SYM - public symbol in [.idata] segment (proxy jump). BufferNew [EDX+SSS.RelocBuffer],SIZE# RELOC ; New absolute relocation record for JMPN's target. Clear EAX,Size=SIZE# RELOC SetSt [EAX+RELOC.Status],relocAbsVA+relocWidth32 ; ECX points to disp32 in the encoding of JMPN proxy jump. ESI is bottom of SPT. SUB ECX,ESI ; Make the pointer to relocated DWORD relative to the start of SPT (which is in ESI). MOV [EAX+RELOC.OrgLow],ECX ; ECX is now 3,10,17,24,,, MOV EDX,[%SssPtr] ; ^SSS [.idata]. MOV [EAX+RELOC.Section],EDX MOV EDX,[EDX+SSS.SymPtr] ; symSe.idata
. MOV [EAX+RELOC.Symbol],EDX .58: POP EDI,ESI,EDX,ECX DEC ECX JNZ .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 bottom of section [.idata]. 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
PgmLink 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: ; Skil when 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_HEADER32.DataDirectory+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_IAT+PFPE_DATA_DIRECTORY.VirtualAddress],EAX MOV [EDI+PFPE_OPTIONAL_HEADER32.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_HEADER32.DataDirectory+SIZE#PFPE_DATA_DIRECTORY*pfpeDIRECTORY_ENTRY_IMPORT+PFPE_DATA_DIRECTORY.VirtualAddress],EAX MOV [EDI+PFPE_OPTIONAL_HEADER32.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 the 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 the 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 the 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 EDI,EAX
; Collect exported public symbols actually used in this program into buffer EDI.
MOV EBX,[%Program]
ListGetFirst [EBX+PGM.SymList]
JZ .25: ; Loop .10: .. .20: collecting exports.
.10:JSt [EAX+SYM.Status],symResolved,.20:
JNSt [EAX+SYM.Status],symExport,.20:
MOV ECX,[EAX+SYM.SymbPtr]
TEST ECX
JNZ .11:
MOV ECX,EAX
JMP .14:
.11:MOV EDX,[EAX+SYM.Status]
SetSt EDX,symResolved
MOV [EAX+SYM.Status],EDX
CMPB [ECX+SYM.Status],0
JNZ .12:
MOV [ECX+SYM.Status],DL ; Inherit symbol type from the resolved one.
.12:SetSt [EAX+SYM.Status],symExport
.14:BufferStoreDword EDI,ECX
.20:ListGetNext EAX
JNZ .10:
.25:Invoke EaBufferSort::,EDI ; Alphabetically by symbol names.
BufferRetrieve EDI
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 the 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 PgmLinkSegments.
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 PgmLinkSegments.
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 PgmLinkSegments.
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 SssCreateSe::,[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
PgmLink and section bottoms fixed.[.edata]
.
[.edata]
emit contents.
[.edata]
emit contents.PfpeExportFixup Procedure Program, edataPtr, ENToffset, FWDoffset edataRVA LocalVar ; RVA of section[.edata]
(low dword). 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 PgmLink.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 SssCreateSe::,[EBX+PGM.CurrentStm],0,=B".reloc",6, \ sssSegment+sssWidth32+sssNotBSS+sssImplicit+sssPublic,sssPurposeBASERELOC,4 MOV [EAX+SSS.TopLow],EDI .90:EndProcedure PfpeBaserelocCreate
[reloc]
with base relocations in the main Program.
Program.SegOrdBuffer
.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. ; First segment pass .10: .. .20: searches for [.reloc] section created by PfpeBaserelocCreate. BufferRetrieve [EBX+PGM.SegOrdBuffer] 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.SegOrdBuffer] 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. PfpeBaserelocFixup.FlushBlock: PROC ; 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 ENDP PfpeBaserelocFixup.FlushBlock: .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 ADD ECX,[EDI+SSS.BottomLow] ; Update SSS virtual size. 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 MOV ESI,PfcoffStub:: ; First assume that the default stub built inside euroasm.exe will be used. MOV EAX,PfcoffStubEnd:: SUB EAX,ESI BufferStore [%StubBuffer],ESI,EAX MOV EBX,[%Pgm] ; Check if stub file is explicitly specified. MOV EDX,[EBX+PGM.Pgmopt.StubFileSize] MOV ESI,[EBX+PGM.Pgmopt.StubFilePtr] TEST EDX JZ .90: ; If not, we're done. PfQueryChar '\' ; Query if Filemask ESI,EDX was specified with path (it contains slash or colon). JE .50: PfQueryChar '/' JE .50: PfQueryChar ':' JE .50: SUB EAX,EAX ; When there is no path in STUBFILE=, try all pathes from LINKPATH=. MOV [%PathNr],EAX .20:Invoke EaoptGetOnePath::,[Ea.Eaopt.LinkPathPtr::],[Ea.Eaopt.LinkPathSize::],[%PathNr] JC .E7742: ; StubFile=!1$ was not 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
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_HEADER32 or PFPE_OPTIONAL_HEADER64.
IdataMap LocalVar Size=SIZE# PFPE_IDATA_MAP ; Room for PFPE_IDATA_MAP.
MOV EBX,[%Pgm]
SUB EAX,EAX
MOV [%MaxTopLow],EAX
MOV [%MaxTopHigh],EAX
Invoke EaBufferReserve::,%^PROC ; Reserve temporary buffers.
MOV [%SectionHeaderBuf],EAX
Invoke EaBufferReserve::,%^PROC
MOV [%RawBuf],EAX
Invoke EaBufferReserve::,%^PROC
MOV [%SymbolTableBuf],EAX
Invoke EaBufferReserve::,%^PROC
MOV [%StringTableBuf],EAX
BufferStoreDword EAX,4 ; Initialize DD StringTableSize in the buffer.
Invoke EaBufferReserve::,%^PROC
MOV [%StubFileBuf],EAX
; Load DOS MZ stub into its buffer.
Invoke PfpeLoadStubFile, EAX, EBX
Invoke EaBufferAlign::,[%StubFileBuf],8 ; The following PE signature should be QWORD aligned.
MOV EBX,[%Pgm] ; Preformat special PECOFF sections.
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 PgmGroupByModel::,EBX
Invoke PgmOrderSegments::,EBX
Invoke PgmSymResolve::,EBX
Invoke SymReportUnresolved::,EBX
.09:BufferRetrieve [%StubFileBuf] ; Calculate the size of stub+headers into EDX.
MOV EDX,ECX ; MZ DOS header + stub program size.
Invoke ExpAlign::,EDX,8,0 ; The following PE signature should be QWORD aligned.
ADD EDX,ECX
ADD EDX,4+SIZE# PFCOFF_FILE_HEADER+SIZE# PFPE_OPTIONAL_HEADER32 ; PE signature + file header + optional header.
JNSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.11:
ADD EDX,SIZE# PFPE_OPTIONAL_HEADER64 - SIZE# PFPE_OPTIONAL_HEADER32
.11:BufferRetrieve [EBX+PGM.SegOrdBuffer]
SHR ECX,2
JZ .19:
.12:LODSD
JNSt [EAX+SSS.Status],sssSegment,.13:
ADD EDX,SIZE# PFCOFF_SECTION_HEADER
.13:LOOP .12:
.19:Invoke PgmLink::,EBX,EDX,EDX
BufferRetrieve [%StubFileBuf] ; Construction of output file of program EBX.
MOV [ESI+PFMZ_DOS_HEADER.e_lfanew],ECX ; ECX is now aligned DOS stub size. Update the pointer to PE signature.
MOV [%FileAddr],ECX ; Dword PE signature may be temporarily ignored.
LEA EDI,[%CoffFileHeader] ; Initialize COFF file header.
Invoke PfcoffFileHeader::,EDI,EBX
MOVZXW ECX,[EDI+PFCOFF_FILE_HEADER.SizeOfOptionalHeader]
LEA EDI,[%PeOptionalHeader] ; Initialize PE optional header.
Invoke PfpeOptionalHeader,EDI,EBX
ADD ECX,4+SIZE# PFCOFF_FILE_HEADER ; Skip PE signature and 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]
Invoke PgmRelocResolve::,EBX
; Create symbol record ".file".
LEA EAX,[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.SegOrdBuffer] ; 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.
MOV EAX,[EAX+SSS.SymPtr]
Invoke PfcoffSymSegment::,EAX,[%SymbolTableBuf],[%StringTableBuf] ; Create symbol assigned to 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_HEADER32.SizeOfHeaders],EAX
Invoke PfpeBaserelocFixup,EBX,[%SectionHeaderBuf]
ListGetFirst [EBX+PGM.SymList] ; Symbol list pass at .40: creates PFCOFF_SYMBOL_TABLE records.
JZ .43:
.40:Invoke PfcoffSymSymbol::,EAX,[%SymbolTableBuf],[%StringTableBuf] ; Create symbol EAX.
ListGetNext EAX
JNZ .40:
.43:BufferRetrieve [EBX+PGM.SegOrdBuffer] ; Second segments pass .43: .. .50: copies data from segments to COFF buffers.
SHR ECX,2
JZ .55:
.47:LODSD
MOV EDX,EAX ; ^SSS.
JNSt [EDX+SSS.Status],sssSegment,.50: ; Skip when EDX is 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.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
BufferRetrieve [%SymbolTableBuf] ; Count the number of symbols.
MOV EAX,ECX
SUB EDX,EDX
MOV ECX,SIZE#PFCOFF_SYMBOL
DIV ECX
MOV [EDI+PFCOFF_FILE_HEADER.NumberOfSymbols],EAX
.60:LEA EDI,[%PeOptionalHeader] ; Third segment pass .65: .. .77: to update optional header.
BufferRetrieve [EBX+PGM.SegOrdBuffer]
SHR ECX,2
JZ .80:
.65:LODSD
PUSH ECX,ESI,EDI
MOV ESI,EAX ; ^SSS.
JNSt [ESI+SSS.Status],sssSegment,.77: ; Skip when ESI is 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
TEST EDX ; EDX:ECX is now aligned section size.
Msg cc=NZ,'8525',ESI ; Size of segment [!1S] exceeded 4 GB.
MOV EDX,[ESI+SSS.BottomLow] ; EDX is now the 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.
JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.74:
LEA EDI,[EDI+PFPE_OPTIONAL_HEADER32.DataDirectory] ; RvaAndSizes of DataDirectories in optional header will be specified here.
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_HEADER32 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_HEADER32.SizeOfCode],ECX
CMPD [EDI+PFPE_OPTIONAL_HEADER32.BaseOfCode],0
JNZ .77: ; If the BaseOfCode was already specified by another section.
MOV [EDI+PFPE_OPTIONAL_HEADER32.BaseOfCode],EDX
JMP .77:
.sssPurposeDATA:
ADD [EDI+PFPE_OPTIONAL_HEADER32.SizeOfInitializedData],ECX
JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.77: ; 64bit version doesn't have BaseOfData.
CMPD [EDI+PFPE_OPTIONAL_HEADER32.BaseOfData],0
JNZ .77: ; If the BaseOfData was already specified by another section.
MOV [EDI+PFPE_OPTIONAL_HEADER32.BaseOfData],EDX
JMP .77:
.sssPurposeBSS:
ADD [EDI+PFPE_OPTIONAL_HEADER32.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_HEADER32.SizeOfImage],EAX
.85:BufferRetrieve [%StubFileBuf] ; Flush all COFF components to the output stream.
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_HEADER32
.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.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
ENDPROGRAM pfpe