From: Robert Pengelly Date: Mon, 25 Nov 2024 20:27:17 +0000 (+0000) Subject: Initial re-write X-Git-Url: https://git.candlhat.org/?a=commitdiff_plain;h=96b30cd325689d13ba87fca917618ae2a86c18a3;p=slink.git Initial re-write --- 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 */