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