📄 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 + -