diff options
author | H. Peter Anvin <hpa@zytor.com> | 2002-04-30 21:03:11 +0000 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2002-04-30 21:03:11 +0000 |
commit | caa82a1e66f6b9517ba2199cdc4297bd7be8eaa2 (patch) | |
tree | be5831b0d3284c0af5c3777e6997d50efa910818 /rdoff | |
parent | 9eb185bfdb10f1dfccbb9a702d2801a32cd1b13d (diff) | |
download | nasm-caa82a1e66f6b9517ba2199cdc4297bd7be8eaa2.tar.gz nasm-caa82a1e66f6b9517ba2199cdc4297bd7be8eaa2.tar.bz2 nasm-caa82a1e66f6b9517ba2199cdc4297bd7be8eaa2.zip |
NASM 0.98.16
Diffstat (limited to 'rdoff')
-rw-r--r-- | rdoff/ldrdf.c | 1378 | ||||
-rw-r--r-- | rdoff/rdflib.c | 1 | ||||
-rw-r--r-- | rdoff/rdfload.c | 1 | ||||
-rw-r--r-- | rdoff/rdoff.h | 305 |
4 files changed, 1546 insertions, 139 deletions
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; +} diff --git a/rdoff/rdflib.c b/rdoff/rdflib.c index 5a8cf69..7a00fc6 100644 --- a/rdoff/rdflib.c +++ b/rdoff/rdflib.c @@ -19,7 +19,6 @@ */ #include <stdio.h> -#include <stdlib.h> #include <errno.h> #include <string.h> diff --git a/rdoff/rdfload.c b/rdoff/rdfload.c index 6928ca2..4737282 100644 --- a/rdoff/rdfload.c +++ b/rdoff/rdfload.c @@ -19,7 +19,6 @@ #include <stdlib.h> #include <stdio.h> -#include <string.h> #include "rdfload.h" #include "symtab.h" diff --git a/rdoff/rdoff.h b/rdoff/rdoff.h index 15f26bc..0c57231 100644 --- a/rdoff/rdoff.h +++ b/rdoff/rdoff.h @@ -1,106 +1,199 @@ -*************** -*** 9,15 **** - * as acknowledgement is given in an appropriate manner to its authors, - * with instructions of how to obtain a copy via ftp. - */ -- - #ifndef _RDOFF_H - #define _RDOFF_H "RDOFF2 support routines v0.3" - ---- 9,15 ---- - * as acknowledgement is given in an appropriate manner to its authors, - * with instructions of how to obtain a copy via ftp. - */ -+ - #ifndef _RDOFF_H - #define _RDOFF_H "RDOFF2 support routines v0.3" - -*************** -*** 48,54 **** - struct ExportRec { - byte type; /* must be 3 */ - byte reclen; /* content length */ -- byte segment; /* segment referred to (0/1) */ - long offset; /* offset within segment */ - char label[33]; /* zero terminated as above. max len = 32 chars */ - }; ---- 48,54 ---- - struct ExportRec { - byte type; /* must be 3 */ - byte reclen; /* content length */ -+ byte segment; /* segment referred to (0/1/2) */ - long offset; /* offset within segment */ - char label[33]; /* zero terminated as above. max len = 32 chars */ - }; -*************** -*** 65,70 **** - long amount; /* number of bytes BSS to reserve */ - }; - - /* GenericRec - contains the type and length field, plus a 128 byte - char array 'data', which will probably never be used! */ - ---- 65,92 ---- - long amount; /* number of bytes BSS to reserve */ - }; - -+ struct ModRec { -+ byte type; /* must be 8 */ -+ byte reclen; /* content length */ -+ char modname[128]; /* module name */ -+ }; -+ -+ #ifdef _MULTBOOT_H -+ -+ #define RDFLDRMOVER_SIZE 22 -+ -+ struct MultiBootHdrRec { -+ byte type; /* must be 9 */ -+ byte reclen; /* content length */ -+ #ifdef __GNUC__ -+ struct tMultiBootHeader mb __attribute__ ((packed)); /* MultiBoot header */ -+ #else -+ struct tMultiBootHeader mb; -+ #endif -+ byte mover[RDFLDRMOVER_SIZE]; /* Mover of RDF loader */ -+ }; -+ #endif -+ - /* GenericRec - contains the type and length field, plus a 128 byte - char array 'data', which will probably never be used! */ - -*************** -*** 82,87 **** - struct ExportRec e; /* type == 3 */ - struct DLLRec d; /* type == 4 */ - struct BSSRec b; /* type == 5 */ - } rdfheaderrec; - - struct SegmentHeaderRec { ---- 104,113 ---- - struct ExportRec e; /* type == 3 */ - struct DLLRec d; /* type == 4 */ - struct BSSRec b; /* type == 5 */ -+ struct ModRec m; /* type == 8 */ -+ #ifdef _MULTBOOT_H -+ struct MultiBootHdrRec mbh; /* type == 9 */ -+ #endif - } rdfheaderrec; - - struct SegmentHeaderRec { -*************** -*** 166,170 **** - int rdfaddsegment(rdf_headerbuf *h, long seglength); - int rdfwriteheader(FILE *fp,rdf_headerbuf *h); - void rdfdoneheader(rdf_headerbuf *h); - - #endif /* _RDOFF_H */ ---- 192,199 ---- - int rdfaddsegment(rdf_headerbuf *h, long seglength); - int rdfwriteheader(FILE *fp,rdf_headerbuf *h); - void rdfdoneheader(rdf_headerbuf *h); -+ -+ /* This is needed by linker to write multiboot header record */ -+ int membuflength(memorybuffer *b); - - #endif /* _RDOFF_H */ +/* rdoff.h RDOFF Object File manipulation routines header file + * + * 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. + * + * Permission to use this file in your own projects is granted, as long + * as acknowledgement is given in an appropriate manner to its authors, + * with instructions of how to obtain a copy via ftp. + */ + +#ifndef _RDOFF_H +#define _RDOFF_H "RDOFF2 support routines v0.3" + +/* Some systems don't define this automatically */ +extern char *strdup(const char *); + +typedef unsigned short int16; +typedef unsigned char byte; + +#define RDF_MAXSEGS 64 + +/* the records that can be found in the RDOFF header */ + +struct RelocRec { + byte type; /* must be 1 */ + byte reclen; /* content length */ + byte segment; /* only 0 for code, or 1 for data supported, + but add 64 for relative refs (ie do not require + reloc @ loadtime, only linkage) */ + long offset; /* from start of segment in which reference is loc'd */ + byte length; /* 1 2 or 4 bytes */ + int16 refseg; /* segment to which reference refers to */ +}; + +struct ImportRec { + byte type; /* must be 2 */ + byte reclen; /* content length */ + int16 segment; /* segment number allocated to the label for reloc + records - label is assumed to be at offset zero + in this segment, so linker must fix up with offset + of segment and of offset within segment */ + char label[33]; /* zero terminated... should be written to file until + the zero, but not after it - max len = 32 chars */ +}; + +struct ExportRec { + byte type; /* must be 3 */ + byte reclen; /* content length */ + byte segment; /* segment referred to (0/1/2) */ + long offset; /* offset within segment */ + char label[33]; /* zero terminated as above. max len = 32 chars */ +}; + +struct DLLRec { + byte type; /* must be 4 */ + byte reclen; /* content length */ + char libname[128]; /* name of library to link with at load time */ +}; + +struct BSSRec { + byte type; /* must be 5 */ + byte reclen; /* content length */ + long amount; /* number of bytes BSS to reserve */ +}; + +struct ModRec { + byte type; /* must be 8 */ + byte reclen; /* content length */ + char modname[128]; /* module name */ +}; + +#ifdef _MULTBOOT_H + +#define RDFLDRMOVER_SIZE 22 + +struct MultiBootHdrRec { + byte type; /* must be 9 */ + byte reclen; /* content length */ +#ifdef __GNUC__ + struct tMultiBootHeader mb __attribute__ ((packed)); /* MultiBoot header */ +#else + struct tMultiBootHeader mb; +#endif + byte mover[RDFLDRMOVER_SIZE]; /* Mover of RDF loader */ +}; +#endif + +/* GenericRec - contains the type and length field, plus a 128 byte + char array 'data', which will probably never be used! */ + +struct GenericRec { + byte type; + byte reclen; + char data[128]; +}; + +typedef union RDFHeaderRec { + char type; /* invariant throughout all below */ + struct GenericRec g; + struct RelocRec r; /* type == 1 / 6 */ + struct ImportRec i; /* type == 2 / 7 */ + struct ExportRec e; /* type == 3 */ + struct DLLRec d; /* type == 4 */ + struct BSSRec b; /* type == 5 */ + struct ModRec m; /* type == 8 */ +#ifdef _MULTBOOT_H + struct MultiBootHdrRec mbh; /* type == 9 */ +#endif +} rdfheaderrec; + +struct SegmentHeaderRec { + /* information from file */ + int16 type; + int16 number; + int16 reserved; + long length; + + /* information built up here */ + long offset; + byte *data; /* pointer to segment data if it exists in memory */ +}; + +typedef struct RDFFileInfo { + FILE *fp; /* file descriptor; must be open to use this struct */ + int rdoff_ver; /* should be 1; any higher => not guaranteed to work */ + long header_len; + long header_ofs; + + byte *header_loc; /* keep location of header */ + long header_fp; /* current location within header for reading */ + + struct SegmentHeaderRec seg[RDF_MAXSEGS]; + int nsegs; + + long eof_offset; /* offset of the first byte beyond the end of this + module */ + + char *name; /* name of module in libraries */ + int *refcount; /* pointer to reference count on file, or NULL */ +} rdffile; + +#define BUF_BLOCK_LEN 4088 /* selected to match page size (4096) + * on 80x86 machines for efficiency */ +typedef struct memorybuffer { + int length; + byte buffer[BUF_BLOCK_LEN]; + struct memorybuffer *next; +} memorybuffer; + +typedef struct { + memorybuffer * buf; /* buffer containing header records */ + int nsegments; /* number of segments to be written */ + long seglength; /* total length of all the segments */ +} rdf_headerbuf; + +/* segments used by RDOFF, understood by rdoffloadseg */ +#define RDOFF_CODE 0 +#define RDOFF_DATA 1 +#define RDOFF_HEADER -1 +/* mask for 'segment' in relocation records to find if relative relocation */ +#define RDOFF_RELATIVEMASK 64 +/* mask to find actual segment value in relocation records */ +#define RDOFF_SEGMENTMASK 63 + +extern int rdf_errno; + +/* utility functions */ +int16 translateshort(int16 in); +long translatelong(long in); + +/* RDOFF file manipulation functions */ +int rdfopen(rdffile *f,const char *name); +int rdfopenhere(rdffile *f, FILE *fp, int *refcount, const char *name); +int rdfclose(rdffile *f); +int rdffindsegment(rdffile * f, int segno); +int rdfloadseg(rdffile *f,int segment,void *buffer); +rdfheaderrec *rdfgetheaderrec(rdffile *f); /* returns static storage */ +void rdfheaderrewind(rdffile *f); /* back to start of header */ +void rdfperror(const char *app,const char *name); + +/* functions to write a new RDOFF header to a file - + use rdfnewheader to allocate a header, rdfaddheader to add records to it, + rdfaddsegment to notify the header routines that a segment exists, and + to tell it how long the segment will be. + rdfwriteheader to write the file id, object length, and header + to a file, and then rdfdoneheader to dispose of the header */ + +rdf_headerbuf *rdfnewheader(void); +int rdfaddheader(rdf_headerbuf *h,rdfheaderrec *r); +int rdfaddsegment(rdf_headerbuf *h, long seglength); +int rdfwriteheader(FILE *fp,rdf_headerbuf *h); +void rdfdoneheader(rdf_headerbuf *h); + +/* This is needed by linker to write multiboot header record */ +int membuflength(memorybuffer *b); + +#endif /* _RDOFF_H */ |