From 7e771d1892d9a51c846b439dfab62ee483f1242f Mon Sep 17 00:00:00 2001 From: Robert Pengelly Date: Mon, 3 Nov 2025 20:58:51 +0000 Subject: [PATCH] Create 32-bit ELF executables --- Makefile.unix | 2 +- Makefile.w32 | 3 +- Makefile.wat | 2 +- Makefile.wcd | 23 -- elf.c | 900 ++++++++++++++++++++++++++++++++++++++++++++++++++ elf.h | 122 +++++++ ld.c | 22 ++ ld.h | 20 +- lib.c | 15 +- link.c | 3 + macho.c | 25 +- 11 files changed, 1088 insertions(+), 49 deletions(-) delete mode 100644 Makefile.wcd create mode 100644 elf.c create mode 100644 elf.h diff --git a/Makefile.unix b/Makefile.unix index 02e559f..88ebe51 100644 --- a/Makefile.unix +++ b/Makefile.unix @@ -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 diff --git a/Makefile.w32 b/Makefile.w32 index 326d9dc..00e19da 100644 --- a/Makefile.w32 +++ b/Makefile.w32 @@ -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 diff --git a/Makefile.wat b/Makefile.wat index a8fed65..daa5883 100644 --- a/Makefile.wat +++ b/Makefile.wat @@ -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 index 26a85c7..0000000 --- a/Makefile.wcd +++ /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 index 0000000..b315c2a --- /dev/null +++ b/elf.c @@ -0,0 +1,900 @@ +/****************************************************************************** + * @file elf.c + *****************************************************************************/ +#include +#include +#include + +#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 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 1ec61d4..a467ff0 100644 --- 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 7424685..c19a7ce 100644 --- 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 9d9c2dc..9289549 100644 --- 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
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 d1885b6..a3ff6ca 100644 --- a/link.c +++ b/link.c @@ -6,6 +6,7 @@ #include #include +#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 477fbce..beb5b30 100644 --- 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; -- 2.34.1