From 88e82a858e9125eb34cde1c56a16f6f0dfa4afc1 Mon Sep 17 00:00:00 2001 From: Robert Pengelly Date: Mon, 3 Jun 2024 04:36:14 +0100 Subject: [PATCH 1/1] New server --- LICENSE | 24 ++ Makefile.p32 | 25 ++ Makefile.pdw | 25 ++ Makefile.unix | 28 +++ Makefile.w32 | 21 ++ README.md | 32 +++ aout.c | 550 ++++++++++++++++++++++++++++++++++++++++ aout.h | 47 ++++ hashtab.c | 215 ++++++++++++++++ hashtab.h | 36 +++ ld.c | 686 ++++++++++++++++++++++++++++++++++++++++++++++++++ ld.h | 62 +++++ lib.c | 315 +++++++++++++++++++++++ lib.h | 18 ++ report.c | 149 +++++++++++ report.h | 25 ++ vector.c | 54 ++++ vector.h | 19 ++ write7x.c | 26 ++ write7x.h | 10 + 20 files changed, 2367 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile.p32 create mode 100644 Makefile.pdw create mode 100644 Makefile.unix create mode 100644 Makefile.w32 create mode 100644 README.md create mode 100644 aout.c create mode 100644 aout.h create mode 100644 hashtab.c create mode 100644 hashtab.h create mode 100644 ld.c create mode 100644 ld.h create mode 100644 lib.c create mode 100644 lib.h create mode 100644 report.c create mode 100644 report.h create mode 100644 vector.c create mode 100644 vector.h create mode 100644 write7x.c create mode 100644 write7x.h diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fdddb29 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/Makefile.p32 b/Makefile.p32 new file mode 100644 index 0000000..4f335bc --- /dev/null +++ b/Makefile.p32 @@ -0,0 +1,25 @@ +#****************************************************************************** +# @file Makefile.p32 +#****************************************************************************** +AS=as386 +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=aout.c hashtab.o ld.o lib.o report.o vector.o write7x.o + +all: clean slink.exe + +slink.exe: $(COBJ) + $(LD) -s -o slink.exe ../pdos/pdpclib/pdosst32.o $(COBJ) ../pdos/pdpclib/pdos.a + +.c.o: + $(CC) $(COPTS) -o $*.s $< + $(AS) -o $@ $*.s + rm -f $*.s + +clean: + for %f in ($(COBJ)) do ( rm -f %f ) + + rm -f slink + rm -f slink.exe diff --git a/Makefile.pdw b/Makefile.pdw new file mode 100644 index 0000000..b61185a --- /dev/null +++ b/Makefile.pdw @@ -0,0 +1,25 @@ +#****************************************************************************** +# @file Makefile.pdw +#****************************************************************************** +AS=aswin +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=aout.c hashtab.o ld.o lib.o report.o vector.o write7x.o + +all: clean slink.exe + +slink.exe: $(COBJ) + $(LD) -s -o slink.exe ../pdos/pdpclib/w32start.o $(COBJ) ../pdos/pdpclib/msvcrt.a ../pdos/src/kernel32.a + +.c.o: + $(CC) $(COPTS) -o $*.s $< + $(AS) -o $@ $*.s + rm -f $*.s + +clean: + for %f in ($(COBJ)) do ( rm -f %f ) + + rm -f slink + rm -f slink.exe diff --git a/Makefile.unix b/Makefile.unix new file mode 100644 index 0000000..1289dc3 --- /dev/null +++ b/Makefile.unix @@ -0,0 +1,28 @@ +#****************************************************************************** +# @file Makefile.unix +#****************************************************************************** +OBJDIR ?= $(CURDIR) +SRCDIR ?= $(CURDIR) + +VPATH := $(SRCDIR) + +CC := gcc +CFLAGS := -D_FILE_OFFSET_BITS=64 -I$(OBJDIR) -I$(SRCDIR)/include -O2 -Wall -Werror -Wextra -ansi -pedantic -std=c90 + +CSRC := aout.c hashtab.c ld.c lib.c report.c vector.c write7x.c + +ifeq ($(OS), Windows_NT) +all: slink.exe + +slink.exe: $(CSRC) + $(CC) $(CFLAGS) -o $@ $^ +else +all: slink + +slink: $(CSRC) + $(CC) $(CFLAGS) -o $@ $^ +endif + +clean: + if [ -f slink ]; then rm -rf slink; fi + if [ -f slink.exe ]; then rm -rf slink.exe; fi diff --git a/Makefile.w32 b/Makefile.w32 new file mode 100644 index 0000000..ee7f0f7 --- /dev/null +++ b/Makefile.w32 @@ -0,0 +1,21 @@ +#****************************************************************************** +# @file Makefile.w32 +#****************************************************************************** +OBJDIR ?= $(CURDIR) +SRCDIR ?= $(CURDIR) + +VPATH := $(SRCDIR) + +CC := gcc +CFLAGS := -D_FILE_OFFSET_BITS=64 -I$(OBJDIR) -I$(SRCDIR)/include -O2 -Wall -Werror -Wextra -ansi -pedantic -std=c90 + +CSRC := aout.c hashtab.c ld.c lib.c report.c vector.c write7x.c + +all: slink.exe + +clean: + if exist slink ( del /q slink ) + if exist slink.exe ( del /q slink.exe ) + +slink.exe: $(CSRC) + $(CC) $(CFLAGS) -o $@ $^ diff --git a/README.md b/README.md new file mode 100644 index 0000000..73f161e --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +## What is slink? + + Small Linker (SLINK) is a very small linker for linking slightly + modified a.out object files into an executable. + +## License + + All source code is Public Domain. + +## Obtain the source code + + git clone https://git.candlhat.org/slink.git + +## Building + + BSD: + + Make sure you have gcc and gmake installed then run gmake -f Makefile.unix. + + Linux: + + Make sure you have gcc and make installed then run make -f Makefile.unix. + + macOS: + + Make sure you have xcode command line tools installed then run + make -f Makefile.unix. + + Windows: + + Make sure you have mingw installed and the location within your PATH variable + then run mingw32-make.exe -f Makefile.w32. diff --git a/aout.c b/aout.c new file mode 100644 index 0000000..c2d6e91 --- /dev/null +++ b/aout.c @@ -0,0 +1,550 @@ +/****************************************************************************** + * @file aout.c + *****************************************************************************/ +#include +#include +#include +#include +#include + +#include "aout.h" +#include "ld.h" +#include "lib.h" +#include "report.h" +#include "write7x.h" + +#include + +typedef signed char int8_t; +typedef signed short int16_t; + +#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 relocation_info *relocations; + +}; + +static struct gr tgr = { 0, 64, NULL }; +static struct gr dgr = { 0, 64, NULL }; + +static int get_symbol (struct aout_object **obj_out, long *index, const char *name, int quiet) { + + long object_i, symbol_i; + + for (object_i = 0; object_i < state->nb_aout_objs; ++object_i) { + + struct aout_object *obj = state->aout_objs[object_i]; + + for (symbol_i = 0; symbol_i < obj->symtab_count; symbol_i++) { + + struct 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; + + } + + } + + } + + if (!quiet) { + report_at (program_name, 0, REPORT_ERROR, "undefined symbol '%s'", name); + } + + return 1; + +} + +static void number_to_chars (unsigned char *p, unsigned long number, unsigned long size) { + + 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 aout_object *object) { + + long i; + + for (i = 0; i < object->symtab_count; i++) { + + struct nlist *symbol = &object->symtab[i]; + unsigned long final_slide = 0, n_value = GET_UINT32 (symbol->n_value); + + 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; + + } + + write741_to_byte_array (symbol->n_value, n_value); + + } + + for (i = 0; i < object->trelocs_count; i++) { + + struct relocation_info *rel = &object->trelocs[i]; + + long r_address = GET_INT32 (rel->r_address); + r_address += object->text_slide; + + write741_to_byte_array ((unsigned char *) rel->r_address, r_address); + + } + + for (i = 0; i < object->drelocs_count; i++) { + + struct relocation_info *rel = &object->drelocs[i]; + + long r_address = GET_INT32 (rel->r_address); + r_address += state->text_size + object->data_slide; + + write741_to_byte_array ((unsigned char *) rel->r_address, r_address); + + } + +} + +static void paste (struct aout_object *object) { + + struct aout_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 { + 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 { + 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 { + obj_bss_size = ALIGN_UP (GET_UINT32 (header->a_bss), SECTION_ALIGNMENT); + } + + bss_ptr += obj_bss_size; + +} + +static void undef_collect (struct aout_object *object) { + + long i, val; + + for (i = 0; i < object->symtab_count; i++) { + + struct 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 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; + + 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 aout_object *object, struct relocation_info *r, int is_data) { + + struct nlist *symbol; + unsigned char *p; + + unsigned long r_symbolnum = GET_UINT32 (r->r_symbolnum); + long result = 0, symbolnum = 0; + + int far_call = 0, pcrel = 0, ext = 0, length = 0; + + 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 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 aout_object *symobj; + long symidx; + + if (!get_symbol (&symobj, &symidx, symname, 0)) { + symbol = &symobj->symtab[symidx]; + } else { + return 1; + } + + } + + if (pcrel) { + result = (long) GET_UINT32 (symbol->n_value) - (GET_INT32 (r->r_address) + length); + } else { + + long r_address; + + if (!ext || (symbol->n_type & N_TYPE) == N_BSS || (symbol->n_type & N_TYPE) == N_DATA || (symbol->n_type & N_TYPE) == N_TEXT) { + + struct relocation_info new_relocation; + + unsigned long r_symbolnum; + long r_address; + + r_symbolnum = GET_UINT32 (r->r_symbolnum) & (3L << 29); + r_address = GET_INT32 (r->r_address); + + if (((r_symbolnum >> 28) & 0xff) != N_ABS) { + + if (state->format == LD_OUTPUT_BIN || state->format == LD_OUTPUT_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); + + } + + } + + r_address = GET_INT32 (r->r_address); + + 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); + } + + if (ext) { + + symbolnum = (symbol->n_type & N_TYPE); + + result += GET_UINT32 (symbol->n_value); + result += state->psp; + + } else { + + 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) { + + result -= GET_UINT32 (object->header->a_data); + result += state->data_size; + result += objbsssize; + + } + + result += state->psp; + + } + + } + + number_to_chars (p, result, length); + return 0; + +} + +static int glue (struct aout_object *object) { + + long i, err = 0; + + for (i = 0; i < object->trelocs_count; i++) { + + if (relocate (object, &object->trelocs[i], 0)) { + err = 1; + } + + } + + for (i = 0; i < object->drelocs_count; i++) { + + if (relocate (object, &object->drelocs[i], 1)) { + err = 1; + } + + } + + objtextsize += GET_UINT32 (object->header->a_text); + objdatasize += GET_UINT32 (object->header->a_data); + objbsssize += GET_UINT32 (object->header->a_bss); + + return err; + +} + + +int create_executable_from_aout_objects (void) { + + struct aout_object *object; + long i; + + int err = 0; + output_size = state->text_size + state->data_size; + + if ((output = malloc (output_size)) == NULL) { + return EXIT_FAILURE; + } + + memset (output, 0, output_size); + + text = (void *) (char *) output; + data = (void *) ((char *) text + state->text_size); + + for (i = 0; i < state->nb_aout_objs; ++i) { + paste (state->aout_objs[i]); + } + + for (i = 0; i < state->nb_aout_objs; ++i) { + apply_slides (state->aout_objs[i]); + } + + for (i = 0; i < state->nb_aout_objs; ++i) { + undef_collect (state->aout_objs[i]); + } + + if (!state->impure) { + state->bss_size = ALIGN_UP (state->bss_size, SECTION_ALIGNMENT); + } + + for (i = 0; i < state->nb_aout_objs; ++i) { + + if (glue (state->aout_objs[i])) { + err = 1; + } + + } + + if (err) { return EXIT_FAILURE; } + + for (i = 0; i < state->nb_aout_objs; i++) { + + if ((object = state->aout_objs[i]) == NULL) { + return EXIT_FAILURE; + } + + /*if (state->mapfile) { + init_map (object); + }*/ + + free (object->raw); + free (object); + + } + + state->nb_aout_objs = 0; + + /*if (state->mapfile) { + + 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 (); + + }*/ + + 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; + + } + + 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; + + } + + return EXIT_SUCCESS; + +} diff --git a/aout.h b/aout.h new file mode 100644 index 0000000..885ff80 --- /dev/null +++ b/aout.h @@ -0,0 +1,47 @@ +/****************************************************************************** + * @file aout.h + *****************************************************************************/ +#ifndef _AOUT_H +#define _AOUT_H + +struct aout_exec { + + unsigned char a_info[4]; + unsigned char a_text[4]; + unsigned char a_data[4]; + unsigned char a_bss[4]; + unsigned char a_syms[4]; + unsigned char a_entry[4]; + unsigned char a_trsize[4]; + unsigned char a_drsize[4]; + +}; + +#define N_UNDF 0x00 +#define N_ABS 0x02 +#define N_TEXT 0x04 +#define N_DATA 0x06 +#define N_BSS 0x08 + +struct relocation_info { + + unsigned char r_address[4]; + unsigned char r_symbolnum[4]; + +}; + +#define N_EXT 0x01 + +struct nlist { + + unsigned char n_strx[4]; + unsigned char n_type; + + unsigned char n_value[4]; + +}; + +#define N_TYPE 0x1e +int create_executable_from_aout_objects (void); + +#endif /* _AOUT_H */ diff --git a/hashtab.c b/hashtab.c new file mode 100644 index 0000000..b6863a5 --- /dev/null +++ b/hashtab.c @@ -0,0 +1,215 @@ +/****************************************************************************** + * @file hashtab.c + *****************************************************************************/ +#include +#include +#include + +#include "hashtab.h" + +static struct hashtab_entry *find_entry (struct hashtab_entry *entries, unsigned int capacity, struct hashtab_name *key); + +static int adjust_capacity (struct hashtab *table, unsigned int new_capacity) { + + struct hashtab_entry *new_entries, *old_entries; + unsigned int i, new_count, old_capacity; + + if ((new_entries = malloc (sizeof (*new_entries) * new_capacity)) == NULL) { + return -2; + } + + for (i = 0; i < new_capacity; i++) { + + struct hashtab_entry *entry = &new_entries[i]; + + entry->key = NULL; + entry->value = NULL; + + } + + old_entries = table->entries; + old_capacity = table->capacity; + + new_count = 0; + + for (i = 0; i < old_capacity; i++) { + + struct hashtab_entry *entry = &old_entries[i], *dest; + + if (entry->key == NULL) { + continue; + } + + dest = find_entry (new_entries, new_capacity, entry->key); + + dest->key = entry->key; + dest->value = entry->value; + + new_count++; + + } + + free (old_entries); + + table->capacity = new_capacity; + table->count = new_count; + table->entries = new_entries; + table->used = new_count; + + return 0; + +} + +static struct hashtab_entry *find_entry (struct hashtab_entry *entries, unsigned int capacity, struct hashtab_name *key) { + + struct hashtab_entry *tombstone = NULL; + unsigned int index; + + for (index = key->hash % capacity; ; index = (index + 1) % capacity) { + + struct hashtab_entry *entry = &entries[index]; + + if (entry->key == NULL) { + + if (entry->value == NULL) { + + if (tombstone == NULL) { + return entry; + } + + return tombstone; + + } else if (tombstone == NULL) { + tombstone = entry; + } + + } else if (entry->key->bytes == key->bytes) { + + if (memcmp (entry->key->chars, key->chars, key->bytes) == 0 && entry->key->hash == key->hash) { + return entry; + } + + } + + } + +} + +static unsigned int hash_string (const void *p, unsigned int length) { + + unsigned char *str = (unsigned char *) p; + unsigned int i, result = 0; + + for (i = 0; i < length; i++) { + result = (((unsigned int) str[i]) << 12) + (result >> 6) + result + (result >> 3) + (((unsigned int) str[i]) << 8) - result; + } + + return result; + +} + +struct hashtab_name *hashtab_alloc_name (const char *str) { + + struct hashtab_name *name; + unsigned int bytes = strlen (str), hash = hash_string (str, bytes); + + if ((name = malloc (sizeof (*name))) == NULL) { + return NULL; + } + + name->bytes = bytes; + name->chars = str; + name->hash = hash; + + return name; + +} + +struct hashtab_name *hashtab_get_key (struct hashtab *table, const char *name) { + + struct hashtab_name *key; + struct hashtab_entry *entry; + + if (table == NULL || table->count == 0 || !(key = hashtab_alloc_name (name))) { + return 0; + } + + entry = find_entry (table->entries, table->capacity, key); + free (key); + + return entry->key; + +} + +void *hashtab_get (struct hashtab *table, struct hashtab_name *key) { + + struct hashtab_entry *entry; + + if (table == NULL || table->count == 0) { + return NULL; + } + + entry = find_entry (table->entries, table->capacity, key); + + if (entry->key == NULL) { + return NULL; + } + + return entry->value; + +} + +int hashtab_put (struct hashtab *table, struct hashtab_name *key, void *value) { + + const int MIN_CAPACITY = 15; + + struct hashtab_entry *entry; + int ret = 0; + + if (table->used >= table->capacity / 2) { + + int capacity = table->capacity * 2 - 1; + + if (capacity < MIN_CAPACITY) { + capacity = MIN_CAPACITY; + } + + if ((ret = adjust_capacity (table, capacity))) { + return ret; + } + + } + + entry = find_entry (table->entries, table->capacity, key); + + if (entry->key == NULL) { + + table->count++; + + if (entry->value == NULL) { + table->used++; + } + + } + + entry->key = key; + entry->value = value; + + return 0; + +} + +void hashtab_remove (struct hashtab *table, struct hashtab_name *key) { + + struct hashtab_entry *entry; + + if ((entry = find_entry (table->entries, table->capacity, key)) != NULL) { + + entry->key = NULL; + entry->value = NULL; + + --table->count; + + } + +} diff --git a/hashtab.h b/hashtab.h new file mode 100644 index 0000000..47aab3b --- /dev/null +++ b/hashtab.h @@ -0,0 +1,36 @@ +/****************************************************************************** + * @file hashtab.h + *****************************************************************************/ +#ifndef _HASHTAB_H +#define _HASHTAB_H + +struct hashtab_name { + + const char *chars; + int bytes, hash; + +}; + +struct hashtab_entry { + + struct hashtab_name *key; + void *value; + +}; + +struct hashtab { + + struct hashtab_entry *entries; + int capacity, count, used; + +}; + +struct hashtab_name *hashtab_alloc_name (const char *str); +struct hashtab_name *hashtab_get_key (struct hashtab *table, const char *name); + +void *hashtab_get (struct hashtab *table, struct hashtab_name *key); + +int hashtab_put (struct hashtab *table, struct hashtab_name *key, void *value); +void hashtab_remove (struct hashtab *table, struct hashtab_name *key); + +#endif /* _HASHTAB_H */ diff --git a/ld.c b/ld.c new file mode 100644 index 0000000..76eaa15 --- /dev/null +++ b/ld.c @@ -0,0 +1,686 @@ +/****************************************************************************** + * @file ld.c + *****************************************************************************/ +#include +#include +#include + +#include "aout.h" +#include "hashtab.h" +#include "ld.h" +#include "lib.h" +#include "report.h" +#include "vector.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_aout_objs; ++i) { + + struct aout_object *obj = state->aout_objs[i]; + + if (obj == NULL) { + continue; + } + + 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_aout (void *obj, unsigned long sz, const char *fname, int quiet) { + + struct aout_exec *hdr = obj; + + struct aout_object *data_obj; + struct nlist *symtab; + + struct relocation_info *trelocs; + struct relocation_info *drelocs; + + long symtab_count, trelocs_count, drelocs_count; + unsigned long symtab_off, strtab_off, trelocs_off, drelocs_off; + + char *strtab; + long i; + + if (!(hdr->a_info[0] == 0x39 && hdr->a_info[1] == 0x01 && hdr->a_info[2] == 0x64 && hdr->a_info[3] == 0x00)) { + + if (!quiet) { + report_at (program_name, 0, REPORT_ERROR, "'%s' is not a valid object", fname); + } + + return 1; + + } + + for (i = 0; i < state->nb_aout_objs; ++i) { + + struct aout_object *obj_to_compare = state->aout_objs[i]; + + if (obj_to_compare->size != sz) { + continue; + } + + if (memcmp (obj_to_compare->raw, obj, sz) == 0) { + return 0; + } + + } + + if (state->impure) { + + 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 { + + 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)))) { + 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->aout_objs, &state->nb_aout_objs, data_obj); + + for (i = 0; i < symtab_count; ++i) { + + struct 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); + + } + + } + + return 0; + +} + +struct ar_header { + + 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; + + 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; + + } + + fseek (ar_file, index, SEEK_SET); + + if (fread (&hdr, sizeof (hdr), 1, ar_file) != 1) { + + if (feof (ar_file)) { + return 0; + } + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", root_fname); + return 1; + + } + + sz = conv_dec (hdr.size, 10); + sz_aligned = (sz % 2) ? (sz + 1) : sz; + + memcpy (fname, hdr.name, 16); + + for (i = 0; i < 16; ++i) { + + if (fname[i] == 0x20 || fname[i] == '/') { + + fname[i] = '\0'; + break; + + } + + } + + 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) { + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", root_fname); + free (obj); + + return 1; + + } + + 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; + + } + + memset (path, 0, len); + sprintf (path, "%s(%s)", root_fname, fname); + + if (process_aout (obj, sz, path, 1)) { + + free (obj); + return 1; + + } + + } else { + + if (process_aout (obj, sz, "", 1)) { + + free (obj); + return 1; + + } + + } + + 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 (fseek (ar_file, 8, SEEK_SET) != 0) { + + report_at (program_name, 0, REPORT_ERROR, "failed whilst seeking '%s'", root_fname); + return 1; + + } + + for (;;) { + + struct ar_header hdr; + + int err, i; + unsigned long sz, sz_aligned; + + if (fread (&hdr, sizeof (hdr), 1, ar_file) != 1) { + + if (feof (ar_file)) { + break; + } + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", root_fname); + return 1; + + } + + 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; + + } + + memcpy (fname, hdr.name, 16); + + for (i = 0; i < 16; ++i) { + + if (fname[i] == 0x20 || fname[i] == '/') { + + fname[i] = '\0'; + break; + + } + + } + + 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) { + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", root_fname); + free (obj); + + return 1; + + } + + 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; + + } + + memset (path, 0, len); + sprintf (path, "%s(%s)", root_fname, fname); + + if ((err = process_aout (obj, sz, path, 1))) { + free (obj); + } + + } else { + + if ((err = process_aout (obj, sz, "", 1))) { + free (obj); + } + + } + + 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_aout (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->ofile) { + state->ofile = xstrdup ("a.out"); + } + + if (state->format == LD_OUTPUT_BIN || state->format == LD_OUTPUT_COM) { + + if (state->format == LD_OUTPUT_COM) { + state->psp = 0x100; + } + + state->impure = 1; + + } + + for (i = 0; i < state->nb_files; i++) { + + if (process_file (state->files[i])) { + return EXIT_FAILURE; + } + + } + + if (state->nb_aout_objs > 0) { + + if (create_executable_from_aout_objects ()) { + return EXIT_FAILURE; + } + + } + + return EXIT_SUCCESS; + +} diff --git a/ld.h b/ld.h new file mode 100644 index 0000000..741a068 --- /dev/null +++ b/ld.h @@ -0,0 +1,62 @@ +/****************************************************************************** + * @file ld.h + *****************************************************************************/ +#ifndef _LD_H +#define _LD_H + +#include "aout.h" + +struct aout_object { + + const char *filename; + void *raw; + + unsigned long size; + + struct aout_exec *header; + struct relocation_info *trelocs, *drelocs; + + struct nlist *symtab; + char *strtab; + + long symtab_count, trelocs_count, drelocs_count; + unsigned long text_slide, data_slide, bss_slide; + +}; + +#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 + +struct ld_state { + + const char **files; + long nb_files; + + const char *ofile; + FILE *ofp; + + int format, impure; + int psp; + + struct aout_object **aout_objs; + long nb_aout_objs; + + unsigned long text_size, data_size, bss_size; + +}; + +#define LD_OUTPUT_COM 0x00 +#define LD_OUTPUT_BIN 0x01 + +extern struct ld_state *state; +extern const char *program_name; + +#define FILE_ALIGNMENT 512 +#define SECTION_ALIGNMENT 4096 + +#define DIV_ROUNDUP(a, b) (((a) + ((b) - 1)) / (b)) +#define ALIGN_UP(x, a) (DIV_ROUNDUP ((x), (a)) * (a)) + +#endif /* _LD_H */ diff --git a/lib.c b/lib.c new file mode 100644 index 0000000..07fb4f6 --- /dev/null +++ b/lib.c @@ -0,0 +1,315 @@ +/****************************************************************************** + * @file lib.c + *****************************************************************************/ +#include +#include +#include +#include + +#include "ld.h" +#include "lib.h" +#include "report.h" + +#if defined (_WIN32) +# define PATHSEP ';' +#else +# define PATHSEP ':' +#endif + +struct ld_option { + + const char *name; + int idx, flgs; + +}; + +#define LD_OPTION_NO_ARG 0 +#define LD_OPTION_HAS_ARG 1 + +#define LD_OPTION_NONE 0 +#define LD_OPTION_FORMAT 1 +#define LD_OPTION_HELP 2 +#define LD_OPTION_IMPURE 3 +#define LD_OPTION_MAP 4 +#define LD_OPTION_OUTFILE 5 + +static struct ld_option opts[] = { + + { "-o", LD_OPTION_OUTFILE, LD_OPTION_HAS_ARG }, + + { "--oformat", LD_OPTION_FORMAT, LD_OPTION_HAS_ARG }, + { "--help", LD_OPTION_HELP, LD_OPTION_NO_ARG }, + + { 0, 0, 0 } + +}; + + +static void print_usage (void) { + + if (program_name) { + + 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, " --oformat FORMAT Specify the format of output file (default msdos)\n"); + fprintf (stderr, " Supported formats are:\n"); + fprintf (stderr, " binary, msdos\n"); + + /*fprintf (stderr, " -e ADDRESS Set start address.\n");*/ + fprintf (stderr, " -s Ignored.\n"); + + fprintf (stderr, " -o FILE Set output file name (default a.out).\n"); + fprintf (stderr, " --help Print this help information.\n"); + + fprintf (stderr, "\n"); + + } + +} + +char *xstrdup (const char *__p) { + + char *p = xmalloc (strlen (__p) + 1); + + strcpy (p, __p); + return p; + +} + +int xstrcasecmp (const char *__s1, const char *__s2) { + + const unsigned char *p1 = (const unsigned char *) __s1; + const unsigned char *p2 = (const unsigned char *) __s2; + + while (*p1 != '\0') { + + if (tolower ((int) *p1) < tolower ((int) *p2)) { + return (-1); + } else if (tolower ((int) *p1) > tolower ((int) *p2)) { + return (1); + } + + p1++; + p2++; + + } + + if (*p2 == '\0') { + return (0); + } + + return (-1); + +} + +int strstart (const char *val, const char **str) { + + const char *p = *str; + const char *q = val; + + while (*q != '\0') { + + if (*p != *q) { + return 0; + } + + ++p; + ++q; + + } + + *str = p; + return 1; + +} + +void dynarray_add (void *ptab, long *nb_ptr, void *data) { + + long nb, nb_alloc; + void **pp; + + nb = *nb_ptr; + pp = *(void ***) ptab; + + if ((nb & (nb - 1)) == 0) { + + if (!nb) { + nb_alloc = 1; + } else { + nb_alloc = nb * 2; + } + + pp = xrealloc (pp, nb_alloc * sizeof (void *)); + *(void ***) ptab = pp; + + } + + pp[nb++] = data; + *nb_ptr = nb; + +} + +void parse_args (int argc, char **argv, int optind) { + + struct ld_option *popt; + const char *optarg, *r; + + if (argc <= optind) { + + print_usage (); + exit (EXIT_SUCCESS); + + } + + while (optind < argc) { + + r = argv[optind++]; + + if (r[0] != '-' || r[1] == '\0') { + + dynarray_add (&state->files, &state->nb_files, xstrdup (r)); + continue; + + } + + for (popt = opts; ; popt++) { + + const char *p1 = popt->name; + const char *r1 = r; + + if (!p1) { + + report_at (program_name, 0, REPORT_ERROR, "invalid option -- '%s'", r); + exit (EXIT_FAILURE); + + } + + 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", r); + exit (EXIT_FAILURE); + + } + + optarg = argv[optind++]; + + } + + } else if (*r1 != '\0') { + continue; + } + + break; + + } + + switch (popt->idx) { + + case LD_OPTION_FORMAT: { + + if (xstrcasecmp (optarg, "binary") == 0) { + + state->format = LD_OUTPUT_BIN; + break; + + } + + if (xstrcasecmp (optarg, "msdos") == 0) { + + state->format = LD_OUTPUT_COM; + 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; + + } + + default: { + + report_at (program_name, 0, REPORT_ERROR, "unsupported option '%s'", r); + exit (EXIT_FAILURE); + + } + + } + + } + + if (!state->ofile) { state->ofile = "a.out"; } + +} + +void *xmalloc (unsigned long __size) { + + void *ptr = malloc (__size); + + if (!ptr && __size) { + + report_at (program_name, 0, REPORT_ERROR, "memory full (malloc)"); + exit (EXIT_FAILURE); + + } + + memset (ptr, 0, __size); + return ptr; + +} + +void *xrealloc (void *__ptr, unsigned long __size) { + + void *ptr = realloc (__ptr, __size); + + if (!ptr && __size) { + + report_at (program_name, 0, REPORT_ERROR, "memory full (realloc)"); + exit (EXIT_FAILURE); + + } + + return ptr; + +} diff --git a/lib.h b/lib.h new file mode 100644 index 0000000..1f82f98 --- /dev/null +++ b/lib.h @@ -0,0 +1,18 @@ +/****************************************************************************** + * @file lib.h + *****************************************************************************/ +#ifndef _LIB_H +#define _LIB_H + +int xstrcasecmp (const char *__s1, const char *__s2); + +int strstart (const char *val, const char **str); +void dynarray_add (void *ptab, long *nb_ptr, void *data); + +char *xstrdup (const char *__p); +void parse_args (int argc, char **argv, int optind); + +void *xmalloc (unsigned long __size); +void *xrealloc (void *__ptr, unsigned long __size); + +#endif /* _LIB_H */ diff --git a/report.c b/report.c new file mode 100644 index 0000000..fb010ce --- /dev/null +++ b/report.c @@ -0,0 +1,149 @@ +/****************************************************************************** + * @file report.c + *****************************************************************************/ +#include +#include +#include + +#include "report.h" +unsigned int errors = 0; + +#ifndef __PDOS__ +#if defined (_WIN32) +# include +static int OriginalConsoleColor = -1; +#endif + +static void reset_console_color (void) { + +#if defined (_WIN32) + + HANDLE hStdError = GetStdHandle (STD_ERROR_HANDLE); + + if (OriginalConsoleColor == -1) { return; } + + SetConsoleTextAttribute (hStdError, OriginalConsoleColor); + OriginalConsoleColor = -1; + +#else + + fprintf (stderr, "\033[0m"); + +#endif + +} + +static void set_console_color (int color) { + +#if defined (_WIN32) + + HANDLE hStdError = GetStdHandle (STD_ERROR_HANDLE); + WORD wColor; + + if (OriginalConsoleColor == -1) { + + CONSOLE_SCREEN_BUFFER_INFO csbi; + + if (!GetConsoleScreenBufferInfo (hStdError, &csbi)) { + return; + } + + OriginalConsoleColor = csbi.wAttributes; + + } + + wColor = (OriginalConsoleColor & 0xF0) + (color & 0xF); + SetConsoleTextAttribute (hStdError, wColor); + +#else + + fprintf (stderr, "\033[%dm", color); + +#endif + +} +#endif + +static void output_message (const char *filename, unsigned int lineno, unsigned int idx, int type, const char *fmt, va_list ap) { + + if (filename) { + + if (lineno == 0) { + fprintf (stderr, "%s: ", filename); + } else { + fprintf (stderr, "%s:", filename); + } + + } + + if (lineno > 0) { + + if (idx == 0) { + fprintf (stderr, "%u: ", lineno); + } else { + fprintf (stderr, "%u:", lineno); + } + + } + + if (idx > 0) { + fprintf (stderr, "%u: ", idx); + } + + if (type == REPORT_ERROR || type == REPORT_FATAL_ERROR) { + +#ifndef __PDOS__ + set_console_color (COLOR_ERROR); +#endif + + if (type == REPORT_ERROR) { + fprintf (stderr, "error:"); + } else { + fprintf (stderr, "fatal error:"); + } + + } else if (type == REPORT_INTERNAL_ERROR) { + +#ifndef __PDOS__ + set_console_color (COLOR_INTERNAL_ERROR); +#endif + + fprintf (stderr, "internal error:"); + + } else if (type == REPORT_WARNING) { + +#ifndef __PDOS__ + set_console_color (COLOR_WARNING); +#endif + + fprintf (stderr, "warning:"); + + } + +#ifndef __PDOS__ + reset_console_color (); +#endif + + fprintf (stderr, " "); + vfprintf (stderr, fmt, ap); + fprintf (stderr, "\n"); + + if (type != REPORT_WARNING) { + ++errors; + } + +} + +unsigned int get_error_count (void) { + return errors; +} + +void report_at (const char *filename, unsigned int lineno, int type, const char *fmt, ...) { + + va_list ap; + + va_start (ap, fmt); + output_message (filename, lineno, 0, type, fmt, ap); + va_end (ap); + +} diff --git a/report.h b/report.h new file mode 100644 index 0000000..8e694dd --- /dev/null +++ b/report.h @@ -0,0 +1,25 @@ +/****************************************************************************** + * @file report.h + *****************************************************************************/ +#ifndef _REPORT_H +#define _REPORT_H + +#if defined (_WIN32) +# define COLOR_ERROR 12 +# define COLOR_WARNING 13 +# define COLOR_INTERNAL_ERROR 19 +#else +# define COLOR_ERROR 91 +# define COLOR_INTERNAL_ERROR 94 +# define COLOR_WARNING 95 +#endif + +#define REPORT_WARNING 0 +#define REPORT_ERROR 1 +#define REPORT_FATAL_ERROR 3 +#define REPORT_INTERNAL_ERROR 4 + +unsigned int get_error_count (void); +void report_at (const char *filename, unsigned int lineno, int type, const char *fmt, ...); + +#endif /* _REPORT_H */ diff --git a/vector.c b/vector.c new file mode 100644 index 0000000..3984039 --- /dev/null +++ b/vector.c @@ -0,0 +1,54 @@ +/****************************************************************************** + * @file vector.c + *****************************************************************************/ +#include +#include + +#include "vector.h" + +extern void *xrealloc (void *__ptr, unsigned int __size); + +int vec_adjust (struct vector *vec, int length) { + + if (vec->capacity <= length) { + + if (vec->capacity == 0) { + vec->capacity = 16; + } else { + vec->capacity <<= 1; + } + + vec->data = xrealloc (vec->data, sizeof (*(vec->data)) * vec->capacity); + + } + + return 0; + +} + +void *vec_pop (struct vector *vec) { + + if (!vec || vec == NULL) { + return NULL; + } + + if (vec->length == 0) { + return NULL; + } + + return vec->data[--vec->length]; + +} + +int vec_push (struct vector *vec, void *elem) { + + int ret; + + if ((ret = vec_adjust (vec, vec->length)) != 0) { + return ret; + } + + vec->data[vec->length++] = elem; + return 0; + +} diff --git a/vector.h b/vector.h new file mode 100644 index 0000000..29d957a --- /dev/null +++ b/vector.h @@ -0,0 +1,19 @@ +/****************************************************************************** + * @file vector.h + *****************************************************************************/ +#ifndef _VECTOR_H +#define _VECTOR_H + +struct vector { + + void **data; + int capacity, length; + +}; + +int vec_adjust (struct vector *vec, int length); +int vec_push (struct vector *vec, void *elem); + +void *vec_pop (struct vector *vec); + +#endif /* _VECTOR_H */ diff --git a/write7x.c b/write7x.c new file mode 100644 index 0000000..144d71c --- /dev/null +++ b/write7x.c @@ -0,0 +1,26 @@ +/****************************************************************************** + * @file write7x.c + *****************************************************************************/ +#include + +#include "write7x.h" + +void write721_to_byte_array (unsigned char *dest, unsigned int val) { + + int i; + + for (i = 0; i < 2; ++i) { + dest[i] = (val >> (CHAR_BIT * i)) & UCHAR_MAX; + } + +} + +void write741_to_byte_array (unsigned char *dest, unsigned long val) { + + int i; + + for (i = 0; i < 4; ++i) { + dest[i] = (val >> (CHAR_BIT * i)) & UCHAR_MAX; + } + +} diff --git a/write7x.h b/write7x.h new file mode 100644 index 0000000..056910b --- /dev/null +++ b/write7x.h @@ -0,0 +1,10 @@ +/****************************************************************************** + * @file write7x.h + *****************************************************************************/ +#ifndef _WRITE7X_H +#define _WRITE7X_H + +void write721_to_byte_array (unsigned char *dest, unsigned int val); +void write741_to_byte_array (unsigned char *dest, unsigned long val); + +#endif /* _WRITE7X_H */ -- 2.34.1