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 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 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 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 value | MS SDK nomenclature | Remark | €ASM RELOC.Type |
|---|---|---|---|
| 0x0000 | IMAGE_REL_I386_ABSOLUTE | Reference is absolute, no relocation is necessary | relocResolved |
| 0x0001 | IMAGE_REL_I386_DIR16 | Direct 16-bit reference to the symbols virtual address | relocAbsVA + relocWidth16 |
| 0x0002 | IMAGE_REL_I386_REL16 | IP-relative 16-bit reference to the symbols virtual address | relocRel + relocWidth16 |
| 0x0006 | IMAGE_REL_I386_DIR32 | Direct 32-bit reference to the symbols virtual address | relocAbsVA + relocWidth32 |
| 0x0007 | IMAGE_REL_I386_DIR32NB | Direct 32-bit reference to the symbols virtual address, base not included | relocAbsRVA + relocWidth32 |
| 0x0009 | IMAGE_REL_I386_SEG12 | Direct 16-bit reference to the segment-selector bits of a 32-bit virtual address | relocPara + relocWidth16 |
| 0x000A | IMAGE_REL_I386_SECTION | The 16-bit section index of the section that contains the target. This is used to support debugging information. | not supported |
| 0x000B | IMAGE_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 |
| 0x000C | IMAGE_REL_I386_TOKEN | CLR token | not supported |
| 0x000D | IMAGE_REL_I386_SECREL7 | 7 bit offset from base of section containing target | not supported |
| 0x0014 | IMAGE_REL_I386_REL32 | EIP-relative 32-bit reference to the symbols virtual address | relocRel + relocWidth32 |
| .Type value | MS SDK nomenclature | Remark | €ASM RELOC.Type |
|---|---|---|---|
| 0x0000 | IMAGE_REL_AMD64_ABSOLUTE | Reference is absolute, no relocation is necessary | relocResolved |
| 0x0001 | IMAGE_REL_AMD64_ADDR64 | The 64-bit VA of the relocation target. | relocAbsVA + relocWidth64 |
| 0x0002 | IMAGE_REL_AMD64_ADDR32 | The 32-bit VA of the relocation target. | relocAbsVA + relocWidth32 |
| 0x0003 | IMAGE_REL_AMD64_ADDR32NB | The 32-bit address without an image base (RVA). | relocAbsRVA + relocWidth32 |
| 0x0004 | IMAGE_REL_AMD64_REL32 | The 32-bit relative address from the byte following the relocation. | relocRel + relocWidth32 |
| 0x0005 | IMAGE_REL_AMD64_REL32_1 | The 32-bit address RIP-relative to byte distance 1 from the relocation. | relocRel + relocWidth32 + 1<<12 |
| 0x0006 | IMAGE_REL_AMD64_REL32_2 | The 32-bit address RIP-relative to byte distance 2 from the relocation. | relocRel + relocWidth32 + 2<<12 |
| 0x0007 | IMAGE_REL_AMD64_REL32_3 | The 32-bit address RIP-relative to byte distance 3 from the relocation. | relocRel + relocWidth32 + 3<<12 |
| 0x0008 | IMAGE_REL_AMD64_REL32_4 | The 32-bit address RIP-relative to byte distance 4 from the relocation. | relocRel + relocWidth32 + 4<<12 |
| 0x0009 | IMAGE_REL_AMD64_REL32_5 | The 32-bit address RIP-relative to byte distance 5 from the relocation. | relocRel + relocWidth32 + 5<<12 |
| 0x000A | IMAGE_REL_AMD64_SECTION | The 16-bit section index of the section that contains the target. This is used to support debugging information. | not supported |
| 0x000B | IMAGE_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 |
| 0x000C | IMAGE_REL_AMD64_SECREL7 | A 7-bit unsigned offset from the base of the section that contains the target. | not supported |
| 0x000D | IMAGE_REL_AMD64_TOKEN | CLR tokens. | not supported |
| 0x000E | IMAGE_REL_AMD64_SREL32 | A 32-bit signed span-dependent value emitted into the object. | not supported |
| 0x000F | IMAGE_REL_AMD64_PAIR | A pair that must immediately follow every span-dependent value. | not supported |
| 0x0010 | IMAGE_REL_AMD64_SSPAN32 | A 32-bit signed span-dependent value that is applied at link time. | not supported |
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 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 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
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 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 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
Segment.NameIndex.
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
Symbol.NameIndex.
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..PfcoffStubEnd represent compiled
16bit SMALL MZ program, built in euroasm.exefile. It reports a message This program was launched in DOS but it requires Windows.
coffstub.exewas 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]
.Machine, .TimeDateStamp, .SizeOfOptionalHeader, .Characteristics
. Other members are zeroed.
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
FileAlign= and then emitted data and relocations
will be virtually saved to output file at this FA.
FileAlign= alignment.
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
.NumberOfSections will be incremented.
This incremented section ordinal will be also written to SSS.SegmIndex of the Segment.
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 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.
BasePgm.ModulePgmList as a new
PGM structure.
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 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.
BasePgm.ModulePgmList as a new
PGM structure.
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 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
pfcoffSYM_UNDEFINED|pfcoffSYM_ABSOLUTE|pfcoffSYM_DEBUG in
PFCOF_SYMBOL encoding.
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
SSS.NameIndex = %SymbolNumber.
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