| Index | Manual | Download | Source | Macros | Embedded word Case ins.
Sitemap | Links | Forum | Tests | Projects

# pfelf.htm
Structures
PFELF
PFELF_DYN32
PFELF_DYN64
PFELF_E_IDENT
PFELF_EHDR32
PFELF_EHDR64
PFELF_PHDR32
PFELF_PHDR64
PFELF_REL32
PFELF_REL64
PFELF_RELA32
PFELF_RELA64
PFELF_SHDR32
PFELF_SHDR64
PFELF_SYM32
PFELF_SYM64
Encodings
PfelfEncodings
Procedures
PfelfCompile
PfelfConvertSymbols
PfelfCreate
PfelfDestroy
PfelfEhdr
PfelfHdrsLink
PfelfHdrsStream
PfelfLink
PfelfLoadPgm
PfelfPhdr
PfelfRelaCreate
PfelfRelaConvert
PfelfShdr
PfelfSssTab
PfelfSssSeg

This EuroAssembler source pfelf.htm generates output linkable object file in program format ELF as described in [ELF32] and [ELF64].

> Section in ELF documentation is equivalent to €ASM SEGMENT.
> Segment in ELF documentation is equivalent to €ASM GROUP or module.

          EUROASM NOWARN=2101
    pfelf PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32
          INCLUDEHEAD euroasm.htm, \  Interface (structures, symbols and macros) of other modules used in this source.
                      ea.htm,eaopt.htm,exp.htm,msg.htm,pf.htm,pfelfx.htm,pgm.htm,pgmopt.htm,reloc.htm,sss.htm,stm.htm,sym.htm

     pfelf HEAD ; Start module interface.

↑ PfelfEncodings
  Boolean flags and attributes used in ELF structure members.
Documented
  [SystemV].

    ; PFELF_E_IDENT.e_class
    pfelfClass32    = 1    ; 32bit object.
    pfelfClass64    = 2    ; 64bit object.

    ; PFELF_E_IDENT.e_osabi
    pfelfOSABI_NONE    = 0
    pfelfOSABI_SYSV    = 0
    pfelfOSABI_HPUX    = 1
    pfelfOSABI_NETBSD  = 2
    pfelfOSABI_LINUX   = 3
    pfelfOSABI_GNU     = 3
    pfelfOSABI_SOLARIS = 6
    pfelfOSABI_AIX     = 7
    pfelfOSABI_FREEBSD = 9
    pfelfOSABI_OPENBSD = 12
    pfelfOSABI_OPENVMS = 13

    ; PFELF_EHDR.e_type
    pfelfRel        = 1    ; Relocatable object file.
    pfelfExec       = 2    ; Executable file.
    pfelfDyn        = 3    ; Shared object file.

    ; PFELF_EHDR.e_machine
    pfelfMachine386 = 0x0003 ; Intel x86.
    pfelfMachineX64 = 0x003E ; Intel x86_64.

    ; PFELF_SHDR.sh_type
    pfelfSHT_NULL     =  0
    pfelfSHT_PROGBITS =  1
    pfelfSHT_SYMTAB   =  2
    pfelfSHT_STRTAB   =  3
    pfelfSHT_RELA     =  4
    pfelfSHT_HASH     =  5
    pfelfSHT_DYNAMIC  =  6
    pfelfSHT_NOTE     =  7
    pfelfSHT_NOBITS   =  8
    pfelfSHT_REL      =  9
    pfelfSHT_SHLIB    = 10
    pfelfSHT_DYNSYM   = 11

    ; PFELF_SHDR.sh_flags
    pfelfSHF_WRITE     = 0x0000_0001 ; Section contains writeble data.
    pfelfSHF_ALLOC     = 0x0000_0002 ; Section will be allocated and readable.
    pfelfSHF_EXECINSTR = 0x0000_0004 ; Section contains executable instructions.

    ; PFELF_SYM.st_info as sum of pfelfSTT* and pfelfSTB*.
    pfelfSTT_NOTYPE    = 0x00 ; Symbol is scalar.
    pfelfSTT_OBJECT    = 0x01 ; Symbol represents data object.
    pfelfSTT_FUNC      = 0x02 ; Symbol represents function/procedure which requires PLT in SO format.
    pfelfSTT_SECTION   = 0x03 ; Symbol represents section identified by PFELF_SYM.st_shndx.
    pfelfSTT_FILE      = 0x04 ; Symbol represents source file.
    pfelfSTB_LOCAL     = 0x00 ; Symbol has standard private scope.
    pfelfSTB_GLOBAL    = 0x10 ; Symbol is public.
    pfelfSTB_WEAK      = 0x20 ; Symbol is public if no other linked object file defines it.

    ; PFELF_SYM.st_shndx special values.
    pfelfSHN_UNDEF     = 0x0000 ; Symbol is external.
    pfelfSHN_ABS       = 0xFFF1 ; Symbol is scalar.
    pfelfSHN_COMMON    = 0xFFF2 ; Symbol represents a common block not allocated yet in linking.

    ; PFELF_REL32.r_info LSB:
    ; Relocation type flag          val width calculation
    pfelfR_386_NONE               =  0 ;  -   none
    pfelfR_386_32                 =  1 ; 32   S + A
    pfelfR_386_PC32               =  2 ; 32   S + A - P
    pfelfR_386_GOT32              =  3 ; 32   G + A - P
    pfelfR_386_PLT32              =  4 ; 32   L + A - P
    pfelfR_386_COPY               =  5 ;  -   none
    pfelfR_386_GLOB_DAT           =  6 ; 32   S
    pfelfR_386_JMP_SLOT           =  7 ; 32   S
    pfelfR_386_RELATIVE           =  8 ; 32   B + A
    pfelfR_386_GOTOFF             =  9 ; 32   S + A - GOT
    pfelfR_386_GOTPC              = 10 ; 32   GOT + A -P
    ; PFELF_RELA64.r_info LSDW:
    pfelfR_X86_64_NONE            =  0 ;  -   none
    pfelfR_X86_64_64              =  1 ; 64   S + A
    pfelfR_X86_64_PC32            =  2 ; 32   S + A - P
    pfelfR_X86_64_GOT32           =  3 ; 32   G + A
    pfelfR_X86_64_PLT32           =  4 ; 32   L + A - P
    pfelfR_X86_64_COPY            =  5 ;  -   none
    pfelfR_X86_64_GLOB_DAT        =  6 ; 64   S
    pfelfR_X86_64_JUMP_SLOT       =  7 ; 64   S
    pfelfR_X86_64_RELATIVE        =  8 ; 64   B + A
    pfelfR_X86_64_GOTPCREL        =  9 ; 32   G + GOT + A - P
    pfelfR_X86_64_32              = 10 ; 32   S + A   ; Field is zero-extended to 64 bits.
    pfelfR_X86_64_32S             = 11 ; 32   S + A   ; Field is sign-extended to 64 bits.
    pfelfR_X86_64_16              = 12 ; 16   S + A
    pfelfR_X86_64_PC16            = 13 ; 16   S + A - P
    pfelfR_X86_64_8               = 14 ;  8   S + A
    pfelfR_X86_64_PC8             = 15 ;  8   S + A - P
    pfelfR_X86_64_DTPMOD64        = 16 ; 64
    pfelfR_X86_64_DTPOFF64        = 17 ; 64
    pfelfR_X86_64_TPOFF64         = 18 ; 64
    pfelfR_X86_64_TLSGD           = 19 ; 32
    pfelfR_X86_64_TLSLD           = 20 ; 32
    pfelfR_X86_64_DTPOFF32        = 21 ; 32
    pfelfR_X86_64_GOTTPOFF        = 22 ; 32
    pfelfR_X86_64_TPOFF32         = 23 ; 32
    pfelfR_X86_64_PC64            = 24 ; 64   S + A - P
    pfelfR_X86_64_GOTOFF64        = 25 ; 64   S + A - GOT
    pfelfR_X86_64_GOTPC32         = 26 ; 32   GOT + A - P
    pfelfR_X86_64_SIZE32          = 32 ; 32   Z + A
    pfelfR_X86_64_SIZE64          = 33 ; 64   Z + A
    pfelfR_X86_64_GOTPC32_TLSDESC = 34 ; 32
    pfelfR_X86_64_TLSDESC_CALL    = 35 ;  -
    pfelfR_X86_64_TLSDESC         = 36 ; 64*2
    pfelfR_X86_64_IRELATIVE       = 37 ; 64   indirect (B + A)

    ; For calculation shortcuts (S,A,GOT etc) see reloc.htm.
    ;
    ; The SYSTEM V architecture uses only PFELF_REL32 relocation entries,
    ; the field to be relocated holds the addend.
    ;
    ; The AMD64 ABI architectures uses only PFELF_RELA64 relocation entries
    ; with explicit addends. The .r_addend member serves as the relocation addend.

    ; PFELF_PHDR.p_type
    pfelfPT_NULL    = 0 ; Unused entry.
    pfelfPT_LOAD    = 1 ; Loadable segment.
    pfelfPT_DYNAMIC = 2 ; Dynamic linking tables.
    pfelfPT_INTERP  = 3 ; Program interpreter path name.
    pfelfPT_NOTE    = 4 ; Note sections.
    pfelfPT_SHLIB   = 5 ; Reserved.
    pfelfPT_PHDR    = 6 ; Program header table.

    ; PFELF_PHDR.p_flags
    pfelfPF_X       = 0x0001 ; Execute permission.
    pfelfPF_W       = 0x0002 ; Write permission.
    pfelfPF_R       = 0x0004 ; Read permission.

    ; PFELF_DYN.d_tag, see Dynamic Section
    pfelfDT_BIND_NOW = 24
    pfelfDT_DEBUG    = 21
    pfelfDT_FLAGS    = 30
    pfelfDT_FLAGS_1  = 0x6FFFFFFB
    pfelfDT_GNU_HASH = 0x6FFFFEF5
    pfelfDT_HASH     =  4
    pfelfDT_JMPREL   = 23
    pfelfDT_NEEDED   =  1
    pfelfDT_NULL     =  0
    pfelfDT_PLTGOT   =  3
    pfelfDT_PLTREL   = 20
    pfelfDT_PLTRELSZ =  2
    pfelfDT_REL      = 17
    pfelfDT_RELA     =  7
    pfelfDT_RELAENT  =  9
    pfelfDT_RELASZ   =  8
    pfelfDT_RELENT   = 19
    pfelfDT_RELSZ    = 18
    pfelfDT_RPATH    = 15
    pfelfDT_RUNPATH  = 29
    pfelfDT_SONAME   = 14
    pfelfDT_STRSZ    = 10
    pfelfDT_STRTAB   =  5
    pfelfDT_SYMENT   = 11
    pfelfDT_SYMTAB   =  6
    pfelfDT_TEXTREL  = 22

    pfelfDF_1_NOW    = 1
    pfelfDF_BIND_NOW = 8
    pfelfDF_TEXTREL  = 4

    ; Flags used in PFELF.Status.
    pfelfFormatELF   = 0x0000_0001 ; Compiled format is object ELF file for static linking.
    pfelfFormatELFX  = 0x0000_0002 ; Compiled format is executable ELF file.
    pfelfFormatELFSO = 0x0000_0004 ; Compiled format is object ELF file for dynamic linking (DSO).
    pfelfDynamic     = 0x0000_0400 ; Compiled ELFX links at least one DSO.
    pfelfTEXTREL     = 0x0000_0100 ; Relocations in non-writeable section(s) exist in DSO.
    pfelfWidth32     = 0x0200_0000 ; Compiled format is 32bit.
    pfelfWidth64     = 0x0400_0000 ; Compiled format is 64bit.

↑ PFELF
  Object of this structure is a set of temporary parameters used for compiling ELF-based file formats.
  It is constructed in `Pfelf*Compile` and its pointer is delivered to other methods as the parameter.

    PFELF        STRUC
                      ; +00h.   Pointers to BUFFER for non-section components:
    .Buffer           D DWORD ; Temporary buffer used by SssFindByPurpose.
    .Buffer.EHDR      D DWORD ; ELF file header.
    .Buffer.PHDR      D DWORD ; ELF program headers.
    .Buffer.SHDR      D DWORD ; ELF section headers.
                      ; +10h.   Values of EHDR members.
    .EHDR.e_phoff     D DWORD ; Program headers offset (FA).
    .EHDR.e_shoff     D DWORD ; Section headers offset (FA).
    .EHDR.e_phnum     D DWORD ; Number of program header entries.
    .EHDR.e_shnum     D DWORD ; Number of section header entries.
                      ; +20h.   Pointers to  SSS groups (ELF "program sections").
    .Sss.LOAD.HDR     D DWORD ; €ASM group of ELF LOAD program segment loading EHDR,PHDR,SHDR and ELF-special sections.
    .Sss.LOAD.CODE    D DWORD ; €ASM group of ELF LOAD program segment loading .text and .plt.
    .Sss.LOAD.RODATA  D DWORD ; €ASM group of ELF LOAD program segment loading .rodata.
    .Sss.LOAD.DATA    D DWORD ; €ASM group of ELF LOAD program segment loading .data, .got and .bss.
    .Sss.LOAD.DYNAMIC D DWORD ; €ASM group of ELF LOAD program segment loading .dynamic section.
    .Sss.DYNAMIC      D DWORD ; €ASM group of ELF DYNAMIC program segment.
    .Sss.PHDR         D DWORD ; €ASM group of ELF PHDR program segment which covers PHDR records.
    .Sss.INTERP       D DWORD ; €ASM group of ELF INTERP program segment, which covers .interp.
                              ; Pointers to BUFFER for DWORD pointers to SSS segments.
    .Buffer.CODE      D DWORD ; Buffer for pointers to executable segments (purpose CODE, PLT).
    .Buffer.RODATA    D DWORD ; Buffer for pointers to read-only data segments (purpose RODATA).
    .Buffer.DATA      D DWORD ; Buffer for pointers to read-write segments (purpose DATA, GOT, BSS).
                              ; Pointers to SSS with ELF-specific sections.
    .Sss              D DWORD ; €ASM segment [] (an empty NULL ELF section).
    .Sss.symtab       D DWORD ; €ASM segment [.symtab] (static symbol table).
    .Sss.strtab       D DWORD ; €ASM segment [.strtab] (names of symbols).
    .Sss.shstrtab     D DWORD ; €ASM segment [.shstrtab] (names of sections).
    .Sss.interp       D DWORD ; €ASM segment [.interp] (name of dynamic runtime linker).
    .Sss.dynsym       D DWORD ; €ASM segment [.dynsym] (dynamic symbol table).
    .Sss.hash         D DWORD ; €ASM segment [.hash] (hashtable of dynamic symbol names).
    .Sss.dynstr       D DWORD ; €ASM segment [.dynstr] (names of dynamic symbols, needed files, runpath).
    .Sss.dynamic      D DWORD ; €ASM segment [.dynamic] (information for dynamic linker).
    .Sss.plt          D DWORD ; €ASM segment [.plt] (Procedure Linkage Table).
    .Sss.got          D DWORD ; €ASM segment [.got] (Global Offset Table).
    .Sss.rela.dyn     D DWORD ; €ASM segment [.rel.dyn] or [.rela.dyn] (relocations in DSO).
    .Sss.rela.plt     D DWORD ; €ASM segment [.rel.plt] or [.rela.plt] (relocations in PLT).
                              ; Miscellaneous data:
    .Status           D DWORD ; See PFELF flags
    .Pgm              D DWORD ; Pointer to PGM of the compiled program.
    .relSize          D DWORD ; Size of one PFELF_REL* record (08h/10h/0Ch*18h).
    .symSize          D DWORD ; Size of one PFELF_SYM* record (10h/18h).
    .dynstrSize       D DWORD ; Size of names in dynamic symbol section before NEEDED and RUNPATH are appended to [.dynstr].
    .PHDRSize         D DWORD ; Size of PFELF_PHDR (20h or 38h).
    .HDRsEnd          D DWORD ; End of the last header from EHDR,PHDR,SHDR (FA).
                 ENDSTRUC PFELF

↑ PFELF_E_IDENT
  Substructure used in ELF file header as a member PFELF_EHDR.e_ident

    PFELF_E_IDENT STRUC        ; Identical for 32bit and 64bit versions.
     .e_mag0       D BYTE 0x7F ; Magic number.
     .e_mag        D BYTE 'ELF'; Format identificator.
     .e_class      D BYTE pfelfClass%^WIDTH ; pfelfClass32 or pfelfClass64, specified by %^Width.
     .e_data       D BYTE 1    ; 1=Little Endian, 2=Big Endian.
     .e_version    D BYTE 1    ; ELF format version. Specified by %^MajorImageVersion=1.
     .e_osabi      D BYTE 0    ; System V ABI.
     .e_abiversion D BYTE 0    ; SystemV ABI 3rd edition.
     .e_pad        D 7*BYTE 0  ; Not used.
     ENDSTRUC PFELF_E_IDENT    ; SIZE# PFELF_IDENT = 16 = 10h.

↑ PFELF_EHDR32
  32bit ELF File Header. Always located at FA=0.

    PFELF_EHDR32 STRUC
     .e_ident      D PFELF_E_IDENT,.e_class=pfelfClass32 ; See above.
                   ; +10h.
     .e_type       D  WORD ; Object file type. 1=linkable, 2=executable, 3=shared object.
     .e_machine    D  WORD ; Machine type. 0x0003=x86
     .e_version    D DWORD ; Object file version. Specified by %^MajorLinkerVersion=1.
     .e_entry      D DWORD ; Entry point VA, specified by %^Entry=.
     .e_phoff      D DWORD ; Program headers offset (FA).
                   ; +20h.
     .e_shoff      D DWORD ; Section headers offset (FA).
     .e_flags      D DWORD ; Processor specific flags, always 0.
     .e_ehsize     D  WORD ; ELF header size = 52.
     .e_phentsize  D  WORD ; Size of one program header entry = 32 = 20h.
     .e_phnum      D  WORD ; Number of program header entries.
     .e_shentsize  D  WORD ; Size of one section header entry = 40 = 28h.
                   ; +30h.
     .e_shnum      D  WORD ; Number of section header entries.
     .e_shstrndx   D  WORD ; Section name string table index.
     ENDSTRUC PFELF_EHDR32 ; SIZE# PFELF_EHDR32 = 52 = 34h.

↑ PFELF_EHDR64
  64bit ELF File Header. Always located at FA=0.

    PFELF_EHDR64 STRUC
     .e_ident      D PFELF_E_IDENT,.e_class=pfelfClass64 ; See above.
                   ; +10h.
     .e_type       D  WORD ; Object file type. 1=linkable, 2=executable, 3=shared object.
     .e_machine    D  WORD ; Machine type. 0x003E=x86_64.
     .e_version    D DWORD ; Object file version. Specified by %^MajorLinkerVersion=1.
     .e_entry      D QWORD ; Entry point VA, specified by %^Entry=.
                   ; +20h.
     .e_phoff      D QWORD ; Program headers offset (FA).
     .e_shoff      D QWORD ; Section headers offset (FA).
                   ; +30h.
     .e_flags      D DWORD ; Processor specific flags, always 0.
     .e_ehsize     D  WORD ; ELF header size = 64.
     .e_phentsize  D  WORD ; Size of one program header entry = 56 = 38h.
     .e_phnum      D  WORD ; Number of program header entries.
     .e_shentsize  D  WORD ; Size of one section header entry = 64 = 40h.
     .e_shnum      D  WORD ; Number of section header entries.
     .e_shstrndx   D  WORD ; Section name string table index.
     ENDSTRUC PFELF_EHDR64 ; SIZE# PFELF_EHDR64 = 64 = 040h.

↑ PFELF_PHDR32
  32bit ELF program (€ASM group) header.

    PFELF_PHDR32 STRUC
     .p_type       D DWORD ; Type of segment. 1=loadable 2=dynamic 3=interpreter etc.
     .p_offset     D DWORD ; FA of group.
     .p_vaddr      D DWORD ; VA of group.
     .p_paddr      D DWORD ; Reserved, copy of p_vaddr.
                   ; +10h.
     .p_filesz     D DWORD ; Group size in file.
     .p_memsz      D DWORD ; Group size in memory.
     .p_flags      D DWORD ; Group attributes. 1=exec  2=write  4=read.
     .p_align      D DWORD ; Group alignment, specified by %^SectionAlign for regular purposes.
     ENDSTRUC PFELF_PHDR32 ; SIZE# PFELF_PHDR32 = 32 = 20h.

↑ PFELF_PHDR64
  64bit ELF program (€ASM group) header.

    PFELF_PHDR64 STRUC
     .p_type       D DWORD ; Type of segment. 1=loadable 2=dynamic 3=interpreter etc.
     .p_flags      D DWORD ; Group attributes. 1=exec  2=write  4=read.
     .p_offset     D QWORD ; FA of group.
                   ; +10h.
     .p_vaddr      D QWORD ; VA of group.
     .p_paddr      D QWORD ; Reserved, copy of p_vaddr.
                   ; +20h.
     .p_filesz     D QWORD ; Group size in file.
     .p_memsz      D QWORD ; Group size in memory.
                   ; +30h.
     .p_align      D QWORD ; Group alignment, specified by %^SectionAlign for regular purposes.
     ENDSTRUC PFELF_PHDR64 ; SIZE# PFELF_PHDR64 = 56 = 38h.

↑ PFELF_SHDR32
  32bit ELF section header.

    PFELF_SHDR32 STRUC
     .sh_name      D DWORD ; Section name (offset in string table section .shstrtab).
     .sh_type      D DWORD ; Section type. See %^PURPOSE: 1=PROGBITS, 2=SYMTAB, 3=STRTAB etc.
     .sh_flags     D DWORD ; Section attributes. 1=writeable, 2=allocated(readable), 4=executable.
     .sh_addr      D DWORD ; VA in memory.
                   ; +10h.
     .sh_offset    D DWORD ; FA in file.
     .sh_size      D DWORD ; Size of section.
     .sh_link      D DWORD ; Link to other section, see above.
     .sh_info      D DWORD ; Miscellaneous information, see above.
                   ; +20h.
     .sh_addralign D DWORD ; Section FA alignment. Specified by SEGMENT ALIGN=.
     .sh_entsize   D DWORD ; Size of entries, if the section has table.
     ENDSTRUC PFELF_SHDR32 ; SIZE# PFELF_SHDR32 = 40 = 28h.

↑ PFELF_SHDR64
  64bit ELF section header.

    PFELF_SHDR64 STRUC
     .sh_name      D DWORD ; Section name (offset in string table section .shstrtab).
     .sh_type      D DWORD ; Section type. See %^PURPOSE: 1=PROGBITS, 2=SYMTAB, 3=STRTAB etc.
     .sh_flags     D QWORD ; Section attributes. Section attributes. 1=writeable, 2=allocated(readable), 4=executable.
                   ; +10h.
     .sh_addr      D QWORD ; VA in memory.
     .sh_offset    D QWORD ; FA in file.
                   ; +20h.
     .sh_size      D QWORD ; Size of section.
     .sh_link      D DWORD ; Link to other section, see above.
     .sh_info      D DWORD ; Miscellaneous information, see above.
                   ; +30h.
     .sh_addralign D QWORD ; Section FA alignment. Specified by SEGMENT ALIGN=.
     .sh_entsize   D QWORD ; Size of entries, if the section has table.
     ENDSTRUC PFELF_SHDR64 ; SIZE# PFELF_SHDR64 64 = 40h.

↑ PFELF_SYM32
  32bit ELF symbol table.

    PFELF_SYM32 STRUC
     .st_name      D DWORD ; Symbol name (offset in string table section .strtab).
     .st_value     D DWORD ; Symbol value/offset.
     .st_size      D DWORD ; Symbol size.
     .st_info      D BYTE  ; Bind and type, see above: 0=LOCAL 1=OBJECT 2=FUNC 3=SECTION 4=FILE 10=GLOBAL
     .st_other     D BYTE  ; Reserved.
     .st_shndx     D  WORD ; Index of section where the symbol belongs.
     ENDSTRUC PFELF_SYM32  ; SIZE# PFELF_SYM32 = 16 = 10h.

↑ PFELF_SYM64
  64bit ELF symbol table.

    PFELF_SYM64 STRUC
     .st_name      D DWORD ; Symbol name (offset in string table section .strtab).
     .st_info      D  BYTE ; Bind and type, see above: 0=LOCAL 1=OBJECT 2=FUNC 3=SECTION 4=FILE 10=GLOBAL
     .st_other     D  BYTE ; Reserved.
     .st_shndx     D  WORD ; Index of section where the symbol belongs.
     .st_value     D QWORD ; Symbol value/offset.
                   ; +10h.
     .st_size      D QWORD ; Symbol size.
     ENDSTRUC PFELF_SYM64 ; SIZE# PFELF_SYM64 = 24 = 18h.

↑ PFELF_REL32
  32bit ELF relocation without addend.

    PFELF_REL32 STRUC
     .r_offset     D DWORD ; Address of reference (of DWORD with addend which is updated).
     .r_info       D DWORD ; Relocation type (in LSB) and symbol index (in bits 8..31).
     ENDSTRUC PFELF_REL32  ; SIZE# PFELF_REL32 = 8.

↑ PFELF_REL64
  64bit ELF relocation without addend.

    PFELF_REL64 STRUC
     .r_offset     D QWORD ; Address of reference (of DWORD/QWORD with addend which is updated).
     .r_info       D QWORD ; Relocation type (in low DWORD) and symbol index (in high DWORD).
     ENDSTRUC PFELF_REL64  ; SIZE# PFELF_REL64 = 16 = 10h.

↑ PFELF_RELA32
  32bit ELF relocation with addend.

    PFELF_RELA32 STRUC
     .r_offset     D DWORD ; Address of reference (of DWORD which is updated).
     .r_info       D DWORD ; Relocation type (in LSB) and symbol index (in bits 8..31).
     .r_addend     D DWORD ; Value to be added to the D|WORD which is fixed-up.
     ENDSTRUC PFELF_RELA32 ; SIZE# PFELF_RELA32 = 12 = 0Ch.

↑ PFELF_RELA64
  64bit ELF relocation with addend.

    PFELF_RELA64 STRUC
     .r_offset     D QWORD ; Address of reference (of DWORD/QWORD which is updated).
     .r_info       D QWORD ; Relocation type (in low DWORD) and symbol index (in high DWORD).
     .r_addend     D QWORD ; Value to be added to the Q|D|WORD which is fixed-up.
     ENDSTRUC PFELF_RELA64 ; SIZE# PFELF_RELA64 = 24 = 18h.

↑ PFELF_DYN32
  32bit ELF Dynamic structure.

    PFELF_DYN32 STRUC
     .d_tag        D DWORD   ; Dynamic element type.
     .d_val        D 0*DWORD ; Dynamic element value, aliased by pointer.
     .d_ptr        D DWORD   ; Dynamic element pointer.
        ENDSTRUC PFELF_DYN32 ; SIZE# PFELF_DYN32 = 8

↑ PFELF_DYN64
  64bit ELF Dynamic structure.

    PFELF_DYN64 STRUC
     .d_tag        D QWORD   ; Dynamic element type.
     .d_val        D 0*QWORD ; Dynamic element value, aliased by pointer.
     .d_ptr        D QWORD   ; Dynamic element pointer.
        ENDSTRUC PFELF_DYN64 ; SIZE# PFELF_DYN64 = 16 = 10h.

     ENDHEAD pfelf  ; End of module interface.

↑ PfelfCompile OutputStream, Pgm
  is constructor of output 32bit or 64bit statically linkable module in ELF format.
Documented
  [ELF32], [ELF64].
Input
  OutputStream is pointer to a STREAM for the output file contents.
  Pgm is pointer to PGM representing completely assembled and combined program.
Output
  OutputStream is filled with output file contents.
Error
  Errors are reported with macro Msg.
Invoked from
  PfOutput
Invokes
  PfDrectveCreate PfelfConvertSymbols PfelfCreate PfelfDestroy PfelfEhdr PfelfHdrsLink PfelfHdrsStream PfelfLink PfelfRelaConvert PfelfRelaCreate PfelfShdr PfelfSssSeg PfelfSssTab PgmOrderSymbols PgmStreamImage PgmSymResolve SssCreateGroup SssFindByPurpose

    PfelfCompile Procedure OutputStream, Pgm
    Pfelf LocalVar Size=SIZE# PFELF                  ; PFELF object.
        MOV EBX,[%Pgm]                               ; Initialization.
        LEA EDI,[%Pfelf]
        Invoke PfelfCreate::,EDI,EBX                 ; Initialize helper object PFELF.
        Invoke PfelfSssTab::,EDI,EAX                  ; Create ELF-special empty SSS segments [], [.symtab], [.strtab], [.shstrtab].
    ;Create groups of regular segments.
        Invoke PfelfSssSeg,EDI                        ; Fill PFELF.Buffer.CODE/RODATA/DATA with pointers to regular segments.
        BufferRetrieve [EDI+PFELF.Buffer.CODE]
        SHR ECX,2
        JZ .15:                                       ; Do not create [LOAD.CODE] group when no CODE/PLT segment exists.
        Invoke SssCreateGroup::,[EBX+PGM.CurrentStm],=B"LOAD.CODE",9,0,0
        MOV EDX,EAX
        MOV [EDX+SSS.Alignment],%LinPageSize
        MOV [EDI+PFELF.Sss.LOAD.CODE],EDX
    .10:LODSD
        MOV [EAX+SSS.GroupPtr],EDX
        LOOP .10:
    .15:BufferRetrieve [EDI+PFELF.Buffer.RODATA]
        SHR ECX,2
        JZ .25:                                       ; Do not create [LOAD.RODATA] group when no RODATA segment exists.
        Invoke SssCreateGroup::,[EBX+PGM.CurrentStm],=B"LOAD.RODATA",11,0,0
        MOV EDX,EAX
        MOV [EDX+SSS.Alignment],%LinPageSize
        MOV [EDI+PFELF.Sss.LOAD.RODATA],EDX
    .20:LODSD
        MOV [EAX+SSS.GroupPtr],EDX
        LOOP .20:
    .25:BufferRetrieve [EDI+PFELF.Buffer.DATA]
        SHR ECX,2
        JZ .35:                                       ; Do not create [LOAD.DATA] group when no DATA/GOT/BSS segment exists.
        Invoke SssCreateGroup::,[EBX+PGM.CurrentStm],=B"LOAD.DATA",9,0,0
        MOV EDX,EAX
        MOV [EDX+SSS.Alignment],%LinPageSize
        MOV [EDI+PFELF.Sss.LOAD.DATA],EDX
    .30:LODSD
        MOV [EAX+SSS.GroupPtr],EDX
        LOOP .30:
    .35:Invoke PfDrectveCreate::,[EDI+PFELF.Pgm]     ; Create NOTE segment [.drectve] when imported|exported symbols exist.
        Invoke PfelfRelaCreate,EDI                   ; Create segments [.rel(a)name] for each segment with relocations.
        ; Order groups and segments.
        BufferStoreDword [EBX+PGM.SegOrdBuffer],[EDI+PFELF.Sss]          ; [] (NULL section).
        BufferStoreDword [EBX+PGM.SegOrdBuffer],[EDI+PFELF.Sss.symtab]   ; [.symtab].
        Invoke SssFindByPurpose::,sssSegment,sssPurposeRELOC,sssNotBSS,0,EBX,[EDI+PFELF.Buffer]
        BufferRetrieve [EDI+PFELF.Buffer]
        BufferStore [EBX+PGM.SegOrdBuffer],ESI,ECX                       ; [.rel(a)name] (all reloc sections).
        Invoke SssFindByPurpose::,sssSegment,sssPurposeDRECTVE,sssNotBSS,0,EBX,[EDI+PFELF.Buffer]
        BufferRetrieve [EDI+PFELF.Buffer]
        JECXZ .41:
        BufferStore [EBX+PGM.SegOrdBuffer],ESI,ECX                       ; [.rel(a)name] (all .drectv sections).
    .41:BufferStoreDword [EBX+PGM.SegOrdBuffer],[EDI+PFELF.Sss.strtab]   ; [.strtab].
        BufferStoreDword [EBX+PGM.SegOrdBuffer],[EDI+PFELF.Sss.shstrtab] ; [.shstrtab].
        BufferRetrieve [EDI+PFELF.Buffer.CODE]
        BufferStore [EBX+PGM.SegOrdBuffer],ESI,ECX                       ; [.text]  (all code sections).
        BufferRetrieve [EDI+PFELF.Buffer.RODATA]
        BufferStore [EBX+PGM.SegOrdBuffer],ESI,ECX                       ; [.rodata]  (all read-only data sections).
        BufferRetrieve [EDI+PFELF.Buffer.DATA]
        BufferStore [EBX+PGM.SegOrdBuffer],ESI,ECX                       ; [.data]  (all data+bss sections).
        ; Enumerate and index all ordered segments.
        BufferRetrieve [EBX+PGM.SegOrdBuffer]       ; Assign an index for all ordered segments.
        SUB EDX,EDX                                 ; SSS.SegmIndex.
        ADD ECX,ESI
    .75:CMP ESI,ECX
        JNB .80:
        LODSD
        JNSt [EAX+SSS.Status],sssSegment,.75:
        MOV [EAX+SSS.SegmIndex],EDX
        INC EDX
        JMP .75:
    .80:Invoke PgmSymResolve::,EBX                   ; Match external symbols to statically linked public symbols.
        Invoke PgmOrderSymbols::,EBX                 ; Index all symbols, ordered by their type and name.
        Invoke PfelfConvertSymbols,EDI,0             ; Convert €ASM symbols to PFELF_SYM in [.symtab], write their names to [.strtab].
        Invoke PfelfRelaConvert,EDI                  ; Convert relocations form regular segments into [.rel(a)name].EmitBuffer.
        Invoke PfelfShdr::,EDI                       ; Create ELF section headers, pass 1.
        Invoke PfelfHdrsLink::,EDI                   ; Calculate PFELF.HDRsEnd.
        Invoke PfelfLink,EDI                         ; Set VA, FA, sizes of all ELF "program segments" and "sections".
        Invoke PfelfEhdr::,EDI,pfelfRel              ; Create ELF file header.
        Invoke PfelfShdr::,EDI                       ; Create ELF section headers, pass 2.
        MOV EDX,[%OutputStream]
        Invoke PfelfHdrsStream,EDI,EDX
        Invoke PgmStreamImage::,EBX,EDX
        Invoke PfelfDestroy::,EDI
       EndProcedure PfelfCompile

↑ PfelfSssTab Pfelf, Group
  PfelfSssTab creates four empty ELF-special SSS segments `[], [.symtab], [.shstrtab], [.strtab]`.
Input
  Pfelf is pointer to PFELF object.
  Group is pointer to ^SSS sssGroup wich should be assigned to all created segments. May be 0.
Output
  Special SSS segments are stored on Pfelf.Pgm.SssList.
Error
  Errors are reported with macro Msg.
See also
  PfelfsoSssDynTab
Invoked by
  PfelfCompile PfelfsoCompile PfelfxCompile
Invokes
  SymCreateSe

    PfelfSssTab Procedure Pfelf, Group
        MOV EDI,[%Pfelf]
        MOV EBX,[EDI+PFELF.Pgm]
        MOV EDX,[%Group]
        ListNew [EBX+PGM.SssList],Zeroed=yes         ; Create SSS [] - empty segment without name, required in ELF section header.
        MOV ESI,EAX
        MOV [EDI+PFELF.Sss],ESI
        SetSt [ESI+SSS.Status],sssSegment+sssUsed
        ListNew [EBX+PGM.SssList],Zeroed=yes         ; Create SSS [.symtab]
        MOV ESI,EAX
        MOV [EDI+PFELF.Sss.symtab],ESI
        SetSt [ESI+SSS.Status],sssSegment+sssUsed+sssNotBSS
        MOV [ESI+SSS.NamePtr],=B".symtab"
        MOV [ESI+SSS.NameSize],7
        MOV [ESI+SSS.GroupPtr],EDX
        MOV [ESI+SSS.Purpose],sssPurposeSYMBOLS
        MOV EAX,8
        JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.20:
        MOV AL,16
    .20:MOV [ESI+SSS.Alignment],EAX
        BufferCreate [EBX+PGM.Pool],Size=1K
        MOV [ESI+SSS.EmitBuffer],EAX
        BufferCreate [EBX+PGM.Pool],Size=1K
        MOV [ESI+SSS.RelocBuffer],EAX
        Invoke SymCreateSe::,ESI,EBX
        ListNew [EBX+PGM.SssList],Zeroed=yes         ; Create SSS  [.strtab]
        MOV ESI,EAX
        MOV [EDI+PFELF.Sss.strtab],ESI
        SetSt [ESI+SSS.Status],sssSegment+sssUsed+sssNotBSS
        MOV [ESI+SSS.NamePtr],=B".strtab"
        MOV [ESI+SSS.NameSize],7
        MOV [ESI+SSS.GroupPtr],EDX
        MOV [ESI+SSS.Purpose],sssPurposeSTRINGS
        MOV [ESI+SSS.Alignment],1
        BufferCreate [EBX+PGM.Pool],Size=512
        MOV [ESI+SSS.EmitBuffer],EAX
        Invoke SymCreateSe::,ESI,EBX
        ListNew [EBX+PGM.SssList],Zeroed=yes         ; Create SSS  [.shstrtab]
        MOV ESI,EAX
        MOV [EDI+PFELF.Sss.shstrtab],ESI
        SetSt [ESI+SSS.Status],sssSegment+sssUsed+sssNotBSS
        MOV [ESI+SSS.NamePtr],=B".shstrtab"
        MOV [ESI+SSS.NameSize],9
        MOV [ESI+SSS.GroupPtr],EDX
        MOV [ESI+SSS.Purpose],sssPurposeSTRINGS
        MOV [ESI+SSS.Alignment],1
        BufferCreate [EBX+PGM.Pool],Size=256
        MOV [ESI+SSS.EmitBuffer],EAX
    EndProcedure PfelfSssTab

↑ PfelfLoadPgm BasePgm, ObjBottom, ObjSize, FileNamePtr

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

  This procedure works with ELF types Relocatable, Executable and SharedObject in both 32bit and 64bit mode.

Input
  BasePgm is pointer to an existing PGM to which the object file is being linked/imported.
  ObjBottom is pointer to the contents of ELF file mapped in memory by the caller. It must start with PFELF_E_IDENT.
  ObjSize is number of bytes in the mapped ELF file.
  FileNamePtr is pointer to zero-terminated object file name (used in error reports).
Output
  Loaded program is stored on `BasePgm.ModulePgmList` as a new PGM structure.
Error
  Errors are reported with macro Msg.
Invoked from
  PfLoad
Invokes
  EaBufferRelease EaBufferReserve EaFs2Id PfDrectveDestroy PgmoptSetLinkProp SssCreateExtern SssFindByIndex SymFindByIndex
Invoked by
  PfelfsoLoadPgm

    PfelfLoadPgm Procedure BasePgm, ObjBottom, ObjSize, FileNamePtr
    EmitBottom     LocalVar ; Pointer to the raw emitted contents of segment whose pointers are relocated.
    EmitTop        LocalVar ; Pointer to the end of the raw contents.
    ObjProgress    LocalVar ; FA where E7771 was detected.
    ObjTop         LocalVar ; Pointer behind the mapped file.
    RelSize        LocalVar ; Size of one ELF REL|RELA record.
    RelBottom      LocalVar ; Pointer to the raw contents of .rel(a) section in memory.
    RelocBuf       LocalVar ; ^BUFFER fo RELOC records.
    RelocSss       LocalVar ; ^SSS to which the relocations apply.
    RelTop         LocalVar ; Pointer to the end of the raw contents of .rel(a) section.
    SectIndex      LocalVar ; Ordinal number of section|symbol in ELF file.
    ShdrBottom     LocalVar ; Pointer to the start of mapped section headers.
    ShdrTop        LocalVar ; Pointer to the end of mapped section headers.
    ShdrSize       LocalVar ; Size of one SHDR (28h or 40h).
    ShInfo         LocalVar ; SHDR.sh_info.
    ShLink         LocalVar ; SHDR.sh_link.
    ShStrTabBottom LocalVar ; Pointer to the raw contents of .shstrtab section.
    ShStrTabTop    LocalVar ; Pointer to the end of .shstrtab contents.
    StrTabEnd      LocalVar ; Pointer to the end of .strtab contents.
    StrTabPtr      LocalVar ; Pointer to the raw contents of .strtab section.
    SymSize        LocalVar ; Size of one PFELF_SYM record (10h or 18h).
    SymIndex       LocalVar ; Ordinal number of symbol in PGM.SymList.
    SymTabBottom   LocalVar ; Pointer to the raw contents of .symtab section.
    SymTabTop      LocalVar ; Pointer to the end of .symtab contents.
    SymSection     LocalVar ; ^SSS with SYM.Section.
    SymShndx       LocalVar ; PFELF_SYM.st_shndx - index of section where the symbol belongs.
    SymInfo        LocalVar ; DH=symbol bind 10=GLOBAL 20=WEAK; DL=symbol type: 0=LOCAL 1=OBJECT 2=FUNC 3=SECTION 4=FILE.
    Entry          LocalVar ; Entry VA, should be 0 in ELF and ELFSO format.
    StackOrg       LocalVar ; ESP at the entry of this Procedure PfelfLoadPgm.
         MOV [%StackOrg],ESP
         MOV EAX,[%ObjBottom]
         ADD EAX,[%ObjSize]
         MOV [%ObjTop],EAX
         MOV EBX,[%BasePgm]
         ; Create loaded module in format PGM. Some properties are inherited from %BasePgm.
         ListNew [EBX+PGM.ModulePgmList],Zeroed=Yes
         MOV ESI,[EBX+PGM.CurrentStm]
         MOV ECX,[EBX+PGM.LinePtr]
         MOV EDX,[EBX+PGM.Pool]
         MOV EBX,EAX                                 ; EBX is now the new loaded module ^PGM.
         MOV [EBX+PGM.CurrentStm],ESI
         MOV [EBX+PGM.LinePtr],ECX
         MOV [EBX+PGM.Pool],EDX
         ListCreate EDX,SIZE#SSS
         MOV [EBX+PGM.SssList],EAX
         ListCreate EDX,SIZE#SYM
         MOV [EBX+PGM.SymList],EAX
         MOV EDI,[%FileNamePtr]                      ; Create module name.
         GetLength$ EDI                              ; EDI,ECX is file name string. Remove path.
         MOV EDX,ECX
         LEA ESI,[EDI+ECX-1]
         STD
    .010:LODSB ; Read the string backward.
         CMP AL,'/'
         JE .020:
         CMP AL,'\'
         JE .020:
         CMP AL,':'
         LOOPNE .010:
    .020:CLD
         LEA ESI,[EDI+ECX]
         SUB EDX,ECX                                 ; ESI,EDX is now the file name without path.
         PUSH EBX
           MOV EBX,[%BasePgm]
           PoolStore [EBX+PGM.Pool],ESI,EDX          ; Make it permanent in BasePgm.Pool.
           MOV ESI,EAX
         POP EBX                                     ; Resore pointer to module PGM.
         MOV EDI,[EBX+PGM.DSONameBuffer]
         TEST EDI
         JNZ .025:
         BufferCreate [EBX+PGM.Pool],Size=64         ; Remove DSONameBuffer?
         MOV [EBX+PGM.DSONameBuffer],EAX
         MOV EDI,EAX
    .025:BufferStoreDword EDI,ESI
         BufferStoreDword EDI,EDX
         LEA EDI,[ESI+EDX]                           ; Remove extension (usually .o) or .so).
    .030:DEC EDI
         CMP EDI,ESI
         JNA .050:
         CMPB [EDI],'.'
         JNE .030:
    .040:SUB EDI,ESI
         JNZ .055:
    .050:MOV EDI,EDX                                 ; When file name has no extension, use it as is.
    .055:; ESI,EDI is now file name without path and without extension, it will be used as the module name.
         Invoke EaBufferReserve::,PfelfLoadPgm
         Invoke EaFs2Id::,ESI,EDI,EAX                ; Replace nonalphanum with underscores.
         BufferRetrieve EAX
         Invoke EaBufferRelease::,EAX
         PoolStore [EBX+PGM.Pool],ESI,ECX            ; Make the module name nonvolatile.
         MOV [EBX+PGM.NamePtr],EAX
         MOV [EBX+PGM.NameSize],ECX
    ; Empty PGM module EBX is now prepared. Examine ELF file header.
         MOV ESI,[%ObjBottom]
         MOVD [%ObjProgress],PFELF_EHDR64.e_type
         MOV DL,[ESI+PFELF_EHDR64.e_type]            ; Module type (ELF, ELFX, ELFSO).
         XOR EAX,EAX
         Dispatch DL,pfelfRel,pfelfExec,pfelfDyn
         JMP .E7771:                                 ; ELF file "!1$" is corrupted at offset 0x!2H.
    .pfelfDyn:MOV AL,pgmoptELFSO
         JMP .060:
    .pfelfExec: MOV AL,pgmoptELFX
         JMP .058:
    .pfelfRel:  MOV AL,pgmoptELF                     ; pgmoptELF=12.
    .058:
        ;;;; MOVD [EBX+PGM.DSONameBuffer],0             ; NonDSO file is not NEEDED in dynamic section, abort its buffer.

    .060:Invoke PgmoptSetLinkProp::,EAX              ; Set Pgmopt.Status properties to EAX.
         MOVD [%ObjProgress],PFELF_EHDR64.e_ident.e_class
         MOV DL,[ESI+PFELF_EHDR64.e_ident.e_class]
         MOV EDI,[%BasePgm]
         MOVD [%ObjProgress],PFELF_EHDR64.e_machine
         Dispatch DL,pfelfClass32,pfelfClass64
    .E7776:Msg '7776'                                ; ELF section .symtab not found.
         JMPS .Er:
    .E7771:Msg '7771',[%FileNamePtr],[%ObjProgress]  ; ELF file "!1$" is corrupted at offset 0x!2H.
    .Er:MOV EDI,[%BasePgm]
         ListRemove [EDI+PGM.ModulePgmList],EBX      ; Delete the unsuccesfully loaded module
         MOV ESP,[%StackOrg]                         ;  and abort this procedure, ignoring current stack level.
         JMP .399:
    .E7721:Msg '7721',EBX,[%BasePgm]                 ; Cannot link 32bit module "%1S" to 64bit program "!2S".
         JMP .Er:
    .E7722:Msg '7722',EBX,[%BasePgm]                 ; Cannot link 64bit module "%1S" to 32bit program "!2S".
         JMP .Er:
         ; ESI is pointer to PFELF_EHDR (file header).
    .pfelfClass32:SetSt EAX,pgmoptWidth32
         MOVD [%ShdrSize],SIZE# PFELF_SHDR32
         MOVD [%SymSize],SIZE# PFELF_SYM32
         JSt [EDI+PGM.Pgmopt.Status],pgmoptWidth64,.E7721:
         MOV ECX,[ESI+PFELF_EHDR32.e_entry]
         MOV [%Entry],ECX
         MOV EDI,[ESI+PFELF_EHDR32.e_shoff]
         MOVZX ECX,[ESI+PFELF_EHDR32.e_shnum]
         MOVZX EDX,[ESI+PFELF_EHDR32.e_shstrndx]
         CMP [ESI+PFELF_EHDR32.e_machine],pfelfMachine386
         JMP .070:
    .pfelfClass64:SetSt EAX,pgmoptWidth64
         MOVD [%ShdrSize],SIZE# PFELF_SHDR64
         MOVD [%SymSize],SIZE# PFELF_SYM64
         JSt [EDI+PGM.Pgmopt.Status],pgmoptWidth32,.E7722:
         MOV ECX,[ESI+PFELF_EHDR64.e_entry+0]
         MOV [%Entry],ECX
         MOV EDI,[ESI+PFELF_EHDR64.e_shoff]
         MOVZX ECX,[ESI+PFELF_EHDR64.e_shnum]       ; Number of SHDR entries.
         MOVZX EDX,[ESI+PFELF_EHDR64.e_shstrndx]
         CMP [ESI+PFELF_EHDR64.e_machine],pfelfMachineX64
    .070:JNE .E7771:
         MOV [EBX+PGM.Pgmopt.Status],EAX             ; Set program width and link properties.
         ADD EDI,ESI

         ; EDI is FA of the first PFELF_SHDR (section headers)
         MOV [%ShdrBottom],EDI                       ; FA of array of ECX section headers mapped in memory.
         MOV EAX,[%ShdrSize]
         PUSH EDX
           MUL ECX                                   ; ECX=EHDR.e_shnum, number of section headers.
         POP EDX                                     ; EDX=EHDR.e_shstrndx.
         ADD EAX,EDI
         MOV [%ShdrTop],EAX
         ; Find .shstrtab section with index EDX.
         MOV EAX,[%ShdrSize]
         MUL EDX
         ADD EDI,EAX                                 ; EDI is now ^PFELF_SHDR of section .shstrtab in memory.
         JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.080:
         MOVD [%ObjProgress],PFELF_EHDR32.e_shstrndx
         MOV EAX,[EDI+PFELF_SHDR32.sh_offset]
         MOV ECX,[EDI+PFELF_SHDR32.sh_size]
         JMP .090:
    .080:MOVD [%ObjProgress],PFELF_EHDR64.e_shstrndx
         MOV EAX,[EDI+PFELF_SHDR64.sh_offset]
         MOV ECX,[EDI+PFELF_SHDR64.sh_size]
    .090:ADD EAX,ESI
         MOV [%ShStrTabBottom],EAX
         ADD ECX,EAX                                 ; EAX..ECX is now memory-mapped contents of .shstrtab.
         MOV [%ShStrTabTop],ECX
         CMP ECX,[%ObjTop]
         JA .E7771:
         ; %ShdrBottom..%ShdrTop is array of PFELF_SHDR records.
         MOV ESI,[%ShdrBottom]

         ; Sections pass 1: convert regular emitting sections to SSS segments
         ;             and store them on PGM.SssList of the loaded module EBX.
         MOVD [%SectIndex],0                         ; Ordinal number of section header, it goes to SSS.SegmIndex.
    .110:CMP ESI,[%ShdrTop]
         JNB .190:                                   ; End of pass 1.
         MOV EAX,ESI
         SUB EAX,[%ObjBottom]
         MOV [%ObjProgress],EAX
         MOV EDX,[ESI+PFELF_SHDR64.sh_type]          ; Works for both 32 and 64bit.
         Dispatch DL,pfelfSHT_PROGBITS,pfelfSHT_NOBITS,pfelfSHT_NOTE
         JMP .180:                                   ; Ignore sections of other types.
    .pfelfSHT_NOTE:                                  ; Used as [.drectve] for linker directives.
    .pfelfSHT_PROGBITS:                              ; Regular CODE, RODATA, DATA segment.
    .pfelfSHT_NOBITS:                                ; Regular BSS segment.
         ListNew [EBX+PGM.SssList],Zeroed=yes        ; Create SSS.
         MOV EDI,EAX                                 ; ^SSS.
         MOV [EDI+SSS.SegmPtr],EAX
         MOV EAX,[%SectIndex]
         MOV [EDI+SSS.SegmIndex],EAX                 ; Section header index in ELF file.
         ; Section name.
         MOV EAX,[ESI+PFELF_SHDR64.sh_name]          ; Index in string table (common for SHDR32 and SHDR64).
         ADD EAX,[%ShStrTabBottom]
         CMP EAX,[%ShStrTabTop]
         JA .E7771:
         GetLength$ EAX
         PoolStore [EBX+PGM.Pool],EAX,ECX
         MOV [EDI+SSS.NamePtr],EAX
         MOV [EDI+SSS.NameSize],ECX                  ; SSS.Name.
         ; Section status.
         MOV EAX,sssSegment+sssPublic                ; SSS.Status.
         CMP DL,pfelfSHT_NOBITS
         JE .120:
         OR EAX,sssNotBSS
    .120:MOV EDX,pgmoptWidthMask
         AND EDX,[EBX+PGM.Pgmopt.Status]
         OR EAX,EDX                                  ; SSS width inherits PGM width.
         MOV [EDI+SSS.Status],EAX

         ; Section purpose. .sh_name, .sh_type and .sh_flags are identical for 32 and 64bit version.
         MOV EDX,sssPurposeDRECTVE
         CMPD [ESI+PFELF_SHDR64.sh_type],pfelfSHT_NOTE
         JE .130:
         MOV EDX,sssPurposeCODE                      ; SSS.Purpose is determined by access rights in SHDR.eh_flags.
         JSt  [ESI+PFELF_SHDR64.sh_flags],pfelfSHF_EXECINSTR,.130: ; [.text], [.plt].
         JNSt [ESI+PFELF_SHDR64.sh_flags],pfelfSHF_ALLOC,.130:
         MOV EDX,sssPurposeBSS
         JNSt [EDI+SSS.Status],sssNotBSS,.130:
         MOV EDX,sssPurposeDATA                      ; [.data], [.got].
         JSt [ESI+PFELF_SHDR64.sh_flags],pfelfSHF_WRITE,.130:
         MOV EDX,sssPurposeRODATA                    ; [.rodata].
    .130:MOV [EDI+SSS.Purpose],EDX                   ; SSS.Purpose specifies acces rights RWX.
         ; Virtual address, size, alignment.
         PUSH EBX
           JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.140:
           MOV EAX,[ESI+PFELF_SHDR32.sh_addr]
           SUB EDX,EDX
           MOV ECX,[ESI+PFELF_SHDR32.sh_size]
           MOV EBX,[ESI+PFELF_SHDR32.sh_addralign]
           JMP .150:
    .140:  MOV EAX,[ESI+PFELF_SHDR64.sh_addr+0]
           MOV EDX,[ESI+PFELF_SHDR64.sh_addr+4]
           MOV ECX,[ESI+PFELF_SHDR64.sh_size+0]
           MOV EBX,[ESI+PFELF_SHDR64.sh_addralign+0]
    .150:  MOV [EDI+SSS.BottomLow],EAX
           MOV [EDI+SSS.BottomHigh],EDX
           ADD EAX,ECX
           ADC EDX,0
           MOV [EDI+SSS.TopLow],EAX
           MOV [EDI+SSS.TopHigh],EDX
           MOV [EDI+SSS.Alignment],EBX
         POP EBX                                     ; Restore loaded module ^PGM.
         ; Section raw contents.
         JNSt [EDI+SSS.Status],sssNotBSS,.180:       ; There's no raw contents in BSS.
         BufferCreate [EBX+PGM.Pool],Size=ECX
         MOV [EDI+SSS.EmitBuffer],EAX
         BufferCreate [EBX+PGM.Pool],Size=8K
         MOV [EDI+SSS.RelocBuffer],EAX
         JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.160:
         MOV EAX,[ESI+PFELF_SHDR32.sh_offset]
         JMP .170:
    .160:MOV EAX,[ESI+PFELF_SHDR64.sh_offset+0]
    .170:ADD EAX,[%ObjBottom]
         LEA EDX,[EAX+ECX]                           ; EAX,ECX now specifies the raw contents of the section EDI.
         CMP EDX,[%ObjTop]
         JA .E7771:
         BufferStore [EDI+SSS.EmitBuffer],EAX,ECX
    .180:INCD [%SectIndex]
         ADD ESI,[%ShdrSize]                         ; Convert the next regular section.
         JMP .110:

    .190:; %ShdrBottom..%ShdrTop is array of PFELF_SHDR records.
         ; Sections pass 2: find ELF section .symtab and its .strtab
         MOV ESI,[%ShdrBottom]
    .203:CMP ESI,[%ShdrTop]
         JNB .E7776:                                 ; ELF section .symtab not found.
         CMPD [ESI+PFELF_SHDR64.sh_type],pfelfSHT_SYMTAB  ; Works for both 32 and 64bit.
         JE .205:
         ADD ESI,[%ShdrSize]
         JMP .203:
    .205:JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.207: ; ESI is SHDR .symtab.
          MOV EAX,[ESI+PFELF_SHDR32.sh_offset]
          MOV ECX,[ESI+PFELF_SHDR32.sh_size]
          MOV EDX,[ESI+PFELF_SHDR32.sh_link]
          JMP .209:
    .207: MOV EAX,[ESI+PFELF_SHDR64.sh_offset]
          MOV ECX,[ESI+PFELF_SHDR64.sh_size]
          MOV EDX,[ESI+PFELF_SHDR64.sh_link]
    .209: ADD EAX,[%ObjBottom]
          MOV [%SymTabBottom],EAX
          ADD EAX,ECX
          MOV [%SymTabTop],EAX
          ; Find ELF section .strtab by index EDX.
          MOV EAX,[%ShdrSize]
          MUL EDX
          ADD EAX,[%ShdrBottom]
          CMP EAX,[%ShdrTop]
          JNB .E7771:                               ; ELF file "!1$" is corrupted at offset 0x!2H.
          MOV ESI,EAX
          SUB EAX,[%ObjBottom]
          MOV [%ObjProgress],EAX
          CMP [ESI+PFELF_SHDR64.sh_type],pfelfSHT_STRTAB
          JNE .E7771:                                ; ELF file "!1$" is corrupted at offset 0x!2H.
          JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.2A1:
          MOV EAX,[ESI+PFELF_SHDR32.sh_offset]
          MOV ECX,[ESI+PFELF_SHDR32.sh_size]
          JMP .2A3:
    .2A1: MOV EAX,[ESI+PFELF_SHDR64.sh_offset+0]
          MOV ECX,[ESI+PFELF_SHDR64.sh_size+0]
    .2A3: ADD EAX,[%ObjBottom]
          MOV [%StrTabPtr],EAX
          ADD EAX,ECX
          MOV [%StrTabEnd],EAX

         ; Convert static ELF symbols from %SymTabBottom..%SymTabEnd into SYM symbols
         ;              and store them on PGM.SymList of the loaded module EBX in the loop .210: .. .290:.
         ; Ordinal number in ELF symbol table goes to SYM.NameIndex.
         MOVD [%SymIndex],0
         MOV ESI,[%SymTabBottom]
    .210:CMP ESI,[%SymTabTop]
         JNB .290:                                  ; When all symbols are converted.
         MOV EAX,ESI
         SUB EAX,[%ObjBottom]
         MOV [%ObjProgress],EAX
         MOVD [%SymSection],0
         JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.242:
         MOV ECX,[ESI+PFELF_SYM32.st_info]           ; .st_info,.st_other,.st_shndx.
         JMP .246:
    .242:MOV ECX,[ESI+PFELF_SYM64.st_info]           ; .st_info,.st_other,.st_shndx.
    .246:TEST ECX
         JZ .278:                                    ; Ignore NUL symbol.
         MOVZX EDX,CL                                ; EDX=symbol bind and type 0=LOCAL 1=OBJECT 2=FUNC 3=SECTION 4=FILE 10h=GLOBAL 20h=WEAK
         SHR ECX,16                                  ; ECX=section index.
         MOV [%SymShndx],ECX
         MOV DH,DL
         AND DX,0xF00F                               ; DH=symbol bind, DL=symbol type.
         MOV [%SymInfo],EDX
         CMP DL,pfelfSTT_FILE
         JE .278:                                    ; Ignore FILE symbol.
         ; Other than NUL and FILE type ELF symbols will be converted to €ASM symbols.
         ListNew [EBX+PGM.SymList],Zeroed=yes        ; Create an empty SYM record.
         MOV EDI,EAX                                 ; ^SYM.
         MOV EAX,[%SymIndex]                         ; PFELF_SYM ordinal.
         MOV [EDI+SYM.NameIndex],EAX
         MOV EAX,[ESI+PFELF_SYM64.st_name]
         ADD EAX,[%StrTabPtr]
         CMP EAX,[%StrTabEnd]
         JNB .E7771:
         GetLength$ EAX                              ; Namesize of the symbol stored in .strtab.
         PoolStore [EBX+PGM.Pool],EAX,ECX
         MOV [EDI+SYM.NamePtr],EAX
         MOV [EDI+SYM.NameSize],ECX
         JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.247:
         MOV EAX,[ESI+PFELF_SYM32.st_value]
         XOR EDX,EDX                                 ; Zero extend pointer to EDX:EAX.
         CMPD [%SymShndx],pfelfSHN_ABS
         JNE .2D6:                                   ; When the symbol is scalar,
         CDQ                                         ;   sign-extend its value to EDX:EAX.
    .2D6:MOV ECX,[ESI+PFELF_SYM32.st_size]
         JMPS .248:
    .247:MOV EAX,[ESI+PFELF_SYM64.st_value+0]
         MOV EDX,[ESI+PFELF_SYM64.st_value+4]
         MOV ECX,[ESI+PFELF_SYM64.st_size+0]
    .248:MOV [EDI+SYM.Size],ECX
         MOV ECX,[%SymShndx]                         ; Restore ELF section index.
         CMP ECX,pfelfSHN_ABS
         JE    .2C1:                                ; Symbol is scalar.
         JECXZ .2C1:                                ; Symbol is undefined (external).
         PUSH EAX                                   ; Otherwise it is defined in section with index ECX.
           Invoke SssFindByIndex::,ECX,EBX
           MOV ECX,EAX
           MOV [%SymSection],EAX
         POP EAX                                    ; Restore symbol value (offset) EDX:EAX. ECX is ^SSS of symbol's segment.
         JC .2C1:                                   ; If SSS not found, treat it as scalar.
    .2C0:SUB EAX,[ECX+SSS.BottomLow]                ; ELFSO symbol might be already linked, i.e. offset elevated by its segment.
         SBB EDX,[ECX+SSS.BottomHigh]               ; ELF segments are not linked, their Bottom=0.
    .2C1:MOV [EDI+SYM.OffsetLow],EAX
         MOV [EDI+SYM.OffsetHigh],EDX
         MOV EAX,[%SymSection]
         MOV [EDI+SYM.Section],EAX
         MOV EDX,[%SymInfo]                          ; DH=symbol bind 10h=GLOBAL 20h=WEAK; DL=symbol type: 0=LOCAL 1=OBJECT 2=FUNC 3=SECTION 4=FILE.
         TEST DH,pfelfSTB_GLOBAL|pfelfSTB_WEAK
         JNZ .2C5:
         TEST EAX
         JNZ .2C5:
         ; EDI is symSe of ELF-special section .strtab, .symtab, .dynamic, .hash etc. It will be discarded.
         ListRemove [EBX+PGM.SymList],EDI
         JMP .278:

    .2C5:MOV EAX,'A'                                 ; EAX used for construction of SYM.Status.
         CMP ECX,pfelfSHN_ABS
         JNE .249:
         MOV AL,'N'                                  ; Symbol is scalar.
    .249:CMP DL,pfelfSTT_OBJECT
         JNE .251:
         CMP AL,'N'
         JNE .251:
         MOV ECX,[EDI+SYM.Size]
         JECXZ .251:
         Dispatch CL,0n1,0n2,0n4,0n8,0n16,0n32,0n64
         JMPS .251:
    .0n1:MOV AL,'B'
         JMPS .251:
    .0n2:MOV AL,'W'
         JMPS .251:
    .0n4:MOV AL,'D'
         JMPS .251:
    .0n8:MOV AL,'Q'
         JMPS .251:
    .0n16:MOV AL,'O'
         JMPS .251:
    .0n32:MOV AL,'Y'
         JMPS .251:
    .0n64:MOV AL,'Z'
    .251: CMP DL,pfelfSTT_FUNC
          JNE .253:
          SetSt EAX,symProc
    .253: CMP DL,pfelfSTT_SECTION
          JNE .254:
          SetSt EAX,symSe
          MOV ECX,[%SymSection]
          JECXZ .254:
          MOV [ECX+SSS.SymPtr],EDI
    .254: CMPD [%SymShndx],pfelfSHN_UNDEF            ; Symbol is external?
          JNE .255:
          SetSt EAX,symExtern
    .255: TEST DH,pfelfSTB_WEAK
          JZ .257:
          SetSt EAX,symWeak
    .257: TEST DH,pfelfSTB_GLOBAL
          JZ .259:
          CMPD [%SymShndx],pfelfSHN_UNDEF            ; Symbol is external?
          JE .259:
          SetSt EAX,symPublic
    .259: MOV [EDI+SYM.Status],EAX
          JNSt EAX,symExtern, .278:
          ; When an external symbol was loaded, its requires its extern pseudosegment.
          Invoke SssCreateExtern::,EDI,EBX
    .278: INCD [%SymIndex]
          ADD ESI,[%SymSize]
          JMP .210:                                  ; The next ELF symbol.

    .290: Invoke PfDrectveDestroy::,EBX               ; Process the contents of [.drectve] segment, if exists.
    ; Sections pass 4: Convert ELF relocations to RELOC
    ;              and store them to SSS.RelocBuffer of their segments.
         ; %ShdrBottom..%ShdrTop is array of PFELF_SHDR records.
         MOV ESI,[%ShdrBottom]
    .300:CMP ESI,[%ShdrTop]    ; Convert each ELF SHT_REL(A) section in the loop .300: .. .395:.
         JNB .399:
         PUSH ESI              ; Save pointer in mapped SHDR.
           MOV EAX,ESI
           SUB EAX,[%ObjBottom]
           MOV [%ObjProgress],EAX
           MOV EDX,[ESI+PFELF_SHDR64.sh_type]        ; SHDR32.sh_type=SHDR64.sh_type.
           Dispatch DL,pfelfSHT_REL,pfelfSHT_RELA
           JMP .395:                                 ; Ignore sections of other types in this pass.
    .pfelfSHT_RELA:
           SetSt [EBX+PGM.Pgmopt.Status],pgmoptRelWithAddend
           MOV EAX,SIZE# PFELF_RELA64
           JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.305:
           MOV EAX,SIZE# PFELF_RELA32
           JMP .305:
    .pfelfSHT_REL:
           RstSt [EBX+PGM.Pgmopt.Status],pgmoptRelWithAddend
           MOV EAX,SIZE# PFELF_REL64
           JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.305:
           MOV EAX,SIZE# PFELF_REL32
    .305:  MOV [%RelSize],EAX
           JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.310:
           MOV EAX,[ESI+PFELF_SHDR32.sh_link]
           MOV ECX,[ESI+PFELF_SHDR32.sh_info]
           MOV [%ShLink],EAX
           MOV [%ShInfo],ECX
           MOV EDI,[ESI+PFELF_SHDR32.sh_offset]
           MOV ECX,[ESI+PFELF_SHDR32.sh_size]
           JMP .315:
    .310:  MOV EAX,[ESI+PFELF_SHDR64.sh_link]
           MOV ECX,[ESI+PFELF_SHDR64.sh_info]
           MOV [%ShLink],EAX
           MOV [%ShInfo],ECX
           MOV EDI,[ESI+PFELF_SHDR64.sh_offset]
           MOV ECX,[ESI+PFELF_SHDR64.sh_size]
    .315:  ADD EDI,[%ObjBottom]
           ADD ECX,EDI
           MOV [%RelBottom],EDI
           MOV [%RelTop],ECX
           CMP ECX,[%ObjTop]
           JA .E7771:
           ; [%ShInfo] specifies index of section to which relocations apply ([.text], [.data], [.rodata]).
           Invoke SssFindByIndex::,[%ShInfo],EBX
           JC .E7771:
           MOV [%RelocSss],EAX
           MOV EDX,[EAX+SSS.RelocBuffer]
           MOV [%RelocBuf],EDX                       ; Buffer where converted RELOC records will be stored.
           BufferRetrieve [EAX+SSS.EmitBuffer]       ; ESI,ECX contains emitted contents of the relocated section.
           ADD ECX,ESI
           MOV [%EmitBottom],ESI
           MOV [%EmitTop],ECX
           MOV ESI,[%RelBottom]                      ; [%RelBottom]..[%RelTop] is an array of PFELF_REL* records.
    .330:  CMP ESI,[%RelTop]                         ; Convert each relocation ESI in the loop .330: .. .380:.
           JNB .395:
           BufferNew [%RelocBuf],SIZE# RELOC,Zeroed=Yes
           MOV EDI,EAX                               ; ESI=^REL(A)   EDI=^RELOC.
           JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.345:
           MOV EAX,[ESI+PFELF_REL32.r_offset]
           SUB EDX,EDX
           JMP .350:
    .345:  MOV EAX,[ESI+PFELF_REL64.r_offset+0]
           MOV EDX,[ESI+PFELF_REL64.r_offset+4]
    .350:  MOV [EDI+RELOC.OrgLow],EAX
           MOV [EDI+RELOC.OrgHigh],EDX
           MOV EAX,[%RelocSss]                       ; Segment of all relocated storage units.
           MOV [EDI+RELOC.Section],EAX
           JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.355:
           MOV ECX,[ESI+PFELF_REL32.r_info]
           MOV EDX,ECX
           AND EDX,0x0000_00FF                       ; .r_type.
           SHR ECX,8                                 ; .r_sym.
           JMP .360:
    .355:  MOV EDX,[ESI+PFELF_REL64.r_info+0]        ; .r_type.
           MOV ECX,[ESI+PFELF_REL64.r_info+4]        ; .r_sym.
    .360:  Invoke SymFindByIndex::,ECX,EBX
           Msg cc=C,'7777',ECX                       ; Symbol nr.!$D not found in ELF section .symtab.
           MOV [EDI+RELOC.Symbol],EAX
           ; Convert r_type DL to RELOC.Status in ECX.
           XOR ECX,ECX
           PUSHD .RelDispatched:                        ; Prepare the return address from Dispatch.
           JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.365:
           Dispatch DL,pfelfR_386_32,pfelfR_386_PC32,pfelfR_386_GOT32,pfelfR_386_PLT32,  \  Relocation types in ELF32.
                       pfelfR_386_GLOB_DAT,pfelfR_386_JMP_SLOT,pfelfR_386_RELATIVE,      \
                       pfelfR_386_GOTOFF,pfelfR_386_GOTPC,pfelfR_386_NONE,pfelfR_386_COPY
           JMP .E7731:                               ; Unsupported relocation type 0x!1B at [!2S]:!3Hh.
    .365:  Dispatch DL,pfelfR_X86_64_PC32, pfelfR_X86_64_64,pfelfR_X86_64_GOT32,pfelfR_X86_64_PLT32,pfelfR_X86_64_32,\ Relocation types in ELF64.
                       pfelfR_X86_64_32S,pfelfR_X86_64_GLOB_DAT,pfelfR_X86_64_JUMP_SLOT,         \
                       pfelfR_X86_64_RELATIVE,pfelfR_X86_64_GOTOFF64,pfelfR_X86_64_GOTPC32,      \
                       pfelfR_X86_64_COPY,pfelfR_X86_64_NONE
    .E7731:Msg '7731',EDX,[EDI+RELOC.Section],[EDI+RELOC.OrgLow] ; Unsupported relocation type 0x!1B at [!2S]:!3Hh.
           RET                                       ; Ignore this REL.
    .pfelfR_X86_64_32:
    .pfelfR_X86_64_32S:
    .pfelfR_386_32:
           OR  ECX,relocWidth32 | relocAbsVA
           RET
    .pfelfR_386_PC32:
    .pfelfR_X86_64_PC32:
           OR  ECX,relocWidth32 | relocRel
           RET
    .pfelfR_X86_64_GOT32:
    .pfelfR_386_GOT32:
           OR  ECX,relocWidth32 | relocGOT
           RET
    .pfelfR_X86_64_PLT32:
    .pfelfR_386_PLT32:
           OR  ECX,relocWidth32 | relocPLT
           RET
    .pfelfR_386_GLOB_DAT:
    .pfelfR_386_JMP_SLOT:
           OR  ECX,relocWidth32 | relocSym
           RET
    .pfelfR_386_RELATIVE:
           OR  ECX,relocWidth32 | relocDyn
           RET
    .pfelfR_386_GOTOFF:
           OR  ECX,relocWidth32 | relocGOToff
           RET
    .pfelfR_X86_64_GOTPC32:
    .pfelfR_386_GOTPC:
           OR  ECX,relocWidth32 | relocGOTrel
           RET
    .pfelfR_X86_64_64:
           OR  ECX,relocWidth64 | relocAbsVA
           RET
    .pfelfR_X86_64_JUMP_SLOT:
    .pfelfR_X86_64_GLOB_DAT:
           OR  ECX,relocWidth64 | relocSym
           RET
    .pfelfR_X86_64_RELATIVE:
           OR  ECX,relocWidth64 | relocDyn
           RET
    .pfelfR_X86_64_GOTOFF64:
           OR  ECX,relocWidth64 | relocGOToff
           RET
    .pfelfR_386_COPY:
    .pfelfR_386_NONE:
    .pfelfR_X86_64_COPY:
    .pfelfR_X86_64_NONE:
           OR ECX,relocResolved
           RET
    .RelDispatched:
           MOV [EDI+RELOC.Status],ECX
           JNSt [EBX+PGM.Pgmopt.Status],pgmoptRelWithAddend,.386:
           JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.382:
           MOV EAX,[ESI+PFELF_RELA32.r_addend]
           CDQ
           JMP .384:
    .382:  MOV EAX,[ESI+PFELF_RELA64.r_addend+0]
           MOV EDX,[ESI+PFELF_RELA64.r_addend+4]
    .384:  ADD [EDI+RELOC.AddendLow],EAX
           ADC [EDI+RELOC.AddendHigh],EDX
    .386:  MOV ECX,[EDI+RELOC.Symbol]
           JECXZ .388:
           MOV EAX,[ECX+SYM.OffsetLow]
           MOV EDX,[ECX+SYM.OffsetHigh]
           SUB [EDI+RELOC.AddendLow],EAX
           SBB [EDI+RELOC.AddendHigh],EDX
    .388:  ADD ESI,[%RelSize]
           JMP .330:                              ; The next REL(A) record.
    .395:POP ESI
         ADD ESI,[%ShdrSize]
         JMP .300:                                ; The next .rel* section.
    .399:; Sections pass 3 - unlink VA of ELFSO sections, as they will be statically combined to the base program.
         ListGetFirst [EBX+PGM.SssList]
         JZ .3E9:
    .3E1:MOV EDI,EAX
         MOV EAX,[EDI+SSS.BottomLow]
         MOV EDX,[EDI+SSS.BottomHigh]
         SUB [EDI+SSS.TopLow],EAX
         SBB [EDI+SSS.TopHigh],EDX
         XOR EAX,EAX
         MOV [EDI+SSS.BottomLow],EAX
         MOV [EDI+SSS.BottomHigh],EAX
         ListGetNext EDI
         JNZ .3E1:
    .3E9:
        EndProcedure PfelfLoadPgm

↑ PfelfCreate PfelfPtr, PgmPtr
  PfelfCreate is a constructor of PFELF object.
Input
  PfelfPtr is a pointer to PFELF object allocated by the caller.
  PgmPtr is pointer to PGM representing the completely assembled and combined program.
Output
  -
Error
  Errors are reported with macro Msg.
Invoked by
  PfelfCompile PfelfsoCompile PfelfxCompile
Invokes
  EaBufferReserve

    PfelfCreate Procedure PfelfPtr, PgmPtr
        MOV ESI,[%PfelfPtr]
        XOR EAX,EAX
        MOV EDI,ESI
        MOV ECX,SIZE# PFELF / 4
        REP STOSD                                    ; Clear the PFELF object.
        MOV EBX,[%PgmPtr]
        MOV [ESI+PFELF.Pgm],EBX
        MOV EAX,[EBX+PGM.Pgmopt.Status]
        MOV EDX,SIZE# PFELF_SYM64
        SAL EDX,16
        MOV  DX,SIZE# PFELF_RELA64
        MOV ECX,SIZE# PFELF_PHDR64
        JSt EAX,pgmoptWidth64,.06:
        MOV EDX,SIZE# PFELF_SYM32
        SAL EDX,16
        MOV DX,SIZE# PFELF_REL32
        MOV CL,SIZE# PFELF_PHDR32
    .06:MOV [ESI+PFELF.relSize],DX
        SAR EDX,16
        MOV [ESI+PFELF.symSize],EDX
        MOV [ESI+PFELF.PHDRSize],ECX
        MOV ECX,EAX
        AND EAX,pgmoptWidthMask
        AND ECX,pgmoptFormatMask
        OR [ESI+PFELF.Status],EAX
        MOV EAX,pfelfFormatELF
        CMP CL,pgmoptELF
        JE .10:
        MOV EAX,pfelfFormatELFX
        CMP CL,pgmoptELFX
        JE .10:
        MOV EAX,pfelfFormatELFSO
        CMP CL,pgmoptELFSO
        JNE .20:
    .10:OR [ESI+PFELF.Status],EAX
    .20:Invoke EaBufferReserve::,%^PROC              ; Create a temporary buffer.
        MOV [ESI+PFELF.Buffer],EAX
        Invoke EaBufferReserve::,%^PROC              ; Create ELF-headers buffers/
        MOV [ESI+PFELF.Buffer.EHDR],EAX
        Invoke EaBufferReserve::,%^PROC
        MOV [ESI+PFELF.Buffer.PHDR],EAX
        Invoke EaBufferReserve::,%^PROC
        MOV [ESI+PFELF.Buffer.SHDR],EAX
        Invoke EaBufferReserve::,%^PROC
        MOV [ESI+PFELF.Buffer.CODE],EAX
        Invoke EaBufferReserve::,%^PROC
        MOV [ESI+PFELF.Buffer.RODATA],EAX
        Invoke EaBufferReserve::,%^PROC
        MOV [ESI+PFELF.Buffer.DATA],EAX
       EndProcedure PfelfCreate

↑ PfelfDestroy PfelfPtr
  PfelfCreate is a destructor of PFELF object.
Input
  PfelfPtr is a pointer to PFELF object allocated by the caller.
Output
  -
Error
  Errors are reported with macro Msg.
Invoked by
  PfelfCompile PfelfsoCompile PfelfxCompile
Invokes
  EaBufferRelease

    PfelfDestroy Procedure PfelfPtr
         LEA ESI,[%PfelfPtr]
         Invoke EaBufferRelease::,[ESI+PFELF.Buffer.DATA]
         Invoke EaBufferRelease::,[ESI+PFELF.Buffer.RODATA]
         Invoke EaBufferRelease::,[ESI+PFELF.Buffer.CODE]
         Invoke EaBufferRelease::,[ESI+PFELF.Buffer.SHDR]
         Invoke EaBufferRelease::,[ESI+PFELF.Buffer.PHDR]
         Invoke EaBufferRelease::,[ESI+PFELF.Buffer.EHDR]
         Invoke EaBufferRelease::,[ESI+PFELF.Buffer]
        EndProcedure PfelfDestroy

↑ PfelfSssSeg Pfelf
  PfelfSssSeg selects SSS segments from Pfelf.Pgm.SssList by purpose and stores pointers to them in `Pfelf.Buffer.CODE,Pfelf.Buffer.RODATA,Pfelf.Buffer.DATA`.
Input
  Pfelf is pointer to PFELF object.
Output
  Buffers in Pfelf object are filled.
Error
  Errors are reported with macro Msg.
Invoked by
  PfelfCompile PfelfsoCompile PfelfxCompile
Invokes
  SssFindByPurpose

    PfelfSssSeg Procedure Pfelf
        MOV EDI,[%Pfelf]
        MOV EBX,[EDI+PFELF.Pgm]
        MOV EDX,[EDI+PFELF.Buffer.CODE]
        BufferClear EDX
        Invoke SssFindByPurpose::,sssSegment,sssPurposeCODE,sssNotBSS,0,EBX,[EDI+PFELF.Buffer]
        BufferRetrieve [EDI+PFELF.Buffer]
        BufferStore EDX,ESI,ECX
        Invoke SssFindByPurpose::,sssSegment,sssPurposePLT,sssNotBSS,0,EBX,[EDI+PFELF.Buffer]
        BufferRetrieve [EDI+PFELF.Buffer]
        BufferStore EDX,ESI,ECX
        MOV EDX,[EDI+PFELF.Buffer.RODATA]
        BufferClear EDX
        Invoke SssFindByPurpose::,sssSegment,sssPurposeRODATA,sssNotBSS,0,EBX,[EDI+PFELF.Buffer]
        BufferRetrieve [EDI+PFELF.Buffer]
        BufferStore EDX,ESI,ECX
        MOV EDX,[EDI+PFELF.Buffer.DATA]
        BufferClear EDX
        Invoke SssFindByPurpose::,sssSegment,sssPurposeDATA,sssNotBSS,0,EBX,[EDI+PFELF.Buffer]
        BufferRetrieve [EDI+PFELF.Buffer]
        BufferStore EDX,ESI,ECX
        Invoke SssFindByPurpose::,sssSegment,sssPurposeGOT,sssNotBSS,0,EBX,[EDI+PFELF.Buffer]
        BufferRetrieve [EDI+PFELF.Buffer]
        BufferStore EDX,ESI,ECX
        Invoke SssFindByPurpose::,sssSegment,sssPurposeDYNAMIC,sssNotBSS,0,EBX,[EDI+PFELF.Buffer]
        BufferRetrieve [EDI+PFELF.Buffer]
        BufferStore EDX,ESI,ECX
        Invoke SssFindByPurpose::,sssSegment,sssPurposeBSS,sssDontCare,0,EBX,[EDI+PFELF.Buffer]
        BufferRetrieve [EDI+PFELF.Buffer]
        BufferStore EDX,ESI,ECX
      EndProcedure PfelfSssSeg

↑ PfelfPhdr Pfelf
  PfelfPhdr converts every €ASM sssGroup SSS to records PFELF_PHDR and stores them in `Pfelf.Buffer.PHDR`.
  The groups are already ordered in Pfelf.Pgm.SegOrdBuffer.
Input
  Pfelf is pointer to PFELF object.
Output
  Pfelf.Buffer.PHDR is filled with ELF program header records.
Error
  Errors are reported with macro Msg.
Invoked by
  PfelfsoCompile PfelfxCompile

    PfelfPhdr Procedure Pfelf
    OrdBufPtr  LocalVar                             ; Ptr to an array of pointers to SSS objects.
    OrdBufEnd  LocalVar                             ; Ptr to the end ot array.
        MOV EDI,[%Pfelf]
        MOV EBX,[EDI+PFELF.Pgm]
        BufferClear [EDI+PFELF.Buffer.PHDR]
        BufferRetrieve [EBX+PGM.SegOrdBuffer]
        ADD ECX,ESI
        MOV [%OrdBufPtr],ESI
        MOV [%OrdBufEnd],ECX
    .10:MOV ESI,[%OrdBufPtr]
        CMP ESI,[%OrdBufEnd]
        JNB .90:
        LODSD
        MOV [%OrdBufPtr],ESI
        JNSt [EAX+SSS.Status],sssGroup,.10:
        MOV ESI,EAX                                  ; Each sssGroup ESI will create one PHDR record.
    .15:MOV ECX,SIZE# PFELF_PHDR64
        JSt [EDI+PFELF.Status],pfelfWidth64,.17:
        MOV CL,SIZE# PFELF_PHDR32
    .17:BufferNew [EDI+PFELF.Buffer.PHDR],ECX,Zeroed=Yes
        MOV EBX,EAX                                  ; EBX=^PHDR, yet empty.
    .18:MOV EAX,[ESI+SSS.BottomLow]                  ; Virtual address. EBX=^empty PHDR, ESI=sssGroup.
        MOV EDX,[ESI+SSS.BottomHigh]
    .19:JSt [EDI+PFELF.Status],pfelfWidth64,.20:
        MOV [EBX+PFELF_PHDR32.p_vaddr],EAX
        MOV [EBX+PFELF_PHDR32.p_paddr],EAX
        JMP .30:
    .20:MOV [EBX+PFELF_PHDR64.p_vaddr+0],EAX
        MOV [EBX+PFELF_PHDR64.p_vaddr+4],EDX
        MOV [EBX+PFELF_PHDR64.p_paddr+0],EAX
        MOV [EBX+PFELF_PHDR64.p_paddr+4],EDX
    .30:MOV EAX,[ESI+SSS.TopLow]                     ; File offset + virtual size.
        MOV EDX,[ESI+SSS.TopHigh]
        SUB EAX,[ESI+SSS.BottomLow]
        SBB EDX,[ESI+SSS.BottomHigh]                 ; EDX:EAX is virtual size.
        MOV ECX,[ESI+SSS.BottomFA]
        JSt [EDI+PFELF.Status],pfelfWidth64,.40:
        MOV [EBX+PFELF_PHDR32.p_memsz],EAX
        MOV [EBX+PFELF_PHDR32.p_offset],ECX
        MOV EAX,[ESI+SSS.TopFA]
        SUB EAX,ECX
        MOV [EBX+PFELF_PHDR32.p_filesz],EAX
        JMP .50:
    .40:MOV [EBX+PFELF_PHDR64.p_memsz+0],EAX
        MOV [EBX+PFELF_PHDR64.p_memsz+4],EDX
        MOV [EBX+PFELF_PHDR64.p_offset+0],ECX
        MOV EAX,[ESI+SSS.TopFA]
        SUB EAX,ECX
        MOV [EBX+PFELF_PHDR64.p_filesz+0],EAX
    .50:XOR EDX,EDX                                  ; Default PHDR.p_flags: none.
        XOR EAX,EAX                                  ; Default PHDR.p_type = PT_NULL.
        MOV ECX,[ESI+SSS.Alignment]                  ; Default PHDR.p_align is SSS.Alignment.
        MOV ESI,[ESI+SSS.Purpose]                    ; Specify PHDR type, access flags and alignment by SSS.Purpose.
        PUSHD .Dispatched:                           ; Prepare return address from dispatching.
    pur %FOR PHDR,CODE,RODATA,DATA,GOT,PLT,BSS,DRECTVE,DYNAMIC,INTERP
          JSt ESI,sssPurpose%pur,.sssPurpose%pur:    ; Dispatch PHDR by purpose.
        %ENDFOR pur
        RET                                          ; Continue at the label .Dispatched: with EAX=p_type, EDX=p_flags, ECX=p_align.
    .sssPurposePHDR:
        MOV AL,pfelfPT_PHDR
        MOV ECX,8
        RET
    .sssPurposeRODATA:
        MOV AL,pfelfPT_LOAD
        MOV DL,pfelfPF_R
        RET
    .sssPurposeDRECTVE:
        MOV AL,pfelfPT_NOTE
        RET
    .sssPurposePLT:
    .sssPurposeCODE:
        MOV AL,pfelfPT_LOAD
        MOV DL,pfelfPF_R + pfelfPF_X
        RET
    .sssPurposeDATA:
    .sssPurposeGOT:
    .sssPurposeBSS:
        MOV AL,pfelfPT_LOAD
        MOV DL,pfelfPF_R + pfelfPF_W
        RET
    .sssPurposeINTERP:
        MOV AL,pfelfPT_INTERP
        MOV DL,pfelfPF_R
        RET
    .sssPurposeDYNAMIC:
        MOV AL,pfelfPT_DYNAMIC
        MOV DL,pfelfPF_R + pfelfPF_W
        RET
    .Dispatched:
        MOV [EBX+PFELF_PHDR64.p_type],EAX            ; PHDR64.p_type == PHDR32.p_type.
        JSt [EDI+PFELF.Status],pfelfWidth64,.60:
        MOV [EBX+PFELF_PHDR32.p_flags],EDX
        MOV [EBX+PFELF_PHDR32.p_align],ECX
        JMP .10:
    .60:MOV [EBX+PFELF_PHDR64.p_flags],EDX
        MOV [EBX+PFELF_PHDR64.p_align+0],ECX
        JMP .10:
    .90:EndProcedure PfelfPhdr

↑ PfelfEhdr Pfelf, e_type
  PfelfEhdr creates PFELF_EHDR in `Pfelf.Buffer.EHDR`.
Input
  Pfelf is pointer to PFELF object.
  e_type is one of `pfelfRel, pfelfExec, pfelfDyn`.
Output
  Pfelf.Buffer.PHDR is filled with ELF program header records.
Error
  Errors are reported with macro Msg.
Invoked by
  PfelfCompile PfelfsoCompile PfelfxCompile
Invokes
  PgmGetEntryAddress

    PfelfEhdr Procedure Pfelf, e_type
        MOV EDI,[%Pfelf]
        MOV EBX,[EDI+PFELF.Pgm]
        MOV EDX,[EDI+PFELF.Buffer.EHDR]
        BufferClear EDX
        MOV ECX,SIZE# PFELF_EHDR64
        JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.20:
        MOV ECX,SIZE# PFELF_EHDR32
    .20:BufferNew EDX,ECX,Zeroed=Yes
        MOV EDI,EAX
        ; EHDR members common for 32bit and 64bit variant: .e_ident, .e_type, .e_machine, .e_version
        MOVD [EDI+PFELF_EHDR64.e_ident.e_mag0],0x464C457F ; ELF format identification.
        MOV ECX,pfelfClass64
        MOV EAX,pfelfMachineX64
        JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.30:
        MOV CL,pfelfClass32
        MOV AL,pfelfMachine386
    .30:MOV [EDI+PFELF_EHDR64.e_ident.e_class],CL    ; E_IDENT.e_class
        MOV [EDI+PFELF_EHDR64.e_machine],AX
        MOVB [EDI+PFELF_EHDR64.e_ident.e_data],1     ; E_IDENT.e_data=1 (little endian).
        MOV ECX,[EBX+PGM.Pgmopt.MajorImageVersion]
        MOV [EDI+PFELF_EHDR64.e_ident.e_version],CL  ; E_IDENT.e_version=%^MajorImageVersion (default=1).
        MOV EDX,[%e_type]
        MOV [EDI+PFELF_EHDR64.e_type],DL
        MOV ECX,[EBX+PGM.Pgmopt.MajorLinkerVersion]  ; EHDR.e_version=%^MajorLinkerVersion (default=1).
        MOV [EDI+PFELF_EHDR64.e_version],CX
        Invoke PgmGetEntryAddress::,EBX              ; Returns Entry VA in EDX:EAX.
        JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.40:
        MOV [EDI+PFELF_EHDR32.e_entry],EAX
        JMPS .50:
    .40:MOV [EDI+PFELF_EHDR64.e_entry+0],EAX
        MOV [EDI+PFELF_EHDR64.e_entry+4],EDX
    .50:MOV ESI,[%Pfelf]
        MOV EAX,[ESI+PFELF.EHDR.e_phnum]
        MOV EDX,[ESI+PFELF.EHDR.e_shnum]
        MOV ECX,[ESI+PFELF.EHDR.e_phoff]
        MOV ESI,[ESI+PFELF.EHDR.e_shoff]
        JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.70:
        ; EHDR32.
        MOV [EDI+PFELF_EHDR32.e_phnum],AX
        MOV [EDI+PFELF_EHDR32.e_shnum],DX
        MOV [EDI+PFELF_EHDR32.e_phoff],ECX
        MOV [EDI+PFELF_EHDR32.e_shoff],ESI
        MOV [EDI+PFELF_EHDR32.e_ehsize],SIZE# PFELF_EHDR32    ; 0x0034.
        MOV [EDI+PFELF_EHDR32.e_phentsize],SIZE# PFELF_PHDR32 ; 0x0020.
        MOV [EDI+PFELF_EHDR32.e_shentsize],SIZE# PFELF_SHDR32 ; 0x0028.
        MOV EDX,[%Pfelf]
        MOV ECX,[EDX+PFELF.Sss.shstrtab]
        JECXZ .90:
        MOV EAX,[ECX+SSS.SegmIndex]
        MOV [EDI+PFELF_EHDR32.e_shstrndx],EAX
    .60:JMP .90:
    .70:; EHDR64.                                      ; EDX:EAX=program entry symbol value or 0 when none.
        MOV [EDI+PFELF_EHDR64.e_phnum],AX
        MOV [EDI+PFELF_EHDR64.e_shnum],DX
        MOV [EDI+PFELF_EHDR64.e_phoff+0],ECX
        MOV [EDI+PFELF_EHDR64.e_shoff+0],ESI
        MOV [EDI+PFELF_EHDR64.e_ehsize],SIZE# PFELF_EHDR64    ; 0x0040.
        MOV [EDI+PFELF_EHDR64.e_phentsize],SIZE# PFELF_PHDR64 ; 0x0038.
        MOV [EDI+PFELF_EHDR64.e_shentsize],SIZE# PFELF_SHDR64 ; 0x0040.
        MOV EDX,[%Pfelf]
        MOV ECX,[EDX+PFELF.Sss.shstrtab]
        JECXZ .90:
        MOV EAX,[ECX+SSS.SegmIndex]
        MOV [EDI+PFELF_EHDR64.e_shstrndx],EAX
    .90:EndProcedure PfelfEhdr

↑ PfelfShdr Pfelf
  PfelfShdr converts segments ordered on Pgm.SegmOrdBuffer to PFELF_SHDR32 or PFELF_SHDR64 object stored to `Pfelf.Buffer.SHDR`.
Input
  Pfelf is pointer to PFELF object.
Output
  SHDR objects are stored to Pfelf.Buffer.SHDR, their names to Pfelf.Sss.shstrtab.EmitBuffer.
Error
  -
Invoked by
  PfelfCompile PfelfsoCompile PfelfxCompile

    PfelfShdr Procedure Pfelf
    NameBuf    LocalVar                               ; ^BUFFER for ELF section names = [.shstrtab].EmitBuffer.
    ShdrSize   LocalVar                               ; SIZE# PFELF_SHDR (28h or 40h).
    OrdBufPtr  LocalVar                               ; Pointer inside array of pointers to SSS.
    OrdBufEnd  LocalVar                               ; Pointer to the end of array.
    ShdrBottom LocalVar                               ; ^PFELF_SHDR - pointer to the bottom of ELF section headers.
    ShdrPtr    LocalVar                               ; ^PFELF_SHDR - pointer to the current ELF section header.
       MOV EDI,[%Pfelf]
        MOV EBX,[EDI+PFELF.Pgm]
        MOV ECX,[EDI+PFELF.Sss.shstrtab]
        MOV EDX,[ECX+SSS.EmitBuffer]
        MOV [%NameBuf],EDX
        BufferClear EDX
        BufferStoreByte EDX,0
        BufferClear [EDI+PFELF.Buffer.SHDR]
        MOV ECX,SIZE# PFELF_SHDR64
        JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.10:
        MOV CL,SIZE# PFELF_SHDR32
    .10:MOV [%ShdrSize],ECX
        BufferRetrieve [EBX+PGM.SegOrdBuffer]
        ADD ECX,ESI
        MOV [%OrdBufPtr],ESI
        MOV [%OrdBufEnd],ECX
    .20:MOV ESI,[%OrdBufPtr]
        CMP ESI,[%OrdBufEnd]
        JNB .80:
        LODSD                                        ; Load pointer to SSS.
        MOV [%OrdBufPtr],ESI
        BufferRetrieve [%NameBuf]                    ; Size of %NameBuf in ECX will be SHDR.sh_name.
        MOV ESI,EAX
        JNSt [ESI+SSS.Status],sssSegment,.20:        ; Skip groups.
        MOV EDI,[%Pfelf]
        BufferNew [EDI+PFELF.Buffer.SHDR],[%ShdrSize],Zeroed=Yes  ; Allocate ELF section header.
        MOV EDI,EAX                                  ; Convert ESI=^SSS to EDI=^PFELF_SHDR.
        MOV [%ShdrBottom],EAX
        CMPD [ESI+SSS.NameSize],0
        JZ .20:                                      ; Leave [] section header blank.
        ; SHDR.sh_name, SHDR.type, SHDR.flags are identical properties in PFELF_SHDR32 and PFELF_SHDR64.
        MOV [EDI+PFELF_SHDR64.sh_name],ECX
        BufferStore [%NameBuf],[ESI+SSS.NamePtr],[ESI+SSS.NameSize]
        BufferStoreByte [%NameBuf],0
        MOV EDX,[ESI+SSS.Purpose]
        MOV EDI,[ESI+SSS.NamePtr]                    ; Pointer to section name used to distinguish purpose of spec.section.
        MOV ECX,[ESI+SSS.NameSize]
        MOV EDI,[EDI+1]                              ; Omit '.' and load EDI with first 4 characters of section name.
    .30:XOR EAX,EAX                                  ; AH will be dispatched to SHDR.sh_flags, AL to SHDR.sh_type.
        XOR ECX,ECX                                  ; CH will be dispatched to SHDR64.sh_entsize, CL to SHDR32.sh_entsize.
        PUSHD .Dispatched:                           ; Specify return address from dispatching.
    pur %FOR %SssPurposeList{17..}                   ; Omit PECOFF special purposes.
          TEST EDX,sssPurpose%pur
          JZ .P%.:
          CALL .sssPurpose%pur:                      ; Prepare flags in AH,AL,CH,CL.
          .P%.:
         %ENDFOR pur
         RET                                         ; Continue at .Dispatched:
        ; Subprocedures which set AH=SHDR.sh_flags, AL=SHDR.sh_type, CH=SHDR64.sh_entsize, CL=SHDR32.sh_entsize.
    .sssPurposePLT:
        MOV CL,4
        MOV CH,16
        OR AH,pfelfSHF_WRITE
    .sssPurposeCODE:
        MOV AL,pfelfSHT_PROGBITS
        OR  AH,pfelfSHF_ALLOC+pfelfSHF_EXECINSTR
        RET
    .sssPurposeDRECTVE:
        MOV AL,pfelfSHT_NOTE
        RET
    .sssPurposePHDR:
        RET
    .sssPurposeGOT:
        MOV CL,4
        MOV CH,8
    .sssPurposeDATA:
        MOV AL,pfelfSHT_PROGBITS
        OR  AH,pfelfSHF_ALLOC+pfelfSHF_WRITE
        RET
    .sssPurposeSTACK:
    .sssPurposeBSS:
        MOV AL,pfelfSHT_NOBITS
        OR  AH,pfelfSHF_ALLOC+pfelfSHF_WRITE
        RET
    .sssPurposeLITERAL:
    .sssPurposeRODATA:
        MOV AL,pfelfSHT_PROGBITS
        OR  AH,pfelfSHF_ALLOC
        RET
    .sssPurposeSYMBOLS:
        MOV CL,SIZE# PFELF_SYM32
        MOV CH,SIZE# PFELF_SYM64
        MOV AL,pfelfSHT_SYMTAB
        CMP EDI,'dyns'                               ; [.dynsym] ?
        JNE .Ret:
        MOV AL,pfelfSHT_DYNSYM
        OR  AH,pfelfSHF_ALLOC
    .Ret:RET
    .sssPurposeDYNAMIC:
        MOV CL,SIZE# PFELF_DYN32
        MOV CH,SIZE# PFELF_DYN64
        MOV AL,pfelfSHT_DYNAMIC
        OR  AH,pfelfSHF_ALLOC+pfelfSHF_WRITE
        RET
    .sssPurposeINTERP:
        MOV AL,pfelfSHT_PROGBITS
        OR  AH,pfelfSHF_ALLOC
        RET
    .sssPurposeSTRINGS:
        MOV AL,pfelfSHT_STRTAB
        CMP EDI,'dyns'                               ; [.dynstr] ?
        JNE .Ret:
        MOV AH,pfelfSHF_ALLOC
        RET
    .sssPurposeRELOC:
        OR  AH,pfelfSHF_ALLOC
        MOV AL,pfelfSHT_REL
        MOV CL,SIZE# PFELF_REL32
        MOV CH,SIZE# PFELF_REL64
        CMP EDI,'rela'                               ; [.rela.Name] ?
        JNE .Ret:
        MOV CL,SIZE# PFELF_RELA32
        MOV CH,SIZE# PFELF_RELA64
        MOV AL,pfelfSHT_RELA
        RET
    .sssPurposeHASH:
        MOV CL,4
        MOV CH,4
        MOV AL,pfelfSHT_HASH
        OR  AH,pfelfSHF_ALLOC
        RET
    .Dispatched:  ; Return from subprocedures which have set AH=SHDR.sh_flags, AL=SHDR.sh_type, CH=SHDR64.sh_entsize, CL=SHDR32.sh_entsize.
        MOV EDI,[%ShdrBottom]
        MOV [EDI+PFELF_SHDR64.sh_type],AL            ; Identical member of SHDR64 and SHDR32.
        MOV [EDI+PFELF_SHDR64.sh_flags],AH           ; Identical member of SHDR64 and SHDR32.
        MOV EAX,[ESI+SSS.TopLow]
        MOV EDX,[ESI+SSS.TopHigh]
        SUB EAX,[ESI+SSS.BottomLow]
        SBB EDX,[ESI+SSS.BottomHigh]                 ; SHDR.sh_size
        JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.70:
        ; PFELF_SHDR32 members.
        MOV [EDI+PFELF_SHDR32.sh_size],EAX
        TEST EDX
        Msg cc=NZ,'8515',ESI                         ; Size of segment [!1S] exceeds 4 GB.
        MOV [EDI+PFELF_SHDR32.sh_entsize],CL
        MOV EAX,[ESI+SSS.BottomLow]
        MOV ECX,[ESI+SSS.BottomFA]
        MOV EDX,[ESI+SSS.Alignment]
        MOV [EDI+PFELF_SHDR32.sh_addr],EAX
        MOV [EDI+PFELF_SHDR32.sh_offset],ECX
        MOV [EDI+PFELF_SHDR32.sh_addralign],EDX
        MOV EAX,[ESI+SSS.sh_link]
        MOV EDX,[ESI+SSS.sh_info]
        MOV [EDI+PFELF_SHDR32.sh_link],EAX
        MOV [EDI+PFELF_SHDR32.sh_info],EDX
        JMP .20:
    .70:; PFELF_SHDR64 members.
        MOV [EDI+PFELF_SHDR64.sh_size+0],EAX
        MOV [EDI+PFELF_SHDR64.sh_size+4],EDX
        MOV [EDI+PFELF_SHDR64.sh_entsize],CH
        MOV [EDI+PFELF_SHDR64.sh_offset+0],ECX
        MOV EDX,[ESI+SSS.Alignment]
        MOV [EDI+PFELF_SHDR64.sh_addralign+0],EDX
        MOV EAX,[ESI+SSS.BottomLow]
        MOV EDX,[ESI+SSS.BottomHigh]
        MOV ECX,[ESI+SSS.BottomFA]
        MOV [EDI+PFELF_SHDR64.sh_addr+0],EAX
        MOV [EDI+PFELF_SHDR64.sh_addr+4],EDX
        MOV [EDI+PFELF_SHDR64.sh_offset+0],ECX
        MOV EAX,[ESI+SSS.sh_link]
        MOV EDX,[ESI+SSS.sh_info]
        MOV [EDI+PFELF_SHDR64.sh_link],EAX
        MOV [EDI+PFELF_SHDR64.sh_info],EDX
        JMP .20:
    .80:MOV EDX,[%Pfelf]                       ; Update VA of [.shstrtab] by emitted data.
        MOV EDI,[EDX+PFELF.Sss.shstrtab]
        BufferRetrieve [EDI+SSS.EmitBuffer]
        MOV EAX,[EDI+SSS.BottomLow]
        MOV EDX,[EDI+SSS.BottomHigh]
        ADD EAX,ECX
        ADC EDX,0
        MOV [EDI+SSS.TopLow],EAX
        MOV [EDI+SSS.TopHigh],EDX
      EndProcedure PfelfShdr

↑ PfelfConvertSymbols Pfelf, TabType
  populates the `[.symtab] | [.dynsym]` segment of the linked program with PFELF_SYM symbols.
  PfelfConvertSymbols creates PFELF_SYM records STN_UNDEFX, STT_FILE and then it converts other symbols from `Pgm.SymOrdBuf` in this order:
  0. STN_UNDEFX empty NUL symbol
  1. STT_FILE with the name of source file
  2. STT_SECTION for each regular program section
  3. STB_LOCAL for each private symbol in Pgm (not when TabType=pfelfDyn) when PFELF.Status:pfelfFormatELFX is not set
  4. STB_GLOBAL for each public, extern, exported or imported symbol in Pgm
Input
  Pfelf is pointer to PFELF object,
  TableType is either `0` (covert to [.symtab]+[.strtab]), or `pfelfDyn` (convert to [.dynsym]+[.dynstr]+[.hash])
Output

  PFELF_SYM records are stored in SSS.EmitBuffer of segment `[.symtab] | [.dynsym]`, their names are stored in SSS.EmitBuffer of segment `[.strtab] | [.dynstr]`.

  `SYM.NameIndex | SYM.NameDynIndex` of converted symbols is updated.
  Segments with sssPurposeRegular create PFELF_SYM with STT_SECTION type, their SSS.NameIndex is updated.
  SSS.sh_info of segment `[.symtab]|[.dynsym]` is updated with the index of the first non-local symbol.

Error
  Errors are reported with macro Msg.
Invoked by
  PfelfCompile PfelfsoCompile PfelfxCompile
Invokes
  SssUpdateByEmit

    PfelfConvertSymbols Procedure Pfelf, TabType
    SymOrd  LocalVar                                 ; Zero-based ordinal number of the symbol in [.symtab]. Also SYM.NameIndex or SYM.NameDynIndex.
    SymSss  LocalVar                                 ; ^SSS [.symtab] or [.dynsym].
    StrSss  LocalVar                                 ; ^SSS [.strtab] or [.dynstr].
    SymPtr  LocalVar                                 ; Pointer to an array of pointers to SYM.
    SymEnd  LocalVar                                 ; End of the array.
    Sym     LocalVar                                 ; ^SYM.
    SymBuf  LocalVar                                 ; ^SSS.EmitBuffer for PFELF_SYM records.
    NameBuf LocalVar                                 ; ^SSS.EmitBuffer for ASCIIZ symbol names.
        MOV EDI,[%Pfelf]
        MOV EBX,[EDI+PFELF.Pgm]
        BufferRetrieve [EBX+PGM.SymOrdBuffer]
        ADD ECX,ESI
        MOV [%SymPtr],ESI
        MOV [%SymEnd],ECX                            ; %SymPtr..%SymEnd is an array of pointers to SYM in the right order.
        JSt [%TabType],pfelfDyn,.15:
        MOV ESI,[EDI+PFELF.Sss.symtab]
        MOV EDX,[EDI+PFELF.Sss.strtab]
        JMP .20:
    .15:MOV ESI,[EDI+PFELF.Sss.dynsym]
        MOV EDX,[EDI+PFELF.Sss.dynstr]
    .20:MOV [%SymSss],ESI
        MOV [%StrSss],EDX
        MOV EAX,[EDX+SSS.SegmIndex]
        MOV [ESI+SSS.sh_link],EAX                    ; Symbol section ESI uses names from string table EDX.
        MOV ECX,[ESI+SSS.EmitBuffer]
        MOV EAX,[EDX+SSS.EmitBuffer]
        MOV [%NameBuf],EAX
        BufferStoreByte EAX,0                        ; Initialize [.strtab] or [.dynstr] with NULL symbol name.
        MOV [%SymBuf],ECX
        BufferNew ECX,[EDI+PFELF.symSize],Zeroed=Yes ; Create NULL PFELF_SYM.
        MOVD [%SymOrd],1
        JSt [%TabType],pfelfDyn,.35:
        BufferNew ECX,[EDI+PFELF.symSize],Zeroed=Yes ; Create STT_FILE SYM of source file.
        BufferRetrieve [%NameBuf]                    ; ECX=1.
        MOV [EAX+PFELF_SYM64.st_name],ECX            ; Offset of .st_name is identical for PFELF_SYM32 and PFELF_SYM64.
        LEA EDX,[Ea.SrcFile.Name::]
        ADD EDX,[Ea.SrcFile.NameOffs::]
        GetLength$ EDX                               ; EDX,ECX is now source file name without path.
        INC ECX                                      ; Zero terminated.
        BufferStore [%NameBuf],EDX,ECX
        MOV EDX,pfelfSTB_LOCAL+pfelfSTT_FILE + pfelfSHN_ABS<<16  ; >>
        JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.25:
        MOV [EAX+PFELF_SYM32.st_info],EDX            ; Store .st_info, .st_other, .st_shndx.
        JMP .30:
    .25:MOV [EAX+PFELF_SYM64.st_info],EDX            ; Store .st_info, .st_other, .st_shndx.
    .30:INCD [%SymOrd]                               ; Skip NUL and FILE ELF symbols.
    .35:MOV EDI,[%Pfelf]
        MOV EBX,[EDI+PFELF.Pgm]
        MOV ESI,[%SymPtr]                            ; The main loop through symbols.  EDI=^PFELF
        CMP ESI,[%SymEnd]
        JNB .90:                                     ; End when no more symbols are left.
        LODSD                                        ; EAX=^SYM.
        MOV [%SymPtr],ESI
        MOV [%Sym],EAX

        JSt [EAX+SYM.Status],symSe|symImport|symExport|symGlobal|symPublic|symExtern,.40:
        jmp .35:                                     ; Only dynamic global symbols will be converted to [.dynsym].

    ;;;    JNSt [%TabType],pfelfDyn,.40:                ; All symbols will be converted to [.symtab].
        JNSt [EAX+SYM.Status],symImport|symExport,.35: ; Only dynamic global symbols will be converted to [.dynsym].

    .40:BufferNew [%SymBuf],[EDI+PFELF.symSize],Zeroed=Yes
        MOV EDI,EAX                                  ; EDI=^PFELF_SYM.
        BufferRetrieve [%NameBuf]
        MOV [EDI+PFELF_SYM64.st_name],ECX            ; PFELF_SYM64.st_name=PFELF_SYM32.st_name.
        MOV ESI,[%Sym]                               ; Restore ESI=^SYM.
        BufferStore [%NameBuf],[ESI+SSS.NamePtr],[ESI+SSS.NameSize]
        BufferStoreByte [%NameBuf],0                 ; Zero-terminate the name in [.strtab] or [.dynstr].
        JNSt [ESI+SYM.Status],symExplScopeMask,.43:  ; Skip local and section symbols.
        MOV EDX,[%SymSss]                            ; Converting GLOBAL symbol. EDX=^[.symtab] or ^[.dynsym].
        CMPD [EDX+SSS.sh_info],0
        JNE .43:                                     ; Symbol table .sh_info wasn't established yet?
        MOV EAX,[%SymOrd]
        MOV [EDX+SSS.sh_info],EAX                    ; Index of the first global symbol.
    .43:XOR EAX,EAX
        XOR EDX,EDX
        MOV ECX,[ESI+SYM.Section]
        JECXZ .45:                                   ; Jump when symbol ESI is an absolute constant.
        MOV EAX,[ECX+SSS.BottomLow]
        MOV EDX,[ECX+SSS.BottomHigh]
    .45:ADD EAX,[ESI+SYM.OffsetLow]
        ADC EDX,[ESI+SYM.OffsetHigh]
        MOV ECX,[%SymOrd]
        JSt [%TabType],pfelfDyn,.48:
        MOV [ESI+SYM.NameIndex],ECX
        JMPS .49:
    .48:INC ECX
        MOV [ESI+SYM.NameDynIndex],ECX
        DEC ECX
    .49:INC ECX
        MOV [%SymOrd],ECX
        MOV ECX,[ESI+SYM.Section]
        JECXZ .50:                                   ; Jump when symbol ESI is an absolute constant (scalar).
        JSt [ECX+SSS.Status],sssStructure,.50:
        MOV ECX,[ECX+SSS.SegmIndex]
        JMP .55:
    .50:MOV CX,pfelfSHN_ABS                          ; 0xFFF1 - scalar symbol special index.
    .55:PUSH EBX
          MOV EBX,[%Pfelf]
          JSt [EBX+PFELF.Status],pfelfWidth64,.60:
          MOV [EDI+PFELF_SYM32.st_shndx],CX
          MOV [EDI+PFELF_SYM32.st_value],EAX
          JMP .65:
    .60:  MOV [EDI+PFELF_SYM64.st_shndx],CX
          MOV [EDI+PFELF_SYM64.st_value+0],EAX
          MOV [EDI+PFELF_SYM64.st_value+4],EDX
    .65:POP EBX                                       ; Restore ^PGM.
        MOV ECX,[ESI+SYM.Status]
        MOV DL,pfelfSTT_FUNC
        JSt ECX,symProc,.70:
        CMP CL,'I'
        JE .70:
        MOV DL,pfelfSTT_SECTION
        JSt ECX,symSe,.70:
        MOV DL,pfelfSTT_NOTYPE
        CMP CL,'A'
        JE .70:
        CMP CL,'N'
        JE .70:
        CMP CL,0
        JE .70:
        CMP CL,'?'
        JE .70:
        MOV DL,pfelfSTT_OBJECT                        ; Undispatched data types 'B','W','D','Q' etc.
    .70:JNSt ECX,symScopeMask,.80:
        JNSt ECX,symWeak,.75:
        OR DL,pfelfSTB_WEAK
        JMP .80:
    .75:OR DL,pfelfSTB_GLOBAL
    .80:MOV EAX,[ESI+SYM.Size]
        JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.85:
        MOV [EDI+PFELF_SYM32.st_size],EAX
        MOV [EDI+PFELF_SYM32.st_info],DL
        JMP .86:
    .85:MOV [EDI+PFELF_SYM64.st_size+0],EAX
        MOV [EDI+PFELF_SYM64.st_info],DL
    .86:; Create relocation of symbol address.
        MOV EDI,[%Pfelf]
        MOV EAX,[%Sym]
        JSt [EAX+SYM.Status],symImport,.35:
        MOV EDX,[%SymSss]                              ; [.symtab] or [.dynsym]
        BufferRetrieve [EDX+SSS.EmitBuffer]            ; ECX=offset behind the just emitted PFELF_SYM
        BufferNew [EDX+SSS.RelocBuffer],SIZE# RELOC,Zeroed=Yes
        MOV EBX,EAX
        MOV [EBX+RELOC.Section],EDX
        JNSt [EDI+PFELF.Status],pfelfWidth64,.87:
        MOV EAX,relocWidth64+relocAbsVA
        SUB ECX,SIZE# PFELF_SYM64 - PFELF_SYM64.st_value
        JMPS .88:
    .87:MOV EAX,relocWidth32+relocAbsVA
        SUB ECX,SIZE# PFELF_SYM32 - PFELF_SYM32.st_value
    .88:MOV [EBX+RELOC.Status],EAX
        MOV [EBX+RELOC.OrgLow],ECX
        MOV ECX,[%Sym]
        MOV [EBX+RELOC.Symbol],ECX
        MOV EAX,[ECX+SYM.OffsetLow]
        MOV EDX,[ECX+SYM.OffsetHigh]
        SUB [EBX+RELOC.AddendLow],EAX
        SBB [EBX+RELOC.AddendHigh],EDX
        JMP .35
    .90:Invoke SssUpdateByEmit::,[%StrSss]
        Invoke SssUpdateByEmit::,[%SymSss]
        JNSt [%TabType],pfelfDyn,.99:
        BufferRetrieve [%NameBuf]
        MOV EDI,[%Pfelf]
        MOV [EDI+PFELF.dynstrSize],ECX
    .99:EndProcedure PfelfConvertSymbols

↑ PfelfLink Pfelf

  PfelfLink calculates size, alignment and file address of each program segment. Virtual address is 0 for each ELF section. It is invoked when the emited contents of all segments has been generated and their order was already established in `Pgm.OrdBuffer`.
  It assumes ImageBase=0 and EHDR,SHDR at the beginning of the output file, followed by emitted contents of the segments (ELF sections).
  PfelfLink calculates segment virtual size as `SSS.Top - SSS.Bottom` for both initialized and uninitialized segments.
  Segment file size is calculated from the size of Sss.EmitBuffer.

  PfelfLink does not change the contents of `SSS.EmitBuffer, SSS.RelocBuffer`

Input
  Pfelf is pointer to PFELF object.
Output
  File addresses of all SSS segments and groups are adjusted.
Error
  Errors are reported with macro Msg.
See also
  PgmLink PfelfLink PfelfxLink PfelfsoLink
Invokes
  ExpAlign PgmResizeGroups
Invoked by
  PfelfCompile

    PfelfLink Procedure Pfelf
    ArrayPtr     LocalVar ; Pointer to the array of DWORD pointers to SSS groups and segments.
    ArrayEnd     LocalVar ; Behind the last pointer.
    Size         LocalVar ; Virtual (emitted or reserved) segment size.
    SHDRsize     LocalVar ; Size of one ELF section header (28h or 40h).
    FA           LocalVar ; Current file address.
    FileAlign    LocalVar ; Effective file alignment.
        MOV EDI,[%Pfelf]
        MOV EBX,[EDI+PFELF.Pgm]
        MOV EDX,SIZE# PFELF_SHDR64
        JSt [EBX+PGM.Pgmopt.Status],pgmoptWidth64,.10:
        MOV DL,SIZE# PFELF_SHDR32
    .10:MOV [%SHDRsize],EDX
        BufferRetrieve [EBX+PGM.SegOrdBuffer]        ; Prepare array of DWORD pointers to SSS.
        ADD ECX,ESI
        MOV [%ArrayPtr],ESI
        MOV [%ArrayEnd],ECX
        MOV EAX,[EBX+PGM.Pgmopt.ImageBaseLow]        ; ImageBase=0 in ELF.
        ADD EAX,[EDI+PFELF.HDRsEnd]                  ; Calculate headers size to start linking at.
        MOV [%FA],EAX
    .20:MOV ESI,[%ArrayPtr]
        CMP ESI,[%ArrayEnd]
        JNB .80:                                     ; End when all segments were linked.
        LODSD
        MOV [%ArrayPtr],ESI
        MOV EDI,EAX
        SetSt [EDI+SSS.Status],sssLinked             ; Tell RelocResolve that segments are linked.
    .40:JNSt [EDI+SSS.Status],sssSegment,.20:
        CMPD [EDI+SSS.NameSize],0                    ; EDI=^SSS.sssSegment (ELF "section").
        JZ .20:                                      ; Skip the empty NULL segment [].
        MOV ECX,[EDI+SSS.Alignment]                  ; ECX is segment's own alignment.
        JNSt [EDI+SSS.Purpose],sssPurposeRegular,.50:
        CMP ECX,[EBX+PGM.Pgmopt.FileAlign]
        JAE .50:
        MOV ECX,[EBX+PGM.Pgmopt.FileAlign]
    .50:MOV [%FileAlign],ECX
    ; Link the segment EDI.
        MOV EAX,[EDI+SSS.TopLow]                     ; Compute segment size in memory.
        MOV EDX,[EDI+SSS.TopHigh]
        SUB EAX,[EDI+SSS.BottomLow]
        SBB EDX,[EDI+SSS.BottomHigh]
        Msg cc=NZ,'8525',EDI                         ; Size of segment [!1S] exceeded 4 GB.
        MOV [%Size],EAX
        MOV ESI,EAX
        ; Compute segment's file address.
        MOV EDX,[%FA]                                ; (Perhaps unaligned) FA of the previous segment's Top.
        Invoke ExpAlign::,EDX,[%FileAlign],0         ; Returns ECX=alignment stuff size following the headers|previous segment contents.
        ADD ECX,EDX
        MOV [EDI+SSS.BottomFA],ECX                   ; Aligned FA of this segment.
        JNSt [EDI+SSS.Status],sssNotBSS,.55:         ; Skip when EDI is BSS segment.
        ADD ECX,ESI                                  ; Add %Size in the file.
    .55:MOV [EDI+SSS.TopFA],ECX
        MOV [%FA],ECX                                ; Unaligned FA of the next segment.
        JMP .20:                                     ; Go to link the next segment.

    .80:Invoke PgmResizeGroups::,EBX                 ; Update FA and VA of groups by their segments.
        MOV EDX,[%Pfelf]                             ; [LOAD.HDR] and [LOAD.DYNAMIC] were resized incorrectly.
        MOV EDI,[EDX+PFELF.Sss.LOAD.HDR]             ; Pseudogroup for segments [], [.symtab], [.rel(a)name], [.strtab], [.shstrtab].
        TEST EDI
        JZ .90:
        XOR EAX,EAX
        MOV [EDI+SSS.BottomLow],EAX
        MOV [EDI+SSS.BottomHigh],EAX
        MOV [EDI+SSS.BottomFA],EAX
    .90:EndProcedure PfelfLink

↑ PfelfRelaCreate Pfelf
  PfelfRelaCreate creates an empty SSS segment [.rel(a)name] for each SSS segment [ name] which has at least one relocation in its .RelocBuffer.
Input
  Pfelf is pointer to PFELF object.
Output
  SSS segment(s) named [.rel(a)name] are created in %Pfelf.Pgm with sssPurposeRELOC.
Error
  Errors are reported with macro Msg.
Invoked by
  PfelfCompile PfelfsoCompile

    PfelfRelaCreate Procedure Pfelf
    RegularSss LocalVar ; ^SSS [name].
    RelaSss    LocalVar ; ^SSS [.rel(a)name].
        MOV EDI,[%Pfelf]
        MOV EBX,[EDI+PFELF.Pgm]
        ListGetFirst [EBX+PGM.SssList]
    .10:MOV [%RegularSss],EAX
        JNSt [EAX+SSS.Status],sssSegment,.80:
        BufferRetrieve [EAX+SSS.RelocBuffer]
        JNA .80:
        ListNew [EBX+PGM.SssList],Zeroed=Yes         ; Create a new SSS segment.
        MOV [%RelaSss],EAX
        MOV ESI,EAX                                  ; SSS [.rel(a).name].
        SetSt [ESI+SSS.Status],sssSegment+sssNotBSS+sssPublic
        SetSt [ESI+SSS.Purpose],sssPurposeRELOC
        MOVD [ESI+SSS.Alignment],8
        BufferClear [EDI+PFELF.Buffer]               ; Temporary buffer for construction .rel(a).name.
        BufferStore [EDI+PFELF.Buffer],=B'.rel',4
        JNSt [EDI+PFELF.Status],pfelfWidth64,.20:
        BufferStoreByte [EDI+PFELF.Buffer],'a'
    .20:MOV EAX,[%RegularSss]
        BufferStore [EDI+PFELF.Buffer],[EAX+SSS.NamePtr],[EAX+SSS.NameSize]
        BufferRetrieve [EDI+PFELF.Buffer]
        PoolStore [EBX+PGM.Pool],ESI,ECX
        MOV ESI,[%RelaSss]
        MOV [ESI+SSS.NamePtr],EAX                    ; [.rel(a).name].
        MOV [ESI+SSS.NameSize],ECX
        MOV EAX,[%RegularSss]
        MOV [ESI+SSS.sh_info],EAX                    ; Temporarity store ^SSS [name], which will be replaced with .SegmIndex after PfelfLink.
    .80:ListGetNext [%RegularSss]
        JNZ .10:
       EndProcedure PfelfRelaCreate

↑ PfelfRelaConvert Pfelf
  PfelfRelaConvert copies all unresolved RELOC relocations from regular SSS segments ([.name]) and converts those RELOC record to the format PFELF_REL* and stores it to [.rel(a) name].EmitBuffer.
Input
  Pfelf is pointer to PFELF object.
Output
  [.rel(a).name] SSS segment is updated for each SSS with relocations.
Error
  Errors are reported with macro Msg.
Invoked by
  PfelfCompile PfelfsoCompile
Invokes
  SssUpdateByEmit

    PfelfRelaConvert Procedure Pfelf
    RegularSss LocalVar ; ^SSS [name].
    RelaSss    LocalVar ; ^SSS [.rel(a)name].
    RelaBuffer LocalVar ; ^SSS [.rel(a)name].EmitBuffer.
    RelocPtr   LocalVar ; Pointer to an array of RELOC records.
    RelocEnd   LocalVar ; Pointer to the end of input RELOC array.
    EmitPtr    LocalVar ; Pointer to the contents of %RegularSss.EmitBuffer.
    EmitEnd    LocalVar ; Pointer to the end of %RegularSss.EmitBuffer.
        MOV EDI,[%Pfelf]
        MOV EBX,[EDI+PFELF.Pgm]
        ListGetFirst [EBX+PGM.SssList]
    .10:MOV [%RelaSss],EAX
        JNSt [EAX+SSS.Status],sssSegment,.85:
        JNSt [EAX+SSS.Purpose],sssPurposeRELOC,.85:
        MOV EDX,[EAX+SSS.sh_info]                    ; ^SSS [name] temporarily stored there by PfelfRelaCreate.
        TEST EDX
        JZ .85:
        MOV [%RegularSss],EDX
        MOV ECX,[EDX+SSS.SegmIndex]
        MOV [EAX+SSS.sh_info],ECX                    ; Replace with index.
        MOV ECX,[EDI+PFELF.Sss.symtab]
        JECXZ .15:
        MOV ECX,[ECX+SSS.SegmIndex]
    .15:MOV [EAX+SSS.sh_link],ECX
        BufferRetrieve [EDX+SSS.RelocBuffer]
        JNA .85:
        MOV [%RelocPtr],ESI
        MOV [%RelocEnd],ESI
        ADD [%RelocEnd],ECX
        MOV ESI,EAX                                  ; SSS [.rel(a).name].
        MOV EBX,[EDI+PFELF.Pgm]
        BufferCreate [EBX+PGM.Pool],Size=ECX
        MOV [ESI+SSS.EmitBuffer],EAX
        MOV [%RelaBuffer],EAX
    .20:MOV ESI,[%RelocPtr]                          ; Convert ESI=^RELOC to PFELF_REL*.
        CMP ESI,[%RelocEnd]
        JNB .80:
        JSt [ESI+RELOC.Status],relocIgnore,.70:
        BufferNew [%RelaBuffer],[EDI+PFELF.relSize],Zeroed=Yes
        MOV EBX,EAX                                  ; Convert ESI=^RELOC to EBX=^PFELF_REL32.
        JSt [EDI+PFELF.Status],pfelfWidth64,.50:

        ; PFELF_REL32 needs to add RELOC.Addend to the relocated DWORD object in emitted code.
        MOV EAX,[ESI+RELOC.OrgLow]
        MOV ECX,[ESI+RELOC.Section]
        TEST ECX
        JZ .E7732:                                   ; Relocation type 0x!1W at [!2S]:!3H is out of range.
        BufferRetrieve [ECX+SSS.EmitBuffer]          ; ESI,ECX is the emitted contents.
        CMP EAX,ECX
        JNB .E7732:                                  ; Relocation type 0x!1W at [!2S]:!3H is out of range.
        LEA ECX,[ESI+EAX]                            ; Pointer to the relocated DWORD object.
        MOV ESI,[%RelocPtr]
        XOR EAX,EAX
        XOR EDX,EDX
        XCHG EAX,[ESI+RELOC.AddendLow]               ; Apply EDX:EAX=RELOC.Addend to the emitted object.
        XCHG EDX,[ESI+RELOC.AddendHigh]
        JNSt [ESI+RELOC.Status],relocWidth32,.25:
        ADD [ECX],EAX
        JMPS .35:
    .25:JNSt [ESI+RELOC.Status],relocWidth64,.30:
        ADD [ECX+0],EAX
        ADC [ECX+4],EDX
        JMPS .35:
    .30:JNSt [ESI+RELOC.Status],relocWidth16,.35:
        ADD [ECX],AX
    .35:MOV ECX,[ESI+RELOC.Section]
        MOV EAX,[ESI+RELOC.OrgLow]
        ADD EAX,[ECX+SSS.BottomLow]
        MOV [EBX+PFELF_REL32.r_offset],EAX
        MOV ECX,[ESI+RELOC.Symbol]
        TEST ECX
        JZ .E7738:
        MOV ECX,[ECX+SYM.NameIndex]
        SHL ECX,8                                    ; Target symbol index in bits 8..31.
        MOV EDX,[ESI+RELOC.Status]
        JSt EDX,relocWidth16|relocWidth64,.E7730:    ; Unsupported !1Dbit relocation type 0x!2W at [!3S]:!4Hh.
        MOV CL,pfelfR_386_32                         ; 1.
        JSt EDX,relocAbsVA,.40:
        MOV CL,pfelfR_386_PC32                       ; 2.
        JSt EDX,relocRel,.40:
        MOV CL,pfelfR_386_GOT32                      ; 3.
        JSt EDX,relocGOT,.40:
        MOV CL,pfelfR_386_PLT32                      ; 4.
        JSt EDX,relocPLT,.40:
        MOV CL,pfelfR_386_GLOB_DAT                   ; 6.
        JSt EDX,relocSym,.40:
        MOV CL,pfelfR_386_RELATIVE                   ; 8.
        JSt EDX,relocDyn,.40:
        MOV CL,pfelfR_386_GOTOFF                     ; 9.
        JSt EDX,relocGOToff,.40:
        MOV CL,pfelfR_386_GOTPC                      ; 10.
        JSt EDX,relocGOTrel,.40:
        MOV CL,0
    .40:MOV [EBX+PFELF_REL32.r_info],ECX             ; PFELF_REL32 at EBX is done.
        JMP .70:
    .E7730:MOV EDX,[ESI+RELOC.Status]
        MOVZX ECX,DX                                  ; relocTypeMask.
        AND EDX,relocWidthMask
        SHR EDX,16
        Msg '7730',EDX,ECX,[ESI+RELOC.Section],[ESI+RELOC.OrgLow] ; Unsupported !1Dbit relocation type 0x!2W at [!3S]:!4Hh.
        JMP .70:
    .E7732:                                          ; Unsupported !1Dbit relocation type 0x!2W at [!3S]:!4Hh.
        MOV ESI,[%RelocPtr]
        MOV EDX,[ESI+RELOC.Status]
        MOVZX ECX,DX                                 ; relocTypeMask.
        AND EDX,relocWidthMask
        SHR EDX,16
        Msg '7730',EDX,ECX,[ESI+RELOC.Section],[ESI+RELOC.OrgLow]
        JMP .70:                                     ; The next RELOC.
    .E7738:Msg '7738',[ESI+RELOC.Section],[ESI+RELOC.OrgLow] ; Missing target symbol of relocation at [!1S]:!2Hh.
        JMP .70:
    .50: ; PFELF_RELA64 has its own Addend field.
        MOV EAX,[ESI+RELOC.OrgLow]
        MOV EDX,[ESI+RELOC.OrgHigh]
        MOV ECX,[ESI+RELOC.Section]
        JECXZ .55:
        ADD EAX,[ECX+SSS.BottomLow]
        ADC EDX,[ECX+SSS.BottomHigh]
    .55:MOV [EBX+PFELF_RELA64.r_offset+0],EAX
        MOV [EBX+PFELF_RELA64.r_offset+4],EDX
        MOV EAX,[ESI+RELOC.AddendLow]
        MOV EDX,[ESI+RELOC.AddendHigh]
        MOV [EBX+PFELF_RELA64.r_addend+0],EAX
        MOV [EBX+PFELF_RELA64.r_addend+4],EDX
        MOV ECX,[ESI+RELOC.Symbol]                   ; Construct PFELF_RELA64.r_info.
        JECXZ .E7738:                                ; Missing target symbol of relocation at [!1S]:!2Hh.
        MOV ECX,[ECX+SYM.NameIndex]
        MOV EDX,[ESI+RELOC.Status]
        JSt EDX,relocWidth16,.E7730:                 ; Unsupported !1Dbit relocation type 0x!2W at [!3S]:!4Hh.
        JSt EDX,relocWidth64,.60:
        MOV EAX,pfelfR_X86_64_32                     ; 10
        JSt EDX,relocAbsVA,.65:
        MOV AL,pfelfR_X86_64_PC32                    ; 2.
        JSt EDX,relocRel,.65:
        MOV AL,pfelfR_X86_64_GOT32                   ; 3.
        JSt EDX,relocGOT,.65:
        MOV AL,pfelfR_X86_64_PLT32                   ; 4.
        JSt EDX,relocPLT,.65:
        MOV AL,pfelfR_X86_64_GOTPC32                 ; 26.
        JSt EDX,relocGOTrel,.65:
        MOV AL,pfelfR_X86_64_NONE                    ; 0.
        JMP .65:
    .60:MOV EAX,pfelfR_X86_64_64                     ; 1.
        JSt EDX,relocAbsVA,.65:
        MOV AL,pfelfR_X86_64_JUMP_SLOT               ; 7.
        JSt EDX,relocSym,.65:
        MOV AL,pfelfR_X86_64_RELATIVE                ; 8.
        JSt EDX,relocDyn,.65:
        MOV AL,pfelfR_X86_64_GOTOFF64                ; 25.
        JSt EDX,relocGOToff,.65:
        MOV AL,pfelfR_X86_64_NONE                    ; 0.
    .65:MOV [EBX+PFELF_RELA64.r_info+0],EAX
        MOV [EBX+PFELF_RELA64.r_info+4],ECX
    .70:ADDD [%RelocPtr],SIZE# RELOC
        JMP .20:                                     ; The next RELOC.
    .80:Invoke SssUpdateByEmit::,[%RelaSss]
    .85:ListGetNext [%RelaSss]
        JNZ .10:                                     ; The next regular segment.
       EndProcedure PfelfRelaConvert

↑ PfelfHdrsLink Pfelf
  PfelfHdrsLink calculates the number of program and section headers and their file offsets. It is invoked when the number of groups and segment is fixed.
  PFELF_EHDR is always at file address 0. Then follows PFELF_PHDR after alignment 16 or 8 (if nonempty) and finally PFELF_SHDR after alignment 8 or 16. File address of end of SHDRs is written to PFELF.HDRsEnd.
Input
  Pfelf is a pointer to PFELF object.
Output
  PFELF members `.EHDR.e_phnum, .EHDR.e_phoff, .EHDR.e_shnum, EHDR.e_shoff, .HDRsEnd` are updated.
Error
  Errors are reported with macro Msg.
Invoked by
  PfelfCompile PfelfsoCompile PfelfxCompile
Invokes
  ExpAlign

    PfelfHdrsLink Procedure Pfelf
    PHDRlen LocalVar
    SHDRlen LocalVar
    OrdEnd  LocalVar
        MOV EDI,[%Pfelf]
        MOV EBX,[EDI+PFELF.Pgm]
        BufferRetrieve [EBX+PGM.SegOrdBuffer]
        ADD ECX,ESI
        MOV [%OrdEnd],ECX
        SUB ECX,ECX                                  ; PHDR counter.
        SUB EDX,EDX                                  ; SHDR counter.
    .10:CMP ESI,[%OrdEnd]
        JNB .30:
        LODSD
        JNSt [EAX+SSS.Status],sssGroup,.20:
        INC ECX
    .20:JNSt [EAX+SSS.Status],sssSegment,.10:
        INC EDX
        JMP .10:
    .30:MOV [EDI+PFELF.EHDR.e_phnum],ECX
        MOV [EDI+PFELF.EHDR.e_shnum],EDX
        MOV ECX,SIZE# PFELF_EHDR64 << 16 +SIZE# PFELF_SHDR64 << 8 + SIZE# PFELF_PHDR64 ; >>>>
        JSt [EDI+PFELF.Status],pfelfWidth64,.40:
        MOV ECX,SIZE# PFELF_EHDR32 << 16 +SIZE# PFELF_SHDR32 << 8 + SIZE# PFELF_PHDR32 ; >>>>
    .40:MOVZX EAX,CH                                 ; Size of one section header entry.
        MUL [EDI+PFELF.EHDR.e_shnum]
        MOV [%SHDRlen],EAX                           ; Total length of SHDRs in bytes.
        MOVZX EAX,CL
        MUL [EDI+PFELF.EHDR.e_phnum]
        MOV [%PHDRlen],EAX                           ; Total length of PHDRs in bytes.
        SHR ECX,16                                   ; ECX=Size of PFELF_EHDR (34h or 40h).
        MOV ESI,ECX                                  ; ESI=Current unaligned FA.
        MOV CL,8                                     ; ECX=PHDR alignment in 64 bits.
        MOV EDX,16                                   ; EDX=SHDR alignment in 64 bits.
        JSt [EDI+PFELF.Status],pfelfWidth64,.45:
        XCHG ECX,EDX                                 ; 32 bits.
    .45:TEST EAX                                     ; Is PHDR present?
        JZ .50:                                      ; Skip if not.
        Invoke ExpAlign::,ESI,ECX,0                  ; Return alignment bytes in ECX.
        ADD ESI,ECX                                  ; ESI is aligned FA of PHDRs.
        MOV [EDI+PFELF.EHDR.e_phoff],ESI
        ADD ESI,EAX
    .50:Invoke ExpAlign::,ESI,EDX,0                  ; Return alignment bytes in ECX.
        ADD ESI,ECX                                  ; ESI is aligned FA of SHDRs.
        MOV [EDI+PFELF.EHDR.e_shoff],ESI
        ADD ESI,[%SHDRlen]
        MOV [EDI+PFELF.HDRsEnd],ESI                  ; Unaligned size of HDRs.
       EndProcedure PfelfHdrsLink

↑ PfelfHdrsStream Pfelf, Stream
  PfelfHdrsStream will store ELF headers (EHDR, PHDR, SHDR) to an empty Stream.
Input
  Pfelf is pointer to PFELF object.
  Stream is pointer to an output memory STREAM.
Output
  Contents of buffers in PFELF members `PFELF.Buffer.EHDR, PFELF.Buffer.PHDR, PFELF.Buffer.SHDR` is aligned and stored to the Stream.
Error
  Errors are reported with macro Msg.
Invoked by
  PfelfCompile PfelfsoCompile PfelfxCompile
Invokes
  EaStreamAlign

    PfelfHdrsStream Procedure Pfelf, Stream
        MOV EDI,[%Pfelf]
        MOV EDX,[%Stream]
        BufferRetrieve [EDI+PFELF.Buffer.EHDR]
        StreamStore EDX,ESI,ECX                     ; ELF file header.
        MOV EAX,8                                   ; PHDR64 alignment.
        MOV EBX,16                                  ; SHDR64 alignment.
        JSt [EDI+PFELF.Status],pfelfWidth64,.20:
        XCHG EAX,EBX                                ; 32 bits.
    .20:BufferRetrieve [EDI+PFELF.Buffer.PHDR]
        JECXZ .30:                                  ; Skip when ELF program headers PHDR are not present.
        Invoke EaStreamAlign::, EDX,EAX,0
        StreamStore EDX,ESI,ECX                     ; ELF program headers.
    .30:Invoke EaStreamAlign::, EDX,EBX,0
        BufferRetrieve [EDI+PFELF.Buffer.SHDR]
        StreamStore EDX,ESI,ECX                     ; ELF section headers.
       EndProcedure PfelfHdrsStream

      ENDPROGRAM pfelf

▲Back to the top▲