diff options
author | H. Peter Anvin <hpa@zytor.com> | 2002-04-30 21:02:01 +0000 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2002-04-30 21:02:01 +0000 |
commit | 788e6c10e175ab4b3e67feefbffe570619ae2330 (patch) | |
tree | fdbac44f81f28b729e74ce8a8379b5211f2d6f95 /rdoff | |
parent | 4cf1748e6829f80be251b8d7d274738009f934cf (diff) | |
download | nasm-788e6c10e175ab4b3e67feefbffe570619ae2330.tar.gz nasm-788e6c10e175ab4b3e67feefbffe570619ae2330.tar.bz2 nasm-788e6c10e175ab4b3e67feefbffe570619ae2330.zip |
NASM 0.98.12
Diffstat (limited to 'rdoff')
-rw-r--r-- | rdoff/Makefile.in | 170 | ||||
-rw-r--r-- | rdoff/ldrdf.c | 1378 |
2 files changed, 1431 insertions, 117 deletions
diff --git a/rdoff/Makefile.in b/rdoff/Makefile.in index 38620d7..5ea14a0 100644 --- a/rdoff/Makefile.in +++ b/rdoff/Makefile.in @@ -1,86 +1,84 @@ -*************** -*** 1,4 **** -- # $Id$ - # - # Auto-configuring Makefile for RDOFF object file utils; part of the - # Netwide Assembler ---- 1,4 ---- -+ # $Id$ - # - # Auto-configuring Makefile for RDOFF object file utils; part of the - # Netwide Assembler -*************** -*** 31,37 **** - .c.o: - $(CC) -c $(CFLAGS) $< - -- all: rdfdump ldrdf rdx rdflib rdf2bin rdf2com - - rdfdump: rdfdump.o - $(CC) $(LDFLAGS) -o rdfdump rdfdump.o ---- 31,37 ---- - .c.o: - $(CC) -c $(CFLAGS) $< - -+ all: rdfdump ldrdf rdx rdflib rdf2bin rdf2com rdf2ihx - - rdfdump: rdfdump.o - $(CC) $(LDFLAGS) -o rdfdump rdfdump.o -*************** -*** 45,51 **** - $(CC) $(LDFLAGS) -o rdf2bin rdf2bin.o $(RDXLIBS) nasmlib.o - rdf2com: - rm -f rdf2com && $(LN_S) rdf2bin rdf2com - - rdf2bin.o: rdf2bin.c - rdfdump.o: rdfdump.c - rdoff.o: rdoff.c rdoff.h ---- 45,54 ---- - $(CC) $(LDFLAGS) -o rdf2bin rdf2bin.o $(RDXLIBS) nasmlib.o - rdf2com: - rm -f rdf2com && $(LN_S) rdf2bin rdf2com -+ rdf2ihx: rdf2ihx.o $(RDXLIBS) nasmlib.o -+ $(CC) $(LDFLAGS) -o rdf2ihx rdf2ihx.o $(RDXLIBS) nasmlib.o - -+ rdf2ihx.o: rdf2ihx.c - rdf2bin.o: rdf2bin.c - rdfdump.o: rdfdump.c - rdoff.o: rdoff.c rdoff.h -*************** -*** 62,78 **** - $(CC) -c $(CFLAGS) $(top_srcdir)/nasmlib.c - - clean: -- rm -f *.o rdfdump ldrdf rdx rdflib rdf2bin rdf2com - - spotless: clean - rm -f Makefile - - distclean: spotless - -- install: rdfdump ldrdf rdx rdflib rdf2bin rdf2com - $(INSTALL_PROGRAM) rdfdump $(INSTALLROOT)$(bindir)/rdfdump - $(INSTALL_PROGRAM) ldrdf $(INSTALLROOT)$(bindir)/ldrdf - $(INSTALL_PROGRAM) rdx $(INSTALLROOT)$(bindir)/rdx - $(INSTALL_PROGRAM) rdflib $(INSTALLROOT)$(bindir)/rdflib - $(INSTALL_PROGRAM) rdf2bin $(INSTALLROOT)$(bindir)/rdf2bin - cd $(INSTALLROOT)$(bindir) && rm -f rdf2com && $(LN_S) rdf2bin rdf2com ---- 65,82 ---- - $(CC) -c $(CFLAGS) $(top_srcdir)/nasmlib.c - - clean: -+ rm -f *.o rdfdump ldrdf rdx rdflib rdf2bin rdf2com rdf2ihx - - spotless: clean - rm -f Makefile - - distclean: spotless - -+ install: rdfdump ldrdf rdx rdflib rdf2bin rdf2com rdf2ihx - $(INSTALL_PROGRAM) rdfdump $(INSTALLROOT)$(bindir)/rdfdump - $(INSTALL_PROGRAM) ldrdf $(INSTALLROOT)$(bindir)/ldrdf - $(INSTALL_PROGRAM) rdx $(INSTALLROOT)$(bindir)/rdx - $(INSTALL_PROGRAM) rdflib $(INSTALLROOT)$(bindir)/rdflib - $(INSTALL_PROGRAM) rdf2bin $(INSTALLROOT)$(bindir)/rdf2bin -+ $(INSTALL_PROGRAM) rdf2ihx $(INSTALLROOT)$(bindir)/rdf2ihx - cd $(INSTALLROOT)$(bindir) && rm -f rdf2com && $(LN_S) rdf2bin rdf2com +# $Id$ +# +# Auto-configuring Makefile for RDOFF object file utils; part of the +# Netwide Assembler +# +# The Netwide Assembler is copyright (C) 1996 Simon Tatham and +# Julian Hall. All rights reserved. The software is +# redistributable under the licence given in the file "Licence" +# distributed in the NASM archive. + +top_srcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +mandir = @mandir@ + +CC = @CC@ +CFLAGS = @CFLAGS@ @GCCFLAGS@ -I$(srcdir) -I$(top_srcdir) +LDFLAGS = @LDFLAGS@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +LN_S = @LN_S@ + +LDRDFLIBS = rdoff.o nasmlib.o symtab.o collectn.o rdlib.o segtab.o hash.o +RDXLIBS = rdoff.o rdfload.o symtab.o collectn.o hash.o + +.c.o: + $(CC) -c $(CFLAGS) $< + +all: rdfdump ldrdf rdx rdflib rdf2bin rdf2com rdf2ihx + +rdfdump: rdfdump.o + $(CC) $(LDFLAGS) -o rdfdump rdfdump.o +ldrdf: ldrdf.o $(LDRDFLIBS) + $(CC) $(LDFLAGS) -o ldrdf ldrdf.o $(LDRDFLIBS) +rdx: rdx.o $(RDXLIBS) + $(CC) $(LDFLAGS) -o rdx rdx.o $(RDXLIBS) +rdflib: rdflib.o + $(CC) $(LDFLAGS) -o rdflib rdflib.o +rdf2bin: rdf2bin.o $(RDXLIBS) nasmlib.o + $(CC) $(LDFLAGS) -o rdf2bin rdf2bin.o $(RDXLIBS) nasmlib.o +rdf2com: + rm -f rdf2com && $(LN_S) rdf2bin rdf2com +rdf2ihx: rdf2ihx.o $(RDXLIBS) nasmlib.o + $(CC) $(LDFLAGS) -o rdf2ihx rdf2ihx.o $(RDXLIBS) nasmlib.o + +rdf2ihx.o: rdf2ihx.c +rdf2bin.o: rdf2bin.c +rdfdump.o: rdfdump.c +rdoff.o: rdoff.c rdoff.h +ldrdf.o: ldrdf.c rdoff.h $(top_srcdir)/nasmlib.h symtab.h collectn.h rdlib.h +symtab.o: symtab.c symtab.h +collectn.o: collectn.c collectn.h +rdx.o: rdx.c rdoff.h rdfload.h symtab.h +rdfload.o: rdfload.c rdfload.h rdoff.h collectn.h symtab.h +rdlib.o: rdlib.c rdlib.h +rdflib.o: rdflib.c +segtab.o: segtab.c + +nasmlib.o: $(top_srcdir)/nasmlib.c +## $(CC) -c $(CFLAGS) $(top_srcdir)/nasmlib.c + cd $(top_srcdir);make nasmlib.o + cp $(top_srcdir)/nasmlib.o $(srcdir) + +clean: + rm -f *.o rdfdump ldrdf rdx rdflib rdf2bin rdf2com rdf2ihx + +spotless: clean + rm -f Makefile + +distclean: spotless + +install: rdfdump ldrdf rdx rdflib rdf2bin rdf2com rdf2ihx + $(INSTALL_PROGRAM) rdfdump $(INSTALLROOT)$(bindir)/rdfdump + $(INSTALL_PROGRAM) ldrdf $(INSTALLROOT)$(bindir)/ldrdf + $(INSTALL_PROGRAM) rdx $(INSTALLROOT)$(bindir)/rdx + $(INSTALL_PROGRAM) rdflib $(INSTALLROOT)$(bindir)/rdflib + $(INSTALL_PROGRAM) rdf2bin $(INSTALLROOT)$(bindir)/rdf2bin + $(INSTALL_PROGRAM) rdf2ihx $(INSTALLROOT)$(bindir)/rdf2ihx + cd $(INSTALLROOT)$(bindir) && rm -f rdf2com && $(LN_S) rdf2bin rdf2com diff --git a/rdoff/ldrdf.c b/rdoff/ldrdf.c index d9aac2f..3d9d749 100644 --- a/rdoff/ldrdf.c +++ b/rdoff/ldrdf.c @@ -1,31 +1,1347 @@ -*************** -*** 29,42 **** - #include <stdlib.h> - #include <string.h> - - #include "rdoff.h" - #include "symtab.h" - #include "collectn.h" - #include "rdlib.h" - #include "segtab.h" -- #include "multboot.h" - -- #define LDRDF_VERSION "1.01 alpha 2" - - #define RDF_MAXSEGS 64 - /* #define STINGY_MEMORY */ ---- 29,42 ---- - #include <stdlib.h> - #include <string.h> - -+ #include "multboot.h" - #include "rdoff.h" - #include "symtab.h" - #include "collectn.h" - #include "rdlib.h" - #include "segtab.h" - -+ #define LDRDF_VERSION "1.02" - - #define RDF_MAXSEGS 64 - /* #define STINGY_MEMORY */ +/* ldrdf.c RDOFF Object File linker/loader main program + * + * The Netwide Assembler is copyright (C) 1996 Simon Tatham and + * Julian Hall. All rights reserved. The software is + * redistributable under the licence given in the file "Licence" + * distributed in the NASM archive. + */ + +/* + * TODO: actually get this new version working! + * - finish off write_output() - appears to be done + * - implement library searching - appears to be done + * - maybe we only want to do one pass, for performance reasons? + * this makes things a little harder, but unix 'ld' copes... + * - implement command line options - appears to be done + * - improve symbol table implementation - done, thanks to Graeme Defty + * - keep a cache of symbol names in each library module so + * we don't have to constantly recheck the file + * - general performance improvements + * + * BUGS & LIMITATIONS: this program doesn't support multiple code, data + * or bss segments, therefore for 16 bit programs whose code, data or BSS + * segment exceeds 64K in size, it will not work. This program probably + * wont work if compiled by a 16 bit compiler. Try DJGPP if you're running + * under DOS. '#define STINGY_MEMORY' may help a little. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "multboot.h" +#include "rdoff.h" +#include "symtab.h" +#include "collectn.h" +#include "rdlib.h" +#include "segtab.h" + +#define LDRDF_VERSION "1.02" + +#define RDF_MAXSEGS 64 +/* #define STINGY_MEMORY */ + +/* ======================================================================= + * Types & macros that are private to this program + */ + +struct segment_infonode { + int dest_seg; /* output segment to be placed into, -1 to + skip linking this segment */ + long reloc; /* segment's relocation factor */ +}; + + +struct modulenode { + rdffile f; /* the RDOFF file structure */ + struct segment_infonode seginfo[RDF_MAXSEGS]; /* what are we doing + with each segment? */ + void * header; + char * name; + struct modulenode * next; + long bss_reloc; +}; + +#include "ldsegs.h" + +#define newstr(str) strcpy(malloc(strlen(str) + 1),str) +#define newstrcat(s1,s2) strcat(strcpy(malloc(strlen(s1)+strlen(s2)+1),s1),s2) + +/* ========================================================================== + * Function prototypes of private utility functions + */ + +void processmodule(const char * filename, struct modulenode * mod); +int allocnewseg(int16 type,int16 reserved); +int findsegment(int16 type,int16 reserved); +void symtab_add(const char * symbol, int segment, long offset); +int symtab_get(const char * symbol, int * segment, long * offset); + +/* ========================================================================= + * Global data structures. + */ + +/* a linked list of modules that will be included in the output */ +struct modulenode * modules = NULL; +struct modulenode * lastmodule = NULL; + +/* a linked list of libraries to be searched for unresolved imported symbols */ +struct librarynode * libraries = NULL; +struct librarynode * lastlib = NULL; + +/* the symbol table */ +void * symtab = NULL; + +/* objects search path */ +char * objpath = NULL; + +/* libraries search path */ +char * libpath = NULL; + +/* error file */ +static FILE * error_file; + +#ifdef _MULTBOOT_H + +/* loading address for multiboot header */ +unsigned MBHloadAddr; + +/* + * Tiny code that moves RDF loader to its working memory region: + * mov esi,SOURCE_ADDR ; BE xx xx xx xx + * mov edi,DEST_ADDR ; BF xx xx xx xx + * mov esp,edi ; 89 FC + * push edi ; 57 + * mov ecx,RDFLDR_LENGTH/4 ; B9 xx xx xx xx + * cld ; FC + * rep movsd ; F3 A5 + * ret ; C3 + */ + +#define RDFLDR_LENGTH 4096 /* Loader will be moved to unused */ +#define RDFLDR_DESTLOC 0xBF000 /* video page */ + +unsigned char RDFloaderMover[]={ + 0xBE, 0, 0, 0, 0, 0xBF, 0, 0xF0, 0xB, 0, + 0x89, 0xFC, 0x57, + 0xB9, 0, 4, 0, 0, + 0xFC, 0xF3, 0xA5, 0xC3 +}; + +#endif + +/* the header of the output file, built up stage by stage */ +rdf_headerbuf * newheader = NULL; + +/* The current state of segment allocation, including information about + * which output segment numbers have been allocated, and their types and + * amount of data which has already been allocated inside them. + */ +struct SegmentHeaderRec outputseg[RDF_MAXSEGS]; +int nsegs = 0; +long bss_length; + +/* global options which affect how the program behaves */ +struct ldrdfoptions { + int verbose; + int align; + int warnUnresolved; + int errorUnresolved; + int strip; + int respfile; + int stderr_redir; + int objpath; + int libpath; + int addMBheader; +} options; + +int errorcount = 0; /* determines main program exit status */ + +/* ========================================================================= + * Utility functions + */ + + +/* + * initsegments() + * + * sets up segments 0, 1, and 2, the initial code data and bss segments + */ + +void initsegments() +{ + nsegs = 3; + outputseg[0].type = 1; + outputseg[0].number = 0; + outputseg[0].reserved = 0; + outputseg[0].length = 0; + outputseg[1].type = 2; + outputseg[1].number = 1; + outputseg[1].reserved = 0; + outputseg[1].length = 0; + outputseg[2].type = 0xFFFF; /* reserved segment type */ + outputseg[2].number = 2; + outputseg[2].reserved = 0; + outputseg[2].length = 0; + bss_length = 0; +} + +/* + * loadmodule + * + * Determine the characteristics of a module, and decide what to do with + * each segment it contains (including determining destination segments and + * relocation factors for segments that are kept). + */ + +void loadmodule(const char * filename) +{ + if (options.verbose) + printf("loading `%s'\n", filename); + + /* allocate a new module entry on the end of the modules list */ + if (!modules) + { + modules = malloc (sizeof(*modules)); + lastmodule = modules; + } + else + { + lastmodule->next = malloc (sizeof(*modules)); + lastmodule = lastmodule->next; + } + + if ( ! lastmodule) + { + fprintf(stderr, "ldrdf: out of memory\n"); + exit(1); + } + + /* open the file using 'rdfopen', which returns nonzero on error */ + + if (rdfopen(&lastmodule->f, filename) != 0) + { + rdfperror("ldrdf", filename); + exit(1); + } + + /* + * store information about the module, and determine what segments + * it contains, and what we should do with them (determine relocation + * factor if we decide to keep them) + */ + + lastmodule->header = NULL; + lastmodule->name = strdup(filename); + lastmodule->next = NULL; + + processmodule(filename, lastmodule); +} + +/* + * processmodule() + * + * step through each segment, determine what exactly we're doing with + * it, and if we intend to keep it, determine (a) which segment to + * put it in and (b) whereabouts in that segment it will end up. + * (b) is fairly easy, cos we're now keeping track of how big each segment + * in our output file is... + */ + +void processmodule(const char * filename, struct modulenode * mod) +{ + struct segconfig sconf; + int seg, outseg; + void * header; + rdfheaderrec * hr; + long bssamount = 0; + + for (seg = 0; seg < mod->f.nsegs; seg++) + { + /* + * get the segment configuration for this type from the segment + * table. getsegconfig() is a macro, defined in ldsegs.h. + */ + getsegconfig(sconf, mod->f.seg[seg].type); + + if (options.verbose > 1) { + printf ("%s %04x [%04x:%10s] ", filename, mod->f.seg[seg].number, + mod->f.seg[seg].type, sconf.typedesc); + } + /* + * sconf->dowhat tells us what to do with a segment of this type. + */ + switch (sconf.dowhat) { + case SEG_IGNORE: + /* + * Set destination segment to -1, to indicate that this segment + * should be ignored for the purpose of output, ie it is left + * out of the linked executable. + */ + mod->seginfo[seg].dest_seg = -1; + if (options.verbose > 1) printf("IGNORED\n"); + break; + + case SEG_NEWSEG: + /* + * The configuration tells us to create a new segment for + * each occurrence of this segment type. + */ + outseg = allocnewseg(sconf.mergetype, + mod->f.seg[seg].reserved); + mod->seginfo[seg].dest_seg = outseg; + mod->seginfo[seg].reloc = 0; + outputseg[outseg].length = mod->f.seg[seg].length; + if (options.verbose > 1) + printf ("=> %04x:%08lx (+%04lx)\n", outseg, + mod->seginfo[seg].reloc, + mod->f.seg[seg].length); + break; + + case SEG_MERGE: + /* + * The configuration tells us to merge the segment with + * a previously existing segment of type 'sconf.mergetype', + * if one exists. Otherwise a new segment is created. + * This is handled transparently by 'findsegment()'. + */ + outseg = findsegment(sconf.mergetype, + mod->f.seg[seg].reserved); + mod->seginfo[seg].dest_seg = outseg; + + /* + * We need to add alignment to these segments. + */ + if (outputseg[outseg].length % options.align != 0) + outputseg[outseg].length += + options.align - (outputseg[outseg].length % options.align); + + mod->seginfo[seg].reloc = outputseg[outseg].length; + outputseg[outseg].length += mod->f.seg[seg].length; + + if (options.verbose > 1) + printf ("=> %04x:%08lx (+%04lx)\n", outseg, + mod->seginfo[seg].reloc, + mod->f.seg[seg].length); + } + + } + + /* + * extract symbols from the header, and dump them into the + * symbol table + */ + header = malloc(mod->f.header_len); + if (!header) { + fprintf(stderr, "ldrdf: not enough memory\n"); + exit(1); + } + if (rdfloadseg(&mod->f, RDOFF_HEADER, header)) { + rdfperror("ldrdf", filename); + exit(1); + } + + while ((hr = rdfgetheaderrec (&mod->f))) + { + switch(hr->type) { + case 2: /* imported symbol - define with seg = -1 */ + case 7: + symtab_add(hr->i.label, -1, 0); + break; + + case 3: /* exported symbol */ + { + int destseg; + long destreloc; + + if (hr->e.segment == 2) + { + destreloc = bss_length; + if (destreloc % options.align != 0) + destreloc += options.align - (destreloc % options.align); + destseg = 2; + } + else + { + if ((destseg = mod->seginfo[(int)hr->e.segment].dest_seg) == -1) + continue; + destreloc = mod->seginfo[(int)hr->e.segment].reloc; + } + symtab_add(hr->e.label, destseg, destreloc + hr->e.offset); + break; + } + + case 5: /* BSS reservation */ + /* + * first, amalgamate all BSS reservations in this module + * into one, because we allow this in the output format. + */ + bssamount += hr->b.amount; + break; + } + } + + if (bssamount != 0) + { + /* + * handle the BSS segment - first pad the existing bss length + * to the correct alignment, then store the length in bss_reloc + * for this module. Then add this module's BSS length onto + * bss_length. + */ + if (bss_length % options.align != 0) + bss_length += options.align - (bss_length % options.align); + + mod->bss_reloc = bss_length; + if (options.verbose > 1) { + printf ("%s 0002 [ BSS] => 0002:%08lx (+%04lx)\n", + filename, bss_length, bssamount); + } + bss_length += bssamount; + } + +#ifdef STINGY_MEMORY + /* + * we free the header buffer here, to save memory later. + * this isn't efficient, but probably halves the memory usage + * of this program... + */ + mod->f.header_loc = NULL; + free(header); + +#endif + +} + + +/* + * Look in list for module by its name. + */ +int lookformodule(const char *name) + { + struct modulenode *curr=modules; + + while(curr) { + if (!strcmp(name,curr->name)) return 1; + curr = curr->next; + } + return 0; + } + + +/* + * allocnewseg() + * findsegment() + * + * These functions manipulate the array of output segments, and are used + * by processmodule(). allocnewseg() allocates a segment in the array, + * initialising it to be empty. findsegment() first scans the array for + * a segment of the type requested, and if one isn't found allocates a + * new one. + */ +int allocnewseg(int16 type,int16 reserved) +{ + outputseg[nsegs].type = type; + outputseg[nsegs].number = nsegs; + outputseg[nsegs].reserved = reserved; + outputseg[nsegs].length = 0; + outputseg[nsegs].offset = 0; + outputseg[nsegs].data = NULL; + + return nsegs++; +} + +int findsegment(int16 type,int16 reserved) +{ + int i; + + for (i = 0; i < nsegs; i++) + if (outputseg[i].type == type) return i; + + return allocnewseg(type,reserved); +} + +/* + * symtab_add() + * + * inserts a symbol into the global symbol table, which associates symbol + * names either with addresses, or a marker that the symbol hasn't been + * resolved yet, or possibly that the symbol has been defined as + * contained in a dynamic [load time/run time] linked library. + * + * segment = -1 => not yet defined + * segment = -2 => defined as dll symbol + * + * If the symbol is already defined, and the new segment >= 0, then + * if the original segment was < 0 the symbol is redefined, otherwise + * a duplicate symbol warning is issued. If new segment == -1, this + * routine won't change a previously existing symbol. It will change + * to segment = -2 only if the segment was previously < 0. + */ + +void symtab_add(const char * symbol, int segment, long offset) +{ + symtabEnt * ste; + + ste = symtabFind(symtab, symbol); + if (ste) + { + if (ste->segment >= 0) { + /* + * symbol previously defined + */ + if (segment < 0) return; + fprintf (error_file, "warning: `%s' redefined\n", symbol); + return; + } + + /* + * somebody wanted the symbol, and put an undefined symbol + * marker into the table + */ + if (segment == -1) return; + /* + * we have more information now - update the symbol's entry + */ + ste->segment = segment; + ste->offset = offset; + ste->flags = 0; + return; + } + /* + * this is the first declaration of this symbol + */ + ste = malloc(sizeof(symtabEnt)); + if (!ste) { + fprintf(stderr, "ldrdf: out of memory\n"); + exit(1); + } + ste->name = strdup(symbol); + ste->segment = segment; + ste->offset = offset; + ste->flags = 0; + symtabInsert(symtab, ste); +} + +/* + * symtab_get() + * + * Retrieves the values associated with a symbol. Undefined symbols + * are assumed to have -1:0 associated. Returns 1 if the symbol was + * successfully located. + */ + +int symtab_get(const char * symbol, int * segment, long * offset) +{ + symtabEnt * ste = symtabFind(symtab, symbol); + if (!ste) { + *segment = -1; + *offset = 0; + return 0; + } + else + { + *segment = ste->segment; + *offset = ste->offset; + return 1; + } +} + +/* + * add_library() + * + * checks that a library can be opened and is in the correct format, + * then adds it to the linked list of libraries. + */ + +void add_library(const char * name) +{ + if (rdl_verify(name)) { + rdl_perror("ldrdf", name); + errorcount++; + return; + } + if (! libraries) + { + lastlib = libraries = malloc(sizeof(*libraries)); + if (! libraries) { + fprintf(stderr, "ldrdf: out of memory\n"); + exit(1); + } + } + else + { + lastlib->next = malloc(sizeof(*libraries)); + if (!lastlib->next) { + fprintf(stderr, "ldrdf: out of memory\n"); + exit(1); + } + lastlib = lastlib->next; + } + lastlib->next = NULL; + if (rdl_open(lastlib, name)) { + rdl_perror("ldrdf", name); + errorcount++; + return; + } +} + +/* + * search_libraries() + * + * scans through the list of libraries, attempting to match symbols + * defined in library modules against symbols that are referenced but + * not defined (segment = -1 in the symbol table) + * + * returns 1 if any extra library modules are included, indicating that + * another pass through the library list should be made (possibly). + */ + +int search_libraries() +{ + struct librarynode * cur; + rdffile f; + int i; + void * header; + int segment; + long offset; + int doneanything = 0, pass = 1, keepfile; + rdfheaderrec * hr; + + cur = libraries; + + while (cur) + { + if (options.verbose > 2) + printf("scanning library `%s', pass %d...\n", cur->name, pass); + + for (i = 0; rdl_openmodule(cur, i, &f) == 0; i++) + { + if (pass == 2 && lookformodule(f.name)) continue; + + if (options.verbose > 3) + printf(" looking in module `%s'\n", f.name); + + header = malloc(f.header_len); + if (!header) { + fprintf(stderr, "ldrdf: not enough memory\n"); + exit(1); + } + if (rdfloadseg(&f, RDOFF_HEADER, header)) { + rdfperror("ldrdf", f.name); + errorcount++; + return 0; + } + + keepfile = 0; + + while ((hr = rdfgetheaderrec (&f))) + { + /* we're only interested in exports, so skip others: */ + if (hr->type != 3) continue; + + /* + * Find the symbol in the symbol table. If the symbol isn't + * defined, we aren't interested, so go on to the next. + * If it is defined as anything but -1, we're also not + * interested. But if it is defined as -1, insert this + * module into the list of modules to use, and go + * immediately on to the next module... + */ + if (! symtab_get(hr->e.label, &segment, &offset) + || segment != -1) + { + continue; + } + + doneanything = 1; + keepfile = 1; + + /* + * as there are undefined symbols, we can assume that + * there are modules on the module list by the time + * we get here. + */ + lastmodule->next = malloc(sizeof(*lastmodule->next)); + if (!lastmodule->next) { + fprintf(stderr, "ldrdf: not enough memory\n"); + exit(1); + } + lastmodule = lastmodule->next; + memcpy(&lastmodule->f, &f, sizeof(f)); + lastmodule->name = strdup(f.name); + lastmodule->next = NULL; + processmodule(f.name, lastmodule); + break; + } + if (!keepfile) + { + free(f.name); + f.name = NULL; + f.fp = NULL; + } + } + if (rdl_error != 0 && rdl_error != RDL_ENOTFOUND) + rdl_perror("ldrdf", cur->name); + + cur = cur->next; + if (cur == NULL && pass == 1) { + cur = libraries; + pass++; + } + } + + return doneanything; +} + +/* + * write_output() + * + * this takes the linked list of modules, and walks through it, merging + * all the modules into a single output module, and then writes this to a + * file. + */ +void write_output(const char * filename) +{ + FILE * f; + rdf_headerbuf * rdfheader; + struct modulenode * cur; + int i, availableseg, seg, localseg, isrelative; + void * header; + rdfheaderrec * hr, newrec; + symtabEnt * se; + segtab segs; + long offset; + byte * data; + + if ((f = fopen(filename, "wb"))==NULL) { + fprintf(stderr, "ldrdf: couldn't open %s for output\n", filename); + exit(1); + } + if ((rdfheader=rdfnewheader())==NULL) { + fprintf(stderr, "ldrdf: out of memory\n"); + exit(1); + } + + /* + * Add multiboot header if appropriate option is specified. + * Multiboot record *MUST* be the first record in output file. + */ + if (options.addMBheader) { + if (options.verbose) + puts("\nadding multiboot header record"); + + hr = (rdfheaderrec *) malloc(sizeof(struct MultiBootHdrRec)); + hr->mbh.type = 9; + hr->mbh.reclen = sizeof(struct tMultiBootHeader)+RDFLDRMOVER_SIZE; + + hr->mbh.mb.Magic = MB_MAGIC; + hr->mbh.mb.Flags = MB_FL_KLUDGE; + hr->mbh.mb.Checksum = ~(MB_MAGIC+MB_FL_KLUDGE-1); + hr->mbh.mb.HeaderAddr = MBHloadAddr+16; + hr->mbh.mb.LoadAddr = MBHloadAddr; + hr->mbh.mb.Entry = MBHloadAddr+16+sizeof(struct tMultiBootHeader); + + memcpy(hr->mbh.mover,RDFloaderMover,RDFLDRMOVER_SIZE); + + rdfaddheader(rdfheader,hr); + free(hr); + } + + if (options.verbose) + printf ("\nbuilding output module (%d segments)\n", nsegs); + + /* + * Allocate the memory for the segments. We may be better off + * building the output module one segment at a time when running + * under 16 bit DOS, but that would be a slower way of doing this. + * And you could always use DJGPP... + */ + for (i = 0; i < nsegs; i++) + { + outputseg[i].data=NULL; + if(!outputseg[i].length) continue; + outputseg[i].data = malloc(outputseg[i].length); + if (!outputseg[i].data) { + fprintf(stderr, "ldrdf: out of memory\n"); + exit(1); + } + } + + /* + * initialise availableseg, used to allocate segment numbers for + * imported and exported labels... + */ + availableseg = nsegs; + + /* + * Step through the modules, performing required actions on each one + */ + for (cur = modules; cur; cur=cur->next) + { + /* + * Read the actual segment contents into the correct places in + * the newly allocated segments + */ + + for (i = 0; i < cur->f.nsegs; i++) + { + int dest = cur->seginfo[i].dest_seg; + + if (dest == -1) continue; + if (rdfloadseg(&cur->f, i, + outputseg[dest].data + cur->seginfo[i].reloc)) + { + rdfperror("ldrdf", cur->name); + exit(1); + } + } + + /* + * Perform fixups, and add new header records where required + */ + + header = malloc(cur->f.header_len); + if (!header) { + fprintf(stderr, "ldrdf: out of memory\n"); + exit(1); + } + + if (cur->f.header_loc) + rdfheaderrewind(&cur->f); + else + if (rdfloadseg(&cur->f, RDOFF_HEADER, header)) + { + rdfperror("ldrdf", cur->name); + exit(1); + } + + /* + * we need to create a local segment number -> location + * table for the segments in this module. + */ + init_seglocations(&segs); + for (i = 0; i < cur->f.nsegs; i++) + { + add_seglocation(&segs, cur->f.seg[i].number, + cur->seginfo[i].dest_seg, cur->seginfo[i].reloc); + } + /* + * and the BSS segment (doh!) + */ + add_seglocation (&segs, 2, 2, cur->bss_reloc); + + while ((hr = rdfgetheaderrec(&cur->f))) + { + switch(hr->type) { + case 1: /* relocation record - need to do a fixup */ + /* + * First correct the offset stored in the segment from + * the start of the segment (which may well have changed). + * + * To do this we add to the number stored the relocation + * factor associated with the segment that contains the + * target segment. + * + * The relocation could be a relative relocation, in which + * case we have to first subtract the amount we've relocated + * the containing segment by. + */ + + if (!get_seglocation(&segs, hr->r.refseg, &seg, &offset)) + { + fprintf(stderr, "%s: reloc to undefined segment %04x\n", + cur->name, (int) hr->r.refseg); + errorcount++; + break; + } + + isrelative = (hr->r.segment & 64) == 64; + hr->r.segment &= 63; + + if (hr->r.segment == 2 || + (localseg = rdffindsegment(&cur->f, hr->r.segment)) == -1) + { + fprintf(stderr, "%s: reloc from %s segment (%d)\n", + cur->name, + hr->r.segment == 2 ? "BSS" : "unknown", + hr->r.segment); + errorcount++; + break; + } + + if (hr->r.length != 1 && hr->r.length != 2 && hr->r.length!=4) + { + fprintf(stderr, "%s: nonstandard length reloc " + "(%d bytes)\n", cur->name, hr->r.length); + errorcount++; + break; + } + + /* + * okay, now the relocation is in the segment pointed to by + * cur->seginfo[localseg], and we know everything else is + * okay to go ahead and do the relocation + */ + data = outputseg[cur->seginfo[localseg].dest_seg].data; + data += cur->seginfo[localseg].reloc + hr->r.offset; + + /* + * data now points to the reference that needs + * relocation. Calculate the relocation factor. + * Factor is: + * offset of referred object in segment [in offset] + * (- relocation of localseg, if ref is relative) + * For simplicity, the result is stored in 'offset'. + * Then add 'offset' onto the value at data. + */ + + if (isrelative) offset -= cur->seginfo[localseg].reloc; + switch (hr->r.length) + { + case 1: + offset += *data; + if (offset < -127 || offset > 128) + fprintf(error_file, "warning: relocation out of range " + "at %s(%02x:%08lx)\n", cur->name, + (int)hr->r.segment, hr->r.offset); + *data = (char) offset; + break; + case 2: + offset += * (short *)data; + if (offset < -32767 || offset > 32768) + fprintf(error_file, "warning: relocation out of range " + "at %s(%02x:%08lx)\n", cur->name, + (int)hr->r.segment, hr->r.offset); + * (short *)data = (short) offset; + break; + case 4: + * (long *)data += offset; + /* we can't easily detect overflow on this one */ + break; + } + + /* + * If the relocation was relative between two symbols in + * the same segment, then we're done. + * + * Otherwise, we need to output a new relocation record + * with the references updated segment and offset... + */ + if (! isrelative + || cur->seginfo[localseg].dest_seg != seg) + { + hr->r.segment = cur->seginfo[localseg].dest_seg; + hr->r.offset += cur->seginfo[localseg].reloc; + hr->r.refseg = seg; + rdfaddheader(rdfheader, hr); + } + break; + + case 2: /* import symbol */ + case 7: + /* + * scan the global symbol table for the symbol + * and associate its location with the segment number + * for this module + */ + se = symtabFind(symtab, hr->i.label); + if (!se || se->segment == -1) { + if (options.warnUnresolved) { + fprintf(error_file, "warning: unresolved reference to `%s'" + " in module `%s'\n", hr->i.label, cur->name); + if (options.errorUnresolved==1) errorcount++; + } + /* + * we need to allocate a segment number for this + * symbol, and store it in the symbol table for + * future reference + */ + if (!se) { + se=malloc(sizeof(*se)); + if (!se) { + fprintf(stderr, "ldrdf: out of memory\n"); + exit(1); + } + se->name = strdup(hr->i.label); + se->flags = 0; + se->segment = availableseg++; + se->offset = 0; + symtabInsert(symtab, se); + } + else { + se->segment = availableseg++; + se->offset = 0; + } + /* + * output a header record that imports it to the + * recently allocated segment number... + */ + newrec = *hr; + newrec.i.segment = se->segment; + rdfaddheader(rdfheader, &newrec); + } + + add_seglocation(&segs, hr->i.segment, se->segment, se->offset); + + break; + + case 3: /* export symbol */ + /* + * need to insert an export for this symbol into the new + * header, unless we're stripping symbols [unless this + * symbol is in an explicit keep list]. *** FIXME *** + */ + if (options.strip) + break; + + if (hr->e.segment == 2) { + seg = 2; + offset = cur->bss_reloc; + } + else { + localseg = rdffindsegment(&cur->f, hr->e.segment); + if (localseg == -1) { + fprintf(stderr, "%s: exported symbol `%s' from " + "unrecognised segment\n", cur->name, + hr->e.label); + errorcount++; + break; + } + offset = cur->seginfo[localseg].reloc; + seg = cur->seginfo[localseg].dest_seg; + } + + hr->e.segment = seg; + hr->e.offset += offset; + rdfaddheader(rdfheader, hr); + break; + + case 8: /* module name */ + /* + * insert module name record if export symbols + * are not stripped. + */ + if (options.strip) break; + + rdfaddheader(rdfheader, hr); + break; + + case 6: /* segment fixup */ + /* + * modify the segment numbers if necessary, and + * pass straight through to the output module header + * + * *** FIXME *** + */ + if (hr->r.segment == 2) { + fprintf(stderr, "%s: segment fixup in BSS section\n", + cur->name); + errorcount++; + break; + } + localseg = rdffindsegment(&cur->f, hr->r.segment); + if (localseg == -1) { + fprintf(stderr, "%s: segment fixup in unrecognised" + " segment (%d)\n", cur->name, hr->r.segment); + errorcount++; + break; + } + hr->r.segment = cur->seginfo[localseg].dest_seg; + hr->r.offset += cur->seginfo[localseg].reloc; + + if (!get_seglocation(&segs, hr->r.refseg, &seg, &offset)) + { + fprintf(stderr, "%s: segment fixup to undefined " + "segment %04x\n", cur->name, (int)hr->r.refseg); + errorcount++; + break; + } + hr->r.refseg = seg; + rdfaddheader(rdfheader, hr); + break; + } + } + + free(header); + done_seglocations(&segs); + + } + + /* + * combined BSS reservation for the entire results + */ + newrec.type = 5; + newrec.b.reclen = 4; + newrec.b.amount = bss_length; + rdfaddheader(rdfheader, &newrec); + + /* + * Write the header + */ + for (i = 0; i < nsegs; i++) + { + if (i == 2) continue; + rdfaddsegment (rdfheader, outputseg[i].length); + } + + if (options.addMBheader) { + struct MultiBootHdrRec *mbhrec = (struct MultiBootHdrRec *)(rdfheader->buf->buffer); + unsigned l = membuflength(rdfheader->buf) + 14 + + 10*rdfheader->nsegments + rdfheader->seglength; + unsigned *ldraddr = (unsigned *)(mbhrec->mover+1); + + mbhrec->mb.LoadEndAddr = MBHloadAddr+l+10+RDFLDR_LENGTH; + mbhrec->mb.BSSendAddr = mbhrec->mb.LoadEndAddr; + *ldraddr = MBHloadAddr+l+10; + } + + rdfwriteheader(f, rdfheader); + rdfdoneheader(rdfheader); + /* + * Step through the segments, one at a time, writing out into + * the output file + */ + + for (i = 0; i < nsegs; i++) + { + int16 s; + long l; + + if (i == 2) continue; + + s = translateshort(outputseg[i].type); + fwrite(&s, 2, 1, f); + s = translateshort(outputseg[i].number); + fwrite(&s, 2, 1, f); + s = translateshort(outputseg[i].reserved); + fwrite(&s, 2, 1, f); + l = translatelong(outputseg[i].length); + fwrite(&l, 4, 1, f); + + fwrite(outputseg[i].data, outputseg[i].length, 1, f); + } + + fwrite("\0\0\0\0\0\0\0\0\0\0", 10, 1, f); +} + +/* ========================================================================= + * Main program + */ + +void usage() +{ + printf("usage:\n"); + printf(" ldrdf [options] object modules ... [-llibrary ...]\n"); + printf(" ldrdf -r\n"); + printf("options:\n"); + printf(" -v[=n] increases verbosity by 1, or sets it to n\n"); + printf(" -a nn sets segment alignment value (default 16)\n"); + printf(" -s strips exported symbols\n"); + printf(" -x warn about unresolved symbols\n"); + printf(" -o name write output in file 'name'\n"); + printf(" -j path specify objects search path\n"); + printf(" -L path specify libraries search path\n"); + printf(" -mbh [address] add multiboot header to output file. Default\n"); + printf(" loading address is 0x110000\n"); + exit(0); +} + +int main(int argc, char ** argv) +{ + char * outname = "aout.rdf"; + int moduleloaded = 0; + char *respstrings[128] = {0, }; + + options.verbose = 0; + options.align = 16; + options.warnUnresolved = 0; + options.strip = 0; + + error_file = stderr; + + argc --, argv ++; + if (argc == 0) usage(); + while (argc && **argv == '-' && argv[0][1] != 'l') + { + switch(argv[0][1]) { + case 'r': + printf("ldrdf (linker for RDF files) version " LDRDF_VERSION "\n"); + printf( _RDOFF_H "\n"); + exit(0); + case 'v': + if (argv[0][2] == '=') { + options.verbose = argv[0][3] - '0'; + if (options.verbose < 0 || options.verbose > 9) { + fprintf(stderr, "ldrdf: verbosity level must be a number" + " between 0 and 9\n"); + exit(1); + } + } + else + options.verbose++; + break; + case 'a': + options.align = atoi(argv[1]); + if (options.align <= 0) { + fprintf(stderr, + "ldrdf: -a expects a positive number argument\n"); + exit(1); + } + argv++, argc--; + break; + case 's': + options.strip = 1; + break; + case 'x': + options.warnUnresolved = 1; + if (argv[0][2]=='e') + options.errorUnresolved = 1; + break; + case 'o': + outname = argv[1]; + argv++, argc--; + break; + case 'j': + if (!objpath) + { + options.objpath = 1; + objpath = argv[1]; + argv++, argc--; + break; + } + else + { + fprintf(stderr,"ldrdf: more than one objects search path specified\n"); + exit(1); + } + case 'L': + if (!libpath) + { + options.libpath = 1; + libpath = argv[1]; + argv++, argc--; + break; + } + else + { + fprintf(stderr,"ldrdf: more than one libraries search path specified\n"); + exit(1); + } + case '@': { + int i=0; + char buf[256]; + FILE *f; + + options.respfile = 1; + if (argv[1] != NULL) f = fopen(argv[1],"r"); + else + { + fprintf(stderr,"ldrdf: no response file name specified\n"); + exit(1); + } + + if (f == NULL) + { + fprintf(stderr,"ldrdf: unable to open response file\n"); + exit(1); + } + argc-=2; + while(fgets(buf,sizeof(buf)-1,f)!=NULL) + { + char *p; + if (buf[0]=='\n') continue; + if ((p = strchr(buf,'\n')) != 0) + *p=0; + if (i >= 128) + { + fprintf(stderr,"ldrdf: too many input files\n"); + exit(1); + } + *(respstrings+i) = newstr(buf); + argc++, i++; + } + goto done; + } + case '2': + options.stderr_redir = 1; + error_file = stdout; + break; + case 'm': + if (argv[0][2] == 'b' && argv[0][3] == 'h') { + if (argv[1][0] != '-') { + MBHloadAddr = atoi(argv[1]); + } else { + MBHloadAddr = MB_DEFAULTLOADADDR; + } + options.addMBheader = 1; + break; + } + default: + usage(); + } + argv++, argc--; + } +done: + if (options.verbose > 4) { + printf("ldrdf invoked with options:\n"); + printf(" section alignment: %d bytes\n", options.align); + printf(" output name: `%s'\n", outname); + if (options.strip) + printf(" strip symbols\n"); + if (options.warnUnresolved) + printf(" warn about unresolved symbols\n"); + if (options.errorUnresolved) + printf(" error if unresolved symbols\n"); + if (options.objpath) + printf(" objects search path: %s\n",objpath); + if (options.libpath) + printf(" libraries search path: %s\n",libpath); + if (options.addMBheader) + printf(" loading address for multiboot header: 0x%X\n",MBHloadAddr); + printf("\n"); + } + + symtab = symtabNew(); + initsegments(); + + if (!symtab) { + fprintf(stderr, "ldrdf: out of memory\n"); + exit(1); + } + + if (*respstrings) argv = respstrings; + while (argc) + { + if (!strncmp(*argv, "-l", 2)) /* library */ + { + if(libpath) add_library(newstrcat(libpath,*argv + 2)); + else add_library(*argv + 2); + } + else { + if(objpath) loadmodule(newstrcat(objpath,*argv)); + else loadmodule(*argv); + moduleloaded = 1; + } + argv++, argc--; + } + + if (! moduleloaded) { + printf("ldrdf: nothing to do. ldrdf -h for usage\n"); + return 0; + } + + + search_libraries(); + + if (options.verbose > 2) + { + printf ("symbol table:\n"); + symtabDump(symtab, stdout); + } + + write_output(outname); + + if (errorcount > 0) exit(1); + return 0; +} |