New server
authorRobert Pengelly <robertapengelly@hotmail.com>
Mon, 3 Jun 2024 03:36:14 +0000 (04:36 +0100)
committerRobert Pengelly <robertapengelly@hotmail.com>
Mon, 3 Jun 2024 03:36:14 +0000 (04:36 +0100)
20 files changed:
LICENSE [new file with mode: 0644]
Makefile.p32 [new file with mode: 0644]
Makefile.pdw [new file with mode: 0644]
Makefile.unix [new file with mode: 0644]
Makefile.w32 [new file with mode: 0644]
README.md [new file with mode: 0644]
aout.c [new file with mode: 0644]
aout.h [new file with mode: 0644]
hashtab.c [new file with mode: 0644]
hashtab.h [new file with mode: 0644]
ld.c [new file with mode: 0644]
ld.h [new file with mode: 0644]
lib.c [new file with mode: 0644]
lib.h [new file with mode: 0644]
report.c [new file with mode: 0644]
report.h [new file with mode: 0644]
vector.c [new file with mode: 0644]
vector.h [new file with mode: 0644]
write7x.c [new file with mode: 0644]
write7x.h [new file with mode: 0644]

diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
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 <https://unlicense.org>
diff --git a/Makefile.p32 b/Makefile.p32
new file mode 100644 (file)
index 0000000..4f335bc
--- /dev/null
@@ -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 (file)
index 0000000..b61185a
--- /dev/null
@@ -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 (file)
index 0000000..1289dc3
--- /dev/null
@@ -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 (file)
index 0000000..ee7f0f7
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..c2d6e91
--- /dev/null
+++ b/aout.c
@@ -0,0 +1,550 @@
+/******************************************************************************
+ * @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;
+
+}
diff --git a/aout.h b/aout.h
new file mode 100644 (file)
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 (file)
index 0000000..b6863a5
--- /dev/null
+++ b/hashtab.c
@@ -0,0 +1,215 @@
+/******************************************************************************
+ * @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;
+    
+    }
+
+}
diff --git a/hashtab.h b/hashtab.h
new file mode 100644 (file)
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 (file)
index 0000000..76eaa15
--- /dev/null
+++ b/ld.c
@@ -0,0 +1,686 @@
+/******************************************************************************
+ * @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;
+
+}
diff --git a/ld.h b/ld.h
new file mode 100644 (file)
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    <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 */
diff --git a/lib.c b/lib.c
new file mode 100644 (file)
index 0000000..07fb4f6
--- /dev/null
+++ b/lib.c
@@ -0,0 +1,315 @@
+/******************************************************************************
+ * @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;
+
+}
diff --git a/lib.h b/lib.h
new file mode 100644 (file)
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 (file)
index 0000000..fb010ce
--- /dev/null
+++ b/report.c
@@ -0,0 +1,149 @@
+/******************************************************************************
+ * @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);
+
+}
diff --git a/report.h b/report.h
new file mode 100644 (file)
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 (file)
index 0000000..3984039
--- /dev/null
+++ b/vector.c
@@ -0,0 +1,54 @@
+/******************************************************************************
+ * @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;
+
+}
diff --git a/vector.h b/vector.h
new file mode 100644 (file)
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 (file)
index 0000000..144d71c
--- /dev/null
+++ b/write7x.c
@@ -0,0 +1,26 @@
+/******************************************************************************
+ * @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;
+    }
+
+}
diff --git a/write7x.h b/write7x.h
new file mode 100644 (file)
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 */