--- /dev/null
+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 <https://unlicense.org>
--- /dev/null
+#******************************************************************************
+# @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
--- /dev/null
+#******************************************************************************
+# @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
--- /dev/null
+#******************************************************************************
+# @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
--- /dev/null
+#******************************************************************************
+# @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 $@ $^
--- /dev/null
+## 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.
--- /dev/null
+/******************************************************************************
+ * @file aout.c
+ *****************************************************************************/
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "aout.h"
+#include "ld.h"
+#include "lib.h"
+#include "report.h"
+#include "write7x.h"
+
+#include <limits.h>
+
+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;
+
+}
--- /dev/null
+/******************************************************************************
+ * @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 */
--- /dev/null
+/******************************************************************************
+ * @file hashtab.c
+ *****************************************************************************/
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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;
+
+ }
+
+}
--- /dev/null
+/******************************************************************************
+ * @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 */
--- /dev/null
+/******************************************************************************
+ * @file ld.c
+ *****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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, "!<arch>\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;
+
+}
--- /dev/null
+/******************************************************************************
+ * @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 <stdio.h>
+
+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 */
--- /dev/null
+/******************************************************************************
+ * @file lib.c
+ *****************************************************************************/
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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;
+
+}
--- /dev/null
+/******************************************************************************
+ * @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 */
--- /dev/null
+/******************************************************************************
+ * @file report.c
+ *****************************************************************************/
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "report.h"
+unsigned int errors = 0;
+
+#ifndef __PDOS__
+#if defined (_WIN32)
+# include <windows.h>
+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);
+
+}
--- /dev/null
+/******************************************************************************
+ * @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 */
--- /dev/null
+/******************************************************************************
+ * @file vector.c
+ *****************************************************************************/
+#include <stddef.h>
+#include <stdlib.h>
+
+#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;
+
+}
--- /dev/null
+/******************************************************************************
+ * @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 */
--- /dev/null
+/******************************************************************************
+ * @file write7x.c
+ *****************************************************************************/
+#include <limits.h>
+
+#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;
+ }
+
+}
--- /dev/null
+/******************************************************************************
+ * @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 */