Create 32-bit ELF executables
authorRobert Pengelly <robertapengelly@hotmail.com>
Mon, 3 Nov 2025 20:58:51 +0000 (20:58 +0000)
committerRobert Pengelly <robertapengelly@hotmail.com>
Mon, 3 Nov 2025 20:58:51 +0000 (20:58 +0000)
Makefile.unix
Makefile.w32
Makefile.wat
Makefile.wcd [deleted file]
elf.c [new file with mode: 0644]
elf.h [new file with mode: 0644]
ld.c
ld.h
lib.c
link.c
macho.c

index 02e559fb0779044147b359b8cf66102da516c7dc..88ebe511b737dc335c2d7dc89fb4387504dfcaa9 100644 (file)
@@ -7,7 +7,7 @@ VPATH               :=  $(SRCDIR)
 CC                  :=  gcc
 CFLAGS              :=  -D_FILE_OFFSET_BITS=64 -O2 -Wall -Werror -Wextra -ansi -pedantic -std=c90
 
-CSRC                :=  aout.c coff.c elks.c hashtab.c ld.c lib.c link.c macho.c map.c mz.c omf.c pe.c report.c section.c symbol.c vector.c write7x.c
+CSRC                :=  aout.c coff.c elf.c elks.c hashtab.c ld.c lib.c link.c macho.c map.c mz.c omf.c pe.c report.c section.c symbol.c vector.c write7x.c
 
 ifeq ($(OS), Windows_NT)
 all: slink.exe
index 326d9dc59f7ff2d6ddbaa07e1a0d0b53dc3c4ed9..00e19dad9603d433ca04601ffca07232dd46eaa9 100644 (file)
@@ -6,7 +6,8 @@ VPATH               :=  $(SRCDIR)
 
 CC                  :=  gcc
 CFLAGS              :=  -D_FILE_OFFSET_BITS=64 -O2 -Wall -Werror -Wextra
-CSRC                :=  aout.c coff.c elks.c hashtab.c ld.c lib.c link.c macho.c map.c mz.c omf.c pe.c report.c section.c symbol.c vector.c write7x.c
+
+CSRC                :=  aout.c coff.c elf.c elks.c hashtab.c ld.c lib.c link.c macho.c map.c mz.c omf.c pe.c report.c section.c symbol.c vector.c write7x.c
 
 all: slink.exe
 
index a8fed6522c7f73237bdf86eef8a00377fb16d140..daa5883303a08be97903800ee100160f13b22c5f 100644 (file)
@@ -4,7 +4,7 @@
 SRCDIR              ?=  $(CURDIR)
 VPATH               :=  $(SRCDIR)
 
-SRC                 :=  aout.c coff.c elks.c hashtab.c ld.c lib.c link.c macho.c map.c mz.c omf.c pe.c report.c section.c symbol.c vector.c write7x.c
+SRC                 :=  aout.c coff.c elf.c elks.c hashtab.c ld.c lib.c link.c macho.c map.c mz.c omf.c pe.c report.c section.c symbol.c vector.c write7x.c
 
 all: slink.exe
 
diff --git a/Makefile.wcd b/Makefile.wcd
deleted file mode 100644 (file)
index 26a85c7..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#******************************************************************************
-# @file             Makefile.wcd
-# 
-# Produce MSDOS executables links with PDPCLIB created by makefile.wcd
-#******************************************************************************
-CC=wcl
-COPTS=-oneatx -ecc -zp1 -q -w -c -ml -zl -D__MSDOS__ -D__PDOS__ -fpi87 -s -zdp -zu -I. -I..\pdos\pdpclib
-
-all: clean slink.exe
-
-slink.exe: aout.obj coff.obj elks.obj hashtab.obj ld.obj \
-     lib.obj link.obj macho.obj map.obj mz.obj omf.obj pe.obj report.obj \
-     section.obj symbol.obj vector.obj write7x.obj
-  wlink File ..\pdos\pdpclib\dosstart.obj,ld.obj Name slink.exe Form dos Library temp.lib,..\pdos\pdpclib\watcom.lib Option quiet,nod,stack=8192,start=___asmstart,map,verbose,dosseg
-
-.c.obj:
-  $(CC) $(COPTS) $<
-  wlib -b -q temp +$*.obj
-
-clean:
-  rm -f *.obj
-  rm -f slink.exe
-  rm -f temp.lib
diff --git a/elf.c b/elf.c
new file mode 100644 (file)
index 0000000..b315c2a
--- /dev/null
+++ b/elf.c
@@ -0,0 +1,900 @@
+/******************************************************************************
+ * @file            elf.c
+ *****************************************************************************/
+#include    <stdio.h>
+#include    <stdlib.h>
+#include    <string.h>
+
+#include    "ld.h"
+#include    "lib.h"
+#include    "elf.h"
+#include    "report.h"
+#include    "section.h"
+#include    "stdint.h"
+#include    "symbol.h"
+
+#define     UNNAMED_SYMBOL_NAME         "(unnamed)"
+
+#define     SHT_RELA                    4
+#define     SHT_REL                     9
+
+#define     ELF32_R_SYM(i)              ((i) >> 8)
+#define     ELF32_R_TYPE(i)             ((unsigned char) (i))
+
+static void translate_relocation (const char *filename, struct reloc_entry *reloc, struct elf32_rel *input_rel, struct section_part *part, int endianess) {
+
+    uint32_t r_info = array_to_integer (input_rel->r_info, 4, endianess);
+    uint32_t r_offset = array_to_integer (input_rel->r_offset, 4, endianess);
+    
+    uint32_t symbol_index = ELF32_R_SYM (r_info), rel_type = ELF32_R_TYPE (r_info);
+    
+    if (symbol_index >= part->of->symbol_cnt) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "%s: relocation has invalid symbol index", filename);
+        goto bad;
+    
+    }
+    
+    if (r_offset >= part->content_size || r_offset + 4 > part->content_size) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "%s: relocation has invalid r_offset", filename);
+        goto bad;
+    
+    }
+    
+    reloc->symbol = part->of->symbol_arr + symbol_index;
+    reloc->offset = r_offset;
+    
+    if ((state->format & LD_TARGET_MACHINE_I386)) {
+    
+        switch (rel_type) {
+        
+            case R_386_NONE:
+            
+                break;
+            
+            case R_386_32:
+            
+                reloc->howto = &reloc_howtos[RELOC_TYPE_32];
+                break;
+            
+            case R_386_PC32:
+            
+                reloc->howto = &reloc_howtos[RELOC_TYPE_PC32];
+                reloc->addend += 4;
+                
+                break;
+            
+            default:
+            
+                report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "+++relocation type 0x%02x not supported yet", rel_type);
+                exit (EXIT_FAILURE);
+        
+        }
+    
+    } else {
+    bad:
+    
+        reloc->symbol = 0;
+        reloc->offset = 0;
+        
+        reloc->howto = &reloc_howtos[RELOC_TYPE_IGNORED];
+    
+    }
+
+}
+
+#define     ELF32_ST_BIND(i)            ((i) >> 4)
+# define        STB_LOCAL               0
+# define        STB_GLOBAL              1
+
+#define     ELF32_ST_TYPE(i)            ((i) & 0x0F)
+# define        STT_SECTION             3
+
+static uint64_t translate_sh_flags_to_section_flags (uint64_t sh_flags) {
+
+    uint64_t flags = 0;
+    
+    if (!(sh_flags & SHF_WRITE)) {
+        flags |= SECTION_FLAG_READONLY;
+    }
+    
+    if (sh_flags & SHF_ALLOC) {
+        flags |= SECTION_FLAG_ALLOC;
+    }
+    
+    if (sh_flags & SHF_EXEC_INSTR) {
+        flags |= SECTION_FLAG_CODE;
+    }
+    
+    return flags;
+
+}
+
+#define     CHECK_READ(memory_position, size_to_read)                           \
+    do                                                                          \
+        if (((memory_position) - data + (size_to_read) > data_size) ||          \
+             (memory_position) < data) {                                        \
+            report_at (program_name, 0, REPORT_FATAL_ERROR,                     \
+                "%s: corrupt input file", filename);                            \
+            exit (EXIT_FAILURE);                                                \
+        }                                                                       \
+    while (0)
+
+void read_elf32_object (const char *filename, unsigned char *data, unsigned long data_size, int endianess) {
+
+    unsigned char *pos = (unsigned char *) data;
+    
+    uint16_t e_shentsize, e_shstrndx, e_shnum;
+    uint32_t e_shoff, sh_type, sh_link, sh_offset, sh_size, sh_entsize, sh_info;
+    
+    struct elf32_exec *header;
+    struct elf32_shdr *shdr, *strtab_hdr;
+    
+    struct elf32_sym *elf_symbol;
+    struct symbol *symbol, *old_symbol;
+    
+    char *section_name_string_table = 0, *sym_strtab, *section_name, *p;
+    uint32_t section_name_string_table_size = 0, sym_strtab_size, st_name, i, j;
+    
+    struct section_part **part_p_array, *bss_part;
+    struct object_file *of = 0;
+    
+    struct section_part *part;
+    struct section *section;
+    struct subsection *subsection;
+    
+    struct section *bss_section = 0;
+    uint64_t bss_section_number = 0;
+    
+    struct elf32_rela *rela;
+    struct elf32_rel rel;
+    
+    CHECK_READ (pos, sizeof (*header));
+    
+    header = (struct elf32_exec *) pos;
+    pos += sizeof (*header);
+    
+    if (header->e_ident[EI_VERSION] != EV_CURRENT) {
+        
+        report_at (program_name, 0, REPORT_ERROR, "%s: unsupported ELF version", filename);
+        return;
+    
+    }
+    
+    if (array_to_integer (header->e_type, 2, endianess) != ET_REL) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "%s: e_type is no ET_REL", filename);
+        return;
+    
+    }
+    
+    if (array_to_integer (header->e_version, 4, endianess) != EV_CURRENT) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "%s: unsupported ELF version", filename);
+        return;
+    
+    }
+    
+    if (array_to_integer (header->e_ehsize, 2, endianess) < sizeof (*header)) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "%s: e_ehsize is too small", filename);
+        return;
+    
+    }
+    
+    e_shnum = array_to_integer (header->e_shnum, 2, endianess);
+    e_shoff = array_to_integer (header->e_shoff, 4, endianess);
+    
+    e_shentsize = array_to_integer (header->e_shentsize, 2, endianess);
+    
+    if (e_shoff == 0 || e_shentsize == 0 || e_shnum == 0) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "%s: missing section header table", filename);
+        return;
+    
+    }
+    
+    if (e_shentsize < sizeof (*shdr)) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "%s: e_shentsize is too small", filename);
+        return;
+    
+    }
+    
+    e_shstrndx = array_to_integer (header->e_shstrndx, 2, endianess);
+    
+    if (e_shstrndx == 0 || e_shstrndx >= e_shnum) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "%s: missing section header table", filename);
+        return;
+    
+    }
+    
+    pos = data + e_shoff;
+    
+    CHECK_READ (pos, (unsigned long) (e_shentsize * e_shnum));
+    pos += e_shentsize * e_shstrndx;
+    
+    shdr = (struct elf32_shdr *) pos;
+    
+    if (array_to_integer (shdr->sh_type, 4, endianess) != SHT_STRTAB) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "%s: section name string table does no have SHT_STRTAB type", filename);
+        return;
+    
+    }
+    
+    section_name_string_table_size = array_to_integer (shdr->sh_size, 4, endianess);
+    
+    pos = data + array_to_integer (shdr->sh_offset, 4, endianess);
+    CHECK_READ (pos, (unsigned long) section_name_string_table_size);
+    
+    section_name_string_table = (char *) pos;
+    part_p_array = xmalloc (sizeof (*part_p_array) * (e_shnum + 1));
+    
+    for (i = 1; i < e_shnum; i++) {
+    
+        shdr = (struct elf32_shdr *) (pos = data + e_shoff + i * e_shentsize);
+        
+        if (array_to_integer (shdr->sh_type, 4, endianess) != SHT_SYMTAB) {
+            continue;
+        }
+        
+        if (of) {
+        
+            report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: more than 1 symbol table per object file", filename);
+            exit (EXIT_FAILURE);
+        
+        }
+        
+        of = object_file_make (filename, array_to_integer (shdr->sh_size, 4, endianess) / array_to_integer (shdr->sh_entsize, 4, endianess));
+    
+    }
+    
+    for (i = 1; i < e_shnum; i++) {
+    
+        shdr = (struct elf32_shdr *) (pos = data + e_shoff + i * e_shentsize);
+        
+        if (array_to_integer (shdr->sh_name, 4, endianess) < section_name_string_table_size) {
+            section_name = xstrdup (section_name_string_table + array_to_integer (shdr->sh_name, 4, endianess));
+        } else {
+        
+            report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: invalid offset into string table", filename);
+            exit (EXIT_FAILURE);
+        
+        }
+        
+        sh_type = array_to_integer (shdr->sh_type, 4, endianess);
+        
+        if (sh_type != SHT_PROGBITS && sh_type != SHT_NOBITS) {
+        
+            part_p_array[i] = 0;
+            
+            free (section_name);
+            continue;
+        
+        }
+        
+        if ((p = strrchr (section_name, '.'))) {
+        
+            if (p != section_name) {
+                *p++ = '\0';
+            }
+        
+        }
+        
+        section = section_find_or_make (section_name);
+        
+        if (array_to_integer (shdr->sh_addralign, 4, endianess) > section->section_alignment) {
+            section->section_alignment = array_to_integer (shdr->sh_addralign, 4, endianess);
+        }
+        
+        section->flags = translate_sh_flags_to_section_flags (array_to_integer (shdr->sh_flags, 4, endianess));
+        
+        if (sh_type == SHT_NOBITS) {
+            section->is_bss = 1;
+        } else {
+            section->flags |= SECTION_FLAG_LOAD;
+        }
+        
+        if (p) {
+            subsection = subsection_find_or_make (section, p);
+        } else {
+            subsection = 0;
+        }
+        
+        free (section_name);
+        
+        part = section_part_new (section, of);
+        part->alignment = array_to_integer (shdr->sh_addralign, 4, endianess);
+        
+        part->content_size = array_to_integer (shdr->sh_size, 4, endianess);
+        
+        if (sh_type != SHT_NOBITS) {
+        
+            pos = data + array_to_integer (shdr->sh_offset, 4, endianess);
+            
+            part->content = xmalloc (part->content_size);
+            CHECK_READ (pos, part->content_size);
+            
+            memcpy (part->content, pos, part->content_size);
+        
+        }
+        
+        if (section->is_bss) {
+        
+            bss_section_number = i;
+            bss_section = section;
+        
+        }
+        
+        if (subsection) {
+            subsection_append_section_part (subsection, part);
+        } else {
+            section_append_section_part (section, part);
+        }
+        
+        part_p_array[i] = part;
+    
+    }
+    
+    for (i = 1; i < e_shnum; i++) {
+    
+        shdr = (struct elf32_shdr *) (pos = data + e_shoff + i * e_shentsize);
+        
+        if (array_to_integer (shdr->sh_type, 4, endianess) != SHT_SYMTAB) {
+            continue;
+        }
+        
+        sh_link = array_to_integer (shdr->sh_link, 4, endianess);
+        
+        if (sh_link == 0 || sh_link >=  e_shnum) {
+        
+            report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: symtab has invalid sh_link", filename);
+            exit (EXIT_FAILURE);
+        
+        }
+        
+        strtab_hdr = (struct elf32_shdr *) (pos = data + e_shoff + sh_link * e_shentsize);
+        
+        sym_strtab_size = array_to_integer (strtab_hdr->sh_size, 4, endianess);
+        
+        pos = data + array_to_integer (strtab_hdr->sh_offset, 4, endianess);
+        CHECK_READ (pos, (unsigned long) sym_strtab_size);
+        
+        sym_strtab = (char *) pos;
+        
+        sh_entsize = array_to_integer (shdr->sh_entsize, 4, endianess);
+        sh_offset = array_to_integer (shdr->sh_offset, 4, endianess);
+        sh_size = array_to_integer (shdr->sh_size, 4, endianess);
+        
+        pos = data + sh_offset;
+        CHECK_READ (pos, (unsigned long) sh_size);
+        
+        if (sh_entsize < sizeof (*elf_symbol)) {
+        
+            report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: symbol table sh_entsize is too small", filename);
+            exit (EXIT_FAILURE);
+        
+        }
+        
+        for (j = 1; j < sh_size / sh_entsize; j++) {
+        
+            elf_symbol = (struct elf32_sym *) (data + sh_offset + j * sh_entsize);
+            symbol = of->symbol_arr + j;
+            
+            if ((st_name = array_to_integer (elf_symbol->st_name, 4, endianess)) < sym_strtab_size) {
+            
+                if (sym_strtab[st_name] == '\0') {
+                    symbol->name = xstrdup (UNNAMED_SYMBOL_NAME);
+                } else {
+                    symbol->name = xstrdup (sym_strtab + st_name);
+                }
+            
+            } else {
+            
+                report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: invalid offset into string table", filename);
+                exit (EXIT_FAILURE);
+            
+            }
+            
+            symbol->value = array_to_integer (elf_symbol->st_value, 4, endianess);
+            symbol->size = array_to_integer (elf_symbol->st_size, 4, endianess);
+            
+            symbol->section_number = array_to_integer (elf_symbol->st_shndx, 2, endianess);
+            
+            if (symbol->section_number == SHN_UNDEF) {
+            
+                symbol->section_number = UNDEFINED_SECTION_NUMBER;
+                symbol->part = 0;
+            
+            } else if (symbol->section_number == SHN_ABS) {
+            
+                symbol->section_number = ABSOLUTE_SECTION_NUMBER;
+                symbol->part = 0;
+            
+            } else if (symbol->section_number == SHN_COMMON) {
+            
+                if (symbol->size) {
+                
+                    old_symbol = symbol_find (symbol->name);
+                    
+                    if (ELF32_ST_BIND (elf_symbol->st_info[0]) != STB_GLOBAL) {
+                    
+                        report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: non-global common symbol", filename);
+                        exit (EXIT_FAILURE);
+                    
+                    }
+                    
+                    if (!old_symbol || symbol_is_undefined (old_symbol)) {
+                    
+                        if (!bss_section) {
+                        
+                            bss_section = section_find_or_make (".bss");
+                            
+                            if (bss_section->section_alignment < 4) {
+                                bss_section->section_alignment = 4;
+                            }
+                            
+                            bss_section->flags = translate_sh_flags_to_section_flags (SHF_WRITE | SHF_ALLOC);
+                            bss_section->is_bss = 1;
+                            
+                            bss_section_number = e_shnum ? e_shnum : 1;
+                        
+                        }
+                        
+                        bss_part = section_part_new (bss_section, of);
+                        section_append_section_part (bss_section, bss_part);
+                        
+                        
+                        bss_part->content_size = symbol->size;
+                        bss_part->alignment = symbol->value;
+                        
+                        symbol->part = bss_part;
+                        symbol->value = 0;
+                        
+                        symbol->section_number = bss_section_number;
+                    
+                    } else {
+                    
+                        if (symbol->size > old_symbol->size) {
+                            old_symbol->part->content_size = old_symbol->size = symbol->size;
+                        }
+                        
+                        if (symbol->value > old_symbol->part->alignment) {
+                            old_symbol->part->alignment = symbol->value;
+                        }
+                        
+                        symbol->part = 0;
+                        symbol->value = 0;
+                        
+                        symbol->section_number = UNDEFINED_SECTION_NUMBER;
+                    
+                    }
+                
+                } else {
+                
+                    symbol->section_number = UNDEFINED_SECTION_NUMBER;
+                    symbol->part = 0;
+                
+                }
+            
+            } else if (symbol->section_number >= SHN_LORESERVE && symbol->section_number <= SHN_HIRESERVE) {
+            
+                report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "+++not yet supported symbol st_shndx: %hu", symbol->section_number);
+                exit (EXIT_FAILURE);
+            
+            } else if (symbol->section_number >= e_shnum) {
+            
+                report_at (program_name, 0, REPORT_ERROR, "%s: invalid symbol st_shndx: %hu", filename, symbol->section_number);
+                symbol->part = 0;
+            
+            } else {
+                symbol->part = part_p_array[symbol->section_number];
+            }
+            
+            switch (ELF32_ST_BIND (elf_symbol->st_info[0])) {
+            
+                case STB_LOCAL:
+                
+                    break;
+                
+                case STB_GLOBAL:
+                
+                    symbol_record_external_symbol (symbol);
+                    break;
+                
+                default:
+                
+                    report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "+++not yet supported symbol ELF32_ST_BIND: %u", ELF32_ST_BIND (elf_symbol->st_info[0]));
+                    exit (EXIT_FAILURE);
+            
+            }
+            
+            if (ELF32_ST_TYPE (elf_symbol->st_info[0]) == STT_SECTION) {
+                symbol->flags |= SYMBOL_FLAG_SECTION_SYMBOL;
+            }
+        
+        }
+    
+    }
+    
+    for (i = 0; i < e_shnum; i++) {
+    
+        shdr = (struct elf32_shdr *) (pos = data + e_shoff + i * e_shentsize);
+        
+        sh_type = array_to_integer (shdr->sh_type, 4, endianess);
+        sh_size = array_to_integer (shdr->sh_size, 4, endianess);
+        
+        if ((sh_type != SHT_RELA && sh_type != SHT_REL) || sh_size == 0) {
+            continue;
+        }
+        
+        if ((sh_info = array_to_integer (shdr->sh_info, 4, endianess)) >= e_shnum) {
+        
+            report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: relocation section has invalid sh_info", filename);
+            exit (EXIT_FAILURE);
+        
+        }
+        
+        if (!(part = part_p_array[sh_info])) {
+        
+            report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: relocation section has invalid sh_info", filename);
+            exit (EXIT_FAILURE);
+        
+        }
+        
+        if (part->section->is_bss) {
+        
+            report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: relocation section sh_info points to BSS section", filename);
+            exit (EXIT_FAILURE);
+        
+        }
+        
+        sh_entsize = array_to_integer (shdr->sh_entsize, 4, endianess);
+        sh_offset = array_to_integer (shdr->sh_offset, 4, endianess);
+        
+        if ((sh_type == SHT_RELA && sh_entsize != sizeof (*rela)) || (sh_type == SHT_REL && sh_entsize != sizeof (rel))) {
+        
+            report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "+++relocation shdr.sh_entsize not yet supported");
+            exit (EXIT_FAILURE);
+        
+        }
+        
+        sh_size = array_to_integer (shdr->sh_size, 4, endianess);
+        
+        pos = data + sh_offset;
+        CHECK_READ (pos, (unsigned long) sh_size);
+        
+        part->reloc_cnt = sh_size / sh_entsize;
+        part->reloc_arr = xmalloc (part->reloc_cnt * sizeof (*part->reloc_arr));
+        
+        if (sh_type == SHT_RELA) {
+        
+            for (j = 0; j < part->reloc_cnt; j++) {
+            
+                rela = (struct elf32_rela *) (pos + sh_entsize * j);
+                
+                integer_to_array (array_to_integer (rela->r_offset, 4, endianess), rel.r_offset, 4, endianess);
+                integer_to_array (array_to_integer (rela->r_info, 4, endianess), rel.r_info, 4, endianess);
+                
+                part->reloc_arr[j].addend = array_to_integer (rela->r_addend, 4, endianess);
+                translate_relocation (filename, part->reloc_arr + j, &rel, part, endianess);
+            
+            }
+        
+        } else {
+        
+            for (j = 0; j < part->reloc_cnt; j++) {
+            
+                memcpy (&rel, pos + sh_entsize * j, sizeof (rel));
+                translate_relocation (filename, part->reloc_arr + j, &rel, part, endianess);
+            
+            }
+        
+        }
+    
+    }
+    
+    free (part_p_array);
+
+}
+
+#define     PAGE_SIZE                   0x1000
+
+void elf_before_link (void) {
+
+    struct section *section;
+    
+    for (section = all_sections; section; section = section->next) {
+    
+        if (section->section_alignment < PAGE_SIZE) {
+            section->section_alignment = PAGE_SIZE;
+        }
+    
+    }
+
+}
+
+uint64_t elf_get_first_section_rva (void) {
+
+    uint64_t size_of_headers = 0;
+    
+    if (state->format == LD_FORMAT_AMD64_ELF) {
+        /*size_of_headers = sizeof (struct elf64_exec);*/
+    } else if (state->format == LD_FORMAT_I386_ELF) {
+        size_of_headers = sizeof (struct elf32_exec);
+    }
+    
+    if (all_sections) {
+        return ALIGN (size_of_headers, all_sections->section_alignment);
+    }
+    
+    return size_of_headers;
+
+}
+
+#define     PT_LOAD                     1
+#define     EM_I386                     3
+
+static uint32_t translate_section_flags_to_sh_flags (uint32_t flags) {
+
+    uint32_t sh_flags = 0;
+    
+    if (!(flags & SECTION_FLAG_READONLY)) {
+        sh_flags |= SHF_WRITE;
+    }
+    
+    if (flags & SECTION_FLAG_CODE) {
+        sh_flags |= SHF_ALLOC;
+    }
+    
+    if (!(flags & SECTION_FLAG_NOREAD)) {
+        sh_flags |= SHF_EXEC_INSTR;
+    }
+    
+    return sh_flags;
+
+}
+
+#define     PF_X                        0x01
+#define     PF_W                        0x02
+#define     PF_R                        0x04
+
+static uint32_t translate_section_flags_to_p_flags (uint32_t flags) {
+
+    uint32_t p_flags = 0;
+    
+    if (!(flags & SECTION_FLAG_READONLY)) {
+        p_flags |= PF_W;
+    }
+    
+    if (flags & SECTION_FLAG_CODE) {
+        p_flags |= PF_X;
+    }
+    
+    if (!(flags & SECTION_FLAG_NOREAD)) {
+        p_flags |= PF_R;
+    }
+    
+    return p_flags;
+
+}
+
+static unsigned char *write_sections (unsigned char *data, struct elf32_exec *header, uint64_t shstrtab_i) {
+
+    struct elf32_phdr phdr = { 0 };
+    struct elf32_shdr shdr = { 0 };
+    
+    struct section *section;
+    
+    unsigned char *pos, *phdr_pos, *shdr_pos;
+    uint32_t flags;
+    
+    phdr_pos = data + array_to_integer (header->e_phoff, 4, 0);
+    pos = data + sizeof (header);
+    
+    /*if (generate_section_headers) */{
+    
+        shdr_pos = data + array_to_integer (header->e_shoff, 4, 0);
+        
+        integer_to_array (SHT_NULL, shdr.sh_type, 4, 0);
+        integer_to_array (SHN_UNDEF, shdr.sh_link, 4, 0);
+        
+        memcpy (shdr_pos, &shdr, sizeof (shdr));
+        shdr_pos += sizeof (shdr);
+    
+    }
+    
+    for (section = all_sections; section; section = section->next) {
+    
+        integer_to_array (PT_LOAD, phdr.p_type, 4, 0);
+        
+        integer_to_array (state->base_address + section->rva, phdr.p_paddr, 4, 0);
+        integer_to_array (state->base_address + section->rva, phdr.p_vaddr, 4, 0);
+        
+        integer_to_array (section->total_size, phdr.p_memsz, 4, 0);
+        
+        if (!section->is_bss) {
+        
+            integer_to_array (section->total_size, phdr.p_filesz, 4, 0);
+            
+            pos = data + ALIGN (pos - data, section->section_alignment);
+            integer_to_array (pos - data, phdr.p_offset, 4, 0);
+            
+            section_write (section, pos);
+            pos += section->total_size;
+        
+        } else {
+        
+            integer_to_array (0, phdr.p_filesz, 4, 0);
+            integer_to_array (0, phdr.p_offset, 4, 0);
+        
+        }
+        
+        flags = translate_section_flags_to_p_flags (section->flags);
+        integer_to_array (flags, phdr.p_flags, 4, 0);
+        
+        integer_to_array (section->section_alignment, phdr.p_align, 4, 0);
+        
+        /*if (generate_section_headers) */{
+        
+            integer_to_array (section->is_bss ? SHT_NOBITS : SHT_PROGBITS, shdr.sh_type, 4, 0);
+            
+            flags = translate_section_flags_to_sh_flags (section->flags);
+            integer_to_array (flags, shdr.sh_flags, 4, 0);
+            
+            integer_to_array (array_to_integer (phdr.p_vaddr, 4, 0), shdr.sh_addr, 4, 0);
+            integer_to_array (array_to_integer (phdr.p_offset, 4, 0), shdr.sh_offset, 4, 0);
+            integer_to_array (array_to_integer (phdr.p_memsz, 4, 0), shdr.sh_size, 4, 0);
+            integer_to_array (array_to_integer (phdr.p_align, 4, 0), shdr.sh_addralign, 4, 0);
+            
+            integer_to_array (shstrtab_i, shdr.sh_name, 4, 0);
+            shstrtab_i += strlen (section->name) + 1;
+            
+            memcpy (shdr_pos, &shdr, sizeof (shdr));
+            shdr_pos += sizeof (shdr);
+        
+        }
+        
+        memcpy (phdr_pos, &phdr, sizeof (phdr));
+        phdr_pos += sizeof (phdr);
+    
+    }
+    
+    return pos;
+
+}
+
+void elf32_write (const char *filename) {
+
+    struct elf32_exec header = { 0 };
+    FILE *fp;
+    
+    unsigned long data_size = 0;
+    unsigned char *data, *pos;
+    
+    struct section *section;
+    uint16_t e_phnum = 0, e_shnum = 0;
+    
+    uint64_t shstrtab_size, shstrtab_start_i = 0;
+    
+    if (!(fp = fopen (filename, "wb"))) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "failed to open '%s' for writing", filename);
+        return;
+    
+    }
+    
+    header.e_ident[0] = 0x7F;
+    header.e_ident[1] = 'E';
+    header.e_ident[2] = 'L';
+    header.e_ident[3] = 'F';
+    
+    header.e_ident[EI_CLASS] = ELF_CLASS32;
+    header.e_ident[EI_DATA] = ELF_DATA2LSB;
+    
+    header.e_ident[EI_VERSION] = EV_CURRENT;
+    
+    integer_to_array (ET_EXEC, header.e_type, 2, 0);
+    integer_to_array (EM_I386, header.e_machine, 2, 0);
+    integer_to_array (EV_CURRENT, header.e_version, 4, 0);
+    
+    integer_to_array (state->entry_point + state->base_address, header.e_entry, 4, 0);
+    integer_to_array (sizeof (header), header.e_ehsize, 2, 0);
+    
+    data_size += sizeof (header);
+    
+    for (section = all_sections; section; section = section->next) {
+    
+        if (!section->is_bss) {
+        
+            data_size = ALIGN (data_size, section->section_alignment);
+            data_size += section->total_size;
+        
+        }
+        
+        e_phnum++;
+    
+    }
+    
+    /*if (generate_section_headers) */{
+    
+        shstrtab_start_i = shstrtab_size = 1 + sizeof (".shstrtab");
+        
+        for (section = all_sections; section; section = section->next) {
+            shstrtab_size += strlen (section->name) + 1;
+        }
+        
+        data_size += shstrtab_size;
+    
+    }
+    
+    data_size = ALIGN (data_size, 4);
+    
+    integer_to_array (data_size, header.e_phoff, 4, 0);
+    integer_to_array (sizeof (struct elf32_phdr), header.e_phentsize, 2, 0);
+    integer_to_array (e_phnum, header.e_phnum, 2, 0);
+    
+    data_size += e_phnum * sizeof (struct elf32_phdr);
+    
+    /*if (generate_section_headers) */{
+    
+        data_size = ALIGN (data_size, 4);
+        
+        integer_to_array (data_size, header.e_shoff, 4, 0);
+        integer_to_array (sizeof (struct elf32_shdr), header.e_shentsize, 2, 0);
+        
+        for (section = all_sections; section; section = section->next) {
+            section->target_index = e_shnum++;
+        }
+        
+        integer_to_array (e_shnum, header.e_shnum, 2, 0);
+        integer_to_array (e_shnum++, header.e_shstrndx, 2, 0);
+        
+        data_size += (e_shnum * sizeof (struct elf32_shdr));
+    
+    }
+    
+    data = xmalloc (data_size);
+    memcpy (data, &header, sizeof (header));
+    
+    pos = write_sections (data, &header, shstrtab_start_i);
+    
+    /*if (generate_section_headers) */{
+    
+        struct elf32_shdr shstrshdr = { 0 };
+        
+        unsigned char *shdr_pos = data + array_to_integer (header.e_shoff, 4, 0) + array_to_integer (header.e_shstrndx, 2, 0) * sizeof (struct elf32_shdr);
+        unsigned char *saved_pos = pos;
+        
+        *pos++ = '\0';
+        
+        memcpy (pos, ".shstrtab", sizeof (".shstrtab"));
+        pos += sizeof (".shstrtab");
+        
+        for (section = all_sections; section; section = section->next) {
+        
+            uint64_t name_len = strlen (section->name);
+            
+            memcpy (pos, section->name, name_len);
+            pos[name_len] = '\0';
+            
+            pos += name_len + 1;
+        
+        }
+        
+        integer_to_array (pos - saved_pos, shstrshdr.sh_size, 4, 0);
+        memcpy (shdr_pos, &shstrshdr, sizeof (shstrshdr));
+    
+    }
+    
+    if (fwrite (data, data_size, 1, fp) != 1) {
+        report_at (program_name, 0, REPORT_ERROR, "failed to write data to '%s'", filename);
+    }
+    
+    free (data);
+    fclose (fp);
+
+}
diff --git a/elf.h b/elf.h
new file mode 100644 (file)
index 0000000..033e2a0
--- /dev/null
+++ b/elf.h
@@ -0,0 +1,122 @@
+/******************************************************************************
+ * @file            elf.h
+ *****************************************************************************/
+#ifndef     _ELF_H
+#define     _ELF_H
+
+#define     EI_CLASS                    4
+# define        ELF_CLASS32             1
+# define        ELF_CLASS64             2
+#define     EI_DATA                     5
+# define        ELF_DATA2LSB            1
+# define        ELF_DATA2MSB            2
+#define     EI_VERSION                  6
+# define        EV_CURRENT              1
+
+#define     ELF_DATA2MSB                2
+
+#define     ET_REL                      1
+#define     ET_EXEC                     2
+
+struct elf32_exec {
+
+    unsigned char e_ident[16];
+    unsigned char e_type[2];
+    unsigned char e_machine[2];
+    unsigned char e_version[4];
+    unsigned char e_entry[4];
+    unsigned char e_phoff[4];
+    unsigned char e_shoff[4];
+    unsigned char e_flags[4];
+    unsigned char e_ehsize[2];
+    unsigned char e_phentsize[2];
+    unsigned char e_phnum[2];
+    unsigned char e_shentsize[2];
+    unsigned char e_shnum[2];
+    unsigned char e_shstrndx[2];
+
+};
+
+#define     SHT_NULL                    0
+#define     SHT_PROGBITS                1
+#define     SHT_SYMTAB                  2
+#define     SHT_STRTAB                  3
+#define     SHT_NOBITS                  8
+
+struct elf32_shdr {
+    
+    unsigned char sh_name[4];
+    unsigned char sh_type[4];
+    unsigned char sh_flags[4];
+    unsigned char sh_addr[4];
+    unsigned char sh_offset[4];
+    unsigned char sh_size[4];
+    unsigned char sh_link[4];
+    unsigned char sh_info[4];
+    unsigned char sh_addralign[4];
+    unsigned char sh_entsize[4];
+
+};
+
+#define     SHF_WRITE                   0x01
+#define     SHF_ALLOC                   0x02
+#define     SHF_EXEC_INSTR              0x04
+
+struct elf32_sym {
+
+    unsigned char st_name[4];
+    unsigned char st_value[4];
+    unsigned char st_size[4];
+    unsigned char st_info[1];
+    unsigned char st_other[1];
+    unsigned char st_shndx[2];
+
+};
+
+#define     SHN_UNDEF                   0x0000
+#define     SHN_LORESERVE               0xFF00
+#define     SHN_ABS                     0xFFF1
+#define     SHN_COMMON                  0xFFF2
+#define     SHN_HIRESERVE               0xFFFF
+
+struct elf32_rela {
+
+    unsigned char r_offset[4];
+    unsigned char r_info[4];
+    unsigned char r_addend[4];
+
+};
+
+struct elf32_rel {
+
+    unsigned char r_offset[4];
+    unsigned char r_info[4];
+
+};
+
+#define     R_386_NONE                  0
+#define     R_386_32                    1
+#define     R_386_PC32                  2
+
+struct elf32_phdr {
+
+    unsigned char p_type[4];
+    unsigned char p_offset[4];
+    unsigned char p_vaddr[4];
+    unsigned char p_paddr[4];
+    unsigned char p_filesz[4];
+    unsigned char p_memsz[4];
+    unsigned char p_flags[4];
+    unsigned char p_align[4];
+
+};
+
+void read_elf32_object (const char *filename, unsigned char *data, unsigned long data_size, int endianess);
+
+void elf_before_link (void);
+void elf32_write (const char *filename);
+
+#include    "stdint.h"
+uint64_t elf_get_first_section_rva (void);
+
+#endif      /* _ELF_H */
diff --git a/ld.c b/ld.c
index 1ec61d4ff1fb2bf23381c5e51abd5a9de8ca2c61..a467ff0c904fdf2ee50cbce411dcd997282c567d 100644 (file)
--- a/ld.c
+++ b/ld.c
@@ -8,6 +8,7 @@
 #include    "aout.h"
 #include    "ar.h"
 #include    "coff.h"
+#include    "elf.h"
 #include    "elks.h"
 #include    "ld.h"
 #include    "lib.h"
@@ -112,6 +113,21 @@ static int read_object_file (const char *filename, unsigned char *data, unsigned
     
     }
     
+    if (data[0] == 0x7F && memcmp (data + 1, "ELF", 3) == 0) {
+    
+        int endianess = (data[EI_DATA] == ELF_DATA2MSB);
+        
+        if (data[EI_CLASS] == ELF_CLASS64) {
+            /*elf64_get_symbols (object, offset + 8, endianess);*/
+            report_at (__FILE__, __LINE__, REPORT_INTERNAL_ERROR, "currently 64-bit elf support isn't implemented");
+        } else {
+            read_elf32_object (filename, data, data_size, endianess);
+        }
+        
+        return 0;
+    
+    }
+    
     if (data[0] == RECORD_TYPE_THEADR) {
     
         read_omf_object (filename, data, data_size);
@@ -519,6 +535,8 @@ int main (int argc, char **argv) {
         
         } else if (state->format == LD_FORMAT_AMD64_MACHO || state->format == LD_FORMAT_AARCH64_MACHO) {
             state->base_address = 0x100000000;
+        } else if (state->format == LD_FORMAT_AMD64_ELF || state->format == LD_FORMAT_I386_ELF) {
+            state->base_address = 0x00400000;
         }
     
     }
@@ -533,6 +551,8 @@ int main (int argc, char **argv) {
         pe_before_link ();
     } else if (state->format == LD_FORMAT_AMD64_MACHO || state->format == LD_FORMAT_AARCH64_MACHO) {
         macho_before_link ();
+    } else if (state->format == LD_FORMAT_AMD64_ELF || state->format == LD_FORMAT_I386_ELF) {
+        elf_before_link ();
     }
     
     link ();
@@ -590,6 +610,8 @@ int main (int argc, char **argv) {
         aout_write (state->output_filename);
     } else if (state->format == LD_FORMAT_I386_ELKS || state->format == LD_FORMAT_IA16_ELKS) {
         elks_write (state->output_filename);
+    } else if (state->format == LD_FORMAT_I386_ELF) {
+        elf32_write (state->output_filename);
     } else if (state->format == LD_FORMAT_I386_PE) {
     
         pe_after_link ();
diff --git a/ld.h b/ld.h
index 7424685bfc48cbf64939dd4d94ac1838fbc9efd1..c19a7ce3d37dde56f6f2652a0dc12f8d0e6ab462 100644 (file)
--- a/ld.h
+++ b/ld.h
@@ -29,6 +29,11 @@ struct ld_state {
 };
 
 
+#define     LD_TARGET_MACHINE_AARCH64                       (1U << 15)
+#define     LD_TARGET_MACHINE_AMD64                         (1U << 14)
+#define     LD_TARGET_MACHINE_I386                          (1U << 13)
+
+
 #define     LD_EMULATION_NONE           0x00
 
 #define     LD_EMULATION_IA16_ELKS      0x01
@@ -43,17 +48,18 @@ struct ld_state {
 #define     LD_FORMAT_COM               0x00
 #define     LD_FORMAT_MZ                0x01
 #define     LD_FORMAT_BIN               0x02
-
 #define     LD_FORMAT_IA16_ELKS         0x03
-#define     LD_FORMAT_I386_ELKS         0x04
 
-#define     LD_FORMAT_I386_AOUT         0x05
-#define     LD_FORMAT_I386_COFF         0x06
+#define     LD_FORMAT_I386_ELKS         (LD_TARGET_MACHINE_I386     | 0x04)
+#define     LD_FORMAT_I386_AOUT         (LD_TARGET_MACHINE_I386     | 0x05)
+#define     LD_FORMAT_I386_COFF         (LD_TARGET_MACHINE_I386     | 0x06)
+#define     LD_FORMAT_I386_PE           (LD_TARGET_MACHINE_I386     | 0x07)
+#define     LD_FORMAT_I386_ELF          (LD_TARGET_MACHINE_I386     | 0x08)
 
-#define     LD_FORMAT_I386_PE           0x07
+#define     LD_FORMAT_AMD64_MACHO       (LD_TARGET_MACHINE_AMD64    | 0x09)
+#define     LD_FORMAT_AMD64_ELF         (LD_TARGET_MACHINE_AMD64    | 0x10)
 
-#define     LD_FORMAT_AMD64_MACHO       0x08
-#define     LD_FORMAT_AARCH64_MACHO     0x09
+#define     LD_FORMAT_AARCH64_MACHO     (LD_TARGET_MACHINE_AARCH64  | 0x11)
 
 
 extern struct ld_state *state;
diff --git a/lib.c b/lib.c
index 9d9c2dc2c4b9efd7ab9063f9336f609aa7d22120..928954989067d391155842fde71efa6ccce58458 100644 (file)
--- a/lib.c
+++ b/lib.c
@@ -109,7 +109,8 @@ static void print_usage (void) {
         fprintf (stderr, "    --help                            Print option help.\n");
         fprintf (stderr, "    --oformat FORMAT                  Specify the format of output file (default msdos).\n");
         fprintf (stderr, "                                          Supported formats are:\n");
-        fprintf (stderr, "                                              a.out-i386, elks-ia16, elks-i386\n");
+        fprintf (stderr, "                                              a.out-i386, elf-i386\n");
+        fprintf (stderr, "                                              elks-ia16, elks-i386\n");
         fprintf (stderr, "                                              macho-aarch64, macho-amd64\n");
         fprintf (stderr, "                                              pe-i386, binary, msdos\n");
         fprintf (stderr, "    --image-base <address>            Set base address of the executable.\n");
@@ -327,6 +328,18 @@ static void use_option (const char *cmd_arg, int idx, const char *optarg) {
             
             }
             
+            if (xstrcasecmp (optarg, "elf-i386") == 0) {
+            
+                state->format = LD_FORMAT_I386_ELF;
+                
+                /*if (state->emulation == LD_EMULATION_NONE) {
+                    state->emulation = LD_EMULATION_I386_COFF;
+                }*/
+                
+                break;
+            
+            }
+            
             report_at (program_name, 0, REPORT_ERROR, "unrecognised output format '%s'", optarg);
             exit (EXIT_FAILURE);
         
diff --git a/link.c b/link.c
index d1885b619e303ff1c1d90ccbcec3f40693a73405..a3ff6ca7da7fae04709048a3908bcfdeacbcb9fb 100644 (file)
--- a/link.c
+++ b/link.c
@@ -6,6 +6,7 @@
 #include    <stdlib.h>
 #include    <string.h>
 
+#include    "elf.h"
 #include    "ld.h"
 #include    "lib.h"
 #include    "macho.h"
@@ -542,6 +543,8 @@ static void calculate_section_sizes_and_rvas (void) {
         rva = pe_get_first_section_rva ();
     } else if (state->format == LD_FORMAT_AMD64_MACHO || state->format == LD_FORMAT_AARCH64_MACHO) {
         rva = macho_get_first_section_rva ();
+    } else if (state->format == LD_FORMAT_AMD64_ELF || state->format == LD_FORMAT_I386_ELF) {
+        rva = elf_get_first_section_rva ();
     }
     
     for (section = all_sections; section; section = section->next) {
diff --git a/macho.c b/macho.c
index 477fbce83526dfe50750f9bb02bc7c4e4f38d7b6..beb5b304b561139093ddff78e3fc6e015cd2d6b2 100644 (file)
--- a/macho.c
+++ b/macho.c
@@ -80,7 +80,7 @@ static void translate_relocation (struct reloc_entry *reloc, struct macho_reloca
     pcrel = (r_symbolnum >> 24) & 1;
     type = r_symbolnum >> 28;
     
-    if (state->format == LD_FORMAT_AMD64_MACHO) {
+    if ((state->format & LD_TARGET_MACHINE_AMD64)) {
     
         switch (size) {
         
@@ -189,12 +189,8 @@ static void translate_relocation (struct reloc_entry *reloc, struct macho_reloca
                 goto unsupported;
         
         }
-        
-        return;
     
-    }
-    
-    if (state->format == LD_FORMAT_AARCH64_MACHO) {
+    } else if ((state->format & LD_TARGET_MACHINE_AARCH64)) {
     
         switch (size) {
         
@@ -277,15 +273,14 @@ static void translate_relocation (struct reloc_entry *reloc, struct macho_reloca
                 goto unsupported;
         
         }
-        
-        return;
     
-    }
+    } else {
+    unsupported:
     
-unsupported:
+        report_at (program_name, 0, REPORT_WARNING, "%s: ignoring not yet supported relocation with size %u, pcrel %u and type %#x", part->of->filename, size, pcrel, type);
+        reloc->howto = &reloc_howtos[RELOC_TYPE_IGNORED];
     
-    report_at (program_name, 0, REPORT_WARNING, "%s: ignoring not yet supported relocation with size %u, pcrel %u and type %#x", part->of->filename, size, pcrel, type);
-    reloc->howto = &reloc_howtos[RELOC_TYPE_IGNORED];
+    }
 
 }
 
@@ -294,7 +289,6 @@ unsupported:
 void read_macho_object (const char *filename, unsigned char *data, unsigned long data_size) {
 
     unsigned char *pos = (unsigned char *) data;
-    struct macho_header_64 *header;
     
     struct section_part **part_p_array = 0, *bss_part;
     struct object_file *of = 0;
@@ -324,6 +318,7 @@ void read_macho_object (const char *filename, unsigned char *data, unsigned long
     struct macho_relocation_info *reloc_info;
     unsigned char *rel_pos;
     
+    struct macho_header_64 *header;
     CHECK_READ (pos, sizeof (*header));
     
     header = (struct macho_header_64 *) pos;
@@ -534,8 +529,8 @@ void read_macho_object (const char *filename, unsigned char *data, unsigned long
             
             for (j = 0; j < num_syms; j++) {
             
-                symbol = of->symbol_arr + j;
                 nlist_64 = (struct nlist_64 *) (sym_pos + j * sizeof (*nlist_64));
+                symbol = of->symbol_arr + j;
                 
                 if ((n_strx = array_to_integer (nlist_64->n_strx, 4, 0)) < array_to_integer (symtab_cmd->string_size, 4, 0)) {
                 
@@ -1038,8 +1033,8 @@ static int log_base2 (uint64_t val) {
 void macho_write (const char *filename) {
 
     struct chained_fixup_starts chained_fixups[NUM_SEGS] = { { 0 } };
-    struct macho_header_64 header = { 0 };
     
+    struct macho_header_64 header = { 0 };
     FILE *fp;
     
     unsigned long data_size = 0, content_offset = 0, sizeof_commands = 0, num_commands = 0, file_offset = 0;