EuroAssembler Index Manual Download Source Macros


Sitemap Links Forum Tests Projects

pfpe.htm
Structures
PFPE_DATA_DIRECTORY
PFPE_EXPORT_DIRECTORY
PFPE_IDATA_MAP
PFPE_IMPORT_DESCRIPTOR
PFPE_OPTIONAL_HEADER
PFPE_OPTIONAL_HEADER64
PFPE_BASERELOC
Encodings
PFPE_encodings
Procedures
PfpeBaserelocCreate
PfpeBaserelocFixup
PfpeCompile
PfpeExportCreate
PfpeExportFixup
PfpeImportCreate
PfpeImportFixup
PfpeLoadStubFile
PfpeLoadPgm
PfpeOptionalHeader

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
Substructure used in PFPE_OPTIONAL_HEADER.DataDirectory
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
32bit COFF optional header used in image files (PE,DLL).
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
64bit COFF optional header used in image files (PE,DLL).
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
↑ PFPE_BASERELOC
This structure describes one base relocation block. Dword-aligned blocks are emitted by the linker in [.reloc] section of PE and DLL executable files.
Base relocation blocks will be used by loader only when the image could not be loaded at the preferred ImageBase (4 MB for PE, 256 MB for DLL).
See also PFCOFF_RELOCATION used in COFF object files, and RELOC used internally by €ASM.
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
↑ PFPE_IDATA_MAP
This is an €ASM internal structure which describes contents of PE section [.idata] created by PfpeImportCreate.
Each .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 [.idata] section.
 ENDSTRUC PFPE_IDATA_MAP
↑ PFPE_EXPORT_DIRECTORY
COFF export properties, used in PfpeExportCreate.
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
COFF import properties, used in PfpeImportCreate.
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
↑ PFPE_encodings
Following symbolic encodings was adopted from Win32 SDK WINNT.h.
See also
SssPurposeEnc.
; 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.
↑ PfpeOptionalHeader OptionalHeader, PgmPtr
PfpeOptionalHeader will initizalize PE optional header structure and set those members which can be derived from program specified with PgmPtr at the moment of executable file construction.
Input
OptionalHeader is pointer to PFPE_OPTIONAL_HEADER or PFPE_OPTIONAL_HEADER64 object, allocated by the caller.
Which class will be actually used depends on program width.
PgmPtr Pointer to the compiled PGM.
Output
Following members of OptionalHeader are filled: .Magic, .AddressOfEntryPoint, ImageBase, SectionAlignment, FileAlignment, Subsystem, *Version, SizeOfStack*, SizeOfHeap*, NumberOfRvaAndSizes.
Other members are zeroed.
Error
CF=1 Errors are reported with macro Msg.
Invoked by
PfpeCompile
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, keep .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 OutputStream, Pgm
PfpeCompile is constructor of output PE and DLL 32bit or 64bit executable file in Microsoft Portable Executable Common Object File Format as specified in [MS_PECOFF].
Input
OutputStream is pointer to a STREAM for the output file contents.
Pgm is pointer to PGM representing completely assembled and combined program.
Output
OutputStream is filled with output file contents.
Error
Errors are reported with macro Msg.
Invoked from
PfOutput
Invokes
EaBufferAlign EaBufferRelease EaBufferReserve ExpAlign PfcoffFileHeader PfcoffSegmCreate PfcoffSegmRawData PfcoffSymFile PfcoffSymSegment PfcoffSymSymbol PfpeBaserelocCreate PfpeBaserelocFixup PfpeExportCreate PfpeExportFixup PfpeImportCreate PfpeImportFixup PfpeLoadStubFile PfpeOptionalHeader PfrsrcLoadIconFile PgmEvalEntry PgmLinkImage PgmOrderSegments RelocSort
Invoked by
PfdllCompile
Tested by
t7487 t7490 t7493 t7499 t7505 t7511 t7517 t7520 t7523 t7526 t7535 t7541 t7547 t7550
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 of pseudosegment [.scalars]. It will be the first one in COFF symbol table.
    Invoke PfcoffSymSegment::, 0, [%SymbolTableBuf], [%StringTableBuf]
    ; 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
    LOOP .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 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 BasePgm, ObjBegin, ObjEnd, FileNamePtr
Executable format PE is not linkable, it cannot be loaded.
Input
BasePgm is pointer to an existing PGM to which the object file is being linked/imported.
ObjBegin is pointer to the contents of linked/imported object file mapped in memory by the caller.
ObjSize is number of bytes in the object file.
FileNamePtr is pointer to zero-terminated object file name (used in error reports).
Output
-
Error
E8534 Format !1S of file "!2$" is not linkable.
PfpeLoadPgm Procedure BasePgm, ObjBegin, ObjSize, FileNamePtr
    Msg '8534',Dict_FormatPE::,[%FileNamePtr] ; Format !1S of file "!2$" is not linkable.
   EndProcedure PfpeLoadPgm
↑ PfpeImportCreate Program, IdataMapPtr
PfpeImportCreate creates a new auxilliary section with name [.idata] and PURPOSE=IMPORT+IAT in the main Program.
Division of the section are:
  1. IAT (Import Address Table) is a copy of ILT as compiled at link-time. It will be fixed up by the loader at bind time and the offsets of ONT records will be replaced with absolute DWORD|QWORD pointer to the imported function loaded in memory.
  2. IDT (Import Descriptor Table) of PFPE_IMPORT_DESCRIPTOR records, one per each imported DLL. The table is terminated with an empty record.
  3. ILT (Import Lookup Table) of DWORD|QWORD offsets into ONT, one offset per each imported symbol. Offsets are grouped by DLL, after the last import offset of each DLL there is a NULL offset which terminates the group.
    Offsets are RVA (related to the ImageBase).
  4. ONT (Ordinal+Name Table) is concatenation of 16bit ordinal number and ASCIIZ string with symbol name. There is one word-aligned Ordinal-Name concatination per each imported symbol.
  5. DNT (Dll Names Table) with ASCIIZ names of each used DLL.
  6. SPT (Stub Proxy Table) with indirect (dword or qword) absolute near jump instruction per import. Each such proxy jump is 7 bytes long and it uses DWORD|QWORD pointer in IAT as its indirect DWORD target. DWORD target in the body of proxy jump needs relocation if the PE|DLL could not be loaded at its preferred ImageBase virtual address at run-time.
    Every reference made to an imported symbol in assembler source is in fact referring to this proxy jump inside [.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.

Input
Program points to PE|DLL PGM which is being linked.
IdataMapPtr is pointer to an empty PFPE_IDATA_MAP allocated by the caller, and it will be filled with offsets of section tables.
Output
New segment [.idata] is created and appended on Program.SssList.
Output structure at IdataMapPtr is filled with pointers to tables.
The structure is zeroed and no section is created when the program does not use imported symbols.
Error
Errors are reported with macro Msg.
See also
PfpeImportFixup
Invoked by
PfpeCompile
Invokes
EaBufferAlign EaBufferRelease EaBufferReserve SssCreate
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
    LOOP .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",7
        ; Assembled as FF2425[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
↑ PfpeImportFixup Program, OptionalHeaderPtr, IdataMapPtr
PfpeImportFixup will update temporary offsets in import section [.idata] . It is invoked when PE image was linked by PgmLinkImage and section bottoms fixed.
PfpeImportFixup also specifies RVA and sizes in Optional Header Data Directory IMPORT and IAT.
Input
PgmPtr is pointer to the compiled and partially linked PGM.
OptionalHeaderPtr is pointer to PFPE_OPTIONAL_HEADER whose data directories IMPORT and IAT will be updated. When program width is 64, this is the pointer to PFPE_OPTIONAL_HEADER64 instead.
IdataMapPtr is pointer to PFPE_IDATA_MAP which have been filled with offsets of section tables by PfpeImportCreate.
Output
Import data section is updated.
Error
CF=1 Errors are reported with macro Msg.
See also
PfpeImportCreate.
Invoked by
PfpeCompile
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
↑ PfpeExportCreate Program
PfpeExportCreate creates a new auxilliary PE/DLL section with name [.edata] and PURPOSE=EXPORT in the main Program.
Division of the section are:
  1. EDT (Export Directory Table) with one PFPE_EXPORT_DIRECTORY structured object. Its members specify other export tables.
  2. EAT (Export Address Table) contains DWORD RVA of exported functions (their entry point).
  3. NPT (Name Pointers Table) contains DWORD RVA of ASCIIZ function names.
  4. ONT (Ordinal Numbers Table) contains WORD ordinal number of function in EAT.
  5. DNT (Dll Name Table) contains ASCIIZ string with DLL name.
  6. ENT (Export Names Table) contains a series of ASCIIZ strings with function names.
  7. FWD (Forwarded Names Table) contains a series of ASCIIZ strings with forwarded DLL name (without extension "dll") concatenated with function name, e.g. NTDLL.RtlAllocateHeap.
Number of objects in NPT and ONT (dwords and words) is the same, they are synchronized. Function names in ENT are alphabetically sorted.
Input
EAX= pointer to SSS segment [.edata] created and appended on Program.SssList.
ECX= offset of ENT related to the start of export section contents.
EDX= offset of FWD related to the start of export section contents.
Error
EAX=ECX=EDX=0 when no program symbol is marked as exportable.
See also
PfpeExportFixup
Invoked by
PfpeCompile
Invokes
EaBufferRelease EaBufferReserve EaBufferSort SssCreate SymStoreForwarderName
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
↑ PfpeExportFixup Program, edataPtr, ENToffset, FWDoffset
PfpeExportFixup will update temporary offsets in export section [.edata] . It is invoked when PE image was linked by PgmLinkImage and section bottoms fixed.
Input
PgmPtr is pointer to the compiled and partially linked PGM.
edataPtr is pointer to SSS object [.edata].
ENToffset is offset of ENT related to the start of [.edata] emit contents.
FWDoffset is offset of FWD related to the start of [.edata] emit contents.
Output
Export data section is updated.
Error
CF=1 Errors are reported with macro Msg.
See also
PfpeExportCreate.
Invoked by
PfpeCompile
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 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
↑ PfpeBaserelocCreate Program
PfpeBaserelocCreate creates auxilliary PE/DLL segment [.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.
Input
New segment is created and appended on Program.SssList.
Error
CF=1 Errors are reported with macro Msg.
See also
PfpeBaserelocFixup.
Invoked by
PfpeCompile
Invokes
SssCreate
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:
    ; Absolute relocation will have a BASERELOC record.
    INCD [%RelocCnt]
.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
↑ PfpeBaserelocFixup Program, SectionHeaderBuffer
PfpeBaserelocFixup fills the emitted contents of auxilliary PE/DLL segment [reloc] with base relocations in the main Program.
It is invoked when all relocation have been fixed and resolved in PgmLinkImage , and all segments ordered in Program.SssPtrBuf.
Input
Program Pointer to the compiled and partially linked PGM.
SectionHeaderBuffer is pointer to BUFFER with array of PFCOFF_SECTION_HEADER records.
Output
EmitBuffer of segment with PURPOSE=BASERELOC (if found) is filled with PFPE_BASERELOC records.
Error
CF=1 Errors are reported with macro Msg.
Invoked by
PfpeCompile
Invokes
EaBufferAlign EaBufferRelease EaBufferReserve
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
     ; Pad the block with an empty word if they weren't even.
     Invoke EaBufferAlign::,[%TypeOffsetBuf],4
     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 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 EDX,[EAX+SSS.BottomLow] ; Linked VA of the section.
      SUB EDX,[EBX+PGM.Pgmopt.ImageBaseLow]
      MOV [%SectionRVA],EDX
      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]
      JE .50: ; Jump if the RELOC ESI is in the same block.
      ; Flush the previous block and start a new one.
      CALL .FlushBlock:
      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 StubBuffer, Pgm
PfpeLoadStubFile is invoked when a PE or DLL executable program is linked. When program option STUBFILE= defines a file without path, it will search for that file in LINKPATH= directories, check MZ format, copy the file contents to StubBuffer and close it.
If the file is not found, or if STUBFILE= is empty, PfpeLoadStubFile will use built-in default instead.
Input
StubBuffer is pointer to the output empty BUFFER, reserved by the caller.
Pgmis pointer to the linked PGM.
Output
StubBuffer if filled with DOS stub.
Error
Errors E6953, E7741, E7742, E7763 are reported with macro Msg , default stub is used in this case.
Depends on
PfQueryChar
Invokes
EaoptGetOnePath
Invoked by
PfpeCompile
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.
    ; One path is in ESI,ECX.
    MOV EDX,[EBX+PGM.Pgmopt.StubFileSize]
    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 slash or backslash, add 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

▲Back to the top▲