📄 ldrdf.c
字号:
/* 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: * - enhance search of required export symbols in libraries (now depends * on modules order in library) * - 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 * won't 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 "rdoff.h"#include "symtab.h"#include "collectn.h"#include "rdlib.h"#include "segtab.h"#define LDRDF_VERSION "1.05"#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;/* file to embed as a generic record */char * generic_rec_file = NULL;/* error file */static FILE * error_file;/* 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 strip; int respfile; int stderr_redir; int objpath; int libpath;} 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, because 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; int bss_was_referenced = 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 RDFREC_IMPORT: /* imported symbol */ case RDFREC_FARIMPORT: /* Define with seg = -1 */ symtab_add(hr->i.label, -1, 0); break; case RDFREC_GLOBAL: { /* exported symbol */ int destseg; long destreloc; if (hr->e.segment == 2) { bss_was_referenced = 1; 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 RDFREC_BSS: /* 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; case RDFREC_COMMON: { /* Common variable */ symtabEnt *ste = symtabFind(symtab, hr->c.label); /* Is the symbol already in the table? */ if (ste) break; /* Align the variable */ if (bss_length % hr->c.align != 0) bss_length += hr->c.align - (bss_length % hr->c.align); if (options.verbose > 1) { printf ("%s %04x common '%s' => 0002:%08lx (+%04lx)\n", filename, hr->c.segment, hr->c.label, bss_length, hr->c.size); } symtab_add(hr->c.label, 2, bss_length); mod->bss_reloc = bss_length; bss_length += hr->c.size; break; } } } if (bssamount != 0 || bss_was_referenced) { /* * 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 the 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -