EuroAssembler Index Manual Download Source Macros


Sitemap Links Forum Tests Projects

pfcoff.htm
Structures
PFCOFF_FILE_HEADER
PFCOFF_RELOCATION
PFCOFF_SECTION_HEADER
PFCOFF_SYMBOL
Encodings
PFCOFF_encodings
Data
PfcoffStub
Procedures
PfcoffCompile
PfcoffFileHeader
PfcoffLoadModule
PfcoffLoadPgm
PfcoffLoadReloc
PfcoffSegmCreate
PfcoffSegmRawData
PfcoffSecNr2Sss
PfcoffSymNr2Sss
PfcoffSymFile
PfcoffSymSegment
PfcoffSymSymbol

This source PF generates EuroAssembler output object file in program format COFF, as specified in [MS_PECOFF].


pfcoff PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32
 INCLUDEHEAD "euroasm.htm" ; Interface (structures, symbols and macros) of other modules.
 pfcoff HEAD ; Start module interface.
↑ PFCOFF_FILE_HEADER
COFF file header used in both object and image file.
PFCOFF_FILE_HEADER STRUC
.Machine              D WORD  ; See PFCOFF_encodings below.
.NumberOfSections     D WORD  ; How many PFCOFF_SECTION_HEADER objects follow the file header.
.TimeDateStamp        D DWORD ; Seconds since 1.1.1970 UTC till the link time.
.PointerToSymbolTable D DWORD ; File offset of COFF symbol table (deprecated), or 0.
.NumberOfSymbols      D DWORD ; How many PFCOFF_SYMBOL objects follow the raw data.
.SizeOfOptionalHeader D WORD  ; Size of PFPE_OPTIONAL_HEADER object including .DataDirectory.
.Characteristics      D WORD  ; See PFCOFF_encodings below.
 ENDSTRUC PFCOFF_FILE_HEADER
↑ PFCOFF_SECTION_HEADER
COFF section header.
PFCOFF_SECTION_HEADER STRUC
.Name                 D 8*BYTE ; Section name, NULL padded but not always NULL terminated. Or /123 (offset to string table).
.VirtualSize          D DWORD  ; Total aligned section size when loaded. 0 in object files.
.VirtualAddress       D DWORD  ; RVA of section relative to ImageBase when loaded in memory. 0 in object files.
.SizeOfRawData        D DWORD  ; Rounded up by FileAlignment. 0 in BSS sections.
.PointerToRawData     D DWORD  ; File pointer to section data. Rounded up to FileAlignment in executables. 0 in BSS.
.PointerToRelocations D DWORD  ; File pointer to relocation entries for this section. 0 if no relocations.
.PointerToLinenumbers D DWORD  ; File pointer to line-number entries for this section. 0 if no line numbers.
.NumberOfRelocations  D WORD   ; Number of relocation entries for this section.
.NumberOfLinenumbers  D WORD   ; Number of line-number entries for this section.
.Characteristics      D DWORD  ; See PFCOFF_encodings below.
 ENDSTRUC PFCOFF_SECTION_HEADER
↑ PFCOFF_RELOCATION

PFCOFF_RELOCATION is a 10 byte structure of objects which are stored in the table immediately following raw data in each section of COFF object file.

Assignment of the .Type member of PFCOFF_RELOCATION depends on the architecture, which is selected in €ASM by PFCOFF_FILE_HEADER.Machine, which is selected by the width of COFF module file:

.Type when .Machine = 0x014C (I386 16|32bit)
.Type valueMS SDK nomenclatureRemark€ASM RELOC.Type
0x0000IMAGE_REL_I386_ABSOLUTE Reference is absolute, no relocation is necessaryrelocResolved
0x0001IMAGE_REL_I386_DIR16 Direct 16-bit reference to the symbols virtual addressrelocAbsVA + relocWidth16
0x0002IMAGE_REL_I386_REL16 IP-relative 16-bit reference to the symbols virtual addressrelocRel + relocWidth16
0x0006IMAGE_REL_I386_DIR32 Direct 32-bit reference to the symbols virtual address relocAbsVA + relocWidth32
0x0007IMAGE_REL_I386_DIR32NB Direct 32-bit reference to the symbols virtual address, base not included relocAbsRVA + relocWidth32
0x0009IMAGE_REL_I386_SEG12 Direct 16-bit reference to the segment-selector bits of a 32-bit virtual addressrelocPara + relocWidth16
0x000AIMAGE_REL_I386_SECTION The 16-bit section index of the section that contains the target. This is used to support debugging information.not supported
0x000BIMAGE_REL_I386_SECREL The 32-bit offset of the target from the beginning of its section. This is used to support debugging information and static thread local storage. not supported
0x000CIMAGE_REL_I386_TOKEN CLR tokennot supported
0x000DIMAGE_REL_I386_SECREL7 7 bit offset from base of section containing targetnot supported
0x0014IMAGE_REL_I386_REL32 EIP-relative 32-bit reference to the symbols virtual address relocRel + relocWidth32
.Type when .Machine = 0x8664 (AMD64 64bit)
.Type valueMS SDK nomenclatureRemark€ASM RELOC.Type
0x0000IMAGE_REL_AMD64_ABSOLUTEReference is absolute, no relocation is necessaryrelocResolved
0x0001IMAGE_REL_AMD64_ADDR64 The 64-bit VA of the relocation target. relocAbsVA + relocWidth64
0x0002IMAGE_REL_AMD64_ADDR32 The 32-bit VA of the relocation target. relocAbsVA + relocWidth32
0x0003IMAGE_REL_AMD64_ADDR32NBThe 32-bit address without an image base (RVA). relocAbsRVA + relocWidth32
0x0004IMAGE_REL_AMD64_REL32 The 32-bit relative address from the byte following the relocation. relocRel + relocWidth32
0x0005IMAGE_REL_AMD64_REL32_1 The 32-bit address RIP-relative to byte distance 1 from the relocation. relocRel + relocWidth32 + 1<<12
0x0006IMAGE_REL_AMD64_REL32_2 The 32-bit address RIP-relative to byte distance 2 from the relocation. relocRel + relocWidth32 + 2<<12
0x0007IMAGE_REL_AMD64_REL32_3 The 32-bit address RIP-relative to byte distance 3 from the relocation. relocRel + relocWidth32 + 3<<12
0x0008IMAGE_REL_AMD64_REL32_4 The 32-bit address RIP-relative to byte distance 4 from the relocation. relocRel + relocWidth32 + 4<<12
0x0009IMAGE_REL_AMD64_REL32_5 The 32-bit address RIP-relative to byte distance 5 from the relocation. relocRel + relocWidth32 + 5<<12
0x000AIMAGE_REL_AMD64_SECTION The 16-bit section index of the section that contains the target. This is used to support debugging information.not supported
0x000BIMAGE_REL_AMD64_SECREL The 32-bit offset of the target from the beginning of its section. This is used to support debugging information and static thread local storage.not supported
0x000CIMAGE_REL_AMD64_SECREL7 A 7-bit unsigned offset from the base of the section that contains the target.not supported
0x000DIMAGE_REL_AMD64_TOKEN CLR tokens. not supported
0x000EIMAGE_REL_AMD64_SREL32 A 32-bit signed span-dependent value emitted into the object.not supported
0x000FIMAGE_REL_AMD64_PAIR A pair that must immediately follow every span-dependent value.not supported
0x0010IMAGE_REL_AMD64_SSPAN32 A 32-bit signed span-dependent value that is applied at link time.not supported

Documentation
[COFFRelocTypes]
See also
RELOC used internally by €ASM, and PFPE_BASERELOC used for base relocations in PE and DLL executables.
PFCOFF_RELOCATION STRUC
.VirtualAddress   D DWORD ; RVA of relocated word/dword/qword in emitted code.
.SymbolTableIndex D DWORD ; Zero-based index of PFCOFF_SYMBOL in the symbol table.
.Type             D  WORD ; See the table above.
 ENDSTRUC PFCOFF_RELOCATION
↑ PFCOFF_LINENUMBER
COFF structure describing the relationship between RVA and source physical line. Deprecated.
PFCOFF_LINENUMBER STRUC
.VirtualAddress   D 0*DWORD ; RVA related to the .LineNumber if .LineNumber > 0. Unioned with .SymbolTableIndex.
.SymbolTableIndex D DWORD ; Zero-based index of PFCOFF_SYMBOL in the symbol table.
.Linenumber       D  WORD ; Physical line number if nonzero, otherwise .SymbolTableIndex is used.
 ENDSTRUC PFCOFF_LINENUMBER
↑ PFCOFF_SYMBOL
COFF symbol structure.
PFCOFF_SYMBOL STRUC
.Name               D 8*BYTE; Alias DD 0,OffsetIntoStringTable if the namesize is longer than 8.
.Value              D DWORD ; Relocatable address or scalar value of the symbol.
.SectionNumber      D WORD  ; 1-based index to table of IMAGE_SECTION_HEADERs or spec.constant in PFCOFF_encodings.
.Type               D WORD  ; See PFCOFF_encodings below.
.StorageClass       D BYTE  ; See PFCOFF_encodings below.
.NumberOfAuxSymbols D BYTE  ; Number of auxiliary symbol table entries that follow this record.
 ENDSTRUC PFCOFF_SYMBOL
↑ PFCOFF_encodings
Following symbolic encodings was adopted from Win32 SDK WINNT.h.
; PFCOFF_SECTION_HEADER.Characteristics:
pfcoffSCN_CNT_CODE                =  0x0000_0020  ; Section contains code.
pfcoffSCN_CNT_INITIALIZED_DATA    =  0x0000_0040  ; Section contains initialized data.
pfcoffSCN_CNT_UNINITIALIZED_DATA  =  0x0000_0080  ; Section contains uninitialized data.
pfcoffSCN_LNK_INFO                =  0x0000_0200  ; Section contains comments or some other type of information.
pfcoffSCN_LNK_REMOVE              =  0x0000_0800  ; Section contents will not become part of image.
pfcoffSCN_LNK_COMDAT              =  0x0000_1000  ; Section contents COMDAT data.
pfcoffSCN_GPREL                   =  0x0000_8000  ; Section content can be accessed relative to global pointer.
pfcoffSCN_MEM_PURGEABLE           =  0x0002_0000  ; Reserved.
pfcoffSCN_ALIGN_1BYTES            =  0x0010_0000  ; Section alignment is BYTE.  Valid only for object files.
pfcoffSCN_ALIGN_2BYTES            =  0x0020_0000  ; Section alignment is WORD.  Valid only for object files.
pfcoffSCN_ALIGN_4BYTES            =  0x0030_0000  ; Section alignment is DWORD. Valid only for object files.
pfcoffSCN_ALIGN_8BYTES            =  0x0040_0000  ; Section alignment is QWORD. Valid only for object files.
pfcoffSCN_ALIGN_16BYTES           =  0x0050_0000  ; Section alignment is OWORD. Valid only for object files. Default.
pfcoffSCN_ALIGN_32BYTES           =  0x0060_0000  ; Section alignment is YWORD. Valid only for object files.
pfcoffSCN_ALIGN_64BYTES           =  0x0070_0000  ; Section alignment is ZWORD. Valid only for object files.
pfcoffSCN_ALIGN_128BYTES          =  0x0080_0000  ; Section alignment is 128.   Valid only for object files.
pfcoffSCN_ALIGN_256BYTES          =  0x0090_0000  ; Section alignment is 256.   Valid only for object files.
pfcoffSCN_ALIGN_512BYTES          =  0x00A0_0000  ; Section alignment is 512.   Valid only for object files.
pfcoffSCN_ALIGN_1024BYTES         =  0x00B0_0000  ; Section alignment is 1K.    Valid only for object files.
pfcoffSCN_ALIGN_2048BYTES         =  0x00C0_0000  ; Section alignment is 2K.    Valid only for object files.
pfcoffSCN_ALIGN_4096BYTES         =  0x00D0_0000  ; Section alignment is 4K.    Valid only for object files.
pfcoffSCN_ALIGN_8192BYTES         =  0x00E0_0000  ; Section alignment is 8K.    Valid only for object files.
pfcoffSCN_ALIGN_MASK              =  0x00F0_0000  ; Mask for section alignment.
pfcoffSCN_LNK_NRELOC_OVFL         =  0x0100_0000  ; Section contains extended relocations.
pfcoffSCN_MEM_DISCARDABLE         =  0x0200_0000  ; Section can be discarded.
pfcoffSCN_MEM_NOT_CACHED          =  0x0400_0000  ; Section is not cacheable.
pfcoffSCN_MEM_NOT_PAGED           =  0x0800_0000  ; Section is not pageable.
pfcoffSCN_MEM_SHARED              =  0x1000_0000  ; Section is shareable.
pfcoffSCN_MEM_EXECUTE             =  0x2000_0000  ; Section is executable.
pfcoffSCN_MEM_READ                =  0x4000_0000  ; Section is readable.
pfcoffSCN_MEM_WRITE               =  0x8000_0000  ; Section is writeable.

; PFCOFF_FILE_HEADER. Machine CPU values:
pfcoffFILE_MACHINE_UNKNOWN         =  0x0000  ; Applicable to any machine type.
pfcoffFILE_MACHINE_I386            =  0x014C  ; Intel 386. Default for 16|32bit modules, regardless on CPU=.
pfcoffFILE_MACHINE_I486            =  0x014D  ; Intel 486.
pfcoffFILE_MACHINE_I586            =  0x014E  ; Intel Pentium.
pfcoffFILE_MACHINE_IA64            =  0x0200  ; Intel Itanium (64bit).
pfcoffFILE_MACHINE_AMD64           =  0x8664  ; AMD 64bit. Default for 64bit modules.

; PFCOFF_FILE_HEADER.Characteristics flags:
pfcoffFILE_RELOCS_STRIPPED         =  0x0001  ; Relocation info stripped from file.
pfcoffFILE_EXECUTABLE_IMAGE        =  0x0002  ; File is executable  (i.e. no unresolved external references).
pfcoffFILE_LINE_NUMS_STRIPPED      =  0x0004  ; Line numbers stripped from file.
pfcoffFILE_LOCAL_SYMS_STRIPPED     =  0x0008  ; Local symbols stripped from file.
pfcoffFILE_AGGRESIVE_WS_TRIM       =  0x0010  ; Agressively trim working set (obsolete).
pfcoffFILE_LARGE_ADDRESS_AWARE     =  0x0020  ; Application can handle more than 2GB addresses - 64bit module.
;                                  =  0x0040  ; Reserved.
pfcoffFILE_BYTES_REVERSED_LO       =  0x0080  ; Bytes of machine word are reversed (deprecated).
pfcoffFILE_32BIT_MACHINE           =  0x0100  ; 32 bit architecture.
pfcoffFILE_DEBUG_STRIPPED          =  0x0200  ; Debugging info stripped from image file into .DBG file.
pfcoffFILE_REMOVABLE_RUN_FROM_SWAP =  0x0400  ; If image is on removable media, copy and run from the swap file.
pfcoffFILE_NET_RUN_FROM_SWAP       =  0x0800  ; If image is on network, copy and run from the swap file.
pfcoffFILE_SYSTEM                  =  0x1000  ; Image is a system file rather than user program.
pfcoffFILE_DLL                     =  0x2000  ; File is a DLL.
pfcoffFILE_UP_SYSTEM_ONLY          =  0x4000  ; File should only be run on a uniprocessor machine
pfcoffFILE_BYTES_REVERSED_HI       =  0x8000  ; Bytes of machine word are reversed (deprecated).

; PFCOFF_SYMBOL.SectionNumber special constants:
pfcoffSYM_UNDEFINED =  0 ; Symbol is undefined or it is common.
pfcoffSYM_ABSOLUTE  = -1 ; Symbol is an absolute value (scalar).
pfcoffSYM_DEBUG     = -2 ; Symbol is a special debug item.

; PFCOFF_SYMBOL.StorageClass constants:
pfcoffSYM_CLASS_EXTERNAL   = 2   ; External or public symbol.
pfcoffSYM_CLASS_STATIC     = 3   ; Standard private symbol or segment.
pfcoffSYM_CLASS_FILE       = 103 ; Source filename symbol.
pfcoffSYM_CLASS_SECTION    = 104 ; Definition of section (MSCOFF uses pfcoffSYM_CLASS_STATIC instead).

; PFCOFF_SYMBOL.Type is reduced to EuroAssembler's limited set of fundamental datatypes:
; LSB Type specifies the width and type of the symbol.
pfcoffSYM_TYPE_NULL       = 0x00 ; No type information.
pfcoffSYM_TYPE_VOID       = 0x01 ; No valid type.
pfcoffSYM_TYPE_CHAR       = 0x02 ; BYTE.
pfcoffSYM_TYPE_SHORT      = 0x03 ; WORD.
pfcoffSYM_TYPE_INT        = 0x04 ; DWORD or QWORD (32bit or 64bit program).
pfcoffSYM_TYPE_LONG       = 0x05 ; DWORD.
pfcoffSYM_TYPE_FLOAT      = 0x06 ; DWORD.
pfcoffSYM_TYPE_DOUBLE     = 0x07 ; QWORD.
pfcoffSYM_TYPE_BYTE       = 0x0C ; BYTE.
pfcoffSYM_TYPE_WORD       = 0x0D ; WORD.
pfcoffSYM_TYPE_UINT       = 0x0E ; DWORD or QWORD (32bit or 64bit program).
pfcoffSYM_TYPE_DWORD      = 0x0F ; DWORD.
pfcoffSYM_TYPE_LONGDOUBLE = 0x10 ; TBYTE.
; MSB Type specifies complex type of symbol.
pfcoffSYM_DTYPE_NULL      = 0x00 ; No derived type.
pfcoffSYM_DTYPE_POINTER   = 0x01 ; Pointer to base type.
pfcoffSYM_DTYPE_FUNCTION  = 0x02 ; Procedure or function.
pfcoffSYM_DTYPE_ARRAY     = 0x03 ; Structure.
 ENDHEAD pfcoff  ; End of module interface.
↑ PfcoffCompile OutputStream, Pgm
PfcoffCompile is constructor of output 32bit object file in Microsoft Common Object File Format as specified in [MS_PECOFF].
Input
OutputStream is pointer to a STREAM for the output image contents.
Pgm is pointer to PGM representing completely assembled program.
Output
OutputStream is filled with output file contents.
Error
Errors are reported with macro Msg.
Invoked from
PfOutput
Invokes
EaBufferRelease EaBufferReserve PfDrectveCreate PfcoffFileHeader PfcoffSegmCreate PfcoffSegmRawData PfcoffSymFile PfcoffSymSegment PfcoffSymSymbol PgmOrderSegments RelocResolveObject RelocSort SymResolveObject
Invoked by
PflibcofStoreObjectModule
Tested by
t7190 t7193 t7196 t7199 t7202 t7205 t7211 t7217 t7223 t7229 t7235 t7247 t7253 t7298 t7301
PfcoffCompile  Procedure OutputStream, Pgm
CoffFileHeader   LocalVar Size=SIZE#PFCOFF_FILE_HEADER ; Room for PFCOFF_FILE_HEADER object.
SectionHeaderBuf LocalVar ; Pointer to BUFFER which keeps PFCOFF_SECTION_HEADER objects, one for each segment.
RawBuf           LocalVar ; Pointer to BUFFER for all segments' emitted contents and relocations.
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 of the next free position in output.
     ; Initialize temporary data structures.
     Invoke EaBufferReserve::,PfcoffCompile
     MOV [%SectionHeaderBuf],EAX ; "Section" headers buffer.
     Invoke EaBufferReserve::,PfcoffCompile
     MOV [%RawBuf],EAX ; Raw data buffer.
     Invoke EaBufferReserve::,PfcoffCompile
     MOV [%SymbolTableBuf],EAX ; Symbols buffer.
     Invoke EaBufferReserve::,PfcoffCompile
     MOV [%StringTableBuf],EAX ; String table buffer.
     BufferStoreDword EAX,4 ; Initialize DD StringTableSize in the buffer.
     MOV EBX,[%Pgm]
     MOV EAX,pgmoptFormatMask
     AND EAX,[EBX+PGM.Pgmopt.Status]
     CMP AL,pgmoptCOFF
     JNE .10:
     Invoke PfDrectveCreate::,EBX
.10: Invoke RelocSort::,EBX
     Invoke PgmOrderSegments::,EBX
     ; Initialize COFF file header.
     LEA EDI,[%CoffFileHeader]
     Invoke PfcoffFileHeader,EDI,[%Pgm] ; Optional header is not used in this format.
     ; Create symbol record ".file".
     LEA EAX,[Ea::+EA.SrcFile]
     Invoke PfcoffSymFile, EAX, [%SymbolTableBuf]
     ; First segments pass .40: .. .45: counts [EBX+PFCOFF_FILE_HEADER.NrOfSections],
     ; stores empty IMAGE_SECTION_HEADER object to [%SectionHeaderBuf] and
     ; creates segment's symbol record + one auxiliary record to [%SymbolTableBuf]
     BufferRetrieve [EBX+PGM.SssPtrBuf] ; Array of pointers to segments, sorted order.
     SHR ECX,2 ; Number of pointers.
     JZ .50:
     LEA EDX,[%CoffFileHeader]
.40: LODSD
     JNSt [EAX+SSS.Status],sssSegment,.45: ; If EAX is not a segment.
     Invoke PfcoffSegmCreate, EAX, [%Pgm],[%SectionHeaderBuf],EDX ; Create section header.
     Invoke PfcoffSymSegment,EAX,[%SymbolTableBuf],[%StringTableBuf] ; Create symbol of the section EAX.
.45: LOOP .40:
.50: MOV ECX,SIZE#PFCOFF_FILE_HEADER
     SUB EAX,EAX
     MOV [%FileAddr],ECX
     BufferRetrieve [%SectionHeaderBuf]
     MOV [%SectionHeaderPtr],ESI
     ADD [%FileAddr],ECX
     MOV ECX,[%Pgm]
     Invoke SymResolveObject::,ECX
     ListGetFirst [ECX+PGM.SymList]
     JZ .56:
      ; Symbol list pass .55: .. .60: creates table of PFCOFF_SYMBOL records.
.55: Invoke PfcoffSymSymbol,EAX,[%SymbolTableBuf],[%StringTableBuf] ; Create symbol EAX in symbol table.
     ListGetNext EAX
     JNZ .55:
.56: ; Resolve relative relocations in each segment.
     MOV EBX,[%Pgm]
     BufferRetrieve [EBX+PGM.SssPtrBuf] ; ESI,ECX is now an sorted array of pointers to SSS.
     SAR ECX,2
     JZ .60:
.57: LODSD
     JNSt [EAX+SSS.Status],sssSegment,.59:
     Invoke RelocResolveObject::,EAX,EBX
.59: LOOP .57:
.60: ; Second segments pass .60: .. .70: copies data from segments to COFF tables.
     MOV EBX,[%Pgm]
     BufferRetrieve [EBX+PGM.SssPtrBuf]
     SHR ECX,2
     JZ .80:
.65: LODSD
     MOV EDX,EAX ; ^SSS.
     JNSt [EDX+SSS.Status],sssSegment,.70: ; If not a segment.
     Invoke PfcoffSegmRawData,EDX,EBX,[%FileAddr],[%SectionHeaderPtr],[%RawBuf],[%StringTableBuf]
     MOV [%FileAddr],EAX
     ADDD [%SectionHeaderPtr],SIZE#PFCOFF_SECTION_HEADER
.70: LOOP .65: ; The next segment.
.80: ; Update file header.
     MOV EAX,[%FileAddr] ; Raw emitted data and relocations of all segments has been just buffered.
     TEST AL,1 
     JZ .85:
     INC EAX ; Word align the symbol table.
     MOV [%FileAddr],EAX
     BufferStoreByte [%RawBuf],0
.85: 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
     ; Flush all COFF components to output stream.
     StreamStore [%OutputStream],EDI,SIZE#PFCOFF_FILE_HEADER ; File header.
     BufferRetrieve [%SectionHeaderBuf]
     StreamStore [%OutputStream],ESI,ECX ; Section headers.
     BufferRetrieve [%RawBuf]
     StreamStore [%OutputStream],ESI,ECX ; Section data + relocations.
     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]
     EndProcedure PfcoffCompile
↑ PfcoffSymFile File, SymbolTableBuffer
PfcoffSymFile creates and stores one PFCOFF_SYMBOL record into SymbolTableBuffer with name .file and one or more auxiliary records with the actual filename.
Input
File Pointer to FILE object with assigned name of source file.
SymbolTableBuffer Pointer to output BUFFER , allocated by caller, where PFCOFF_SYMBOL records will be stored.
Output
Symbol records stored to buffer.
Error
CF=1 Errors are reported with macro Msg.
See also
PfcoffSymSymbol, PfcoffSymSegment.
Invoked by
PfcoffCompile PfpeCompile
PfcoffSymFile Procedure File, SymbolTableBuffer
CoffSymbol LocalVar Size=SIZE#PFCOFF_SYMBOL
      LEA EBX,[%CoffSymbol]
      MOV EDI,EBX
      MOV ECX,SIZE#PFCOFF_SYMBOL / 2
      SUB EAX,EAX
      REP STOSW ; Clear CoffSymbol.
      MOV ESI,[%File]
      LEA EDI,[ESI+FILE.Name] ; EDI=^ASCIIZ filename.
      MOV ECX,SIZE#FILE.Name
      SUB EAX,EAX
      MOV ESI,EDI
      REPNE SCASB
      LEA EAX,[EDI-1]
      SUB EAX,ESI
      SUB EDX,EDX
      MOV ECX,SIZE#PFCOFF_SYMBOL
      DIV ECX
      TEST EDX
      JZ .10:
      INC EAX ; EAX=number of auxiliary records Format 4.
 .10: MOV  [EBX+PFCOFF_SYMBOL.NumberOfAuxSymbols],AL
      MOVB [EBX+PFCOFF_SYMBOL.StorageClass],pfcoffSYM_CLASS_FILE ; 103=0x67
      MOVW [EBX+PFCOFF_SYMBOL.SectionNumber],pfcoffSYM_DEBUG ; -2=0xFFFE
      MOV EAX,".fil"
      MOV [EBX+PFCOFF_SYMBOL.Name+0],EAX
      MOV AL,"e"
      MOV [EBX+PFCOFF_SYMBOL.Name+4],AL
      BufferStore [%SymbolTableBuffer],EBX,ECX
 .20: MOV EDI,EBX
      SUB EAX,EAX
      REP STOSB ; Clear auxiliary symbol space.
      MOV CL,SIZE#PFCOFF_SYMBOL
      MOV EDI,EBX
 .30: LODSB
      CMP AL,0
      JZ .40:
      STOSB
      LOOP .30:
      MOV CL,SIZE#PFCOFF_SYMBOL
      BufferStore [%SymbolTableBuffer],EBX,ECX
      JMP .20:
 .40: CMP EDI,EBX
      JE .90: ; If empty.
      MOV CL,SIZE#PFCOFF_SYMBOL
      BufferStore [%SymbolTableBuffer],EBX,ECX
 .90:EndProcedure PfcoffSymFile
↑ PfcoffSymSegment Segment, SymbolTableBuffer, StringTableBuffer
PfcoffSymSegment creates and stores one PFCOFF_SYMBOL record into SymbolTableBuffer with segment name, and one auxiliary records with netto segment size and with the number of relocations.
Its index is stored to input Segment.NameIndex.
Input
Segment Pointer to SSS sssSegment object.
SymbolTableBuffer Pointer to output BUFFER where to store PFCOFF_SYMBOL records.
StringTableBuffer Pointer to output BUFFER where segment name longer than 8 can be stored.
StringTableBuffer may be NULL, the name will be truncated to 8 characters in this case.
Output
Symbol records stored to buffer.
Error
CF=1 Errors are reported with macro Msg.
See also
PfcoffSymSymbol, PfcoffSymFile.
Invoked by
PfcoffCompile PfpeCompile
PfcoffSymSegment Procedure Segment, SymbolTableBuffer, StringTableBuffer
Image_Symbol LocalVar Size=SIZE#PFCOFF_SYMBOL
      MOV EDX,[%Segment]
      TEST EDX
      JZ .90:
      LEA EBX,[%Image_Symbol]
      MOV EDI,EBX
      MOV ECX,SIZE#PFCOFF_SYMBOL / 2
      SUB EAX,EAX
      REP STOSW ; Clear Image_Symbol.
      ; Update input Segment with Segment.NameIndex.
      BufferRetrieve [%SymbolTableBuffer]
      MOV EAX,ECX ; PFCOFF_SYMBOL records used so far = index in COFF symbol table.
      PUSH EDX
        SUB EDX,EDX
        MOV EDI,SIZE#PFCOFF_SYMBOL ; 18.
        DIV EDI
      POP EDX
      MOV [EDX+SSS.NameIndex],EAX
      ; Symbol name.
      MOV EAX,[EDX+SSS.SegmIndex]
      MOV  [EBX+PFCOFF_SYMBOL.SectionNumber],AX
      MOVB [EBX+PFCOFF_SYMBOL.StorageClass],pfcoffSYM_CLASS_STATIC
      MOVB [EBX+PFCOFF_SYMBOL.NumberOfAuxSymbols],1
      MOV ECX,[EDX+SSS.NameSize]
      MOV ESI,[EDX+SSS.NamePtr]
      MOV EDI,EBX
      CMP ECX,8
      JA .30:
 .10: REP MOVSB
      JMPS .50:
 .30: MOV EAX,[%StringTableBuffer]
      TEST EAX
      JNZ .40:  ; If string table exists, use it to store long segment name.
      Msg '3541',EDX ; Segment name "!1S" is too long, truncated in output file.
      MOV ECX,8
      JMP .10:
 .40: PUSH ECX,ESI
        BufferRetrieve EAX
        MOV [EBX+PFCOFF_SYMBOL.Name+4],ECX
      POP ESI,ECX
      BufferStore EAX,ESI,ECX
      BufferStore [%StringTableBuffer],=B(0),1
 .50: BufferStore [%SymbolTableBuffer],EBX,SIZE#PFCOFF_SYMBOL
      ; An auxiliary record follows.
      MOV EDI,EBX
      MOV CL,SIZE#PFCOFF_SYMBOL / 2
      SUB EAX,EAX
      REP STOSW ; Clear auxiliary Format 5 Image_Symbol.
      BufferRetrieve [EDX+SSS.EmitBuffer]
      MOV [EBX+0],ECX ; Size of raw emitted data.
      BufferRetrieve [EDX+SSS.RelocBuffer]
      MOV EAX,ECX
      SUB EDX,EDX
      MOV ECX,SIZE#RELOC
      DIV ECX
 .70: CMP EAX,0xFFFF
      JNA .75:
      MOV EAX,0xFFFF
 .75: MOV [EBX+4],AX ; Number of relocation entries for the segment.
 .80: BufferStore [%SymbolTableBuffer],EBX,SIZE#PFCOFF_SYMBOL
 .90:EndProcedure PfcoffSymSegment
↑ PfcoffSymSymbol Symbol, SymbolTableBuffer, StringTableBuffer
PfcoffSymSymbol creates and stores one PFCOFF_SYMBOL record into SymbolTableBuffer with symbol name.
Its index is stored to input Symbol.NameIndex.
Input
Symbol Pointer to SYM object.
SymbolTableBuffer Pointer to output BUFFER where to store PFCOFF_SYMBOL records.
StringTableBuffer Pointer to output BUFFER where symbol name longer than 8 can be stored.
StringTableBuffer may be NULL, the name will be truncated to 8 characters in this case.
Output
Symbol record stored to buffer, its .NameIndex updated.
Error
CF=1 Errors are reported with macro Msg.
See also
PfcoffSymSegment, PfcoffSymFile.
Invoked by
PfcoffCompile PfpeCompile
PfcoffSymSymbol Procedure Symbol, SymbolTableBuffer, StringTableBuffer
Image_Symbol LocalVar Size=SIZE#PFCOFF_SYMBOL + 2
     ClearLocalVar
     MOV EDX,[%Symbol]
     JSt [EDX+SYM.Status],symResolved,.90:
     ;  Symbol.Name.
     LEA EBX,[%Image_Symbol]
     MOV ECX,[EDX+SYM.NameSize]
     MOV ESI,[EDX+SYM.NamePtr]
     LEA EDI,[EBX+PFCOFF_SYMBOL.Name]
     CMP ECX,8
     JA .20:
 .10:REP MOVSB
     JMPS .40:
 .20:MOV EAX,[%StringTableBuffer]
     TEST EAX
     JNZ .30:  ; If string table exists, use it to store long segment name.
     Msg '3541',EDX ; Segment name "!1S" is too long, truncated in output file.
     MOV ECX,8
     JMP .10:
 .30:PUSH ECX,ESI
       BufferRetrieve EAX
       MOV [EBX+PFCOFF_SYMBOL.Name+4],ECX
     POP ESI,ECX
     BufferStore EAX,ESI,ECX
     BufferStoreByte [%StringTableBuffer],0
 .40:; Update .NameIndex of the input symbol.
     BufferRetrieve [%SymbolTableBuffer]
     MOV EAX,ECX ; Number of PFCOFF_SYMBOL records used so far = index in COFF symbol table.
     SUB EDX,EDX
     MOV EDI,SIZE#PFCOFF_SYMBOL ; 18.
     DIV EDI
     MOV EDX,[%Symbol]
     MOV [EDX+SYM.NameIndex],EAX
     JNSt [EDX+SYM.Status],symExtern|symImport,.45:
     ; Update .NameIndex of the pseudosegment of external/imported symbol.
     MOV EDI,[EDX+SYM.Section]
     TEST EDI
     JZ .45:
     MOV [EDI+SSS.NameIndex],EAX
     MOV EDI,[EDI+SSS.SegmPtr]
     TEST EDI
     JZ .45:
     MOV [EDI+SSS.NameIndex],EAX
 .45:; Symbol.StorageClass.
     MOVB [EBX+PFCOFF_SYMBOL.StorageClass],pfcoffSYM_CLASS_EXTERNAL ; 2 = global symbol.
     JSt [EDX+SYM.Status],symExtern | symPublic | symExport | symImport,.50:
     MOVB [EBX+PFCOFF_SYMBOL.StorageClass],pfcoffSYM_CLASS_STATIC ; 3 = standard private symbol.
 .50:;Symbol.SectionNumber.
     MOV AX,pfcoffSYM_UNDEFINED ; 0 = create extern pseudosegment at link time.
     JSt [EDX+SYM.Status],symExtern | symImport, .60:
     MOV ECX,[EDX+SYM.Section]
     MOV AX,pfcoffSYM_ABSOLUTE ; -1 = symbol is scalar.
     JECXZ .60:
     MOV EAX,[ECX+SSS.SegmIndex]
     MOV ESI,[ECX+SSS.SegmPtr]
     TEST ESI
     JZ .60:
     MOV EAX,[ESI+SSS.SegmIndex]
 .60:MOV [EBX+PFCOFF_SYMBOL.SectionNumber],AX
     ; Symbol.Value.
     MOV EAX,[EDX+SYM.OffsetLow]
     MOV [EBX+PFCOFF_SYMBOL.Value],EAX
     ; Symbol.Type.
     MOV EAX,[EDX+SYM.Status]
     MOV CX,pfcoffSYM_DTYPE_FUNCTION <<8 + pfcoffSYM_TYPE_VOID ; >> 0x2001
     JSt EAX,symProc,.70: ; If the symbol EDX is PROC (procedure/function name).
     Dispatch AL,'A','B','U','W','D','Q','T'
     MOV CX,pfcoffSYM_DTYPE_NULL    <<8 + pfcoffSYM_TYPE_NULL ; >> 0x0000 default to unknown type.
     JMPS .70:
.A:  MOV CX,pfcoffSYM_DTYPE_POINTER <<8 + pfcoffSYM_TYPE_VOID ; >> 0x0101
     JMPS .70:
.B:  MOV CX,pfcoffSYM_DTYPE_NULL    <<8 + pfcoffSYM_TYPE_BYTE ; >> 0x000C
     JMPS .70:
.U:
.W:  MOV CX,pfcoffSYM_DTYPE_NULL    <<8 + pfcoffSYM_TYPE_WORD ; >> 0x000D
     JMPS .70:
.D:  MOV CX,pfcoffSYM_DTYPE_NULL    <<8 + pfcoffSYM_TYPE_DWORD; >> 0x000F
     JMPS .70:
.Q:  MOV CX,pfcoffSYM_DTYPE_NULL    <<8 + pfcoffSYM_TYPE_UINT ; >> 0x000E
     JMPS .70:
.T:  MOV CX,pfcoffSYM_DTYPE_NULL    <<8 + pfcoffSYM_TYPE_LONGDOUBLE ; >> 0x0010
   ;  JMPS .70:
 .70:MOV [EBX+PFCOFF_SYMBOL.Type],CX
 .80:BufferStore [%SymbolTableBuffer],EBX,SIZE#PFCOFF_SYMBOL
.90:EndProcedure PfcoffSymSymbol
↑ PfcoffStub
Data between PfcoffStub..PfcoffStubEnd represent compiled 16bit SMALL MZ program, built in euroasm.exe file. It reports a message This program was launched in DOS but it requires Windows.
coffstub.exe was created from the source coffstub.htm , it is used as the default stub when the program option STUBFILE= is empty or when it specifies nonexisting or damaged file.
[.data]
PfcoffStub::
   INCLUDEBIN "../objlib/coffstub.exe"
PfcoffStubEnd::
[.text]
↑ PfcoffFileHeader FileHeader, PgmPtr
PfcoffFileHeader will initizalize COFF file header structure and set those members which can be derived from program specified with PgmPtr at the moment of COFF construction.
Input
FileHeader Pointer to PFCOFF_FILE_HEADER object, allocated by the caller.
PgmPtr Pointer to PGM from which is the COFF file compiled.
Output
Following members of FileHeader are filled: .Machine, .TimeDateStamp, .SizeOfOptionalHeader, .Characteristics . Other members are zeroed.
Error
CF=1 Errors are reported with macro Msg.
Invoked by
PfcoffCompile PfpeCompile
PfcoffFileHeader Procedure FileHeader,PgmPtr
    MOV EDI,[%FileHeader]
    MOV EBX,[%PgmPtr]
    Clear EDI,Size=SIZE#PFCOFF_FILE_HEADER
    MOV [EDI+PFCOFF_FILE_HEADER.Machine],pfcoffFILE_MACHINE_I386
    JNSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.20:
    MOV [EDI+PFCOFF_FILE_HEADER.Machine],pfcoffFILE_MACHINE_AMD64
.20:MOV ESI,[Ea.Eaopt.TimeStamp::]
    MOV EAX,[EBX+PGM.Pgmopt.Status]
    MOV [EDI+PFCOFF_FILE_HEADER.TimeDateStamp],ESI
    SUB ECX,ECX ; ECX keeps the size of optional header.
    XOR EDX,EDX ; EDX keeps .Characteristics.
    JNSt EAX,pgmoptWidth64,.40:
    MOV CX,SIZE# PFPE_OPTIONAL_HEADER64
    SetSt EDX,pfcoffFILE_LARGE_ADDRESS_AWARE
    JMP .50:
.40:JNSt EAX,pgmoptWidth32,.50:
    MOV CX,SIZE# PFPE_OPTIONAL_HEADER
    SetSt EDX,pfcoffFILE_32BIT_MACHINE
.50:Dispatch AL,pgmoptPE,pgmoptDLL
    SUB ECX,ECX ; Size of optional header in nonexecutable is 0.
    JMP .80: ; Skip if pgmoptCOFF.
.pgmoptDLL: OR DX,pfcoffFILE_EXECUTABLE_IMAGE + pfcoffFILE_DLL
.pgmoptPE:  OR DX,pfcoffFILE_EXECUTABLE_IMAGE
.80:MOV [EDI+PFCOFF_FILE_HEADER.SizeOfOptionalHeader],CX
    MOV [EDI+PFCOFF_FILE_HEADER.Characteristics],DX
   EndProcedure PfcoffFileHeader
↑ PfcoffSegmRawData Segment, Program, FileAddress, SectionHeader, RawBuffer, StringTableBuffer
PfcoffSegmRawData will flush segment data and relocations into RawBuffer and update SectionHeader.
Raw emitted data are followed by PFCOFF_RELOCATION records, word aligned.
Input
Segment is pointer to SSS whose data are processed.
Program is pointer to current PGM.
FileAddress is unaligned FA of this raw data within the output object file. It will be aligned by FileAlign= and then emitted data and relocations will be virtually saved to output file at this FA.
SectionHeader is pointer to PFCOFF_SECTION_HEADER initialized object.
RawBuffer is pointer to a BUFFER for emitted data+reloc. It will be stuffed first with zeros to achieve FileAlign= alignment.
StringTableBuffer is pointer to BUFFER for longer symbol names. It may be NULL for executable images.
Output
EAX= New (unaligned) FileAddress, i.e. the input FileAddress increased by the amount of data actually stored to RawBuffer.
SectionHeader, RawBuffer, StringTableBuffer are updated.
Error
CF=1 Errors are reported with macro Msg.
Invoked by
PfcoffCompile PfpeCompile
Invokes
ExpAlign SssGetCoffCharacteristics
PfcoffSegmRawData Procedure Segment,Program,FileAddress,SectionHeader,RawBuffer,StringTableBuffer
PgmoptStatus    LocalVar ; Local copy of program option Status.
SectionAlign    LocalVar ; Local copy of program option SECTIONALIGN=.
FileAlign       LocalVar ; Local copy of program option FILEALIGN=.
FirstRelocation LocalVar ; Distance of the 1st PFCOFF_RELOCATION record from %RawBuf.Bottom.
CoffRelocation  LocalVar Size=SIZE# PFCOFF_RELOCATION ; Temporary room for one relocation.
    MOV EDX,[%Program]
    MOV ECX,[EDX+PGM.Pgmopt.SectionAlign]
    MOV ESI,[EDX+PGM.Pgmopt.FileAlign]
    MOV EAX,[EDX+PGM.Pgmopt.Status]
    MOV [%SectionAlign],ECX
    MOV [%FileAlign],ESI
    MOV [%PgmoptStatus],EAX
    MOV EAX,[%FileAddress]
    MOV EDI,[%SectionHeader]
    MOV EBX,[%Segment]
    MOV [%ReturnEAX],EAX ; Initialize returned output FileAddress.
    ; SectionHeader.Name
    MOV ECX,[EBX+SSS.NameSize]
    MOV ESI,[EBX+SSS.NamePtr]
    LEA EDI,[EDI+PFCOFF_SECTION_HEADER.Name]
    CMP ECX,8
    JA .15:
.10:REP MOVSB ; Short names go directly to section header.
    JMP .25:
.15:MOV EAX,[%StringTableBuffer] ; Longer name is stored to string table.
    TEST EAX
    JNZ .20: ; Skip if long section names are supported via string table.
    Msg '3541',EBX ; Segment name "!1S" is too long, truncated in output file.
    MOV ECX,8 ; Executable formats limit segment name size to 8 characters.
    JMP .10:
.20:PUSH ECX,ESI
      BufferRetrieve EAX ; Get current index to string table.
      MOV EDX,ECX ; Remember buffer offset of the free space in StringTableBufer.
    POP ESI,ECX
    BufferStore EAX,ESI,ECX ; Store long name into the string table.
    BufferStoreByte EAX,0 ; Terminating zero.
    ; Continue with section header.
    MOV AL,'/'
    STOSB ; Instead of long segment name store /ASCII_offset to section header.Name.
    MOV EAX,EDX ; Offset within string table.
    StoD EDI,Size=7
.25:MOV EDI,[%SectionHeader]
    ; SectionHeader.VirtualSize and .VirtualAddress.
    JNSt [%PgmoptStatus],pgmoptImage,.30: ; Leave VS and VA at 0 if COFF object file is created.
    ; Section bottom of executables has already been section-aligned and
    ;   increased by ImageBase in PgmLinkImage.
    MOV ESI,[EBX+SSS.BottomLow]
    MOV ECX,[EBX+SSS.BottomHigh] ; Section bottom ECX:ESI of linkables is always 0.
    MOV EDX,[%Program]
    PUSH ESI
      SUB ESI,[EDX+PGM.Pgmopt.ImageBaseLow] ; Convert VA to RVA.
      SBB ECX,[EDX+PGM.Pgmopt.ImageBaseHigh]
      MOV [EDI+PFCOFF_SECTION_HEADER.VirtualAddress],ESI
    POP ESI
    MOV EAX,[EBX+SSS.TopLow]
    MOV EDX,[EBX+SSS.TopHigh]
    SUB EAX,ESI
    SBB EDX,ECX                             ; EDX:EAX is now the netto data size.
    Invoke ExpAlign::,EAX,[%SectionAlign],0 ; VirtualSize will be section-aligned upward, too.
    ADD EAX,ECX
    MOV [EDI+PFCOFF_SECTION_HEADER.VirtualSize],EAX
.30:; Set .SizeOfRawData to section header.
    MOV ESI,[EBX+SSS.BottomLow]
    MOV EAX,[EBX+SSS.TopLow]
    MOV EDX,[EBX+SSS.TopHigh]
    SUB EAX,ESI                                   ; EDX:EAX is now netto section size, both for
    SBB EDX,[EBX+SSS.BottomHigh]                  ;  initialized and uninitialized sections.
    MOV ECX,EAX ; RawDataSize.
    JSt [EBX+SSS.Purpose],sssPurposeInitMask,.35: ; Skip if initialized.
    JSt [EBX+SSS.Status],sssNotBSS,.35:
    JNSt [%PgmoptStatus],pgmoptExecutable,.35:    ; Skip if COFF linkable format.
    SUB ECX,ECX                                   ; BSS raw size in executable image is 0.
.35:MOV [EDI+PFCOFF_SECTION_HEADER.SizeOfRawData],ECX
    JSt [EBX+SSS.Purpose],sssPurposeBSS,.44:
    JNSt [EBX+SSS.Status],sssNotBSS,.44: ; BSS section has .PointerToRawData=0.
    ; Set .PointerToRawData into section header. Raw data must be file-aligned first.
    MOV EAX,[%ReturnEAX] ; Current unaligned FA.
    Invoke ExpAlign::,EAX,[%FileAlign],0
    ADD EAX,ECX
    MOV [%ReturnEAX],EAX
    MOV [EDI+PFCOFF_SECTION_HEADER.PointerToRawData],EAX
    JECXZ .40:
.37:BufferStoreByte [%RawBuffer],0                ; Alignment stuff.
    SUB ECX,1
    JNZ .37:
.40:; Store emitted raw data to aligned RawBuffer.
    BufferRetrieve [EBX+SSS.EmitBuffer]
    BufferStore [%RawBuffer],ESI,ECX
    ADD [%ReturnEAX],ECX
    MOV EAX,[EDI+PFCOFF_SECTION_HEADER.SizeOfRawData] ; Virtual size of raw data.
    SUB EAX,ECX                                   ; Compare with emitted size.
    JNA .44:
    ADD [%ReturnEAX],EAX
.42:BufferStoreByte [%RawBuffer],0                ; Pad emitted size with EAX NULL bytes.
    DEC EAX
    JNZ .42:
.44:JNSt [%PgmoptStatus],pgmoptExecutable,.48:    ; COFF object files will have relocations stored.
    JNSt [Ea.Eaopt.Status::],eaoptDEBUG,.85:      ; No relocation records in PE+DLL when DEBUG=OFF.
.48:; SectionHeader Relocations. Relocation records will be word-aligned.
    BufferRetrieve [EBX+SSS.RelocBuffer]
    JECXZ .56:                                    ; If there are no relocations in the segment EBX.
.52:JNSt [ESI+RELOC.Status],relocResolved | relocDisp8,.58:
    ADD ESI,SIZE# RELOC
    SUB ECX,SIZE# RELOC
    JA .52:
.56:JMP .85:                                      ; If there are no unresolved relocations in the segment EBX.
.58:; There is at least one unresolved relocation in segment EBX.
    MOV EAX,[%ReturnEAX]                          ; Current unaligned FA. Align it to WORD.
    TEST AL,1
    JZ .61:
    INC EAX
    MOV [%ReturnEAX],EAX
    BufferStoreByte [%RawBuffer],0
.61:MOV [EDI+PFCOFF_SECTION_HEADER.PointerToRelocations],EAX
    BufferRetrieve [%RawBuffer]
    MOV [%FirstRelocation],ECX
    ; ECX is the distance of 1st PFCOFF_RELOCATION record from %RawBuffer.Bottom,
    ;   whose .VirtualAddress might be rewritten later if the number of relocations exceeds 64K.
    LEA EDI,[%CoffRelocation]                     ; EDI points to output COFF format of relocation.
    BufferRetrieve [EBX+SSS.RelocBuffer]          ; Loop .64: .. .80: through all RELOC records.
    ; Check if the RELOC record is valid and if it goes to COFF.
.64:JSt [ESI+RELOC.Status],relocResolved | relocDisp8N, .75:
    PUSH ECX,ESI
      MOV EAX,ESI ; Pointer to the input RELOC (internal format of relocation).
      ; Set RVA of the word/dword/qword which is relocated.
      MOV ESI,[EAX+RELOC.OrgLow]
      MOV ECX,[EAX+RELOC.OrgHigh]
      MOV EDX,[EAX+RELOC.Section]
      JECXZ .65:
      Msg '7927',EDX,ESI                          ; Relocation offset out of 4GB range at [!1S]:!2Hh.
      JMP .74:                                    ; Ignore invalid relocation.
.65:  MOV [EDI+PFCOFF_RELOCATION.VirtualAddress],ESI
      ; Set the index of COFF section which is the target of relocation.
      MOV ECX,[EAX+RELOC.Target]                  ; Section or segment of relocation target.
      SUB EBX,EBX                                 ; Default symbol index is 0, which represents no segment.
      JECXZ .67:                                  ; If the symbol was plain number, .SymbolTableIndex=EBX=0.
      MOV EBX,[ECX+SSS.NameIndex]
      MOV ECX,[ECX+SSS.SegmPtr]                   ; Segment of relocation target.
      JECXZ .67:
      MOV EBX,[ECX+SSS.NameIndex]
.67:  MOV [EDI+PFCOFF_RELOCATION.SymbolTableIndex],EBX
      ; Set the type of relocation.
      MOV ECX,[%Program]
      MOV EBX,[EAX+RELOC.Status]
      MOV EDX,[EAX+RELOC.Section]
      JSt [ECX+PGM.Pgmopt.Status],pgmoptWidth64,.AMD64:
.I386:; Machine=I386 in 16|32bit programs.
      JSt EBX,relocWidth16,.W16:
      JSt EBX,relocWidth64,.E7924:                ; Invalid relocation.
      MOV CX,0x0006                               ; IMAGE_REL_I386_DIR32.
      JSt EBX,relocAbsVA,.70:
      MOV CL,0x0014                               ; IMAGE_REL_I386_REL32.
      JSt EBX,relocRel,.70:
      MOV CL,0x0007                               ; IMAGE_REL_I386_DIR32NB.
      JSt EBX,relocAbsRVA,.70:
.E7924:POP ESI                                    ; Restore ^RELOC.
      PUSH ESI
      MOV EDX,[ESI+RELOC.Section]
      Msg '7924',[ESI+RELOC.Section],[ESI+RELOC.OrgLow] ; Invalid relocation [!1S]:!2Hh.
      JMP .74:
.W16: MOV CX,0x0001                               ; IMAGE_REL_I386_DIR16.
      JSt EBX,relocAbsVA,.70:
      MOV CL,0x0002                               ; IMAGE_REL_I386_REL16.
      JSt EBX,relocRel,.70:
.AMD64:; Machine=AMD64 in 64bit programs.
      MOV CX,0x0004                               ; IMAGE_REL_AMD64_REL32.
      JSt EBX,relocWidth16,.E7924:                ; Invalid relocation [!1S]:!2Hh.
      JSt EBX,relocWidth64,.W64:
      MOV EAX,relocRelDist
      AND EAX,EBX
      SHR EAX,12                                  ; Convert relocRelDist to imm+imm2 size 0..5.
      CMP EAX,5
      JA .E7924:                                  ; Invalid relocation [!1S]:!2Hh.
      ADD CL,AL                                   ; Change reloc type from 0x0004 to 0x0004..0x0009.
      JSt EBX,relocRel,.70:
      MOV CL,0x0002                               ; IMAGE_REL_AMD64_ADDR32.
      JSt EBX,relocAbsVA,.70:
      MOV CL,0x0003                               ; IMAGE_REL_AMD64_ADDR32NB.
      JSt EBX,relocAbsRVA,.70:
      JMP .E7924:                                 ; Invalid relocation [!1S]:!2Hh.
.W64: MOV CL,0x0001                               ; IMAGE_REL_AMD64_ADDR64.
.70:  MOV [EDI+PFCOFF_RELOCATION.Type],CX
      MOV ECX,SIZE#PFCOFF_RELOCATION
      BufferStore [%RawBuffer],EDI,ECX
      MOV EDX,[%SectionHeader]
      ADD [%ReturnEAX],ECX
      INCD [EDX+PFCOFF_SECTION_HEADER.NumberOfRelocations] ; This 16bit counter temporarily misuses
      ; it's unemployed 16bit neighbour .NumberOfLinenumbers, so the counter is in fact 32bit.
      ; Word overflow will be healed at .80:.
.74:POP ESI,ECX
.75:ADD ESI,SIZE#RELOC
    SUB ECX,SIZE#RELOC
    JA .64:                                       ; The next RELOC record.
.80:MOV EDI,[%SectionHeader]
    MOV EAX,[EDI+PFCOFF_SECTION_HEADER.NumberOfRelocations]
    TEST EAX,0xFFFF0000
    JZ .85:
    ; The .NumberOfRelocations exceeded 64K, design patch will be applied.
    BufferRetrieve [%RawBuffer]                   ; Reread the buffer, as it might get reallocated during BufferStore.
    ADD ESI,[%FirstRelocation]                    ; Find position of the 1st PFCOFF_RELOCATION record in RawBuffer.
    ; First copy the 1st record to the end.
    MOV ECX,SIZE#PFCOFF_RELOCATION
    BufferStore [%RawBuffer],ESI,ECX
    ADD [%ReturnEAX],ECX
    INC EAX                                       ; Increment number of relocations, because one more record was added.
    BufferRetrieve [%RawBuffer]                   ; Reread the buffer, as it might get reallocated during BufferStore.
    ADD ESI,[%FirstRelocation]                    ; Find position of the 1st PFCOFF_RELOCATION record.
    MOV [ESI+PFCOFF_RELOCATION.VirtualAddress],EAX; Real number of relocation, which is above 64K.
    MOV EAX,0x0000FFFF
    MOVD [EDI+PFCOFF_SECTION_HEADER.NumberOfRelocations],EAX ; Simultaneously clear neighbouring .NumberOfLinenumbers.
    SetSt [EDI+PFCOFF_SECTION_HEADER.Characteristics],pfcoffSCN_LNK_NRELOC_OVFL
.85:; Update SectionHeader.Characteristics.
    Invoke SssGetCoffCharacteristics::, [%Segment]
    SetSt [EDI+PFCOFF_SECTION_HEADER.Characteristics],EAX
  EndProcedure PfcoffSegmRawData
↑ PfcoffSegmCreate Segment, Program, SectionHeaderBuffer, FileHeader
PfcoffSegmCreate creates an empty SectionHeader in SectionHeaderBuffer.
Input
Segment is pointer to SSS whose data are processed.
Program is pointer to current PGM.
SectionHeaderBuffer is pointer to a BUFFER where the coresponding PFCOFF_SECTION_HEADER record will be stored.
FileHeader is pointer to PFCOFF_FILE_HEADER whose member .NumberOfSections will be incremented. This incremented section ordinal will be also written to SSS.SegmIndex of the Segment.
Output
%Segment.SegmIndex, %FileHeader.NumberOfSections and all buffers are updated.
Error
CF=1 Errors are reported with macro Msg.
Invoked by
PfcoffCompile PfpeCompile
PfcoffSegmCreate Procedure Segment, Program, SectionHeaderBuffer, FileHeader  ; , SymbolTableBuffer, StringTableBuffer
    MOV EBX,[%FileHeader]
    MOV ESI,[%Segment]
    MOVZXW EDX,[EBX+PFCOFF_FILE_HEADER.NumberOfSections]
    INC EDX ; One-based section number.
    MOV [ESI+SSS.SegmIndex],EDX
    TEST EDX,0xFFFF_0000
    MsgUnexpected cc=NZ
    MOV [EBX+PFCOFF_FILE_HEADER.NumberOfSections],DX
    BufferNew [%SectionHeaderBuffer],SIZE#PFCOFF_SECTION_HEADER
    MOV EDI,EAX
    SUB EAX,EAX
    MOV ECX,SIZE#PFCOFF_SECTION_HEADER / 4
    REP STOSD ; Clear the new section header.
   EndProcedure PfcoffSegmCreate
↑ PfcoffLoadPgm BasePgm, ObjBegin, ObjSize, FileNamePtr

PfcoffLoadPgm reads the contents of one COFF object file and converts it to structures of a fresh new program, which then will be stored on BasePgm.ModulePgmList.

Input
BasePgm is pointer to an existing PGM to which the object file is being linked/imported.
ObjBegin is pointer to the contents of COFF object mapped in memory by the caller. It must start with PFCOFF_FILE_HEADER
ObjSize is number of bytes in the COFF object.
FileNamePtr is pointer to zero-terminated object file name (used in error reports).
Output
Loaded program is stored on BasePgm.ModulePgmList as a new PGM structure.
Error
Error E7766 reported with macro Msg.
Invoked from
PfLoad
Invokes
EaBufferRelease EaBufferReserve EaFs2Id PfcoffLoadModule
Tested by
t7019 t7043 t7109 t7133 t7199 t7223 t7247 t7313 t7337 t7361 t7427 t7466 t7481 t7505 t7535 t7571 t7601
PfcoffLoadPgm Procedure BasePgm, ObjBegin, ObjSize, FileNamePtr
    MOV EDI,[%FileNamePtr]
    GetLength$ EDI
    ; EDI,ECX is file name string. Remove path.
    MOV EDX,ECX
    LEA ESI,[EDI+ECX-1]
    STD
.20:LODSB ; Read the string backward.
    CMP AL,'\'
    JE .30:
    CMP AL,'/'
    JE .30:
    CMP AL,':'
    LOOPNE .20:
.30:CLD
    LEA ESI,[EDI+ECX]
    SUB EDX,ECX
    ; ESI,EDX is now file name without path. Remove file extension (usually .obj).
    LEA EDI,[ESI+EDX]
.40:DEC EDI
    CMP EDI,ESI
    JNA .60:
    CMPB [EDI],'.'
    JNE .40:
.50:SUB EDI,ESI
    JNZ .70:
.60:MOV EDI,EDX ; When file name has no extension use it as whole.
.70:; ESI,EDI is now file name without path and without extension, it will be used as the module name.
    Invoke EaBufferReserve::,PfcoffLoadPgm
    Invoke EaFs2Id::,ESI,EDI,EAX ; Replace nonalphanum with underscores.
    BufferRetrieve EAX
    Invoke EaBufferRelease::,EAX
    Invoke PfcoffLoadModule,[%BasePgm],[%ObjBegin],[%ObjSize],[%FileNamePtr],ESI,EDI,pgmoptCOFF
.90:EndProcedure PfcoffLoadPgm
↑ PfcoffLoadModule BaseProgram, ModBegin, ModSize, FileName$Ptr, ModNamePtr, ModNameSize, Format

PfcoffLoadModule reads the contents of one COFF object module and converts it to structures of a fresh new program, which then will be stored on BasePgm.ModulePgmList.
The object can be the whole contents of a separate file in COFF format, or it can be one member of object library in LIBCOF format.

Input
BaseProgram is pointer to an existing PGM to which the object file is being loaded.
ModBegin is pointer to the contents of COFF object mapped in memory by the caller. It must start with PFCOFF_FILE_HEADER.
ModSize is number of bytes in the module.
FileName$Ptr is pointer to zero-terminated file name (used in error reports).
ModNamePtr is pointer to module name which will be used as loaded program name. It may be volatile.
ModNameSize is number of characters in module name.
Format is either pgmoptCOFF or pgmoptLIBCOF.
Output
Loaded program is stored on BasePgm.ModulePgmList as a new PGM structure.
Error
E7766 reported with macro Msg.
Invoked by
PfcoffLoadPgm PflibcofLoadPgm
Invokes
PfDrectveDestroy PfcoffLoadReloc PfcoffSecNr2Sss PgmoptSetLinkProp SssCreateExtern SssFind SssGuessPurpose
PfcoffLoadModule Procedure BaseProgram, ModBegin, ModSize, FileName$Ptr,ModNamePtr,ModNameSize,Format
ModEnd       LocalVar ; Pointer to the end of loaded file.
CoffScnBegin LocalVar ; Pointer to the 1st section header in memory mapped file.
CoffScnPtr   LocalVar ; Pointer to the current section header in memory mapped file.
CoffScnCount LocalVar ; Number of PFCOFF_SECTION_HEADER records in the file.
CoffScnIndex LocalVar ; Ordinal number of PFCOFF_SECTION_HEADER in COFF file (1,2,3..).
CoffSymBegin LocalVar ; Pointer to the 1st PFCOFF_SYMBOL record in memory mapped file.
CoffSymCount LocalVar ; Number of PFCOFF_SYMBOL records in the module.
CoffSymIndex LocalVar ; 0-based ordinal number of PFCOFF_SYMBOL in COFF module (0,1,2,3..).
CoffSymAux   LocalVar ; Number of auxiliary symbols pending in PFCOFF_SYMBOL table.
ModSymPtr    LocalVar ; Pointer to symbol SYM in the module.
CoffStrBegin LocalVar ; Pointer to COFF string table in memory mapped file.
    MOV EBX,[%BaseProgram]
    MOV EDX,[EBX+PGM.Pool]
    ; Create loaded module. Simplified intialization inherits Pool from BaseProgram.
    ListNew [EBX+PGM.ModulePgmList],Zeroed=yes
    JC .90:
    MOV EBX,EAX  ; EBX is now the new module program.
    MOV ESI,[%ModNamePtr]
    MOV ECX,[%ModNameSize]
    MOV [EBX+PGM.Pool],EDX
    PoolStore EDX,ESI,ECX ; Make the PGM.Name nonvolatile.
    MOV [EBX+PGM.NamePtr],EAX
    MOV [EBX+PGM.NameSize],ECX
    ListCreate EDX,SIZE# SSS
    MOV [EBX+PGM.SssList],EAX
    ListCreate EDX,SIZE# SYM
    MOV [EBX+PGM.SymList],EAX
    Invoke PgmoptSetLinkProp::,[%Format] ; Get default properties for the module format.
    AND EAX,pgmoptLinkPropMask
    MOV ECX,[%Format]
    CMP CL,pgmoptLIBCOF
    JNE .F0:
    SetSt EAX,pgmoptLibMember
.F0:SetSt [EBX+PGM.Pgmopt.Status],EAX
    MOV ESI,[%ModBegin]
    MOV ECX,[%ModSize]
    ADD ECX,ESI
    MOV [%ModEnd],ECX
    ; Retrieve information from COFF file header mapped at ESI.
    ; Try to specify the module width from COFF file header characteristics.
    MOVZXW EDX,[ESI+PFCOFF_FILE_HEADER.Machine]
    MOVZXW EAX,[ESI+PFCOFF_FILE_HEADER.Characteristics]
    MOV ECX,pgmoptCOFF+pgmoptFLAT+pgmoptWidth64 ; Assume 64bit protected mode COFF.
    CMP DX,pfcoffFILE_MACHINE_AMD64
    JE .F1:
    CMP DX,pfcoffFILE_MACHINE_IA64
    JE .F1:
    MOV ECX,pgmoptCOFF+pgmoptFLAT+pgmoptWidth32 ; Assume 32bit protected mode COFF.
    JSt EAX,pfcoffFILE_32BIT_MACHINE,.F1:
    MOV ECX,pgmoptCOFF+pgmoptFLAT+pgmoptWidth16 ; Otherwise assume 16bit COFF.
.F1:SetSt [EBX+PGM.Pgmopt.Status],ECX ; Store format, model and width of the loaded module.
    MOV EDX,[ESI+PFCOFF_FILE_HEADER.PointerToSymbolTable] ; File address.
    MOV ECX,[ESI+PFCOFF_FILE_HEADER.NumberOfSymbols]
    ADD EDX,[%ModBegin] ; Convert FA to pointer to symbol table in mapped memory.
    MOV [%CoffSymCount],ECX
    MOV [%CoffSymBegin],EDX
    MOV EAX,SIZE# PFCOFF_SYMBOL
    MUL ECX
    ADD EAX,[%CoffSymBegin]
    MOV [%CoffStrBegin],EAX ; Pointer to string table following the symbol table.
    MOVZXW EDX,[ESI+PFCOFF_FILE_HEADER.SizeOfOptionalHeader]
    MOVZXW ECX,[ESI+PFCOFF_FILE_HEADER.NumberOfSections]
    LEA ESI,[ESI+EDX+SIZE# PFCOFF_FILE_HEADER] ; ESI now points to the 1st of ECX section headers.
    MOV [%CoffScnCount],ECX
    MOV [%CoffScnBegin],ESI ; Pointer to the 1st COFF section header in mapped memory.
    SUB EAX,EAX
.G1:; Convert section headers to program segments in a loop .G1: .. .G9:.
    ; ESI=^PFCOFF_SECTION_HEADER, EAX=CoffSymIndex.
    INC EAX
    MOV [%CoffScnIndex],EAX ; One-based symbol ordinal in COFF section headers table.
    CMP EAX,[%CoffScnCount]
    JA .H1: ; If there are no more section headers left to convert.
    LEA EAX,[ESI+SIZE#PFCOFF_SECTION_HEADER] ; End of this section header.
    CMP EAX,[%ModEnd]
    JNB .E7766: ; Invalid format of COFF object file "!1S".
    ; Find segment name.
    LEA EAX,[ESI+PFCOFF_SECTION_HEADER.Name]
    MOV ECX,8 ; First assume short section name.
    CMPB [EAX],'/'
    JNE .G2: ; If segment name is short, it was stored in section header directly.
    MOV EDI,EAX ; Longnames are represented as "/123" (123 is offset in the string table).
    SUB EAX,EAX
    INC EDI
    PUSH ESI
      LodD EDI,Size=7 ; Load decimal offset in the string table.
    POP ESI
    MOV EDX,[%CoffStrBegin]
    CMP EAX,4
    JNA .E7766: ; Invalid format of COFF object file "!1S".
    CMP EAX,[EDX] ; Check if out of string table.
    JNC .E7766: ; Invalid format of COFF object file "!1S".
    LEA EAX,[EDX+EAX]
    GetLength$ EAX ; Segment name at EAX is a zero-terminated string. Get its length to ECX.
.G2:MOV ESI,EAX
    StripSpaces ESI,ECX ; Get rid of trailing NULL characters, if any.
    ; ESI,ECX is now the netto section name.
    ; Create new segment in the loaded module.
    MOV EDX,[EBX+PGM.Pool]
    ListNew [EBX+PGM.SssList],Zeroed=yes ; Create a new empty segment.
    MOV EDI,EAX
    MOV [EDI+SSS.SegmPtr],EDI ; Segment always refers to itself.
    PoolStore EDX,ESI,ECX ; Make the segment name nonvolatile.
    MOV [EDI+SSS.NamePtr],EAX
    MOV [EDI+SSS.NameSize],ECX
    BufferCreate EDX
    MOV [EDI+SSS.EmitBuffer],EAX
    BufferCreate EDX
    MOV [EDI+SSS.RelocBuffer],EAX
    ; As the format COFF cannot carry COMBINE property, PUBLIC is hardwired.
    SetSt [EDI+SSS.Status],sssSegment+sssPublic
    ; Width of the loaded segment is specified by module width retrieved from the file header.
    MOV EDX,pgmoptWidthMask
    AND EDX,[EBX+PGM.Pgmopt.Status]
    OR  EDX,sssSegment ; "Section" in MS terminology is "Segment" in €ASM.
    SetSt [EDI+SSS.Status],EDX ; Encoding of pgmoptWidthMask is synchronized with sssWidthMask.
    MOV EAX,[ESI+PFCOFF_SECTION_HEADER.Characteristics] ; Find the segment's purpose.
    JNSt EAX,pfcoffSCN_CNT_CODE,.G3:
    SetSt [EDI+SSS.Purpose],sssPurposeCODE
.G3:JNSt EAX,pfcoffSCN_CNT_INITIALIZED_DATA,.G4:
    SetSt [EDI+SSS.Purpose],sssPurposeDATA
.G4:JNSt EAX,pfcoffSCN_CNT_UNINITIALIZED_DATA,.G5:
    SetSt [EDI+SSS.Purpose],sssPurposeBSS
.G5:AND EAX,pfcoffSCN_ALIGN_MASK
    SHR EAX,20 ; Convert pfcoffSCN alignment to SSS alignment.
    MOV ECX,EAX
    JECXZ .G6: ; If no section alignment was specified.
    DEC ECX
    MOV AL,1
    SHL EAX,CL
    MOV [EDI+SSS.Alignment],EAX
.G6:; If the purpose of the section still isn't set, guess by its name.
    Invoke SssGuessPurpose::,EDI ; This will set sssPurposeDRECTVE in [.drectve].
    ; Examine if the section has initialized raw data.
    MOV EAX,[ESI+PFCOFF_SECTION_HEADER.SizeOfRawData] ; Nonzero in BSS.
    MOV ECX,[ESI+PFCOFF_SECTION_HEADER.PointerToRawData] ; Zero in BSS.
    MOV [EDI+SSS.TopLow],EAX
    JECXZ .G9: ; Skip if uninitialized data section.
    SetSt [EDI+SSS.Status],sssNotBSS
    ADD ECX,[%ModBegin] ; Convert FA to pointer.
    CMP ECX,[%ModEnd]
    JNB .E7766: ; Invalid format of COFF object file "!1S".
    BufferStore [EDI+SSS.EmitBuffer],ECX,EAX ; Segment raw data.
.G9:MOV EAX,[%CoffScnIndex]
    MOV [EDI+SSS.SegmIndex],EAX ; Set one-based ordinal in COFF section headers.
    ; Section header ESI is converted, go and get the next one.
    ADD ESI,SIZE# PFCOFF_SECTION_HEADER
    JMP .G1:
.E7766:Msg '7765',EBX ; Invalid format of COFF object module "!1S".
    JMP .90:
.H1:; Convert symbols from COFF symbol table to PGM EBX symbols in  a loop .H2: .. .M9:.
    SUB EAX,EAX
    MOV [%CoffSymAux],EAX
    MOV [%CoffSymIndex],EAX
    MOV ESI,[%CoffSymBegin]
.H2:; Symbol table pass is initialized. ESI=^PFCOFF_SYMBOL, EAX=CoffSymIndex.
    CMP EAX,[%CoffSymCount]
    JAE .R1: ; If there are no more symbols records left to convert.
    MOV EAX,[%CoffSymAux]
    TEST EAX
    JZ .H3:
    DEC EAX
    MOV [%CoffSymAux],EAX
    JMP .M9: ; Skip auxiliary record ESI in the symbol table.
.H3:MOVZXB EAX,[ESI+PFCOFF_SYMBOL.NumberOfAuxSymbols]
    MOV [%CoffSymAux],EAX
    TEST EAX
    JZ .M1: ; If it has no auxilliary record, it can't be a section's symbol.
    ; Handle COFF symbols which represent a COFF section.
    MOVSXW EAX,[ESI+PFCOFF_SYMBOL.SectionNumber]
    TEST EAX
    JZ .M1:
    JS .M1: ; If not positive, it can't be a section's symbol.
    CMPB [ESI+PFCOFF_SYMBOL.StorageClass],pfcoffSYM_CLASS_STATIC
    JNE .M9: ; If storage class != 3, it can't be a section's symbol.
    ; COFF symbol at ESI represents a COFF section. Its index will be put to SSS.NameIndex.
    ; Find the corresponding segment by name.
    LEA EDX,[ESI+PFCOFF_SYMBOL.Name]
    MOV ECX,[EDX]
    JECXZ .H4: ; If the symbol name is long.
    MOV ECX,8 ; Symbol name EDX,ECX is short, NULL padded.
    JMP .H5:
.H4:MOV EAX,[EDX+4] ; Get the offset of long symbol name in string table.
    MOV EDX,[%CoffStrBegin]
    CMP EAX,[EDX] ; The first dword is the table size.
    JNB .E7766: ; Return error if out of string table.
    ADD EDX,EAX ; EDX is now pointer to ASCIIZ string.
    GetLength$ EDX
.H5:StripSpaces,EDX,ECX ; Get rid of trailing NULL characters, if any.
    Invoke SssFind::,sssSegment,0,EDX,ECX,EBX
    JC .E7766: ; If the segment was not declared in COFF section header.
    MOV ECX,[%CoffSymIndex]
    MOV [EAX+SSS.NameIndex],ECX
    JMP .M9:
.M1: ; COFF symbol at ESI is global or scalar.
    CMPB [ESI+PFCOFF_SYMBOL.StorageClass],pfcoffSYM_CLASS_EXTERNAL ; 2.
    JNE .M9: ; Skip symbol which is not global.
    ; Create a new symbol in the loaded module.
    ListNew [EBX+PGM.SymList],Zeroed=yes
    MOV EDI,EAX
    ; Convert symbol index.
    MOV EAX,[%CoffSymIndex]
    MOV [EDI+SYM.NameIndex],EAX
    ; Convert symbol name.
    LEA EDX,[ESI+PFCOFF_SYMBOL.Name]
    MOV ECX,[EDX]
    JECXZ .M3:
    MOV ECX,8 ; Symbol name EDX,ECX is short.
    JMP .M4:
.M3:MOV EAX,[EDX+4] ; Get the offset of long symbol name in string table.
    MOV EDX,[%CoffStrBegin]
    CMP EAX,[EDX] ; The first dword is the table size.
    JNB .E7766: ; Return error if out of string table.
    ADD EDX,EAX ; EDX is now pointer to ASCIIZ string.
    GetLength$ EDX
.M4:StripSpaces EDX,ECX ; Get rid of trailing NULL characters, if any.
    TEST ECX
    JZ .E7766: ; Something went wrong when the name is empty.
    PoolStore [EBX+PGM.Pool],EDX,ECX ; Make the symbol name nonvolatile.
    MOV [EDI+SYM.NamePtr],EAX
    MOV [EDI+SYM.NameSize],ECX
    ; Get scope and section assigned to COFF symbol ESI.
    SetSt [EDI+SYM.Status],symPublic+symUsed ; First assume the symbol is PUBLIC.
    MOVSXW EDX,[ESI+PFCOFF_SYMBOL.SectionNumber]
    Invoke PfcoffSecNr2Sss,EDX,EBX
    MOV [EDI+SYM.Section],EAX
    JC .M5: ; If EDI is scalar symbol.
    JA .M5: ; If EDI is public symbol in segment EAX.
    SetSt [EDI+SYM.Status],symExtern
    RstSt [EDI+SYM.Status],symPublic
    Invoke SssCreateExtern::,EDI,EBX ; Assign SYM.Section with extern pseudosegment.
.M5:; Convert symbol value. It is 0 in EXTERN|IMPORT symbols.
    MOV EAX,[ESI+PFCOFF_SYMBOL.Value]
    MOV [EDI+SYM.OffsetLow],EAX
    ; Convert symbol properties.
    MOV EDX,'N'
    MOV ECX,[EDI+SYM.Section]
    JECXZ .M6:
    MOV DL,'A'
.M6:MOV AX,[ESI+PFCOFF_SYMBOL.Type]
    CMP AH,pfcoffSYM_DTYPE_FUNCTION
    JNE .M7:
    SetSt EDX,symProc
.M7:Dispatch AL,pfcoffSYM_TYPE_CHAR,pfcoffSYM_TYPE_SHORT,pfcoffSYM_TYPE_INT,\
           pfcoffSYM_TYPE_LONG,pfcoffSYM_TYPE_FLOAT,pfcoffSYM_TYPE_DOUBLE, \
           pfcoffSYM_TYPE_BYTE,pfcoffSYM_TYPE_WORD,pfcoffSYM_TYPE_UINT, \
           pfcoffSYM_TYPE_DWORD,pfcoffSYM_TYPE_LONGDOUBLE
    JMP .M8:
.pfcoffSYM_TYPE_INT:
.pfcoffSYM_TYPE_UINT: ; DWORD or QWORD, depending on program width.
    MOV DL,'D'
    JNSt [EDI+PGM.Pgmopt.Status],pgmoptWidth64,.M8:
    MOV DL,'Q'
    JMP .M8:
.pfcoffSYM_TYPE_BYTE:
.pfcoffSYM_TYPE_CHAR:
    MOV DL,'B'
    JMP .M8:
.pfcoffSYM_TYPE_WORD:
.pfcoffSYM_TYPE_SHORT:
    MOV DL,'W'
    JMP .M8:
.pfcoffSYM_TYPE_DWORD:
.pfcoffSYM_TYPE_LONG:
.pfcoffSYM_TYPE_FLOAT:
    MOV DL,'D'
    JMP .M8:
.pfcoffSYM_TYPE_DOUBLE:
    MOV DL,'Q'
     JMP .M8:
.pfcoffSYM_TYPE_LONGDOUBLE
    MOV DL,'T'
.M8:OR [EDI+SYM.Status],EDX
.M9:; COFF symbol ESI is converted, go get the next one.
    MOV EAX,[%CoffSymIndex]
    ADD ESI,SIZE# PFCOFF_SYMBOL
    INC EAX
    MOV [%CoffSymIndex],EAX
    JMP .H2:
.R1: ; Convert COFF relocations to program relocations for each program segment.
    ListGetFirst [EBX+PGM.SssList] ; Enumerate all segments in a loop .R2: .. .R9:.
    JZ .90:
.R2:MOV EDI,EAX
    JNSt [EDI+SSS.Status],sssSegment,.R9:
    MOV EDX,[EDI+SSS.SegmIndex] ; One-based ordinal in COFF section header table.
    DEC EDX
    JS .R9: ; If segment EDI wasn't converted from COFF section header.
    MOV EAX,SIZE# PFCOFF_SECTION_HEADER
    MUL EDX
    ADD EAX,[%CoffScnBegin] ; Convert relative offset to ^PFCOFF_SECTION_HEADER.
    MOV ECX,[EAX+PFCOFF_SECTION_HEADER.NumberOfRelocations]
    JECXZ .R9:
    MOV ESI,[EAX+PFCOFF_SECTION_HEADER.PointerToRelocations]
    ADD ESI,[%ModBegin] ; Convert FA to the pointer to the first ^PFCOFF_RELOCATION.
    ; Convert ECX times PFCOFF_RELOCATION ESI to RELOC in segment EDI.
.R8:Invoke PfcoffLoadReloc,ESI,[%CoffSymBegin],[%CoffSymCount],EDI,EBX
    ADD ESI,SIZE# PFCOFF_RELOCATION
    LOOP .R8: ; Convert the next relocation.
.R9:ListGetNext EDI
    JNZ .R2: ; The next segment.
    Invoke PfDrectveDestroy::,EBX ; Transform segment [.drectve] (if exists) to symbols in module EBX.
.90:EndProcedure PfcoffLoadModule
↑ PfcoffLoadReloc CoffRelocation, CoffSymbolTable, CoffSymbolCount, Segment, ModProgram
PfcoffLoadReloc converts one COFF relocation record to €ASM reloc, which then will be stored to Segment.RelocBuffer.
Input
CoffRelocation is pointer to the input PFCOFF_RELOCATION.
CoffSymbolTable is pointer to the start of PFCOFF_SYMBOL table in COFF module mapped in memory.
CoffSymbolCount is number of symbols stored in CoffSymbolTable.
Segment is pointer to SSS segment which contains the relocation.
ModProgram is pointer to PGM loaded module.
Output
Relocation is converted.
Error
E7734, E7736.
Invokes
PfcoffSymNr2Sss
Invoked by
PfcoffLoadModule
PfcoffLoadReloc Procedure CoffRelocation, CoffSymbolTable, CoffSymbolCount, Segment, ModProgram
Reloc LocalVar Size=SIZE# RELOC ; Working room for the program relocation.
    LEA EDI,[%Reloc]
    Clear EDI,Size=SIZE#RELOC ; Here will be the RELOC record constructed.
    MOV EDX,[%CoffRelocation]
    MOV ESI,[%Segment]
    MOV ECX,[%ModProgram]
    MOV EAX,[EDX+PFCOFF_RELOCATION.VirtualAddress]
    MOV [EDI+RELOC.Section],ESI
    MOV [EDI+RELOC.OrgLow],EAX
    MOVZXW EAX,[EDX+PFCOFF_RELOCATION.Type]
    JSt [ECX+PGM.Pgmopt.Status],pgmoptWidth64,.AMD64:
.I386: ; Machine=I386 in 16|32bit programs.
    Dispatch AX,0x0006,0x0014,0x0007,0x0001,0x0002 ; Supported types of I386 relocations.
.E7734:Msg '7734',EAX,ESI ; Relocation type 0x!1W in [!2S] is not resolvable in this program format.
    JMP .90:
.0x0001:MOV EAX,relocWidth16+relocAbsVA
    JMP .30:
.0x0002:MOV EAX,relocWidth16+relocRel
    JMP .30:
.0x0006:MOV EAX,relocWidth32+relocAbsVA
    JMP .30:
.0x0007:MOV EAX,relocWidth32+relocAbsRVA
    JMP .30:
.0x0014:MOV EAX,relocWidth32+relocRel
    JMP .30:
.AMD64:; Machine=AMD64 in 64bit programs.
    Dispatch AX,0004h,0001h,0002h,0003h,0008h,0005h,0006h,0007h,0009h ; Supported types of AMD64 relocations.
    JMP .E7734:  ; Relocation type 0x!1W in [!2S] is not resolvable in this program format.
.0005h:MOV EAX,relocWidth32+relocRel+(1<<12) ; IMAGE_REL_AMD64_REL32_1.   >>
    JMP .30:
.0006h:MOV EAX,relocWidth32+relocRel+(2<<12) ; IMAGE_REL_AMD64_REL32_2.   >>
    JMP .30:
.0007h:MOV EAX,relocWidth32+relocRel+(3<<12) ; IMAGE_REL_AMD64_REL32_3.   >>
    JMP .30:
.0008h:MOV EAX,relocWidth32+relocRel+(4<<12) ; IMAGE_REL_AMD64_REL32_4.   >>
    JMP .30:
.0009h:MOV EAX,relocWidth32+relocRel+(5<<12) ; IMAGE_REL_AMD64_REL32_5.   >>
    JMP .30:
.0001h:MOV EAX,relocWidth64+relocAbsVA      ; IMAGE_REL_AMD64_ADDR64.
    JMP .30:
.0002h:MOV EAX,relocWidth32+relocAbsVA      ; IMAGE_REL_AMD64_ADDR32.
    JMP .30:
.0003h:MOV EAX,relocWidth32+relocAbsRVA     ; IMAGE_REL_AMD64_ADDR32NB.
    JMP .30:
.0004h:MOV EAX,relocWidth32+relocRel        ; IMAGE_REL_AMD64_REL32.  relocRelDist = 0.
    ;JMP .30:
.30:OR [EDI+RELOC.Status],EAX
    MOV EBX,[EDX+PFCOFF_RELOCATION.SymbolTableIndex]
    MOV ECX,[%CoffSymbolCount]
    CMP EBX,ECX
    JB .40:
    Msg '7736',EBX,ESI,ECX ; Relocation symbol index !1D in [!2S] is out of range !3D.
    JMP .90:
.40:Invoke PfcoffSymNr2Sss,EBX,[%ModProgram]
    Msg cc=C,'7739',EBX,[%ModProgram] ; Relocation of frame symbol indexed as !1D in module "!2S" not found.
    MOV [EDI+RELOC.Target],EAX
    BufferStore [ESI+SSS.RelocBuffer],EDI,SIZE#RELOC
.90:EndProcedure PfcoffLoadReloc
↑ PfcoffSecNr2Sss SectionNumber, ModulePgm
PfcoffSecNr2Sss converts COFF section number to a pointer to the coresponding segment of ModulePgm.
Input
SectionNumber is one-based signed word ordinal of COFF section header table or a special constant pfcoffSYM_UNDEFINED|pfcoffSYM_ABSOLUTE|pfcoffSYM_DEBUG in PFCOF_SYMBOL encoding.
ModulePgm is pointer to PGM (the loaded module).
Output
CF=0, ZF=0, EAX= pointer to the segment SSS of the symbol.
CF=0, ZF=1, EAX=0 when the segment is external or imported.
CF=1, ZF=1, EAX=0 when the segment is scalar.
Error
-
Invoked by
PfcoffLoadModule
PfcoffSecNr2Sss Procedure SectionNumber, ModulePgm
    MOV EDX,[%SectionNumber] ; DX=-2,-1,0,+1,+2,+3..
    MOV EBX,[%ModulePgm]
    SUB EAX,EAX
    CMP DX,AX
    JNA .80:
    ; EDX is regular one-based section index greater than zero.
    ListGetFirst [EBX+PGM.SssList] ; Find the segment with that index in module EBX.
    STC
    JZ .80:
.30:CMP [EAX+SSS.SegmIndex],EDX
    JE .70: ; Segment EAX was found.
    ListGetNext EAX
    JNZ .30:
.70:TEST EAX
.80:MOV [%ReturnEAX],EAX
   EndProcedure PfcoffSecNr2Sss
↑ PfcoffSymNr2Sss SymbolNumber, ModuleProg
PfcoffSymNr2Sss converts COFF symbol index to a pointer to the coresponding segment of ModuleProg, which represents the addressing frame.
Input
SymbolNumber is zero-based ordinal of COFF symbol in PFCOFF_SYMBOL table.
ModuleProg is pointer to PGM (the loaded module).
Output
CF=0, ZF=0, EAX= pointer to the segment or external pseudosegment SSS with SSS.NameIndex = %SymbolNumber.
CF=0, ZF=1, EAX=0 when the symbol is scalar.
CF=1, ZF=1, EAX=0 when the symbol not found.
Error
-
Invoked by
PfcoffLoadReloc
PfcoffSymNr2Sss Procedure SymbolNumber, ModuleProg
    MOV EBX,[%ModuleProg]
    MOV EDX,[%SymbolNumber] ; EDX=0,1,2,3..
   ; First search module EBX segments.
    ListGetFirst [EBX+PGM.SssList]
    JZ .20:
.10:CMP [EAX+SSS.NameIndex],EDX
    JE .40:
    ListGetNext EAX
    JNZ .10:
.20:; If %SymbolNumber doesn't match a segment, investigate program symbols and use their SYM.Section.
    ListGetFirst [EBX+PGM.SymList]
    STC
    JZ .80:
.30:CMP [EAX+SYM.NameIndex],EDX
    JNE .50:
    MOV EAX,[EAX+SYM.Section]
.40:TEST EAX ; Set ZF=1 if its scalar.
    JMP .80:
.50:ListGetNext EAX
    STC
    JNZ .30:
.80:MOV [%ReturnEAX],EAX
   EndProcedure PfcoffSymNr2Sss
    ENDPROGRAM pfcoff

▲Back to the top▲