From 96b30cd325689d13ba87fca917618ae2a86c18a3 Mon Sep 17 00:00:00 2001 From: Robert Pengelly Date: Mon, 25 Nov 2024 20:27:17 +0000 Subject: [PATCH] Initial re-write --- Makefile.p32 | 2 +- Makefile.pdw | 2 +- Makefile.unix | 2 +- Makefile.w32 | 2 +- elks.c | 1117 ++++++++++--------------------------------------- elks.h | 5 +- ld.c | 706 +++++-------------------------- ld.h | 63 +-- lib.c | 442 +++++++++++-------- lib.h | 15 + link.c | 449 ++++++++++++++++++++ map.c | 128 ++++++ pe.c | 872 ++++++++++++++++++++++++++++++++++++++ pe.h | 87 ++-- reloc.h | 56 +++ report.c | 8 +- report.h | 1 + section.c | 184 ++++++++ section.h | 104 +++++ symbol.c | 105 +++++ symbol.h | 38 ++ 21 files changed, 2609 insertions(+), 1779 deletions(-) create mode 100644 link.c create mode 100644 map.c create mode 100644 pe.c create mode 100644 reloc.h create mode 100644 section.c create mode 100644 section.h create mode 100644 symbol.c create mode 100644 symbol.h diff --git a/Makefile.p32 b/Makefile.p32 index 40bbfc4..2fc36a5 100644 --- a/Makefile.p32 +++ b/Makefile.p32 @@ -6,7 +6,7 @@ CC=gcc386 LD=ld386 COPTS=-S -O2 -fno-common -ansi -I. -I./include -I../pdos/pdpclib -I../pdos/src -D__PDOS386__ -D__32BIT__ -D__NOBIVA__ -D__PDOS__ -Wall -Werror -ansi -m32 -pedantic -COBJ=elks.c hashtab.o ld.o lib.o report.o vector.o write7x.o +COBJ=elks.c hashtab.o ld.o lib.o link.o map.o pe.o report.o section.o symbol.o vector.o write7x.o all: clean slink.exe diff --git a/Makefile.pdw b/Makefile.pdw index a1bee13..017e3f7 100644 --- a/Makefile.pdw +++ b/Makefile.pdw @@ -6,7 +6,7 @@ CC=gccwin LD=ldwin COPTS=-S -O2 -fno-common -ansi -I. -I./include -I../pdos/pdpclib -I../pdos/src -D__WIN32__ -D__NOBIVA__ -D__PDOS__ -Wall -Werror -ansi -m32 -pedantic -COBJ=elks.c hashtab.o ld.o lib.o report.o vector.o write7x.o +COBJ=elks.c hashtab.o ld.o lib.o link.o map.o pe.o report.o section.o symbol.o vector.o write7x.o all: clean slink.exe diff --git a/Makefile.unix b/Makefile.unix index e9625c6..e49bb9a 100644 --- a/Makefile.unix +++ b/Makefile.unix @@ -9,7 +9,7 @@ VPATH := $(SRCDIR) CC := gcc CFLAGS := -D_FILE_OFFSET_BITS=64 -I$(OBJDIR) -I$(SRCDIR)/include -O2 -Wall -Werror -Wextra -ansi -pedantic -std=c90 -CSRC := elks.c hashtab.c ld.c lib.c report.c vector.c write7x.c +CSRC := elks.c hashtab.c ld.c lib.c link.c map.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 f3aaa1c..c9c0241 100644 --- a/Makefile.w32 +++ b/Makefile.w32 @@ -9,7 +9,7 @@ VPATH := $(SRCDIR) CC := gcc CFLAGS := -D_FILE_OFFSET_BITS=64 -I$(OBJDIR) -I$(SRCDIR)/include -O2 -Wall -Werror -Wextra -ansi -pedantic -std=c90 -CSRC := elks.c hashtab.c ld.c lib.c report.c vector.c write7x.c +CSRC := elks.c hashtab.c ld.c lib.c link.c map.c pe.c report.c section.c symbol.c vector.c write7x.c all: slink.exe diff --git a/elks.c b/elks.c index dd18195..2c17f53 100644 --- a/elks.c +++ b/elks.c @@ -1,1053 +1,360 @@ /****************************************************************************** * @file elks.c *****************************************************************************/ -#include -#include -#include -#include #include -#include -#include "aout.h" #include "elks.h" #include "ld.h" #include "lib.h" -#include "pe.h" +#include "reloc.h" #include "report.h" -#include "write7x.h" +#include "section.h" +#include "symbol.h" -typedef signed char int8_t; -typedef signed short int16_t; +static void translate_relocation (const char *filename, struct reloc_entry *reloc, struct elks_relocation_info *input_reloc, struct section_part *part, struct elks_exec *exec_p) { -#if INT_MAX == 32767 -typedef signed long int32_t; -#else -typedef signed int int32_t; -#endif - -static unsigned long header_size = 0, output_size = 0; -static void *data = 0, *output = 0, *text = 0; - - -struct gr { - - long relocations_count, relocations_max; - struct elks_relocation_info *relocations; - -}; - -static struct gr tgr = { 0, 64, NULL }; -static struct gr dgr = { 0, 64, NULL }; - -static int get_symbol (struct elks_object **obj_out, long *index, const char *name, int quiet) { - - long object_i, symbol_i; - - for (object_i = 0; object_i < state->nb_elks_objs; ++object_i) { - - struct elks_object *obj = state->elks_objs[object_i]; - - for (symbol_i = 0; symbol_i < obj->symtab_count; symbol_i++) { - - struct elks_nlist *sym = &obj->symtab[symbol_i]; - char *symname = obj->strtab + GET_INT32 (sym->n_strx); - - if ((sym->n_type & N_EXT) == 0) { - continue; - } - - if ((sym->n_type & N_TYPE) != N_TEXT && (sym->n_type & N_TYPE) != N_DATA && (sym->n_type & N_TYPE) != N_BSS && (sym->n_type & N_TYPE) != N_ABS) { - continue; - } - - if (strcmp (symname, name) == 0) { - - if (obj_out) { - *obj_out = obj; - } - - if (index) { - *index = symbol_i; - } - - return 0; - - } - - } + unsigned long r_symbolnum = array_to_integer (input_reloc->r_symbolnum, 4); - } - - if (!quiet) { - report_at (program_name, 0, REPORT_ERROR, "undefined symbol '%s'", name); - } - - return 1; - -} - -static unsigned long get_entry (void) { - - struct elks_object *symobj; - long symidx; - - if (get_symbol (&symobj, &symidx, state->entry, 1)) { - - report_at (program_name, 0, REPORT_WARNING, "cannot find entry symbol %s; defaulting to 00000000", state->entry); - return 0; - - } - - return GET_UINT32 (symobj->symtab[symidx].n_value); - -} - -static void number_to_chars (unsigned char *p, unsigned long number, unsigned long size) { + unsigned long r_address = array_to_integer (input_reloc->r_address, 4); + long symbolnum = (r_symbolnum & 0x7ffffff); - unsigned long i; - - for (i = 0; i < size; i++) { - p[i] = (number >> (8 * i)) & 0xff; - } - -} - -static long objtextsize = 0, objdatasize = 0, objbsssize = 0; -static unsigned long text_ptr = 0, data_ptr = 0, bss_ptr = 0; - -static void apply_slides (struct elks_object *object) { - - long i; - - for (i = 0; i < object->symtab_count; i++) { + if ((r_symbolnum >> 31) & 1) { /* ext */ + reloc->symbol = part->of->symbol_arr + symbolnum; + } else { - struct elks_nlist *symbol = &object->symtab[i]; - unsigned long final_slide = 0, n_value = GET_UINT32 (symbol->n_value); + if (state->format == LD_FORMAT_BIN || state->format == LD_FORMAT_COM) { - if ((symbol->n_type & N_TYPE) != N_TEXT && (symbol->n_type & N_TYPE) != N_DATA && (symbol->n_type & N_TYPE) != N_BSS) { - continue; - } - - switch (symbol->n_type & N_TYPE) { - - case N_BSS: - - final_slide += state->text_size; - final_slide += state->data_size; - final_slide += object->bss_slide; - - break; - - case N_DATA: - - final_slide += state->text_size; - final_slide += object->data_slide; - - break; - - case N_TEXT: - - final_slide += object->text_slide; - break; - - } - - n_value += final_slide; - - switch (symbol->n_type & N_TYPE) { - - case N_BSS: - - n_value -= GET_UINT32 (object->header->a_data); - /* fall through */ - - case N_DATA: - - n_value -= GET_UINT32 (object->header->a_text); - break; + if ((((r_symbolnum & (3 << 29)) >> 28) & 0xff) != N_ABS) { + report_at (program_name, 0, REPORT_ERROR, "%s: segment relocation at %04x:%04x", filename, r_address / 0xffff, r_address % 0xffff); + } } - write741_to_byte_array (symbol->n_value, n_value); - - } - - for (i = 0; i < object->trelocs_count; i++) { - - struct elks_relocation_info *rel = &object->trelocs[i]; + if (symbolnum == N_TEXT) { + reloc->symbol = part->of->symbol_arr + part->of->symbol_cnt - 3; + } else if (symbolnum == N_DATA) { - long r_address = GET_INT32 (rel->r_address); - r_address += object->text_slide; + reloc->symbol = part->of->symbol_arr + part->of->symbol_cnt - 2; + reloc->addend -= array_to_integer (exec_p->a_text, 4); - write741_to_byte_array ((unsigned char *) rel->r_address, r_address); - - } - - for (i = 0; i < object->drelocs_count; i++) { - - struct elks_relocation_info *rel = &object->drelocs[i]; + } else if (symbolnum == N_BSS) { - long r_address = GET_INT32 (rel->r_address); - r_address += state->text_size + object->data_slide; + reloc->symbol = part->of->symbol_arr + part->of->symbol_cnt - 1; + reloc->addend -= (array_to_integer (exec_p->a_text, 4) + array_to_integer (exec_p->a_data, 4)); - write741_to_byte_array ((unsigned char *) rel->r_address, r_address); - - } - -} - -static void paste (struct elks_object *object) { - - struct elks_exec *header = object->header; - - char *obj_text, *obj_data; - unsigned long obj_text_size, obj_data_size, obj_bss_size; - - object->text_slide = text_ptr; - obj_text = (char *) object->raw + sizeof (*header); - - if (state->impure) { - obj_text_size = GET_UINT32 (header->a_text); - } else { - - if (state->format == LD_FORMAT_I386_PE) { - obj_text_size = ALIGN_UP (GET_UINT32 (header->a_text), FILE_ALIGNMENT); - } else { - obj_text_size = ALIGN_UP (GET_UINT32 (header->a_text), SECTION_ALIGNMENT); - } - - } - - memcpy ((char *) text + text_ptr, obj_text, GET_UINT32 (header->a_text)); - text_ptr += obj_text_size; - - object->data_slide = data_ptr; - obj_data = (char *) object->raw + sizeof (*header) + GET_UINT32 (header->a_text); - - if (state->impure) { - obj_data_size = GET_UINT32 (header->a_data); - } else { - - if (state->format == LD_FORMAT_I386_PE) { - obj_data_size = ALIGN_UP (GET_UINT32 (header->a_data), FILE_ALIGNMENT); } else { - obj_data_size = ALIGN_UP (GET_UINT32 (header->a_data), SECTION_ALIGNMENT); - } - - } - - memcpy ((char *) data + data_ptr, obj_data, GET_UINT32 (header->a_data)); - data_ptr += obj_data_size; - - object->bss_slide = bss_ptr; - - if (state->impure) { - obj_bss_size = GET_UINT32 (header->a_bss); - } else { - - if (state->format == LD_FORMAT_I386_PE) { - obj_bss_size = ALIGN_UP (GET_UINT32 (header->a_bss), FILE_ALIGNMENT); - } else { - obj_bss_size = ALIGN_UP (GET_UINT32 (header->a_bss), SECTION_ALIGNMENT); - } - - } - - bss_ptr += obj_bss_size; - -} - -static void undef_collect (struct elks_object *object) { - - long i, val; - - for (i = 0; i < object->symtab_count; i++) { - - struct elks_nlist *sym = &object->symtab[i]; - char *symname = object->strtab + GET_INT32 (sym->n_strx); - - if ((sym->n_type & N_TYPE) != N_UNDF || GET_UINT32 (sym->n_value) == 0) { - continue; - } - - if (get_symbol (NULL, NULL, symname, 1)) { - continue; - } - - sym->n_type = N_BSS | N_EXT; - val = GET_UINT32 (sym->n_value); - - write741_to_byte_array (sym->n_value, state->text_size + state->data_size + state->bss_size); - state->bss_size += val; - - } - -} - -static int add_relocation (struct gr *gr, struct elks_relocation_info *r) { - - if (gr->relocations == NULL) { - - if ((gr->relocations = malloc (gr->relocations_max * sizeof (*r))) == NULL) { - return 1; - } - - } - - if (gr->relocations_count >= gr->relocations_max) { - - void *tmp; - gr->relocations_max *= 2; + report_at (program_name, 0, REPORT_INTERNAL_ERROR, "local input_reloc->r_symbolnum %#lx is not yet supported", symbolnum); + return; - if ((tmp = realloc (gr->relocations, gr->relocations_max * sizeof (*r))) == NULL) { - return 1; } - - gr->relocations = tmp; } - gr->relocations[gr->relocations_count] = *r; - gr->relocations_count++; - - return 0; - -} - -static int relocate (struct elks_object *object, struct elks_relocation_info *r, int is_data) { - - struct elks_nlist *symbol; - unsigned char *p; - - unsigned long r_symbolnum = GET_UINT32 (r->r_symbolnum); - long result = 0, symbolnum = 0; + reloc->offset = r_address; - int far_call = 0, pcrel = 0, ext = 0, length = 0; - /*int need_relocate = 1;*/ + switch (1U << ((r_symbolnum >> 29) & 3)) { - symbolnum = r_symbolnum & 0x7ffffff; - length = (r_symbolnum & (3LU << 29)) >> 29; - - pcrel = (r_symbolnum & (1LU << 28)) >> 28; - ext = (r_symbolnum & (1LU << 31)) >> 31; - - far_call = (r_symbolnum & (1LU << 27)) >> 27; - - if ((is_data && pcrel) || far_call) { - - report_at (program_name, 0, REPORT_ERROR, "unsupported relocation type"); - return 1; - - } - - switch (length) { - - case 0: - - length = 1; - break; + case 8: - case 1: - - length = 2; - break; - - case 2: - - length = 4; - break; - - } - - symbol = &object->symtab[symbolnum]; - p = (unsigned char *) output + header_size + GET_INT32 (r->r_address); - - if (ext) { - - char *symname = object->strtab + GET_INT32 (symbol->n_strx); - - struct elks_object *symobj; - long symidx; - - if (strcmp (symname, "__edata") == 0) { - - int32_t data_addr = ((char *) text - (char *) output) - header_size; - data_addr += state->text_size; + if ((r_symbolnum >> 28) & 1) { - write741_to_byte_array (symbol->n_value, data_addr / 16); - /*need_relocate = 0;*/ - - } else if (strcmp (symname, "__end") == 0) { - - int32_t data_addr = ((char *) data - (char *) output) - header_size; + reloc->howto = &reloc_howtos[RELOC_TYPE_PC64]; + reloc->addend += reloc->offset + 8; - data_addr += state->data_size; - data_addr += state->bss_size; + } else { + reloc->howto = &reloc_howtos[RELOC_TYPE_64]; + } - write741_to_byte_array (symbol->n_value, data_addr % 16); - /*need_relocate = 0;*/ + break; - } else if (!get_symbol (&symobj, &symidx, symname, 0)) { - symbol = &symobj->symtab[symidx]; - } else { - return 1; - } - - } - - if (pcrel) { - - if (result == 0) { - result = (long) GET_UINT32 (symbol->n_value) - (GET_INT32 (r->r_address) + length); - } - - } else { - - if (!ext || (symbol->n_type & N_TYPE) == N_BSS || (symbol->n_type & N_TYPE) == N_DATA || (symbol->n_type & N_TYPE) == N_TEXT) { + case 4: - struct elks_relocation_info new_relocation; + if ((r_symbolnum >> 28) & 1) { - unsigned long r_symbolnum; - long r_address; - - r_symbolnum = GET_UINT32 (r->r_symbolnum) & (3L << 29); - r_address = GET_INT32 (r->r_address); - - if (state->format == LD_FORMAT_IA16_ELKS || state->format == LD_FORMAT_I386_ELKS || state->format == LD_FORMAT_I386_AOUT || ((r_symbolnum >> 28) & 0xff) != N_ABS) { - - if (state->format == LD_FORMAT_BIN || state->format == LD_FORMAT_COM) { - - report_at (object->filename, 0, REPORT_ERROR, "segment relocation at %04x:%04x", r_address / 0xffff, r_address % 0xffff); - return 1; - - } - - if (is_data) { - r_address -= state->text_size; - } - - write741_to_byte_array ((unsigned char *) new_relocation.r_address, r_address); - write741_to_byte_array (new_relocation.r_symbolnum, r_symbolnum); - - add_relocation (is_data ? &dgr : &tgr, &new_relocation); + reloc->howto = &reloc_howtos[RELOC_TYPE_PC32]; + reloc->addend += reloc->offset + 4; + } else { + reloc->howto = &reloc_howtos[RELOC_TYPE_32]; } - } + break; - if (result == 0) { + case 2: - int32_t r_address = GET_INT32 (r->r_address); + if ((r_symbolnum >> 28) & 1) { - if (length == 4) { - result = *(int32_t *) ((char *) output + header_size + r_address); - } else if (length == 2) { - result = *(int16_t *) ((char *) output + header_size + r_address); - } else if (length == 1) { - result = *(int8_t *) ((char *) output + header_size + r_address); + reloc->howto = &reloc_howtos[RELOC_TYPE_PC16]; + reloc->addend += reloc->offset + 2; + + } else { + reloc->howto = &reloc_howtos[RELOC_TYPE_16]; } - - } - - if (ext) { - - symbolnum = (symbol->n_type & N_TYPE); - result += GET_UINT32 (symbol->n_value); - result += state->psp; + break; - } else { + case 1: - if ((symbolnum == 6) || (symbolnum == 8)) { - - result -= GET_UINT32 (object->header->a_text); - result += state->text_size; - - } - - if (symbolnum == 4) { - result += objtextsize; - } - - if (symbolnum == 6) { - result += objdatasize; - } - - if (symbolnum == 8) { + if ((r_symbolnum >> 28) & 1) { - result -= GET_UINT32 (object->header->a_data); - result += state->data_size; - result += objbsssize; + reloc->howto = &reloc_howtos[RELOC_TYPE_PC8]; + reloc->addend += reloc->offset + 4; + } else { + reloc->howto = &reloc_howtos[RELOC_TYPE_8]; } - result += state->psp; - - } + break; } - - number_to_chars (p, result, length); - return 0; } -static int glue (struct elks_object *object) { +void read_elks_object (const char *filename, unsigned char *data, unsigned long data_size) { - long i, err = 0; - - for (i = 0; i < object->trelocs_count; i++) { - - if (relocate (object, &object->trelocs[i], 0)) { - err = 1; - } + struct elks_exec *elks_exec; + struct elks_nlist *elks_nlist; - } + const char *strtab; + unsigned long strtab_size; - for (i = 0; i < object->drelocs_count; i++) { + unsigned long num_symbols; + unsigned long i; - if (relocate (object, &object->drelocs[i], 1)) { - err = 1; - } + struct section_part *part, *part_p_array[4] = { 0 }; + struct section *section, *bss_section; - } + struct symbol *symbol, *old_symbol; + struct section_part *bss_part; - objtextsize += GET_UINT32 (object->header->a_text); - objdatasize += GET_UINT32 (object->header->a_data); - objbsssize += GET_UINT32 (object->header->a_bss); + struct elks_relocation_info *reloc_info; + struct object_file *of; - return err; - -} - - -static int init_elks_object (void) { - - if (state->format == LD_FORMAT_I386_AOUT) { - header_size = sizeof (struct aout_exec); - } else if (state->format == LD_FORMAT_IA16_ELKS || state->format == LD_FORMAT_I386_ELKS) { - header_size = sizeof (struct elks_exec); - } + unsigned char *pos = data; - if (!state->impure) { - header_size = ALIGN_UP (header_size, SECTION_ALIGNMENT); - } + if ((pos - data + sizeof (*elks_exec) > data_size) || pos < data) { - output_size = header_size + state->text_size + state->data_size; + report_at (program_name, 0, REPORT_FATAL_ERROR, "corrupted input file"); + return; - if ((output = malloc (output_size)) == NULL) { - return 1; } - memset (output, 0, output_size); - - text = (void *) ((char *) output + header_size); - data = (void *) ((char *) text + state->text_size); - - return 0; - -} - -static int write_elks_object (unsigned long a_entry) { - - if (state->format == LD_FORMAT_I386_AOUT) { + elks_exec = (struct elks_exec *) pos; + pos += sizeof (*elks_exec); - struct aout_exec *header = output; - - write741_to_byte_array (header->a_info, state->impure ? OMAGIC : ZMAGIC); - write741_to_byte_array (header->a_text, state->text_size); - write741_to_byte_array (header->a_data, state->data_size); - write741_to_byte_array (header->a_bss, state->bss_size); - write741_to_byte_array (header->a_entry, a_entry); - write741_to_byte_array (header->a_trsize, tgr.relocations_count * sizeof (struct relocation_info)); - write741_to_byte_array (header->a_drsize, dgr.relocations_count * sizeof (struct relocation_info)); + num_symbols = array_to_integer (elks_exec->a_syms, 4) / sizeof (*elks_nlist); + of = object_file_make (filename, num_symbols + 4); - } else if (state->format == LD_FORMAT_IA16_ELKS || state->format == LD_FORMAT_I386_ELKS) { + section = section_find_or_make (".text"); + section->flags = SECTION_FLAG_ALLOC | SECTION_FLAG_LOAD | SECTION_FLAG_READONLY | SECTION_FLAG_CODE; - struct elks_exec *header = output; - - header->a_magic[0] = (ELKS_MAGIC >> 8) & 0xff; - header->a_magic[1] = ELKS_MAGIC & 0xff; - - header->a_flags = 0x10; - header->a_cpu = (state->format == LD_FORMAT_I386_ELKS) ? 0x10 : 0x04; - header->a_hdrlen = sizeof (*header); - - write741_to_byte_array (header->a_text, state->text_size); - write741_to_byte_array (header->a_data, state->data_size); - write741_to_byte_array (header->a_bss, state->bss_size); - write741_to_byte_array (header->a_entry, a_entry); - write721_to_byte_array (header->a_total, (state->text_size + state->data_size) + 0x8000); - - write741_to_byte_array (header->a_trsize, tgr.relocations_count * sizeof (struct elks_relocation_info)); - write741_to_byte_array (header->a_drsize, dgr.relocations_count * sizeof (struct elks_relocation_info)); + part = section_part_new (section, of); - } + part->content_size = array_to_integer (elks_exec->a_text, 4); + part->content = xmalloc (part->content_size); - if (fwrite ((char *) output, output_size, 1, state->ofp) != 1) { + if ((pos - data + part->content_size > data_size) || pos < data) { - report_at (program_name, 0, REPORT_ERROR, "failed to write data to '%s'", state->ofile); - return 1; + report_at (program_name, 0, REPORT_FATAL_ERROR, "corrupted input file"); + return; } - if (state->format == LD_FORMAT_I386_AOUT) { - - if (tgr.relocations_count > 0) { - - if (fwrite (tgr.relocations, tgr.relocations_count * sizeof (struct relocation_info), 1, state->ofp) != 1) { - - report_at (program_name, 0, REPORT_ERROR, "failed to write text relocations to '%s'", state->ofile); - return 1; - - } - - } - - if (dgr.relocations_count > 0) { - - if (fwrite (dgr.relocations, dgr.relocations_count * sizeof (struct relocation_info), 1, state->ofp) != 1) { - - report_at (program_name, 0, REPORT_ERROR, "failed to write data relocations to '%s'", state->ofile); - return 1; - - } - - } - - } else if (state->format == LD_FORMAT_IA16_ELKS || state->format == LD_FORMAT_I386_ELKS) { + memcpy (part->content, pos, part->content_size); + pos += part->content_size; - if (tgr.relocations_count > 0) { - - if (fwrite (tgr.relocations, tgr.relocations_count * sizeof (struct elks_relocation_info), 1, state->ofp) != 1) { - - report_at (program_name, 0, REPORT_ERROR, "failed to write text relocations to '%s'", state->ofile); - return 1; - - } - - } - - if (dgr.relocations_count > 0) { - - if (fwrite (dgr.relocations, dgr.relocations_count * sizeof (struct elks_relocation_info), 1, state->ofp) != 1) { - - report_at (program_name, 0, REPORT_ERROR, "failed to write data relocations to '%s'", state->ofile); - return 1; - - } - - } + section_append_section_part (section, part); + part_p_array[1] = part; - } + section = section_find_or_make (".data"); + section->flags = SECTION_FLAG_ALLOC | SECTION_FLAG_LOAD | SECTION_FLAG_DATA; - return 0; - -} - - -static struct msdos_header *doshdr; -static struct pe_header *pehdr; -static struct pe_optional_header *opthdr; - -static struct section_table_entry *section_text; -static struct section_table_entry *section_data; - -unsigned char dos_stub[] = { - - 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, - 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x54, 0x68, + part = section_part_new (section, of); - 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, - 0x61, 0x6D, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, + part->content_size = array_to_integer (elks_exec->a_data, 4); + part->content = xmalloc (part->content_size); - 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, - 0x20, 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, + if ((pos - data + part->content_size > data_size) || pos < data) { - 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A, - 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - -}; - -static int no_sections = 0; - -static unsigned long bytearray_read_4_bytes (unsigned char *src) { - - unsigned long value = 0; - int i; + report_at (program_name, 0, REPORT_FATAL_ERROR, "corrupted input file"); + return; - for (i = 0; i < 4; i++) { - value |= (unsigned long) src[i] << (CHAR_BIT * i); } - return value; - -} - -static unsigned long pe_chksum (unsigned char *base, unsigned long chksum_off, unsigned long size) { - - unsigned long checksum = 0, data, i; - int carry; - - for (i = 0; i < size / 4; i++) { + memcpy (part->content, pos, part->content_size); + pos += part->content_size; - if (i == chksum_off / 4) { - continue; - } - - data = bytearray_read_4_bytes (base + i * 4); - - carry = checksum > 0xFFFFFFFFLU - data; - - checksum += data; - checksum += carry; - - checksum &= 0xFFFFFFFFLU; + section_append_section_part (section, part); + part_p_array[2] = part; - } + bss_section = section = section_find_or_make (".bss"); - checksum = (checksum >> 16) + (checksum & 0xFFFF); - - checksum += checksum >> 16; - checksum &= 0xFFFF; - checksum += size; - - return checksum & 0xFFFFFFFFLU; - -} - -static int init_pe_object (void) { - - header_size = sizeof (*doshdr) + sizeof (dos_stub) + sizeof (*pehdr) + sizeof (*opthdr); + section->flags = SECTION_FLAG_ALLOC; + section->is_bss = 1; - header_size += sizeof (*section_data); - header_size += sizeof (*section_text); + part = section_part_new (section, of); + part->content_size = array_to_integer (elks_exec->a_bss, 4); - no_sections = 0; + section_append_section_part (section, part); + part_p_array[3] = part; - if (state->raw_text_size > 0) { - no_sections++; - } + pos += array_to_integer (elks_exec->a_syms, 4) + array_to_integer (elks_exec->a_trsize, 4) + array_to_integer (elks_exec->a_drsize, 4); - if (state->raw_data_size > 0) { - no_sections++; - } + if ((unsigned long) (pos - data + 4) > data_size || pos < data) { - header_size = ALIGN_UP (header_size, FILE_ALIGNMENT); - output_size = header_size + state->text_size + state->data_size; + report_at (program_name, 0, REPORT_FATAL_ERROR, "corrupted input file"); + return; - if ((output = malloc (output_size)) == NULL) { - return 2; } - memset (output, 0, output_size); - - doshdr = (struct msdos_header *) output; - pehdr = (struct pe_header *) ((char *) doshdr + sizeof (*doshdr) + sizeof (dos_stub)); - opthdr = (struct pe_optional_header *) ((char *) pehdr + sizeof (*pehdr)); - - section_text = (struct section_table_entry *) ((char *) opthdr + sizeof (*opthdr)); - section_data = (struct section_table_entry *) ((char *) section_text + sizeof (*section_text)); - - text = (void *) ((char *) output + header_size); - data = (void *) ((char *) text + ALIGN_UP (state->text_size, FILE_ALIGNMENT)); - - return 0; - -} - -static int write_pe_object (unsigned long entry) { - - unsigned long checksum_pos, size; - - doshdr->e_magic[0] = 'M'; - doshdr->e_magic[1] = 'Z'; - - write721_to_byte_array (doshdr->e_cblp, 0x0090); - write721_to_byte_array (doshdr->e_cp, 0x0003); - - write721_to_byte_array (doshdr->e_cparhdr, ALIGN_UP (sizeof (*doshdr), 16) / 16); + strtab_size = array_to_integer (pos, 4); - write721_to_byte_array (doshdr->e_maxalloc, 0xFFFF); - write721_to_byte_array (doshdr->e_sp, 0x00B8); + if (strtab_size < 4) { - write721_to_byte_array (doshdr->e_lfarlc, sizeof (*doshdr)); - write741_to_byte_array (doshdr->e_lfanew, sizeof (*doshdr) + sizeof (dos_stub)); + report_at (program_name, 0, REPORT_ERROR, "%s: invalid string table size %lu", filename, strtab_size); + return; - memcpy ((char *) output + GET_UINT16 (doshdr->e_lfarlc), dos_stub, sizeof (dos_stub)); - - - pehdr->Signature[0] = 'P'; - pehdr->Signature[1] = 'E'; - - write721_to_byte_array (pehdr->Machine, IMAGE_FILE_MACHINE_I386); - write721_to_byte_array (pehdr->NumberOfSections, no_sections); - write741_to_byte_array (pehdr->TimeDateStamp, time (0)); - write721_to_byte_array (pehdr->SizeOfOptionalHeader, sizeof (*opthdr)); - - { + } else { - unsigned short characteristics = 0; + if ((pos - data + strtab_size > data_size) || pos < data) { - characteristics |= IMAGE_FILE_RELOCS_STRIPPED; - characteristics |= IMAGE_FILE_EXECUTABLE_IMAGE; - characteristics |= IMAGE_FILE_LINE_NUMS_STRIPPED; - characteristics |= IMAGE_FILE_LOCAL_SYMS_STRIPPED; - characteristics |= IMAGE_FILE_32BIT_MACHINE; - characteristics |= IMAGE_FILE_DEBUG_STRIPPED; + report_at (program_name, 0, REPORT_FATAL_ERROR, "corrupted input file"); + return; - write721_to_byte_array (pehdr->Characteristics, characteristics); - - } - - - write721_to_byte_array (opthdr->Magic, IMAGE_FILE_MAGIC_I386); - - opthdr->MajorLinkerVersion = 0; - opthdr->MinorLinkerVersion = 10; - - write741_to_byte_array (opthdr->SizeOfCode, state->text_size); - write741_to_byte_array (opthdr->SizeOfInitializedData, state->data_size); - write741_to_byte_array (opthdr->SizeOfUninitializedData, state->bss_size); - write741_to_byte_array (opthdr->AddressOfEntryPoint, ALIGN_UP ((char *) text - (char *) output, SECTION_ALIGNMENT) + entry); - - if (state->raw_text_size > 0) { - write741_to_byte_array (opthdr->BaseOfCode, ALIGN_UP (state->text_size, SECTION_ALIGNMENT)); - } + } + + strtab = (char *) pos; - if (state->raw_data_size > 0) { - write741_to_byte_array (opthdr->BaseOfData, ALIGN_UP (state->data_size, SECTION_ALIGNMENT)); } - write741_to_byte_array (opthdr->ImageBase, state->psp); - - write741_to_byte_array (opthdr->SectionAlignment, SECTION_ALIGNMENT); - write741_to_byte_array (opthdr->FileAlignment, FILE_ALIGNMENT); - - write721_to_byte_array (opthdr->MajorOperatingSystemVersion, 4); - write721_to_byte_array (opthdr->MajorImageVersion, 1); - write721_to_byte_array (opthdr->MajorSubsystemVersion, 4); + pos -= array_to_integer (elks_exec->a_syms, 4); - size = GET_UINT32 (opthdr->BaseOfCode) + GET_UINT32 (opthdr->SizeOfCode) + GET_UINT32 (opthdr->BaseOfData) + GET_UINT32 (opthdr->SizeOfInitializedData); - write741_to_byte_array (opthdr->SizeOfImage, ALIGN_UP (size, SECTION_ALIGNMENT)); + for (i = 0; i < num_symbols; i++) { - write741_to_byte_array (opthdr->SizeOfHeaders, header_size); - write721_to_byte_array (opthdr->Subsystem, state->subsystem); - - { - - unsigned long ibss_addr = ((char *) data - (char *) output) + state->data_size; - unsigned long ibss_size = state->bss_size; + elks_nlist = (struct elks_nlist *) (pos + (i * sizeof (*elks_nlist))); + symbol = of->symbol_arr + i; - unsigned long stack_addr = ibss_addr + ibss_size; - unsigned long stack_size = ALIGN_UP (stack_addr, SECTION_ALIGNMENT); + if (array_to_integer (elks_nlist->n_strx, 4) < strtab_size) { + symbol->name = xstrdup (strtab + array_to_integer (elks_nlist->n_strx, 4)); + } else { - write741_to_byte_array (opthdr->SizeOfStackReserved, SECTION_ALIGNMENT << 9); - write741_to_byte_array (opthdr->SizeOfStackCommit, ALIGN_UP (stack_addr % 16 + stack_size, SECTION_ALIGNMENT)); - write741_to_byte_array (opthdr->SizeOfHeapReserved, SECTION_ALIGNMENT << 8); - write741_to_byte_array (opthdr->SizeOfHeapCommit, ALIGN_UP (stack_addr % 16 + stack_size, SECTION_ALIGNMENT)); - - } - - write741_to_byte_array (opthdr->NumberOfRvaAndSizes, 16); - - - if (state->raw_text_size > 0) { - - unsigned long characteristics = 0; - memcpy (section_text->Name, ".text", 5); + report_at (program_name, 0, REPORT_FATAL_ERROR, "%s: invalid offset into string table", filename); + return; - write741_to_byte_array (section_text->VirtualSize, state->raw_text_size); - write741_to_byte_array (section_text->VirtualAddress, GET_UINT32 (opthdr->BaseOfCode)); - write741_to_byte_array (section_text->SizeOfRawData, state->text_size); - write741_to_byte_array (section_text->PointerToRawData, ((char *) text - (char *) output)); + } - characteristics |= IMAGE_SCN_CNT_CODE; - characteristics |= IMAGE_SCN_ALIGN_4BYTES; - characteristics |= IMAGE_SCN_MEM_EXECUTE; - characteristics |= IMAGE_SCN_MEM_READ; + symbol->value = array_to_integer (elks_nlist->n_value, 4); + symbol->size = 0; - write741_to_byte_array (section_text->Characteristics, characteristics); - - } - - if (state->raw_data_size > 0) { - - unsigned long characteristics = 0; - memcpy (section_text->Name, ".data", 5); + if ((elks_nlist->n_type & N_TYPE) == N_UNDF || (elks_nlist->n_type & N_TYPE) == N_COMM) { - write741_to_byte_array (section_data->VirtualSize, state->raw_data_size); - write741_to_byte_array (section_data->VirtualAddress, GET_UINT32 (opthdr->BaseOfData)); - write741_to_byte_array (section_data->SizeOfRawData, state->data_size); - write741_to_byte_array (section_data->PointerToRawData, ((char *) data - (char *) output)); + if (symbol->value) { + + old_symbol = symbol_find (symbol->name); + + if (!old_symbol || symbol_is_undefined (old_symbol)) { + + bss_part = section_part_new (bss_section, of); + section_append_section_part (bss_section, bss_part); + + bss_part->content_size = symbol->size = symbol->value; + + symbol->part = bss_part; + symbol->value = 0; + symbol->section_number = 3; + + } else { + + if (symbol->value > old_symbol->size) { + old_symbol->part->content_size = old_symbol->size = symbol->value; + } + + symbol->part = 0; + symbol->value = 0; + symbol->section_number = UNDEFINED_SECTION_NUMBER; + + } + + } else { + + symbol->section_number = UNDEFINED_SECTION_NUMBER; + symbol->part = 0; + + } - characteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA; - characteristics |= IMAGE_SCN_ALIGN_4BYTES; - characteristics |= IMAGE_SCN_MEM_READ; - characteristics |= IMAGE_SCN_MEM_WRITE; + } else if ((elks_nlist->n_type & N_TYPE) == N_ABS) { - write741_to_byte_array (section_data->Characteristics, characteristics); - - } - - checksum_pos = sizeof (*doshdr) + sizeof (dos_stub) + sizeof (*pehdr) + offsetof (struct pe_optional_header, Checksum); - write741_to_byte_array (opthdr->Checksum, pe_chksum (output, checksum_pos, output_size)); - - - /* write the file */ - if (fwrite ((char *) output, output_size, 1, state->ofp) != 1) { - - report_at (program_name, 0, REPORT_ERROR, "failed to write data to '%s'", state->ofile); - return 1; - - } - - return 0; - -} - - -int create_executable_from_elks_objects (void) { - - struct elks_object *object; - long i; - - unsigned long entry = 0; - int err = 0; - - if (state->format == LD_FORMAT_BIN || state->format == LD_FORMAT_COM) { - - output_size = state->text_size + state->data_size; + symbol->section_number = ABSOLUTE_SECTION_NUMBER; + symbol->part = 0; - if ((output = malloc (output_size)) == NULL) { - return EXIT_FAILURE; - } + } else if ((elks_nlist->n_type & N_TYPE) == N_TEXT) { - memset (output, 0, output_size); + symbol->section_number = 1; + symbol->part = part_p_array[1]; - text = (void *) (char *) output; - data = (void *) ((char *) text + state->text_size); - - } else if (state->format == LD_FORMAT_IA16_ELKS || state->format == LD_FORMAT_I386_ELKS || state->format == LD_FORMAT_I386_AOUT) { - - if (init_elks_object ()) { + } else if ((elks_nlist->n_type & N_TYPE) == N_DATA) { - report_at (program_name, 0, REPORT_ERROR, "failed to initialize a.out object"); - return EXIT_FAILURE; + symbol->section_number = 2; + symbol->part = part_p_array[2]; + symbol->value -= array_to_integer (elks_exec->a_text, 4); - } - - } else if (state->format == LD_FORMAT_I386_PE) { - - if (init_pe_object ()) { + } else if ((elks_nlist->n_type & N_TYPE) == N_BSS) { - report_at (program_name, 0, REPORT_ERROR, "failed to initialize pe object"); - return EXIT_FAILURE; + symbol->section_number = 3; + symbol->part = part_p_array[3]; + symbol->value -= (array_to_integer (elks_exec->a_text, 4) + array_to_integer (elks_exec->a_data, 4)); - } - - } - - for (i = 0; i < state->nb_elks_objs; ++i) { - paste (state->elks_objs[i]); - } - - for (i = 0; i < state->nb_elks_objs; ++i) { - apply_slides (state->elks_objs[i]); - } - - for (i = 0; i < state->nb_elks_objs; ++i) { - undef_collect (state->elks_objs[i]); - } - - if (!state->impure) { - - if (state->format == LD_FORMAT_I386_PE) { - state->bss_size = ALIGN_UP (state->bss_size, FILE_ALIGNMENT); } else { - state->bss_size = ALIGN_UP (state->bss_size, SECTION_ALIGNMENT); + + report_at (program_name, 0, REPORT_INTERNAL_ERROR, "unsupported nlist.n_type: %#x", elks_nlist->n_type); + return; + } - - } - - for (i = 0; i < state->nb_elks_objs; ++i) { - - if (glue (state->elks_objs[i])) { - err = 1; + + if ((elks_nlist->n_type & N_EXT) || (elks_nlist->n_type & N_TYPE) == N_UNDF || (elks_nlist->n_type & N_TYPE) == N_COMM) { + symbol_record_external_symbol (symbol); } } - if (err) { - return EXIT_FAILURE; - } + for (i = 1; i < 4; i++) { - if (state->format == LD_FORMAT_IA16_ELKS || state->format == LD_FORMAT_I386_ELKS || state->format == LD_FORMAT_I386_AOUT || state->format == LD_FORMAT_I386_PE) { - entry = get_entry (); - } - - for (i = 0; i < state->nb_elks_objs; i++) { - - if ((object = state->elks_objs[i]) == NULL) { - return EXIT_FAILURE; - } + symbol = of->symbol_arr + num_symbols + i; + part = part_p_array[i]; - /*if (state->mapfile) { - init_map (object); - }*/ - - free (object->raw); - free (object); + symbol->name = xstrdup (part->section->name); + symbol->value = 0; + symbol->size = 0; + symbol->part = part; + symbol->section_number = i; } - state->nb_elks_objs = 0; - - /*if (state->mapfile) { + pos -= (array_to_integer (elks_exec->a_trsize, 4) + array_to_integer (elks_exec->a_drsize, 4)); + part = part_p_array[1]; - set_map_sections_size (state->text_size, state->data_size, state->bss_size); - set_map_sections_start (0, state->text_size, state->text_size + state->data_size); - - generate_map (); + part->reloc_cnt = array_to_integer (elks_exec->a_trsize, 4) / sizeof (*reloc_info); + part->reloc_arr = xmalloc (sizeof (*part->reloc_arr) * part->reloc_cnt); - }*/ + for (i = 0; i < part->reloc_cnt; i++) { - if ((state->ofp = fopen (state->ofile, "wb")) == NULL) { - - report_at (program_name, 0, REPORT_ERROR, "failed to open '%s' for writing", state->ofile); - return EXIT_FAILURE; + reloc_info = (struct elks_relocation_info *) (pos + (sizeof (*reloc_info) * i)); + translate_relocation (filename, part->reloc_arr + i, reloc_info, part, elks_exec); } - if (state->format == LD_FORMAT_BIN || state->format == LD_FORMAT_COM) { - - if (fwrite ((char *) output, output_size, 1, state->ofp) != 1) { - - report_at (program_name, 0, REPORT_ERROR, "failed to write data to '%s'", state->ofile); - return EXIT_FAILURE; - - } + pos += array_to_integer (elks_exec->a_trsize, 4); + part = part_p_array[2]; - } else if (state->format == LD_FORMAT_IA16_ELKS || state->format == LD_FORMAT_I386_ELKS || state->format == LD_FORMAT_I386_AOUT) { + part->reloc_cnt = array_to_integer (elks_exec->a_drsize, 4) / sizeof (*reloc_info); + part->reloc_arr = xmalloc (sizeof (*part->reloc_arr) * part->reloc_cnt); - if (write_elks_object (entry)) { - - report_at (program_name, 0, REPORT_ERROR, "failed to write a.out object"); - return EXIT_FAILURE; - - } + for (i = 0; i < part->reloc_cnt; i++) { - } else if (state->format == LD_FORMAT_I386_PE) { + reloc_info = (struct elks_relocation_info *) (pos + (sizeof (*reloc_info) * i)); + translate_relocation (filename, part->reloc_arr + i, reloc_info, part, elks_exec); - if (write_pe_object (entry)) { - - report_at (program_name, 0, REPORT_ERROR, "failed to write pe object"); - return EXIT_FAILURE; - - } } - - return EXIT_SUCCESS; } diff --git a/elks.h b/elks.h index dfbe08f..2a592d3 100644 --- a/elks.h +++ b/elks.h @@ -32,6 +32,7 @@ struct elks_exec { #define N_TEXT 0x04 #define N_DATA 0x06 #define N_BSS 0x08 +#define N_COMM 0x12 struct elks_relocation_info { @@ -55,8 +56,8 @@ struct elks_nlist { }; #define N_TYPE 0x1e -int create_executable_from_elks_objects (void); - #define ELKS_MAGIC 0403 +void read_elks_object (const char *filename, unsigned char *data, unsigned long data_size); + #endif /* _ELKS_H */ diff --git a/ld.c b/ld.c index 27d2886..19c6c23 100644 --- a/ld.c +++ b/ld.c @@ -6,712 +6,208 @@ #include #include "elks.h" -#include "hashtab.h" #include "ld.h" #include "lib.h" +#include "pe.h" #include "report.h" -#include "vector.h" +#include "section.h" struct ld_state *state = 0; const char *program_name = 0; static void cleanup (void) { - long i; - - if (state->ofp) { - fclose (state->ofp); - } - if (get_error_count () > 0) { - if (state->ofile) { - remove (state->ofile); - } - - } - - for (i = 0; i < state->nb_elks_objs; ++i) { - - struct elks_object *obj = state->elks_objs[i]; - - if (obj == NULL) { - continue; + if (state->output_filename) { + remove (state->output_filename); } - - if (obj->raw != NULL) { - free (obj->raw); - } - - free (obj); - - } - -} - - -static unsigned long conv_dec (char *str, long max) { - - unsigned long value = 0; - - while (*str != ' ' && max-- > 0) { - - value *= 10; - value += *str++ - '0'; } - - return value; } -static struct hashtab hashtab_globals = { 0 }; -static struct vector vec_undef = { 0 }; - -static int process_elks (void *obj, unsigned long sz, const char *fname, int quiet) { +static int read_file_into_memory (const char *filename, unsigned char **memory_p, unsigned long *size_p) { - struct elks_exec *hdr = obj; - - struct elks_object *data_obj; - struct elks_nlist *symtab; - - struct elks_relocation_info *trelocs; - struct elks_relocation_info *drelocs; - - long symtab_count, trelocs_count, drelocs_count; - unsigned long symtab_off, strtab_off, trelocs_off, drelocs_off; - - char *strtab; - long i; + unsigned char *memory; + FILE *fp; - if (!(hdr->a_magic[0] == ((ELKS_MAGIC >> 8) & 0xff) && hdr->a_magic[1] == (ELKS_MAGIC & 0xff))) { + unsigned long mem_size; - if (!quiet) { - report_at (program_name, 0, REPORT_ERROR, "'%s' is not a valid object", fname); - } - + if (!(fp = fopen (filename, "rb"))) { return 1; - } - /*for (i = 0; i < state->nb_elks_objs; ++i) { - - struct elks_object *obj_to_compare = state->elks_objs[i]; - - if (obj_to_compare->size != sz) { - continue; - } - - if (memcmp (obj_to_compare->raw, obj, sz) == 0) { - return 0; - } - - }*/ + fseek (fp, 0, SEEK_END); + mem_size = ftell (fp); - state->raw_text_size += GET_UINT32 (hdr->a_text); - state->raw_data_size += GET_UINT32 (hdr->a_data); - state->raw_bss_size += GET_UINT32 (hdr->a_bss); + fseek (fp, 0, SEEK_SET); + memory = xmalloc (mem_size); - if (state->impure) { + if (fread (memory, mem_size, 1, fp) != 1) { - state->text_size += GET_UINT32 (hdr->a_text); - state->data_size += GET_UINT32 (hdr->a_data); - state->bss_size += GET_UINT32 (hdr->a_bss); - - } else { - - if (state->format == LD_FORMAT_I386_PE) { - - state->text_size += ALIGN_UP (GET_UINT32 (hdr->a_text), FILE_ALIGNMENT); - state->data_size += ALIGN_UP (GET_UINT32 (hdr->a_data), FILE_ALIGNMENT); - state->bss_size += ALIGN_UP (GET_UINT32 (hdr->a_bss), FILE_ALIGNMENT); - - } else { + fclose (fp); - state->text_size += ALIGN_UP (GET_UINT32 (hdr->a_text), SECTION_ALIGNMENT); - state->data_size += ALIGN_UP (GET_UINT32 (hdr->a_data), SECTION_ALIGNMENT); - state->bss_size += ALIGN_UP (GET_UINT32 (hdr->a_bss), SECTION_ALIGNMENT); - - } - - } - - trelocs_off = sizeof (*hdr) + GET_UINT32 (hdr->a_text) + GET_UINT32 (hdr->a_data); - drelocs_off = trelocs_off + GET_UINT32 (hdr->a_trsize); - symtab_off = drelocs_off + GET_UINT32 (hdr->a_drsize); - strtab_off = symtab_off + GET_UINT32 (hdr->a_syms); - - trelocs_count = GET_UINT32 (hdr->a_trsize) / sizeof (*trelocs); - drelocs_count = GET_UINT32 (hdr->a_drsize) / sizeof (*drelocs); - symtab_count = GET_UINT32 (hdr->a_syms) / sizeof (*symtab); - - trelocs = (void *) ((char *) obj + trelocs_off); - drelocs = (void *) ((char *) obj + drelocs_off); - symtab = (void *) ((char *) obj + symtab_off); - strtab = (char *) obj + strtab_off; - - if (!(data_obj = malloc (sizeof (*data_obj)))) { + free (memory); return 1; - } - - memset (data_obj, 0, sizeof (*data_obj)); - data_obj->filename = fname; - data_obj->header = hdr; - data_obj->raw = obj; - data_obj->size = sz; - - data_obj->trelocs = trelocs; - data_obj->drelocs = drelocs; - data_obj->symtab = symtab; - data_obj->strtab = strtab; - data_obj->trelocs_count = trelocs_count; - data_obj->drelocs_count = drelocs_count; - data_obj->symtab_count = symtab_count; - - dynarray_add (&state->elks_objs, &state->nb_elks_objs, data_obj); - - for (i = 0; i < symtab_count; ++i) { + } - struct elks_nlist *sym = &symtab[i]; - char *symname = strtab + GET_INT32 (sym->n_strx); - - if ((sym->n_type & N_TYPE) == N_UNDF) { - vec_push (&vec_undef, (void *) symname); - } else if (sym->n_type == 5 || sym->n_type == 7 || sym->n_type == 9) { - - struct hashtab_name *key; - - if ((key = hashtab_alloc_name (symname)) == NULL) { - - free (key); - - report_at (program_name, 0, REPORT_ERROR, "failed to allocate memory (memory full)"); - return 1; - - } - - if (hashtab_get (&hashtab_globals, key) != NULL) { - - free (key); - continue; - - } - - hashtab_put (&hashtab_globals, key, symname); - - } + fclose (fp); - } + *memory_p = memory; + *size_p = mem_size; return 0; } -struct ar_header { +static void read_input_file (const char *filename) { - char name[16]; - char mtime[12]; - char owner[6]; - char group[6]; - char mode[8]; - char size[10]; - char endsig[2]; - -}; - -static int read_ar_obj (FILE *ar_file, const char *root_fname, unsigned long index) { - - struct ar_header hdr; - unsigned long sz, sz_aligned; - - int i; + unsigned char *data; + unsigned long data_size; - char *fname, *path; - void *obj; + if (read_file_into_memory (filename, &data, &data_size)) { - if ((fname = malloc (17)) == NULL) { + report_at (program_name, 0, REPORT_ERROR, "failed to read file '%s' into memory", filename); + return; - report_at (program_name, 0, REPORT_ERROR, "failed to allocation memory (memory full)"); - return 1; + } + if (data[0] == ((ELKS_MAGIC >> 8) & 0xff) && data[1] == (ELKS_MAGIC & 0xff)) { + read_elks_object (filename, data, data_size); + } else { + report_at (program_name, 0, REPORT_ERROR, "'%s' is not a valid object", filename); } - fseek (ar_file, index, SEEK_SET); + free (data); + +} + +int main (int argc, char **argv) { + + long i; - if (fread (&hdr, sizeof (hdr), 1, ar_file) != 1) { + if (argc && *argv) { - if (feof (ar_file)) { - return 0; - } + char *p; + program_name = *argv; - report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", root_fname); - return 1; + if ((p = strrchr (program_name, '/')) || (p = strrchr (program_name, '\\'))) { + program_name = (p + 1); + } } - sz = conv_dec (hdr.size, 10); - sz_aligned = (sz % 2) ? (sz + 1) : sz; + atexit (cleanup); - memcpy (fname, hdr.name, 16); + state = xmalloc (sizeof (*state)); + parse_args (argc, argv, 1); - for (i = 0; i < 16; ++i) { + if (state->nb_input_files == 0) { - if (fname[i] == 0x20 || fname[i] == '/') { - - fname[i] = '\0'; - break; - - } + report_at (program_name, 0, REPORT_ERROR, "no input files provided"); + exit (EXIT_FAILURE); } - if ((obj = malloc (sz_aligned)) == NULL) { + if (state->format == LD_FORMAT_I386_AOUT || state->format == LD_FORMAT_I386_ELKS || state->format == LD_FORMAT_IA16_ELKS) { - report_at (program_name, 0, REPORT_ERROR, "failed to allocate memory (memory full)"); - return 1; + section_find_or_make (".text"); + section_find_or_make (".data"); + section_find_or_make (".bss"); } - if (fread (obj, sz_aligned, 1, ar_file) != 1) { - - report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", root_fname); - free (obj); - - return 1; + if (!state->output_filename) { + state->output_filename = "a.out"; + } + for (i = 0; i < state->nb_input_files; i++) { + read_input_file (state->input_files[i]); } - if (fname) { + if (get_error_count () > 0) { return EXIT_FAILURE; } + sections_destroy_empty_before_collapse (); - unsigned long len = strlen (root_fname) + 1 + strlen (fname) + 2; - - if ((path = malloc (len)) == NULL) { - - report_at (program_name, 0, REPORT_ERROR, "failed to allocate memory (memory full)"); - free (obj); - - return 1; - - } - - memset (path, 0, len); - sprintf (path, "%s(%s)", root_fname, fname); - - if (process_elks (obj, sz, path, 1)) { - - free (obj); - return 1; - - } + if (!state->use_custom_base_address) { - } else { - - if (process_elks (obj, sz, "", 1)) { - - free (obj); - return 1; - + if (state->format == LD_FORMAT_COM) { + state->base_address = 0x00000100; + } else if (state->format == LD_FORMAT_I386_PE) { + state->base_address = 0x00400000; } } - free (fname); - return 0; - -} - -static int process_archive (FILE *ar_file, const char *root_fname) { - - struct vector vec_undef2 = { 0 }; - char *fname, *path; - - void *obj; - - if ((fname = malloc (17)) == NULL) { - - report_at (program_name, 0, REPORT_ERROR, "failed to allocation memory (memory full)"); - return 1; - + if (state->format == LD_FORMAT_I386_PE) { + pe_before_link (); } - if (fseek (ar_file, 8, SEEK_SET) != 0) { - - report_at (program_name, 0, REPORT_ERROR, "failed whilst seeking '%s'", root_fname); - return 1; + link (); + if (get_error_count () > 0) { + return EXIT_FAILURE; } - for (;;) { + if (state->format == LD_FORMAT_BIN || state->format == LD_FORMAT_COM) { - struct ar_header hdr; + FILE *fp; - int err, i; - unsigned long sz, sz_aligned; + unsigned char *data; + unsigned long data_size = 0; - if (fread (&hdr, sizeof (hdr), 1, ar_file) != 1) { + struct section *section; - if (feof (ar_file)) { - break; - } - - report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", root_fname); - return 1; + if (!(fp = fopen (state->output_filename, "wb"))) { - } - - sz = conv_dec (hdr.size, 10); - sz_aligned = (sz % 2) ? (sz + 1) : sz; - - if (memcmp (hdr.name, "__.SYMDEF", 9) == 0 || memcmp (hdr.name, "/", 1) == 0) { - - unsigned char temp[4], *temp3; - unsigned long cnt, j, k, *temp2; - - if (fread (temp, 4, 1, ar_file) != 1) { - - report_at (program_name, 0, REPORT_ERROR, "failed to read symdef count"); - return 1; - - } - - cnt = (unsigned long) temp[3] | (((unsigned long) temp[2]) << 8) | (((unsigned long) temp[1]) << 16) | (((unsigned long) temp[0]) << 24); - sz -= 4; - - if ((temp2 = (unsigned long *) malloc (cnt * sizeof (unsigned long))) == NULL) { - - report_at (program_name, 0, REPORT_ERROR, "failed to allocate memory (memory full)"); - return 1; - - } - - for (j = 0; j < cnt; ++j) { - - if (fread (temp, 4, 1, ar_file) != 1) { - - report_at (program_name, 0, REPORT_ERROR, "failed to read symdef index"); - return 1; - - } - - temp2[j] = (unsigned long) temp[3] | (((unsigned long) temp[2]) << 8) | (((unsigned long) temp[1]) << 16) | (((unsigned long) temp[0]) << 24); - - } - - sz -= cnt * 4; - - if ((temp3 = (unsigned char *) malloc (sz)) == NULL) { - - report_at (program_name, 0, REPORT_ERROR, "failed to allocate memory (memory full)"); - return 1; - - } - - if (fread (temp3, sz, 1, ar_file) != 1) { - - report_at (program_name, 0, REPORT_ERROR, "failed to read symdef symbols"); - return 1; - - } - - while (vec_undef.length > 0) { - - char *symname = (char *) vec_pop (&vec_undef); - - struct hashtab_name *key; - char *temp; - - if ((key = hashtab_alloc_name (symname)) == NULL) { - - report_at (program_name, 0, REPORT_ERROR, "failed to allocate memory (memory full)"); - return 1; - - } - - if ((temp = hashtab_get (&hashtab_globals, key)) != NULL) { - - if (strcmp (temp, symname) != 0) { - - free (key); - - report_at (program_name, 0, REPORT_ERROR, "hashtab collison"); - return 1; - - } - - continue; - - } - - free (key); - - for (j = 0, k = 0; k < sz; ) { - - unsigned long len = strlen ((char *) temp3 + k) + 1; - - if (strcmp ((char *) temp3 + k, symname) == 0) { - break; - } - - k += len; - j++; - - } - - if (j >= cnt) { - - vec_push (&vec_undef2, symname); - continue; - - } - - if (read_ar_obj (ar_file, root_fname, temp2[j])) { - - report_at (program_name, 0, REPORT_ERROR, "failed whilst reading ar object"); - return 1; - - } - - } - - memcpy (&vec_undef, &vec_undef2, sizeof (vec_undef)); - return 0; + report_at (program_name, 0, REPORT_ERROR, "cannot open '%s' for writing", state->output_filename); + return EXIT_FAILURE; } - memcpy (fname, hdr.name, 16); + for (section = all_sections; section; section = section->next) { - for (i = 0; i < 16; ++i) { - - if (fname[i] == 0x20 || fname[i] == '/') { - - fname[i] = '\0'; - break; - + if (data_size < section->rva + section->total_size) { + data_size = section->rva + section->total_size; } } - if ((obj = malloc (sz_aligned)) == NULL) { - - report_at (program_name, 0, REPORT_ERROR, "failed to allocate memory (memory full)"); - return 1; - - } - - if (fread (obj, sz_aligned, 1, ar_file) != 1) { + data = xmalloc (data_size); - report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", root_fname); - free (obj); - - return 1; - - } + for (section = all_sections; section; section = section->next) { - if (fname) { - - unsigned long len = strlen (root_fname) + 1 + strlen (fname) + 2; - - if ((path = malloc (len)) == NULL) { - - report_at (program_name, 0, REPORT_ERROR, "failed to allocate memory (memory full)"); - free (obj); - - return 1; - + if (section->is_bss) { + continue; } - memset (path, 0, len); - sprintf (path, "%s(%s)", root_fname, fname); - - if ((err = process_elks (obj, sz, path, 1))) { - free (obj); - } - - } else { - - if ((err = process_elks (obj, sz, "", 1))) { - free (obj); - } + section_write (section, data + section->rva); } - obj = NULL; - - } - - free (fname); - return 0; - -} - -static int process_file (const char *fname) { - - FILE *ifp; - void *obj; - - char *ar_magic[8]; - long obj_sz; - - int err; - - if ((ifp = fopen (fname, "rb")) == NULL) { - - report_at (program_name, 0, REPORT_ERROR, "failed to open '%s' for reading", fname); - return 1; - - } - - if (fread (ar_magic, 8, 1, ifp) != 1) { - - report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", fname); - fclose (ifp); - - return 1; - - } - - if (memcmp (ar_magic, "!\n", 8) == 0) { - - int err = process_archive (ifp, fname); - fclose (ifp); - - if (err) { - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; - - } - - if (fseek (ifp, 0, SEEK_END) != 0) { - - report_at (program_name, 0, REPORT_ERROR, "failed whist seeking '%s'", fname); - fclose (ifp); - - return 1; - - } - - if ((obj_sz = ftell (ifp)) == -1) { - - report_at (program_name, 0, REPORT_ERROR, "size of '%s' is -1", fname); - fclose (ifp); - - return 1; - - } - - rewind (ifp); - - if ((obj = malloc (obj_sz)) == NULL) { - - report_at (program_name, 0, REPORT_ERROR, "failed to allocation memory (memory full)"); - fclose (ifp); - - return 1; - - } - - if (fread (obj, obj_sz, 1, ifp) != 1) { - - report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", fname); - free (obj); - - fclose (ifp); - return 1; - - } - - if ((err = process_elks (obj, obj_sz, fname, 1))) { - - report_at (fname, 0, REPORT_ERROR, "file format not recognized"); - free (obj); - - fclose (ifp); - return 1; - - } - - fclose (ifp); - return 0; - -} - -int main (int argc, char **argv) { - - long i; - - if (argc && *argv) { - - char *p; - program_name = *argv; - - if ((p = strrchr (program_name, '/')) || (p = strrchr (program_name, '\\'))) { - program_name = (p + 1); - } - - } - - atexit (cleanup); - - state = xmalloc (sizeof (*state)); - parse_args (argc, argv, 1); - - if (state->nb_files == 0) { - - report_at (program_name, 0, REPORT_ERROR, "no input files provided"); - exit (EXIT_FAILURE); - - } - - if (!state->entry || strcmp (state->entry, "") == 0) { - state->entry = "_start"; - } - - if (!state->ofile) { - state->ofile = xstrdup ("a.out"); - } - - if (state->format == LD_FORMAT_BIN || state->format == LD_FORMAT_COM || state->format == LD_FORMAT_IA16_ELKS || state->format == LD_FORMAT_I386_ELKS) { - - if (state->format == LD_FORMAT_COM) { - state->psp = 0x100; + if (fwrite (data, data_size, 1, fp) != 1) { + report_at (program_name, 0, REPORT_ERROR, "failed to write data to '%s'", state->output_filename); } - state->impure = 1; - - } else if (state->format == LD_FORMAT_I386_PE) { - state->psp = 0x00400000; - } - - for (i = 0; i < state->nb_files; i++) { - - if (process_file (state->files[i])) { - return EXIT_FAILURE; - } - - } + free (data); + fclose (fp); - if (state->format == LD_FORMAT_IA16_ELKS) { + }/* else if (state->format == LD_FORMAT_I386_AOUT) { + 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_PE) { - if (state->text_size > 65536L || state->data_size > 65536L || (state->text_size + state->data_size) > 65536L) { - - report_at (program_name, 0, REPORT_ERROR, "binary to large for elks-ia16"); - return EXIT_FAILURE; - - } + pe_after_link (); + pe_write (state->output_filename); } - if (state->nb_elks_objs > 0) { - - if (create_executable_from_elks_objects ()) { - return EXIT_FAILURE; - } - + if (state->output_map_filename) { + map_write (state->output_map_filename); } - return EXIT_SUCCESS; + return (get_error_count () > 0 ? EXIT_FAILURE : EXIT_SUCCESS); } diff --git a/ld.h b/ld.h index cf5710f..ddfdf9e 100644 --- a/ld.h +++ b/ld.h @@ -4,52 +4,24 @@ #ifndef _LD_H #define _LD_H -#include "aout.h" - -struct elks_object { - - const char *filename; - void *raw; - - unsigned long size; - - struct elks_exec *header; - struct elks_relocation_info *trelocs, *drelocs; - - struct elks_nlist *symtab; - char *strtab; - - long symtab_count, trelocs_count, drelocs_count; - unsigned long text_slide, data_slide, bss_slide; - -}; - -#define GET_UINT16(arr) ((unsigned long) arr[0] | (((unsigned long) arr[1]) << 8)) - -#define GET_INT32(arr) ((long) arr[0] | (((long) arr[1]) << 8) | (((long) arr[2]) << 16) | (((long) arr[3]) << 24)) -#define GET_UINT32(arr) ((unsigned long) arr[0] | (((unsigned long) arr[1]) << 8) | (((unsigned long) arr[2]) << 16) | (((unsigned long) arr[3]) << 24)) - -#include +#define FAKE_LD_FILENAME "autogenerated" struct ld_state { - const char **files; - long nb_files; - - const char *ofile; - FILE *ofp; + const char **input_files; + long nb_input_files; - int format, impure; - int psp; + const char *entry_symbol_name; + unsigned long entry_point; - struct elks_object **elks_objs; - long nb_elks_objs; + const char *output_map_filename; + unsigned long base_address; - unsigned long raw_text_size, raw_data_size, raw_bss_size; - unsigned long text_size, data_size, bss_size; + const char *output_filename; + int create_shared_library, format; - unsigned long subsystem; - char *entry; + int emit_relocs, use_custom_base_address; + unsigned long size_of_headers; }; @@ -57,18 +29,15 @@ struct ld_state { #define LD_FORMAT_BIN 0x01 #define LD_FORMAT_IA16_ELKS 0x02 -#define LD_FORMAT_I386_ELKS 0x04 +#define LD_FORMAT_I386_ELKS 0x03 -#define LD_FORMAT_I386_AOUT 0x08 -#define LD_FORMAT_I386_PE 0x10 +#define LD_FORMAT_I386_AOUT 0x04 +#define LD_FORMAT_I386_PE 0x05 extern struct ld_state *state; extern const char *program_name; -#define SECTION_ALIGNMENT 4096 -#define FILE_ALIGNMENT 512 - -#define DIV_ROUNDUP(a, b) (((a) + ((b) - 1)) / (b)) -#define ALIGN_UP(x, a) (DIV_ROUNDUP ((x), (a)) * (a)) +void map_write (const char *filename); +void link (void); #endif /* _LD_H */ diff --git a/lib.c b/lib.c index 7aeac36..1da8663 100644 --- a/lib.c +++ b/lib.c @@ -10,47 +10,61 @@ #include "ld.h" #include "lib.h" +#include "pe.h" #include "report.h" -#if defined (_WIN32) -# define PATHSEP ';' -#else -# define PATHSEP ':' -#endif +struct options_with_use { -struct ld_option { - - const char *name; - int idx, flgs; + int (*check_option) (const char *cmd_arg, int argc, char **argv, int *optind, const char **optarg); + void (*use_option) (const char *cmd_arg, int idx, const char *optarg); }; -#define LD_OPTION_NO_ARG 0 -#define LD_OPTION_HAS_ARG 1 +static struct options_with_use fmt_tbl[] = { + + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { &pe_check_option, &pe_use_option } + +}; -#define LD_OPTION_NONE 0 -#define LD_OPTION_ENTRY 1 -#define LD_OPTION_FORMAT 2 -#define LD_OPTION_HELP 3 -#define LD_OPTION_IMPURE 4 -#define LD_OPTION_MAP 5 -#define LD_OPTION_OUTFILE 6 -#define LD_OPTION_PSP 7 -#define LD_OPTION_SUBSYSTEM 8 +#define LD_OPTION_IGNORED 0 +#define LD_OPTION_EMIT_RELOCS 1 +#define LD_OPTION_ENTRY 2 +#define LD_OPTION_FORMAT 3 +#define LD_OPTION_HELP 4 +#define LD_OPTION_IMAGE_BASE 5 +#define LD_OPTION_MAP 6 +#define LD_OPTION_MAP_FILE 7 +#define LD_OPTION_OUTFILE 8 static struct ld_option opts[] = { - { "-N", LD_OPTION_IMPURE, LD_OPTION_NO_ARG }, - { "-T", LD_OPTION_PSP, LD_OPTION_HAS_ARG }, + { "-M", LD_OPTION_MAP, LD_OPTION_NO_ARG }, + { "-Map", LD_OPTION_MAP_FILE, LD_OPTION_HAS_ARG }, + { "-N", LD_OPTION_IGNORED, LD_OPTION_NO_ARG }, - { "-e", LD_OPTION_ENTRY, LD_OPTION_HAS_ARG }, - { "-o", LD_OPTION_OUTFILE, LD_OPTION_HAS_ARG }, + { "-e", LD_OPTION_ENTRY, LD_OPTION_HAS_ARG }, + { "-o", LD_OPTION_OUTFILE, LD_OPTION_HAS_ARG }, + { "-s", LD_OPTION_IGNORED, LD_OPTION_NO_ARG }, + { "-q", LD_OPTION_EMIT_RELOCS, LD_OPTION_NO_ARG }, - { "--oformat", LD_OPTION_FORMAT, LD_OPTION_HAS_ARG }, - { "--help", LD_OPTION_HELP, LD_OPTION_NO_ARG }, - { "--subsystem", LD_OPTION_SUBSYSTEM, LD_OPTION_HAS_ARG }, - { 0, 0, 0 } + { "--emit_relocs", LD_OPTION_EMIT_RELOCS, LD_OPTION_NO_ARG }, + { "--entry", LD_OPTION_ENTRY, LD_OPTION_HAS_ARG }, + { "--help", LD_OPTION_HELP, LD_OPTION_NO_ARG }, + { "--image_base", LD_OPTION_IMAGE_BASE, LD_OPTION_HAS_ARG }, + { "--oformat", LD_OPTION_FORMAT, LD_OPTION_HAS_ARG }, + { "--omagic", LD_OPTION_IGNORED, LD_OPTION_NO_ARG }, + { "--output", LD_OPTION_OUTFILE, LD_OPTION_HAS_ARG }, + { "--print-map", LD_OPTION_MAP, LD_OPTION_NO_ARG }, + { "--strip-all", LD_OPTION_IGNORED, LD_OPTION_NO_ARG }, + + + { 0, 0, 0 } }; @@ -62,22 +76,206 @@ static void print_usage (void) { fprintf (stderr, "Usage: %s [options] file...\n\n", program_name); fprintf (stderr, "Options:\n\n"); - fprintf (stderr, " -N Do not page align data.\n"); - fprintf (stderr, " -T OFFSET Offset addresses by the specified offset.\n"); + fprintf (stderr, " -e ADDRESS, --entry ADDRESS Set start address.\n"); + fprintf (stderr, " -q, --emit-relocs Generate relocations in final output.\n"); + fprintf (stderr, " -s, --strip-all Ignored.\n"); + + fprintf (stderr, "\n"); + + fprintf (stderr, " -M, --print-map Print map file on standard output.\n"); + fprintf (stderr, " -N, --omagic Ignored.\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, " binary, msdos\n"); + fprintf (stderr, "\n"); + fprintf (stderr, " -Map FILE Write a linker map to FILE.\n"); - fprintf (stderr, " -e ADDRESS Set start address.\n"); - fprintf (stderr, " -s Ignored.\n"); + fprintf (stderr, "\n"); - fprintf (stderr, " -o FILE Set output file name (default a.out).\n"); - fprintf (stderr, " --help Print this help information.\n"); + 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, " pe-i386, binary, msdos\n"); + fprintf (stderr, " --image-base
Set base address of the executable.\n"); fprintf (stderr, "\n"); + pe_print_help (); + fprintf (stderr, "\n"); + + } + +} + +static void use_option (const char *cmd_arg, int idx, const char *optarg) { + + switch (idx) { + + case LD_OPTION_IGNORED: { + + break; + + } + + case LD_OPTION_EMIT_RELOCS: { + + state->emit_relocs = 1; + break; + + } + + case LD_OPTION_ENTRY: { + + char *p; + state->entry_point = strtoul (optarg, &p, 0); + + if (p == optarg + strlen (optarg)) { + state->entry_symbol_name = ""; + } else { + + state->entry_point = 0; + state->entry_symbol_name = optarg; + + } + + break; + + } + + case LD_OPTION_FORMAT: { + + if (xstrcasecmp (optarg, "binary") == 0) { + + state->format = LD_FORMAT_BIN; + break; + + } + + if (xstrcasecmp (optarg, "msdos") == 0) { + + state->format = LD_FORMAT_COM; + break; + + } + + /*if (xstrcasecmp (optarg, "elks-ia16") == 0) { + + state->format = LD_FORMAT_IA16_ELKS; + break; + + }*/ + + /*if (xstrcasecmp (optarg, "elks-i386") == 0) { + + state->format = LD_FORMAT_I386_ELKS; + break; + + }*/ + + /*if (xstrcasecmp (optarg, "a.out-i386") == 0) { + + state->format = LD_FORMAT_I386_AOUT; + break; + + }*/ + + if (xstrcasecmp (optarg, "pe-i386") == 0) { + + state->format = LD_FORMAT_I386_PE; + break; + + } + + report_at (program_name, 0, REPORT_ERROR, "unrecognised output format '%s'", optarg); + exit (EXIT_FAILURE); + + } + + case LD_OPTION_HELP: { + + print_usage (); + exit (EXIT_SUCCESS); + + } + + case LD_OPTION_IMAGE_BASE: { + + long conversion; + char *temp; + + errno = 0; + conversion = strtol (optarg, &temp, 0); + + if (!*optarg || isspace ((int) *optarg) || errno || *temp) { + + report_at (program_name, 0, REPORT_ERROR, "bad number for base address"); + exit (EXIT_FAILURE); + + } + + state->base_address = (unsigned long) conversion; + state->use_custom_base_address = 1; + + break; + + } + + case LD_OPTION_MAP: { + + state->output_map_filename = ""; + break; + + } + + case LD_OPTION_MAP_FILE: { + + state->output_map_filename = optarg; + break; + + } + + case LD_OPTION_OUTFILE: { + + if (state->output_filename) { + + report_at (program_name, 0, REPORT_ERROR, "multiple output files provided"); + exit (EXIT_FAILURE); + + } + + state->output_filename = optarg; + break; + + } + + default: { + + report_at (program_name, 0, REPORT_ERROR, "unsupported option '%s'", cmd_arg); + exit (EXIT_FAILURE); + + } + + } + +} + +unsigned long array_to_integer (unsigned char *arr, int size) { + + unsigned long value = 0; + int i; + + for (i = 0; i < size; i++) { + value |= arr[i] << (CHAR_BIT * i); + } + + return value; + +} + +void integer_to_array (unsigned long value, unsigned char *dest, int size) { + + int i; + for (i = 0; i < size; i++) { + dest[i] = (value >> (CHAR_BIT * i)) & UCHAR_MAX; } } @@ -166,7 +364,9 @@ void dynarray_add (void *ptab, long *nb_ptr, void *data) { void parse_args (int argc, char **argv, int optind) { + struct options_with_use *options_with_use; struct ld_option *popt; + const char *optarg, *r; if (argc <= optind) { @@ -176,15 +376,13 @@ void parse_args (int argc, char **argv, int optind) { } - state->subsystem = 3; - while (optind < argc) { r = argv[optind++]; if (r[0] != '-' || r[1] == '\0') { - dynarray_add (&state->files, &state->nb_files, xstrdup (r)); + dynarray_add (&state->input_files, &state->nb_input_files, xstrdup (r)); continue; } @@ -195,10 +393,7 @@ void parse_args (int argc, char **argv, int optind) { const char *r1 = r; if (!p1) { - - report_at (program_name, 0, REPORT_ERROR, "invalid option -- '%s'", r); - exit (EXIT_FAILURE); - + break; } if (!strstart (p1, &r1)) { @@ -230,155 +425,32 @@ void parse_args (int argc, char **argv, int optind) { } - switch (popt->idx) { + if (popt && popt->name) { - case LD_OPTION_ENTRY: { - - if (state->entry) { - free (state->entry); - } - - state->entry = xstrdup (optarg); - break; - - } - - case LD_OPTION_FORMAT: { - - if (xstrcasecmp (optarg, "binary") == 0) { - - state->format = LD_FORMAT_BIN; - break; - - } - - if (xstrcasecmp (optarg, "msdos") == 0) { - - state->format = LD_FORMAT_COM; - break; - - } - - if (xstrcasecmp (optarg, "elks-ia16") == 0) { - - state->format = LD_FORMAT_IA16_ELKS; - break; - - } - - if (xstrcasecmp (optarg, "elks-i386") == 0) { - - state->format = LD_FORMAT_I386_ELKS; - break; - - } - - if (xstrcasecmp (optarg, "a.out-i386") == 0) { - - state->format = LD_FORMAT_I386_AOUT; - break; - - } - - if (xstrcasecmp (optarg, "pe-i386") == 0) { - - state->format = LD_FORMAT_I386_PE; - break; - - } - - report_at (program_name, 0, REPORT_ERROR, "unrecognised output format '%s'", optarg); - exit (EXIT_FAILURE); - - } - - case LD_OPTION_HELP: { - - print_usage (); - exit (EXIT_SUCCESS); - - } - - case LD_OPTION_IMPURE: { - - state->impure = 1; - break; - - } - - case LD_OPTION_OUTFILE: { - - if (state->ofile) { - - report_at (program_name, 0, REPORT_ERROR, "multiple output files provided"); - exit (EXIT_FAILURE); - - } - - state->ofile = xstrdup (optarg); - break; - - } - - case LD_OPTION_PSP: { - - long conversion; - char *temp; - - errno = 0; - conversion = strtol (optarg, &temp, 0); - - if (!*optarg || isspace ((int) *optarg) || errno || *temp) { - - report_at (program_name, 0, REPORT_ERROR, "bad number for text start"); - exit (EXIT_FAILURE); - - } - - if (conversion < 0 || conversion > LONG_MAX) { - - report_at (program_name, 0, REPORT_ERROR, "text start must be between 0 and %u", ULONG_MAX); - exit (EXIT_FAILURE); - - } - - state->psp = (unsigned long) conversion; - break; - - } - - case LD_OPTION_SUBSYSTEM: { - - long conversion; - char *temp; - - errno = 0; - conversion = strtol (optarg, &temp, 0); - - if (!*optarg || isspace ((int) *optarg) || errno || *temp) { - - report_at (program_name, 0, REPORT_ERROR, "bad number for subsystem"); - exit (EXIT_FAILURE); - - } - - state->subsystem = (unsigned long) conversion; - break; - - } + use_option (r, popt->idx, optarg); + continue; + + } + + options_with_use = &fmt_tbl[state->format]; + + if (options_with_use->check_option && options_with_use->use_option) { + + int idx; - default: { + if ((idx = options_with_use->check_option (r, argc, argv, &optind, &optarg)) >= 0) { - report_at (program_name, 0, REPORT_ERROR, "unsupported option '%s'", r); - exit (EXIT_FAILURE); + options_with_use->use_option (r, idx, optarg); + continue; } } + + report_at (program_name, 0, REPORT_ERROR, "invalid option -- '%s'", r); + exit (EXIT_FAILURE); } - - if (!state->ofile) { state->ofile = "a.out"; } } diff --git a/lib.h b/lib.h index 1f82f98..ad1f860 100644 --- a/lib.h +++ b/lib.h @@ -4,6 +4,21 @@ #ifndef _LIB_H #define _LIB_H +#define ALIGN(a, b) (((a) / (b) + (((a) % (b)) ? 1 : 0)) * (b)) + +struct ld_option { + + const char *name; + int idx, flgs; + +}; + +#define LD_OPTION_NO_ARG 0 +#define LD_OPTION_HAS_ARG 1 + +unsigned long array_to_integer (unsigned char *arr, int size); +void integer_to_array (unsigned long value, unsigned char *dest, int size); + int xstrcasecmp (const char *__s1, const char *__s2); int strstart (const char *val, const char **str); diff --git a/link.c b/link.c new file mode 100644 index 0000000..2573f94 --- /dev/null +++ b/link.c @@ -0,0 +1,449 @@ +/****************************************************************************** + * @file link.c + *****************************************************************************/ +#include +#include + +#include "ld.h" +#include "lib.h" +#include "pe.h" +#include "reloc.h" +#include "report.h" +#include "section.h" +#include "symbol.h" + +struct reloc_howto reloc_howtos[RELOC_TYPE_END] = { + + { 0, 0, 0, 0, 0, "RELOC_TYPE_IGNORED", 0, 0 }, + + { 8, 0, 0, 0, 0, "RELOC_TYPE_64", 0, 0 }, + { 8, 1, 0, 0, 0, "RELOC_TYPE_PC64", 0, 0 }, + + { 4, 0, 0, 0, 0, "RELOC_TYPE_32", 0, 0 }, + { 4, 1, 0, 0, 0, "RELOC_TYPE_PC32", 0, 0 }, + + { 2, 0, 0, 0, 0, "RELOC_TYPE_16", 0, 0 }, + { 2, 1, 0, 0, 0, "RELOC_TYPE_PC16", 0, 0 }, + + { 1, 0, 0, 0, 0, "RELOC_TYPE_8", 0, 0 }, + { 1, 1, 0, 0, 0, "RELOC_TYPE_PC8", 0, 0 } + +}; + +static void check_unresolved (void) { + + struct object_file *of; + struct symbol *symbol; + + unsigned long i; + unsigned long unresolved = 0; + + for (of = all_object_files; of; of = of->next) { + + for (i = 0; i < of->symbol_cnt; i++) { + + symbol = of->symbol_arr + i; + + if (symbol->auxiliary || !symbol_is_undefined (symbol) || (symbol->flags & SYMBOL_FLAG_SECTION_SYMBOL) || !symbol->name) { + continue; + } + + if (!(symbol = symbol_find (symbol->name))) { + + symbol = of->symbol_arr + i; + report_at (program_name, 0, REPORT_INTERNAL_ERROR, "external symbol '%s' not found in hashtab", symbol->name); + + } + + if (symbol_is_undefined (symbol)) { + + report_at (program_name, 0, REPORT_ERROR, "%s: unresolved external symbol '%s'", of->filename, symbol->name); + unresolved++; + + } + + } + + } + + if (unresolved) { + report_at (program_name, 0, REPORT_FATAL_ERROR, "%lu unresolved external%s", unresolved, (unresolved > 1 ? "s" : "")); + } + +} + +static void collapse_subsections (void) { + + struct section *section; + struct subsection *subsection; + + for (section = all_sections; section; section = section->next) { + + for (subsection = section->all_subsections; subsection; subsection = subsection->next) { + + if (subsection->first_part) { + + *section->last_part_p = subsection->first_part; + section->last_part_p = subsection->last_part_p; + + } + + } + + } + +} + +static void calculate_section_sizes_and_rvas (void) { + + struct section *section; + struct section_part *part; + + unsigned long rva = 0; + + if (state->format == LD_FORMAT_I386_PE) { + rva = pe_get_first_section_rva (); + } + + for (section = all_sections; section; section = section->next) { + + rva = ALIGN (rva, section->section_alignment); + + section->rva = rva; + section->total_size = 0; + + for (part = section->first_part; part; part = part->next) { + + if (part->next && part->next->alignment > 1) { + + unsigned long new_rva = ALIGN (rva + part->content_size, part->next->alignment); + + if (new_rva != rva + part->content_size) { + + part->content = xrealloc (part->content, new_rva - rva); + memset (part->content + part->content_size, 0, new_rva - rva - part->content_size); + + part->content_size = new_rva - rva; + + } + + } + + part->rva = rva; + + section->total_size += part->content_size; + rva += part->content_size; + + } + + } + +} + +static void reloc_generic (struct section_part *part, struct reloc_entry *rel, struct symbol *symbol) { + + unsigned long result = 0; + + switch (rel->howto->size) { + + case 8: { + + result = array_to_integer (part->content + rel->offset, 8); + break; + + } + + case 4: { + + result = array_to_integer (part->content + rel->offset, 4); + break; + + } + + case 3: { + + result = array_to_integer (part->content + rel->offset, 3); + break; + + } + + case 2: { + + result = array_to_integer (part->content + rel->offset, 2); + break; + + } + + case 1: { + + result = array_to_integer (part->content + rel->offset, 1); + break; + + } + + default: { + + report_at (program_name, 0, REPORT_INTERNAL_ERROR, "invalid relocation size"); + break; + + } + + } + + result += rel->addend; + + if (rel->howto->pc_rel || rel->howto->no_base) { + + result += symbol_get_value_no_base (symbol); + + if (rel->howto->pc_rel) { + + result -= (part->rva + rel->offset); + result -= rel->howto->size; + + } + + } else { + result += symbol_get_value_with_base (symbol); + } + + if ((unsigned long) rel->howto->size < sizeof (result)) { + + unsigned long mask = (((unsigned long) 1) << (CHAR_BIT * rel->howto->size)) - 1; + result &= mask; + + } + + result >>= rel->howto->final_right_shift; + + switch (rel->howto->size) { + + case 8: { + + integer_to_array (result, part->content + rel->offset, 8); + break; + + } + + case 4: { + + integer_to_array (result, part->content + rel->offset, 4); + break; + + } + + case 3: { + + integer_to_array (result, part->content + rel->offset, 3); + break; + + } + + case 2: { + + integer_to_array (result, part->content + rel->offset, 2); + break; + + } + + case 1: { + + integer_to_array (result, part->content + rel->offset, 1); + break; + + } + + default: { + + report_at (program_name, 0, REPORT_INTERNAL_ERROR, "invalid relocation size"); + break; + + } + + } + +} + +static void relocate_part (struct section_part *part) { + + struct reloc_entry *relocs = part->reloc_arr; + + struct symbol *symbol; + unsigned long i; + + for (i = 0; i < part->reloc_cnt; i++) { + + if (relocs[i].howto->size == 0) { + continue; + } + + symbol = relocs[i].symbol; + + if (symbol_is_undefined (symbol)) { + + if (!(symbol = symbol_find (symbol->name))) { + + symbol = relocs[i].symbol; + report_at (program_name, 0, REPORT_INTERNAL_ERROR, "external symbol '%s' not found in hashtab", symbol->name); + + } + + if (symbol_is_undefined (symbol)) { + + report_at (program_name, 0, REPORT_ERROR, "%s:(%s+%#lu): undefined reference to '%s'", part->of->filename, part->section->name, relocs[i].offset, symbol->name); + continue; + + } + + } + + if (relocs[i].howto->special_function) { + + (*relocs[i].howto->special_function) (part, &relocs[i], symbol); + continue; + + } + + reloc_generic (part, &relocs[i], symbol); + + } + +} + +static void relocate_sections (void) { + + struct section *section; + struct section_part *part; + + for (section = all_sections; section; section = section->next) { + + for (part = section->first_part; part; part = part->next) { + relocate_part (part); + } + + } + +} + +static void calculate_entry_point (void) { + + struct symbol *symbol; + struct section *section; + + if (state->entry_symbol_name) { + + if (state->entry_symbol_name[0] == '\0') { + + state->entry_point -= state->base_address; + return; + + } + + if ((symbol = symbol_find (state->entry_symbol_name)) && !symbol_is_undefined (symbol)) { + + state->entry_point = symbol_get_value_no_base (symbol); + return; + + } + + } + + if (!state->entry_symbol_name) { + + if (state->format == LD_FORMAT_I386_AOUT || state->format == LD_FORMAT_I386_ELKS || state->format == LD_FORMAT_IA16_ELKS) { + + state->entry_symbol_name = xstrdup ("_start"); + + if ((symbol = symbol_find (state->entry_symbol_name))) { + + state->entry_point = symbol_get_value_no_base (symbol); + return; + + } + + } else if (state->format == LD_FORMAT_I386_PE) { + + state->entry_symbol_name = xstrdup ("_mainCRTStartup"); + + if ((symbol = symbol_find (state->entry_symbol_name))) { + + state->entry_point = symbol_get_value_no_base (symbol); + return; + + } + + } + + } + + if ((section = section_find (".text"))) { + state->entry_point = section->rva; + } + + if (state->entry_symbol_name) { + report_at (program_name, 0, REPORT_WARNING, "cannot find entry symbol '%s'; defaulting to 0x%08lx", state->entry_symbol_name, state->base_address + state->entry_point); + } + +} + +void link (void) { + + struct section *section; + unsigned long value = 0; + + struct object_file *of; + struct symbol *symbol; + + collapse_subsections (); + calculate_section_sizes_and_rvas (); + + if (!(symbol = symbol_find ("__edata")) || symbol_is_undefined (symbol)) { + + if ((section = section_find (".text"))) { + + value += (/*section->rva + */section->total_size); + /*value -= state->size_of_headers;*/ + + } + + of = object_file_make (FAKE_LD_FILENAME, 1); + + symbol = of->symbol_arr; + symbol->name = xstrdup ("__edata"); + + symbol->section_number = ABSOLUTE_SECTION_NUMBER; + symbol->value = value / 16; + + symbol_record_external_symbol (symbol); + value %= 16; + + } + + if (!(symbol = symbol_find ("__end")) || symbol_is_undefined (symbol)) { + + if ((section = section_find (".data"))) { + value += section->total_size; + } + + if ((section = section_find (".bss"))) { + value += section->total_size; + } + + of = object_file_make (FAKE_LD_FILENAME, 1); + + symbol = of->symbol_arr; + symbol->name = xstrdup ("__end"); + + symbol->section_number = ABSOLUTE_SECTION_NUMBER; + symbol->value = value % 16; + + symbol_record_external_symbol (symbol); + + } + + check_unresolved (); + + relocate_sections (); + calculate_entry_point (); + +} diff --git a/map.c b/map.c new file mode 100644 index 0000000..f3ca848 --- /dev/null +++ b/map.c @@ -0,0 +1,128 @@ +/****************************************************************************** + * @file map.c + *****************************************************************************/ +#include +#include +#include + +#include "ld.h" +#include "report.h" +#include "section.h" +#include "symbol.h" + +static int symbol_compare (const void *a, const void *b) { + + const struct symbol *sa = a, *sb = b; + + if (sa->auxiliary && sb->auxiliary) { + return 0; + } + + if (sa->auxiliary) { + return 1; + } + + if (sb->auxiliary) { + return -1; + } + + if (symbol_get_value_with_base (sa) < symbol_get_value_with_base (sb)) { + return -1; + } + + if (symbol_get_value_with_base (sa) > symbol_get_value_with_base (sb)) { + return 1; + } + + return 0; + +} + +static void sort_symbols (void) { + + struct object_file *of; + + for (of = all_object_files; of; of = of->next) { + qsort (of->symbol_arr, of->symbol_cnt, sizeof (*of->symbol_arr), &symbol_compare); + } + +} + +void map_write (const char *filename) { + + FILE *fp; + + struct symbol *symbol; + struct section *section; + struct section_part *part; + + int printed_chars; + + if (strcmp (filename, "") == 0) { + fp = stdout; + } else if (!(fp = fopen (filename, "wb"))) { + + report_at (program_name, 0, REPORT_ERROR, "cannot open '%s' for writing", filename); + return; + + } + + sort_symbols (); + + for (section = all_sections; section; section = section->next) { + + if ((printed_chars = fprintf (fp, "%-16s", section->name)) > 16) { + fprintf (fp, "\n%-16s", ""); + } + + fprintf (fp, "0x%08lx %12lu\n\n", state->base_address + section->rva, section->total_size); + + for (part = section->first_part; part; part = part->next) { + + if ((printed_chars = fprintf (fp, " %-12s", section->name)) > 16) { + fprintf (fp, "\n%-16s", ""); + } + + fprintf (fp, "0x%08lx %12lu %s\n", state->base_address + part->rva, part->content_size, part->of->filename); + + for (symbol = part->of->symbol_arr; symbol < part->of->symbol_arr + part->of->symbol_cnt; symbol++) { + + if (symbol->auxiliary || symbol->part != part) { + continue; + } + + if (symbol->value == 0 || strncmp (section->name, symbol->name, strlen (section->name)) == 0) { + continue; + } + + fprintf (fp, "%-16s %12lu %10s %s\n", "", symbol_get_value_with_base (symbol), "", symbol->name); + + } + + } + + fprintf (fp, "\n"); + + } + + fprintf (fp, "\n"); + + { + + struct section *last_section = 0; + + for (section = all_sections; section; section = section->next) { + last_section = section; + } + + fprintf (fp, "Sizeof Module: %lu bytes\n", last_section ? (last_section->rva + last_section->total_size) : 0); + + } + + fprintf (fp, "Entry Point: 0x%08lx\n", state->base_address + state->entry_point); + + if (strcmp (filename, "") != 0) { + fclose (fp); + } + +} diff --git a/pe.c b/pe.c new file mode 100644 index 0000000..dee77cc --- /dev/null +++ b/pe.c @@ -0,0 +1,872 @@ +/****************************************************************************** + * @file pe.c + *****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ld.h" +#include "lib.h" +#include "pe.h" +#include "report.h" +#include "section.h" +#include "write7x.h" + +static unsigned short subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI; + +static unsigned long section_alignment = DEFAULT_SECTION_ALIGNMENT; +static unsigned long file_alignment = DEFAULT_FILE_ALIGNMENT; + +struct exclude_symbol { + + char *name; + struct exclude_symbol *next; + +}; + +static struct exclude_symbol *exclude_symbols = 0; + +#define LD_OPTION_IGNORED 0 +#define LD_OPTION_FILE_ALIGNMENT 1 +#define LD_OPTION_SECTION_ALIGNMENT 2 +#define LD_OPTION_STACK 3 +#define LD_OPTION_SUBSYSTEM 4 + +static struct ld_option opts[] = { + + { "--file-alignment", LD_OPTION_FILE_ALIGNMENT, LD_OPTION_HAS_ARG }, + { "--section-alignment", LD_OPTION_SECTION_ALIGNMENT, LD_OPTION_HAS_ARG }, + { "--subsystem", LD_OPTION_SUBSYSTEM, LD_OPTION_HAS_ARG }, + + + { 0, 0, 0 } + +}; + +int pe_check_option (const char *cmd_arg, int argc, char **argv, int *optind, const char **optarg) { + + struct ld_option *popt; + + for (popt = opts; ; popt++) { + + const char *p1 = popt->name; + const char *r1 = cmd_arg; + + if (!p1) { + break; + } + + if (!strstart (p1, &r1)) { + continue; + } + + (*optarg) = r1; + + if (popt->flgs & LD_OPTION_HAS_ARG) { + + if (*r1 == '\0') { + + if ((*optind) >= argc) { + + report_at (program_name, 0, REPORT_ERROR, "argument to '%s' is missing", cmd_arg); + exit (EXIT_FAILURE); + + } + + (*optarg) = argv[(*optind)++]; + + } + + } else if (*r1 != '\0') { + continue; + } + + return popt->idx; + + } + + return -1; + +} + +void pe_use_option (const char *cmd_arg, int idx, const char *optarg) { + + switch (idx) { + + case LD_OPTION_IGNORED: { + + break; + + } + + case LD_OPTION_FILE_ALIGNMENT: { + + long conversion; + char *temp; + + errno = 0; + conversion = strtol (optarg, &temp, 0); + + if (!*optarg || isspace ((int) *optarg) || errno || *temp) { + + report_at (program_name, 0, REPORT_ERROR, "invalid file alignment number '%s'", optarg); + exit (EXIT_FAILURE); + + } + + file_alignment = (unsigned long) conversion; + + if (file_alignment < 512 || file_alignment > 0x10000 || (file_alignment & (file_alignment - 1))) { + report_at (program_name, 0, REPORT_WARNING, "file alignment should be a power of two between 512 and 64 KiB (0x10000) inclusive according to the specification"); + } + + break; + + } + + case LD_OPTION_SECTION_ALIGNMENT: { + + long conversion; + char *temp; + + errno = 0; + conversion = strtol (optarg, &temp, 0); + + if (!*optarg || isspace ((int) *optarg) || errno || *temp) { + + report_at (program_name, 0, REPORT_ERROR, "invalid section alignment number '%s'", optarg); + exit (EXIT_FAILURE); + + } + + section_alignment = (unsigned long) conversion; + + if (section_alignment < file_alignment) { + report_at (program_name, 0, REPORT_WARNING, "section alignment must be greater than or equal to file alignment according to the specification"); + } + + break; + + } + + case LD_OPTION_SUBSYSTEM: { + + long conversion; + char *temp; + + errno = 0; + conversion = strtol (optarg, &temp, 0); + + if (!*optarg || isspace ((int) *optarg) || errno || *temp) { + + report_at (program_name, 0, REPORT_ERROR, "invalid subsystem type '%s'", optarg); + exit (EXIT_FAILURE); + + } + + subsystem = (unsigned short) conversion; + break; + + } + + default: { + + report_at (program_name, 0, REPORT_ERROR, "unsupported option '%s'", cmd_arg); + exit (EXIT_FAILURE); + + } + + } + +} + + +#define NUMBER_OF_DATA_DIRECTORIES 16 + +unsigned char dos_stub[] = { + + 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, + 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x54, 0x68, + + 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, + 0x61, 0x6D, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, + + 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, + 0x20, 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, + + 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + +}; + +static int generate_reloc_section = 1; +static int can_be_relocated = 0; + +static struct section_part *iat_first_part = 0, *iat_last_part = 0; + +static int check_reloc_section_needed_section_part (struct section_part *part) { + + struct reloc_howto *reloc_howto; + unsigned long i; + + for (i = 0; i < part->reloc_cnt; i++) { + + reloc_howto = part->reloc_arr[i].howto; + + if (reloc_howto == &reloc_howtos[RELOC_TYPE_64] || reloc_howto == &reloc_howtos[RELOC_TYPE_32]) { + return 1; + } + + } + + return 0; + +} + +static int check_reloc_section_needed (void) { + + struct section *section; + struct section_part *part; + struct subsection *subsection; + + for (section = all_sections; section; section = section->next) { + + for (part = section->first_part; part; part = part->next) { + + if (check_reloc_section_needed_section_part (part)) { + return 1; + } + + } + + for (subsection = section->all_subsections; subsection; subsection = subsection->next) { + + for (part = subsection->first_part; part; part = part->next) { + + if (check_reloc_section_needed_section_part (part)) { + return 1; + } + + } + + } + + } + + return 0; + +} + +static int translate_characteristics_to_section_flags (unsigned long characteristics) { + + int flags = 0; + + if (!(characteristics & IMAGE_SCN_MEM_WRITE)) { + flags |= SECTION_FLAG_READONLY; + } + + if (characteristics & (IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE)) { + flags |= SECTION_FLAG_CODE; + } + + if (characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) { + flags |= SECTION_FLAG_DATA; + } + + if (characteristics & IMAGE_SCN_TYPE_NOLOAD) { + flags |= SECTION_FLAG_NEVER_LOAD; + } + + if (characteristics & IMAGE_SCN_LNK_INFO) { + flags |= SECTION_FLAG_DEBUGGING; + } + + if (characteristics & IMAGE_SCN_LNK_REMOVE) { + flags |= SECTION_FLAG_EXCLUDE; + } + + if (!(characteristics & IMAGE_SCN_MEM_READ)) { + flags |= SECTION_FLAG_NOREAD; + } + + if (characteristics & IMAGE_SCN_MEM_SHARED) { + flags |= SECTION_FLAG_SHARED; + } + + if (characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) { + flags |= SECTION_FLAG_ALLOC; + } + + return flags; + +} + +static void exclude_symbols_free (void) { + + struct exclude_symbol *exclude_symbol; + + for (exclude_symbol = exclude_symbols; exclude_symbol; exclude_symbol = exclude_symbols) { + + exclude_symbols = exclude_symbol->next; + + free (exclude_symbol->name); + free (exclude_symbol); + + } + +} + +#define FLOOR_TO(a, b) ((a) / (b) * (b)) + +static void generate_base_relocation_block (struct section *reloc_section, struct pe_base_relocation *ibr_hdr_p, unsigned long num_relocs, struct section *saved_section, struct section_part *saved_part, unsigned long saved_i) { + + struct section_part *reloc_part; + struct reloc_entry *relocs; + + unsigned char *write_pos; + unsigned long i; + + struct section *section; + struct section_part *part; + + integer_to_array (ALIGN (sizeof (*ibr_hdr_p) + num_relocs * 2, 4), ibr_hdr_p->SizeOfBlock, 4); + + reloc_part = section_part_new (reloc_section, object_file_make (FAKE_LD_FILENAME, 0)); + reloc_part->content_size = array_to_integer (ibr_hdr_p->SizeOfBlock, 4); + + reloc_part->content = xmalloc (reloc_part->content_size); + reloc_part->content[reloc_part->content_size - 2] = reloc_part->content[reloc_part->content_size - 1] = 0; + + memcpy (reloc_part->content, ibr_hdr_p, sizeof (*ibr_hdr_p)); + write_pos = reloc_part->content + sizeof (*ibr_hdr_p); + + for (section = saved_section; section; section = section->next) { + + for (part = ((section = saved_section) ? saved_part : section->first_part); part; part = part->next) { + + relocs = part->reloc_arr; + + for (i = ((part == saved_part) ? saved_i : 0); i < part->reloc_cnt; i++) { + + unsigned short base_relocation_type, rel_word; + + if (relocs[i].howto == &reloc_howtos[RELOC_TYPE_64]) { + base_relocation_type = IMAGE_REL_BASED_DIR64; + } else if (relocs[i].howto == &reloc_howtos[RELOC_TYPE_32]) { + base_relocation_type = IMAGE_REL_BASED_HIGHLOW; + } else { + continue; + } + + rel_word = (part->rva + relocs[i].offset - array_to_integer (ibr_hdr_p->RVAOfBlock, 4)) & 0xfff; + rel_word |= base_relocation_type << 12; + + integer_to_array (rel_word, write_pos, 2); + write_pos += 2; + + if (!--num_relocs) { + goto _finish; + } + + } + + } + + } + + report_at (program_name, 0, REPORT_INTERNAL_ERROR, "num_relocs mismatch while generating .reloc section"); + +_finish: + + section_append_section_part (reloc_section, reloc_part); + reloc_section->total_size += reloc_part->content_size; + +} + +static struct section *last_section = 0; + +static unsigned long base_of_code = 0; +static unsigned long base_of_data = 0; + +static unsigned long size_of_code = 0; +static unsigned long size_of_initialized_data = 0; +static unsigned long size_of_uninitialized_data = 0; + +static unsigned short translate_section_flags_to_characteristics (int flags) { + + unsigned short characteristics = 0; + + if (!(flags & SECTION_FLAG_READONLY)) { + characteristics |= IMAGE_SCN_MEM_WRITE; + } + + if (flags & SECTION_FLAG_CODE) { + characteristics |= IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE; + } + + if (flags & SECTION_FLAG_DATA) { + characteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA; + } + + if (flags & SECTION_FLAG_NEVER_LOAD) { + characteristics |= IMAGE_SCN_TYPE_NOLOAD; + } + + if (flags & SECTION_FLAG_DEBUGGING) { + characteristics |= IMAGE_SCN_LNK_INFO; + } + + if (flags & SECTION_FLAG_EXCLUDE) { + characteristics |= IMAGE_SCN_LNK_REMOVE; + } + + if (!(flags & SECTION_FLAG_NOREAD)) { + characteristics |= IMAGE_SCN_MEM_READ; + } + + if (flags & SECTION_FLAG_SHARED) { + characteristics |= IMAGE_SCN_MEM_SHARED; + } + + if ((flags & SECTION_FLAG_ALLOC) && !(flags & SECTION_FLAG_LOAD)) { + characteristics |= IMAGE_SCN_CNT_UNINITIALIZED_DATA; + } + + return characteristics; + +} + +static void write_sections (unsigned char *data) { + + unsigned char *pos = data + state->size_of_headers; + unsigned short characteristics = 0; + + struct pe_section_table_entry *hdr; + struct section *section; + + for (section = all_sections; section; section = section->next) { + + section->object_dependent_data = (hdr = xmalloc (sizeof (*hdr))); + + memset (hdr->Name, 0, sizeof (hdr->Name)); + memcpy (hdr->Name, section->name, (strlen (section->name) >= sizeof (hdr->Name)) ? sizeof (hdr->Name) : strlen (section->name)); + + write741_to_byte_array (hdr->VirtualSize, section->total_size); + write741_to_byte_array (hdr->VirtualAddress, section->rva); + + if (!section->is_bss) { + + write741_to_byte_array (hdr->SizeOfRawData, ALIGN (section->total_size, file_alignment)); + write741_to_byte_array (hdr->PointerToRawData, pos - data); + + section_write (section, pos); + pos += ALIGN (section->total_size, file_alignment); + + } + + characteristics = translate_section_flags_to_characteristics (section->flags); + write721_to_byte_array (hdr->Characteristics, characteristics); + + if (characteristics & IMAGE_SCN_CNT_CODE) { + + if (!base_of_code) { + base_of_code = array_to_integer (hdr->VirtualAddress, 4); + } + + size_of_code += array_to_integer (hdr->VirtualSize, 4); + + } else if (characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) { + + if (!base_of_data) { + base_of_data = array_to_integer (hdr->VirtualAddress, 4); + } + + size_of_initialized_data += array_to_integer (hdr->VirtualSize, 4); + + } else if (characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) { + size_of_uninitialized_data += array_to_integer (hdr->VirtualSize, 4); + } + + last_section = section; + + } + +} + +static unsigned long bytearray_read_4_bytes (unsigned char *src) { + + unsigned long value = 0; + int i; + + for (i = 0; i < 4; i++) { + value |= (unsigned long) src[i] << (CHAR_BIT * i); + } + + return value; + +} + +static unsigned long calculate_checksum (unsigned char *base, unsigned long chksum_off, unsigned long size) { + + unsigned long checksum = 0, data, i; + int carry; + + for (i = 0; i < size / 4; i++) { + + if (i == chksum_off / 4) { + continue; + } + + data = bytearray_read_4_bytes (base + i * 4); + + carry = checksum > 0xFFFFFFFFLU - data; + + checksum += data; + checksum += carry; + + checksum &= 0xFFFFFFFFLU; + + } + + checksum = (checksum >> 16) + (checksum & 0xFFFF); + + checksum += checksum >> 16; + checksum &= 0xFFFF; + checksum += size; + + return checksum & 0xFFFFFFFFLU; + +} + +void pe_after_link (void) { + + unsigned long num_relocs = 0, saved_i = 0, i; + + struct section *saved_section = 0, *section, *reloc_section; + struct section_part *saved_part = 0, *part; + + struct pe_base_relocation ibr_hdr; + struct reloc_entry *relocs; + + if (!generate_reloc_section) { + return; + } + + if (!(reloc_section = section_find (".reloc"))) { + report_at (program_name, 0, REPORT_INTERNAL_ERROR, ".reloc section count not be found"); + } + + integer_to_array (0, ibr_hdr.RVAOfBlock, 4); + + for (section = all_sections; section; section = section->next) { + + for (part = section->first_part; part; part = part->next) { + + relocs = part->reloc_arr; + + for (i = 0; i < part->reloc_cnt; i++) { + + if (relocs[i].howto != &reloc_howtos[RELOC_TYPE_64] && relocs[i].howto != &reloc_howtos[RELOC_TYPE_32]) { + continue; + } + + if (num_relocs && (part->rva + relocs[i].offset >= array_to_integer (ibr_hdr.RVAOfBlock, 4) + BASE_RELOCATION_PAGE_SIZE || part->rva + relocs[i].offset < array_to_integer (ibr_hdr.RVAOfBlock, 4))) { + + generate_base_relocation_block (reloc_section, &ibr_hdr, num_relocs, saved_section, saved_part, saved_i); + num_relocs = 0; + + } + + if (num_relocs == 0) { + + integer_to_array (FLOOR_TO (part->rva + relocs[i].offset, BASE_RELOCATION_PAGE_SIZE), ibr_hdr.RVAOfBlock, 4); + + saved_section = section; + saved_part = part; + + saved_i = i; + + } + + num_relocs++; + + } + + } + + } + + if (num_relocs) { + generate_base_relocation_block (reloc_section, &ibr_hdr, num_relocs, saved_section, saved_part, saved_i); + } + +} + +void pe_before_link (void) { + + struct section *section; + struct section_part *part; + struct subsection *subsection; + + if (state->base_address % 0x10000) { + report_at (program_name, 0, REPORT_WARNING, "base address must be a multiple of 64 KiB (0x10000) according to the specification"); + } + + exclude_symbols_free (); + + if (!check_reloc_section_needed ()) { + + can_be_relocated = 1; + generate_reloc_section = 0; + + } + + if (generate_reloc_section) { + + can_be_relocated = 1; + + section = section_find_or_make (".reloc"); + section->flags = translate_characteristics_to_section_flags (IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE); + + } + + for (section = all_sections; section; section = section->next) { + + if (section->section_alignment < section_alignment) { + section->section_alignment = section_alignment; + } + + } + + state->size_of_headers = sizeof (struct msdos_header) + sizeof (dos_stub) + sizeof (struct pe_header) + sizeof (struct pe_optional_header); + + state->size_of_headers += NUMBER_OF_DATA_DIRECTORIES * sizeof (struct pe_image_data_directory); + state->size_of_headers += sizeof (struct pe_section_table_entry) * section_count (); + + state->size_of_headers = ALIGN (state->size_of_headers, file_alignment); + + if (!(section = section_find (".idata"))) { + return; + } + + if (!(subsection = subsection_find (section, "2"))) { + return; + } + + part = section_part_new (section, object_file_make (FAKE_LD_FILENAME, 0)); + + part->content_size = sizeof (struct pe_import_directory_table); + part->content = xmalloc (part->content_size); + + subsection_append_section_part (subsection, part); + + if (!(subsection = subsection_find (section, "5"))) { + return; + } + + iat_first_part = subsection->first_part; + + if (!iat_first_part) { + return; + } + + for (part = iat_first_part; ; part = part->next) { + + if (!part->next) { + break; + } + + } + + iat_last_part = part; + +} + +void pe_write (const char *filename) { + + FILE *fp; + + unsigned long data_size = 0, checksum_pos = 0; + unsigned char *data, *pos; + + struct msdos_header *doshdr; + struct pe_header *pehdr; + struct pe_optional_header *opthdr; + + struct section *section; + + if (!(fp = fopen (filename, "wb"))) { + + report_at (program_name, 0, REPORT_ERROR, "failed to open '%s' for writing", filename); + return; + + } + + for (section = all_sections; section; section = section->next) { + + if (!section->is_bss) { + data_size += ALIGN (section->total_size, file_alignment); + } + + } + + data_size += state->size_of_headers; + + data = xmalloc (data_size); + write_sections (data); + + doshdr = (struct msdos_header *) data; + pehdr = (struct pe_header *) ((char *) doshdr + sizeof (*doshdr) + sizeof (dos_stub)); + + opthdr = (struct pe_optional_header *) ((char *) pehdr + sizeof (*pehdr)); + pos = (unsigned char *) opthdr + sizeof (*opthdr); + + doshdr->e_magic[0] = 'M'; + doshdr->e_magic[1] = 'Z'; + + write721_to_byte_array (doshdr->e_cblp, 0x0090); + write721_to_byte_array (doshdr->e_cp, 0x0003); + + write721_to_byte_array (doshdr->e_cparhdr, ALIGN (sizeof (*doshdr), 16) / 16); + + write721_to_byte_array (doshdr->e_maxalloc, 0xFFFF); + write721_to_byte_array (doshdr->e_sp, 0x00B8); + + write721_to_byte_array (doshdr->e_lfarlc, sizeof (*doshdr)); + write741_to_byte_array (doshdr->e_lfanew, sizeof (*doshdr) + sizeof (dos_stub)); + + memcpy ((char *) data + array_to_integer (doshdr->e_lfarlc, 2), dos_stub, sizeof (dos_stub)); + + + pehdr->Signature[0] = 'P'; + pehdr->Signature[1] = 'E'; + + write721_to_byte_array (pehdr->Machine, IMAGE_FILE_MACHINE_I386); + write721_to_byte_array (pehdr->NumberOfSections, section_count ()); + write741_to_byte_array (pehdr->TimeDateStamp, time (0)); + write721_to_byte_array (pehdr->SizeOfOptionalHeader, sizeof (*opthdr)); + + { + + unsigned short characteristics = 0; + + characteristics |= IMAGE_FILE_EXECUTABLE_IMAGE; + /*characteristics |= IMAGE_FILE_LINE_NUMS_STRIPPED;*/ + /*characteristics |= IMAGE_FILE_LOCAL_SYMS_STRIPPED;*/ + characteristics |= IMAGE_FILE_32BIT_MACHINE; + characteristics |= IMAGE_FILE_DEBUG_STRIPPED; + + if (!can_be_relocated) { + characteristics |= IMAGE_FILE_RELOCS_STRIPPED; + } + + write721_to_byte_array (pehdr->Characteristics, characteristics); + + } + + + write721_to_byte_array (opthdr->Magic, IMAGE_FILE_MAGIC_I386); + + opthdr->MajorLinkerVersion = 0; + opthdr->MinorLinkerVersion = 10; + + write741_to_byte_array (opthdr->SizeOfCode, ALIGN (size_of_code, file_alignment)); + write741_to_byte_array (opthdr->SizeOfInitializedData, ALIGN (size_of_initialized_data, file_alignment)); + write741_to_byte_array (opthdr->SizeOfUninitializedData, ALIGN (size_of_uninitialized_data, file_alignment)); + + write741_to_byte_array (opthdr->AddressOfEntryPoint, state->entry_point); + + write741_to_byte_array (opthdr->BaseOfCode, base_of_code); + write741_to_byte_array (opthdr->BaseOfData, base_of_data); + + write741_to_byte_array (opthdr->ImageBase, state->base_address); + + write741_to_byte_array (opthdr->SectionAlignment, section_alignment); + write741_to_byte_array (opthdr->FileAlignment, file_alignment); + + write721_to_byte_array (opthdr->MajorOperatingSystemVersion, 4); + write721_to_byte_array (opthdr->MajorImageVersion, 1); + write721_to_byte_array (opthdr->MajorSubsystemVersion, 4); + + if (last_section) { + write741_to_byte_array (opthdr->SizeOfImage, ALIGN (last_section->rva + last_section->total_size, section_alignment)); + } else { + write741_to_byte_array (opthdr->SizeOfImage, state->size_of_headers); + } + + write741_to_byte_array (opthdr->SizeOfHeaders, state->size_of_headers); + write721_to_byte_array (opthdr->Subsystem, subsystem); + + { + + struct section *bss_section; + + if (!(bss_section = section_find (".bss"))) { + + write741_to_byte_array (opthdr->SizeOfStackReserved, section_alignment << 9); + write741_to_byte_array (opthdr->SizeOfStackCommit, 0x1000); + write741_to_byte_array (opthdr->SizeOfHeapReserved, section_alignment << 8); + write741_to_byte_array (opthdr->SizeOfHeapCommit, 0x1000); + + } else { + + unsigned long ibss_addr = bss_section->rva; + unsigned long ibss_size = bss_section->total_size; + + unsigned long stack_addr = ibss_addr + ibss_size; + unsigned long stack_size = ALIGN (stack_addr, section_alignment); + + write741_to_byte_array (opthdr->SizeOfStackReserved, section_alignment << 9); + write741_to_byte_array (opthdr->SizeOfStackCommit, ALIGN (stack_addr % 16 + stack_size, section_alignment)); + write741_to_byte_array (opthdr->SizeOfHeapReserved, section_alignment << 8); + write741_to_byte_array (opthdr->SizeOfHeapCommit, ALIGN (stack_addr % 16 + stack_size, section_alignment)); + + } + + } + + write741_to_byte_array (opthdr->NumberOfRvaAndSizes, NUMBER_OF_DATA_DIRECTORIES); + + + for (section = all_sections; section; section = section->next) { + + struct pe_section_table_entry *hdr = section->object_dependent_data; + + memcpy (pos, hdr, sizeof (*hdr)); + pos += sizeof (*hdr); + + free (hdr); + + } + + + checksum_pos = sizeof (*doshdr) + sizeof (dos_stub) + sizeof (*pehdr) + offsetof (struct pe_optional_header, Checksum); + write741_to_byte_array (opthdr->Checksum, calculate_checksum (data, checksum_pos, data_size)); + + 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); + +} + +void pe_print_help (void) { + + fprintf (stderr, "i386pe:\n\n"); + + fprintf (stderr, " --file-alignment Set file alignment.\n"); + fprintf (stderr, " --section-alignment Set section alignment.\n"); + fprintf (stderr, " --subsystem Set required OS subsystem.\n"); + +} + +unsigned long pe_get_first_section_rva (void) { + return ALIGN (state->size_of_headers, section_alignment); +} diff --git a/pe.h b/pe.h index 59a7724..88723fc 100644 --- a/pe.h +++ b/pe.h @@ -4,6 +4,9 @@ #ifndef _PE_H #define _PE_H +#define DEFAULT_SECTION_ALIGNMENT 0x1000 +#define DEFAULT_FILE_ALIGNMENT 0x0200 + struct msdos_header { unsigned char e_magic[2]; /* Magic number */ @@ -28,6 +31,19 @@ struct msdos_header { }; +#define IMAGE_SUBSYSTEM_UNKNOWN 0 +#define IMAGE_SUBSYSTEM_NATIVE 1 +#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 +#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 +#define IMAGE_SUBSYSTEM_OS2_CUI 5 +#define IMAGE_SUBSYSTEM_POSIX_CUI 7 +#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 +#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 +#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 +#define IMAGE_SUBSYSTEM_EFI_ROM 13 +#define IMAGE_SUBSYSTEM_XBOX 14 +#define IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPILCATION 16 + struct pe_header { unsigned char Signature[4]; @@ -105,7 +121,7 @@ struct pe_optional_header { #define IMAGE_FILE_MAGIC_I386 0x010B -struct section_table_entry { +struct pe_section_table_entry { char Name[8]; @@ -124,54 +140,67 @@ struct section_table_entry { }; +#define IMAGE_SCN_TYPE_NOLOAD 0x00000002 #define IMAGE_SCN_CNT_CODE 0x00000020 #define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 #define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 +#define IMAGE_SCN_LNK_INFO 0x00000200 +#define IMAGE_SCN_LNK_REMOVE 0x00000800 #define IMAGE_SCN_ALIGN_4BYTES 0x00300000 +#define IMAGE_SCN_MEM_SHARED 0x10000000 #define IMAGE_SCN_MEM_EXECUTE 0x20000000 #define IMAGE_SCN_MEM_READ 0x40000000 #define IMAGE_SCN_MEM_WRITE 0x80000000 -struct relocation_entry { +struct pe_image_data_directory { - unsigned int VirtualAddress; - unsigned int SymbolTableIndex; - - unsigned short Type; + unsigned char VirtualAddress[4]; + unsigned char Size[4]; }; -#define RELOCATION_ENTRY_SIZE 10 +struct pe_base_relocation { -#define IMAGE_REL_I386_ABSOLUTE 0x0000 -#define IMAGE_REL_I386_DIR32 0x0006 -#define IMAGE_REL_I386_DIR32NB 0x0007 -#define IMAGE_REL_I386_REL32 0x0014 + unsigned char RVAOfBlock[4]; + unsigned char SizeOfBlock[4]; -struct symbol_table_entry { +}; - char Name[8]; - unsigned int Value; - - signed short SectionNumber; - unsigned short Type; - - unsigned char StorageClass; - unsigned char NumberOfAuxSymbols; +#define BASE_RELOCATION_PAGE_SIZE 4096 + +#define IMAGE_REL_BASED_ABSOLUTE 0 +#define IMAGE_REL_BASED_HIGH 1 +#define IMAGE_REL_BASED_LOW 2 +#define IMAGE_REL_BASED_HIGHLOW 3 +#define IMAGE_REL_BASED_HIGHADJ 4 +#define IMAGE_REL_BASED_MIPS_JMPADDR 5 +#define IMAGE_REL_BASED_ARM_MOV32 5 +#define IMAGE_REL_BASED_RISCV_HIGH20 5 +#define IMAGE_REL_BASED_THUMB_MOV32 7 +#define IMAGE_REL_BASED_RISCV_LOW12I 7 +#define IMAGE_REL_BASED_RISCV_LOW12S 8 +#define IMAGE_REL_BASED_MIPS_JMPADDR16 9 +#define IMAGE_REL_BASED_DIR64 10 + +struct pe_import_directory_table { + + unsigned char ImportNameTableRVA[4]; + unsigned char TimeDateStamp[4]; + unsigned char ForwarderChain[4]; + unsigned char NameRVA[4]; + unsigned char ImportAddressTableRVA[4]; }; -#define SYMBOL_TABLE_ENTRY_SIZE 18 -#define IMAGE_SYM_UNDEFINED 0 +int pe_check_option (const char *cmd_arg, int argc, char **argv, int *optind, const char **optarg); -#define IMAGE_SYM_ABSOLUTE -1 -#define IMAGE_SYM_DEBUG -2 +void pe_after_link (void); +void pe_before_link (void); +void pe_print_help (void); -#define IMAGE_SYM_TYPE_NULL 0 -#define IMAGE_SYM_DTYPE_NULL 0 +unsigned long pe_get_first_section_rva (void); -#define IMAGE_SYM_CLASS_EXTERNAL 2 -#define IMAGE_SYM_CLASS_STATIC 3 -#define IMAGE_SYM_CLASS_FILE 103 +void pe_write (const char *filename); +void pe_use_option (const char *cmd_arg, int idx, const char *optarg); #endif /* _PE_H */ diff --git a/reloc.h b/reloc.h new file mode 100644 index 0000000..118719e --- /dev/null +++ b/reloc.h @@ -0,0 +1,56 @@ +/****************************************************************************** + * @file reloc.h + *****************************************************************************/ +#ifndef _RELOC_H +#define _RELOC_H + +enum { + + RELOC_TYPE_IGNORED, + + RELOC_TYPE_64, + RELOC_TYPE_PC64, + + RELOC_TYPE_32, + RELOC_TYPE_PC32, + + RELOC_TYPE_16, + RELOC_TYPE_PC16, + + RELOC_TYPE_8, + RELOC_TYPE_PC8, + + RELOC_TYPE_END + +}; + +struct section_part; +struct symbol; +struct reloc_entry; + +struct reloc_howto { + + int size, pc_rel, no_base, final_right_shift; + void (*special_function) (struct section_part *part, struct reloc_entry *rel, struct symbol *symbol); + + const char *name; + unsigned long dst_mask; + + int final_left_shift; + +}; + +extern struct reloc_howto reloc_howtos[RELOC_TYPE_END]; + +struct reloc_entry { + + struct symbol *symbol; + + unsigned long offset; + unsigned long addend; + + struct reloc_howto *howto; + +}; + +#endif /* _RELOC_H */ diff --git a/report.c b/report.c index fb010ce..8f020a3 100644 --- a/report.c +++ b/report.c @@ -110,13 +110,17 @@ static void output_message (const char *filename, unsigned int lineno, unsigned fprintf (stderr, "internal error:"); - } else if (type == REPORT_WARNING) { + } else if (type == REPORT_WARNING || type == REPORT_NOTE) { #ifndef __PDOS__ set_console_color (COLOR_WARNING); #endif - fprintf (stderr, "warning:"); + if (type == REPORT_WARNING) { + fprintf (stderr, "warning:"); + } else { + fprintf (stderr, "note:"); + } } diff --git a/report.h b/report.h index 8e694dd..fefee4b 100644 --- a/report.h +++ b/report.h @@ -16,6 +16,7 @@ #define REPORT_WARNING 0 #define REPORT_ERROR 1 +#define REPORT_NOTE 2 #define REPORT_FATAL_ERROR 3 #define REPORT_INTERNAL_ERROR 4 diff --git a/section.c b/section.c new file mode 100644 index 0000000..f8a9d48 --- /dev/null +++ b/section.c @@ -0,0 +1,184 @@ +/****************************************************************************** + * @file section.c + *****************************************************************************/ +#include + +#include "lib.h" +#include "section.h" + +struct object_file *all_object_files = 0; +struct section *all_sections = 0; + +static struct object_file **last_object_file_p = &all_object_files; +static struct section **last_section_p = &all_sections; + +static struct section *discarded_sections = 0; + +struct object_file *object_file_make (const char *filename, unsigned long symbol_cnt) { + + struct object_file *of = xmalloc (sizeof (*of)); + + of->filename = xstrdup (filename); + + of->symbol_arr = symbol_cnt ? xmalloc (sizeof (*of->symbol_arr) * symbol_cnt) : 0; + of->symbol_cnt = symbol_cnt; + + *last_object_file_p = of; + last_object_file_p = &of->next; + + return of; + +} + +struct section *section_find (const char *name) { + + struct section *section = 0; + + for (section = all_sections; section; section = section->next) { + + if (strcmp (section->name, name) == 0) { + break; + } + + } + + return section; + +} + +struct section *section_find_or_make (const char *name) { + + struct section *section; + + if ((section = section_find (name))) { + return section; + } + + section = xmalloc (sizeof (*section)); + section->name = xstrdup (name); + + section->last_part_p = §ion->first_part; + section->section_alignment = 1; + + *last_section_p = section; + last_section_p = §ion->next; + + return section; + +} + +struct section_part *section_part_new (struct section *section, struct object_file *of) { + + struct section_part *part = xmalloc (sizeof (*part)); + + part->section = section; + part->of = of; + part->alignment = 1; + + return part; + +} + +void section_append_section_part (struct section *section, struct section_part *part) { + + *section->last_part_p = part; + section->last_part_p = &part->next; + +} + +void sections_destroy_empty_before_collapse (void) { + + struct section *section, **next_p; + + struct section_part *part; + struct subsection *subsection; + + for (next_p = &all_sections, section = *next_p; section; section = *next_p) { + + for (part = section->first_part; part; part = part->next) { + + if (part->content_size) { + goto _done_section; + } + + } + + for (subsection = section->all_subsections; subsection; subsection = subsection->next) { + + for (part = subsection->first_part; part; part = part->next) { + + if (part->content_size) { + goto _done_section; + } + + } + + } + + _done_section: + + if (part && part->content_size) { + next_p = §ion->next; + } else { + + *next_p = section->next; + + section->next = discarded_sections; + discarded_sections = section; + + } + + } + + last_section_p = next_p; + +} + +void section_write (struct section *section, unsigned char *memory) { + + struct section_part *part; + + for (part = section->first_part; part; part = part->next) { + + memcpy (memory, part->content, part->content_size); + memory += part->content_size; + + } + +} + +int section_count (void) { + + struct section *section; + int cnt = 0; + + for (section = all_sections; section; section = section->next) { + cnt++; + } + + return cnt; + +} + +struct subsection *subsection_find (struct section *section, const char *name) { + + struct subsection *subsection; + + for (subsection = section->all_subsections; subsection; subsection = subsection->next) { + + if (strcmp (subsection->name, name) == 0) { + break; + } + + } + + return subsection; + +} + +void subsection_append_section_part (struct subsection *subsection, struct section_part *part) { + + *subsection->last_part_p = part; + subsection->last_part_p = &part->next; + +} diff --git a/section.h b/section.h new file mode 100644 index 0000000..0f0686b --- /dev/null +++ b/section.h @@ -0,0 +1,104 @@ +/****************************************************************************** + * @file section.h + *****************************************************************************/ +#ifndef _SECTION_H +#define _SECTION_H + +#include "reloc.h" +#include "symbol.h" + +struct object_file { + + char *filename; + + struct symbol *symbol_arr; + unsigned long symbol_cnt; + + struct object_file *next; + +}; + +struct section_part { + + struct section *section; + struct object_file *of; + + unsigned char *content; + unsigned long content_size, alignment; + + struct reloc_entry *reloc_arr; + unsigned long reloc_cnt; + + unsigned long rva; + struct section_part *next; + +}; + +struct subsection { + + char *name; + + struct section_part *first_part; + struct section_part **last_part_p; + + struct subsection *next; + +}; + +#define UNDEFINED_SECTION_NUMBER 0 +#define ABSOLUTE_SECTION_NUMBER (-1) + +struct section { + + char *name; + int flags; + + struct section_part *first_part; + struct section_part **last_part_p; + + struct subsection *all_subsections; + + unsigned long total_size; + int is_bss; + + unsigned long rva; + unsigned long section_alignment; + + int target_index; + void *object_dependent_data; + + struct section *next; + +}; + +#define SECTION_FLAG_ALLOC (1U << 0) +#define SECTION_FLAG_LOAD (1U << 1) +#define SECTION_FLAG_READONLY (1U << 2) +#define SECTION_FLAG_CODE (1U << 3) +#define SECTION_FLAG_DATA (1U << 4) +#define SECTION_FLAG_NEVER_LOAD (1U << 5) +#define SECTION_FLAG_DEBUGGING (1U << 6) +#define SECTION_FLAG_EXCLUDE (1U << 7) +#define SECTION_FLAG_NOREAD (1U << 8) +#define SECTION_FLAG_SHARED (1U << 9) + +extern struct object_file *all_object_files; +extern struct section *all_sections; + +struct object_file *object_file_make (const char *filename, unsigned long symbol_cnt); + +struct section_part *section_part_new (struct section *section, struct object_file *of); +void section_append_section_part (struct section *section, struct section_part *part); + +struct section *section_find (const char *name); +struct section *section_find_or_make (const char *name); + +void sections_destroy_empty_before_collapse (void); +void section_write (struct section *section, unsigned char *memory); + +int section_count (void); + +struct subsection *subsection_find (struct section *section, const char *name); +void subsection_append_section_part (struct subsection *subsection, struct section_part *part); + +#endif /* _SECTION_H */ diff --git a/symbol.c b/symbol.c new file mode 100644 index 0000000..d6598e9 --- /dev/null +++ b/symbol.c @@ -0,0 +1,105 @@ +/****************************************************************************** + * @file symbol.c + *****************************************************************************/ +#include + +#include "hashtab.h" +#include "ld.h" +#include "report.h" +#include "symbol.h" + +static struct hashtab symbol_hashtab = { 0 }; + +struct symbol *symbol_find (const char *name) { + + struct hashtab_name *key; + struct symbol *symbol; + + if (!(key = hashtab_alloc_name (name))) { + return 0; + } + + symbol = hashtab_get (&symbol_hashtab, key); + free (key); + + return symbol; + +} + +void symbol_record_external_symbol (struct symbol *symbol) { + + struct symbol *old_symbol = symbol_find (symbol->name); + + if (!old_symbol) { + + symbol_add_to_hashtab (symbol); + return; + + } + + if (symbol_is_undefined (old_symbol)) { + + symbol_remove_from_hashtab (old_symbol); + symbol_add_to_hashtab (symbol); + + return; + + } + + if (symbol_is_undefined (symbol)) { + return; + } + + report_at (program_name, 0, REPORT_ERROR, "%s:(%s+0x%lx): multiple definition of '%s'", symbol->part->of->filename, symbol->part->section->name, symbol->value, symbol->name); + report_at (program_name, 0, REPORT_NOTE, "%s:(%s+0x%lx): multiple definition of '%s'", old_symbol->part->of->filename, old_symbol->part->section->name, old_symbol->value, old_symbol->name); + +} + +int symbol_is_undefined (const struct symbol *symbol) { + return symbol->section_number == UNDEFINED_SECTION_NUMBER; +} + +void symbol_add_to_hashtab (struct symbol *symbol) { + + struct hashtab_name *key; + + if (!(key = hashtab_alloc_name (symbol->name))) { + return; + } + + hashtab_put (&symbol_hashtab, key, symbol); + +} + +void symbol_remove_from_hashtab (struct symbol *symbol) { + + struct hashtab_name *key; + + if (!(key = hashtab_alloc_name (symbol->name))) { + return; + } + + hashtab_remove (&symbol_hashtab, key); + free (key); + +} + +unsigned long symbol_get_value_with_base (const struct symbol *symbol) { + + if (symbol->part) { + return state->base_address + symbol->part->rva + symbol->value; + } + + return symbol->value; + +} + +unsigned long symbol_get_value_no_base (const struct symbol *symbol) { + + if (symbol->part) { + return symbol->part->rva + symbol->value; + } + + return (symbol->value - state->base_address); + +} diff --git a/symbol.h b/symbol.h new file mode 100644 index 0000000..82e8c98 --- /dev/null +++ b/symbol.h @@ -0,0 +1,38 @@ +/****************************************************************************** + * @file symbol.h + *****************************************************************************/ +#ifndef _SYMBOL_H +#define _SYMBOL_H + +#include "section.h" + +struct symbol { + + char *name; + int flags; + + unsigned long value; + unsigned long size; + + long section_number; /* 1-based, 0 means undefined, negative numbers have special meaning. */ + int auxiliary; /* such symbol should be ignored and is only a filter. */ + + struct section_part *part; + +}; + +#define SYMBOL_FLAG_EXCLUDE_EXPORT (1U << 0) +#define SYMBOL_FLAG_SECTION_SYMBOL (1U << 1) + +struct symbol *symbol_find (const char *name); + +void symbol_record_external_symbol (struct symbol *symbol); +int symbol_is_undefined (const struct symbol *symbol); + +void symbol_add_to_hashtab (struct symbol *symbol); +void symbol_remove_from_hashtab (struct symbol *symbol); + +unsigned long symbol_get_value_with_base (const struct symbol *symbol); +unsigned long symbol_get_value_no_base (const struct symbol *symbol); + +#endif /* _SYMBOL_H */ -- 2.34.1