#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define RDOFF_UTILS
#include "rdoff.h"
#include "symtab.h"
#include "collectn.h"
#include "rdlib.h"
#include "segtab.h"
#define LDRDF_VERSION "1.07"
struct segment_infonode {
int dest_seg;
long reloc;
};
struct modulenode {
rdffile f;
struct segment_infonode seginfo[RDF_MAXSEGS];
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)
void processmodule(const char *filename, struct modulenode *mod);
int allocnewseg(uint16 type, uint16 reserved);
int findsegment(uint16 type, uint16 reserved);
void symtab_add(const char *symbol, int segment, long offset);
int symtab_get(const char *symbol, int *segment, long *offset);
struct modulenode *modules = NULL;
struct modulenode *lastmodule = NULL;
struct librarynode *libraries = NULL;
struct librarynode *lastlib = NULL;
void *symtab = NULL;
char *objpath = NULL;
char *libpath = NULL;
char *generic_rec_file = NULL;
static FILE *error_file;
rdf_headerbuf *newheader = NULL;
struct SegmentHeaderRec outputseg[RDF_MAXSEGS];
int nsegs = 0;
long bss_length;
struct ldrdfoptions {
int verbose;
int align;
int dynalink;
int strip;
int respfile;
int stderr_redir;
int objpath;
int libpath;
} options;
int errorcount = 0;
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;
outputseg[2].number = 2;
outputseg[2].reserved = 0;
outputseg[2].length = 0;
bss_length = 0;
}
void loadmodule(const char *filename)
{
if (options.verbose)
printf("loading `%s'\n", filename);
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);
}
if (rdfopen(&lastmodule->f, filename) != 0) {
rdfperror("ldrdf", filename);
exit(1);
}
lastmodule->header = NULL;
lastmodule->name = strdup(filename);
lastmodule->next = NULL;
processmodule(filename, lastmodule);
}
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++) {
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);
}
switch (sconf.dowhat) {
case SEG_IGNORE:
mod->seginfo[seg].dest_seg = -1;
if (options.verbose > 1)
printf("IGNORED\n");
break;
case SEG_NEWSEG:
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:
outseg = findsegment(sconf.mergetype,
mod->f.seg[seg].reserved);
mod->seginfo[seg].dest_seg = outseg;
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);
}
}
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:
case RDFREC_FARIMPORT:
symtab_add(hr->i.label, -1, 0);
break;
case RDFREC_GLOBAL:{
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:
bssamount += hr->b.amount;
break;
case RDFREC_COMMON:{
symtabEnt *ste = symtabFind(symtab, hr->c.label);
if (ste)
break;
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) {
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
mod->f.header_loc = NULL;
free(header);
#endif
}
int lookformodule(const char *name)
{
struct modulenode *curr = modules;
while (curr) {
if (!strcmp(name, curr->name))
return 1;
curr = curr->next;
}
return 0;
}
int allocnewseg(uint16 type, uint16 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(uint16 type, uint16 reserved)
{
int i;
for (i = 0; i < nsegs; i++)
if (outputseg[i].type == type)
return i;
return allocnewseg(type, reserved);
}
void symtab_add(const char *symbol, int segment, long offset)
{
symtabEnt *ste;
ste = symtabFind(symtab, symbol);
if (ste) {
if (ste->segment >= 0) {
if (segment < 0)
return;
fprintf(error_file, "warning: `%s' redefined\n", symbol);
return;
}
if (segment == -1)
return;
ste->segment = segment;
ste->offset = offset;
ste->flags = 0;
return;
}
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);
}
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;
}
}
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;
}
}
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))) {
if (hr->type != RDFREC_GLOBAL)
continue;
if ((hr->e.flags & SYM_GLOBAL) == 0) {
if (!symtab_get(hr->e.label, &segment, &offset)
|| segment != -1)
continue;
}
doneanything = 1;
keepfile = 1;
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;
}
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);
}
if (generic_rec_file) {
FILE *ff;
if (options.verbose)
printf("\nadding generic record from binary file %s\n",
generic_rec_file);
hr = (rdfheaderrec *) malloc(sizeof(struct GenericRec));
if ((ff = fopen(generic_rec_file, "r")) == NULL) {
fprintf(stderr, "ldrdf: couldn't open %s for input\n",
generic_rec_file);
exit(1);
}
i = fread(hr->g.data, 1, sizeof(hr->g.data), ff);
fseek(ff, 0, SEEK_END);
if (ftell(ff) > sizeof(hr->g.data)) {
fprintf(error_file,
"warning: maximum generic record size is %d, rest of file ignored\n",
sizeof(hr->g.data));
}
fclose(ff);
hr->g.type = 0;
hr->g.reclen = i;
rdfaddheader(rdfheader, hr);
free(hr);
}
if (options.verbose)
printf("\nbuilding output module (%d segments)\n", nsegs);
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);
}
}
availableseg = nsegs;
for (cur = modules; cur; cur = cur->next) {
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);
}
}
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);
}
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);
}
add_seglocation(&segs, 2, 2, cur->bss_reloc);
while ((hr = rdfgetheaderrec(&cur->f))) {
switch (hr->type) {
case RDFREC_RELOC:
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 & RDOFF_RELATIVEMASK) ==
RDOFF_RELATIVEMASK;
hr->r.segment &= (RDOFF_RELATIVEMASK - 1);
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;
}
data = outputseg[cur->seginfo[localseg].dest_seg].data;
data += cur->seginfo[localseg].reloc + hr->r.offset;
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;
break;
}
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;
if (isrelative)
hr->r.segment += RDOFF_RELATIVEMASK;
rdfaddheader(rdfheader, hr);
}
break;
case RDFREC_IMPORT:
case RDFREC_FARIMPORT:
se = symtabFind(symtab, hr->i.label);
if (!se || se->segment == -1) {
if (!options.dynalink && !(hr->i.flags & SYM_IMPORT)) {
fprintf(error_file,
"error: unresolved reference to `%s'"
" in module `%s'\n", hr->i.label,
cur->name);
errorcount++;
}
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;
}
newrec = *hr;
newrec.i.segment = se->segment;
rdfaddheader(rdfheader, &newrec);
}
add_seglocation(&segs, hr->i.segment, se->segment,
se->offset);
break;
case RDFREC_GLOBAL:
if (options.strip && !(hr->e.flags & SYM_GLOBAL))
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 RDFREC_MODNAME:
if (options.strip && hr->m.modname[0] != '$')
break;
rdfaddheader(rdfheader, hr);
break;
case RDFREC_DLL:
if (hr->d.libname[0] != '$')
break;
rdfaddheader(rdfheader, hr);
break;
case RDFREC_SEGRELOC:
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;
case RDFREC_COMMON:
se = symtabFind(symtab, hr->c.label);
if (!se) {
printf("%s is not in symtab yet\n", hr->c.label);
break;
}
add_seglocation(&segs, hr->c.segment, se->segment,
se->offset);
break;
}
}
free(header);
done_seglocations(&segs);
}
newrec.type = RDFREC_BSS;
newrec.b.reclen = 4;
newrec.b.amount = bss_length;
rdfaddheader(rdfheader, &newrec);
for (i = 0; i < nsegs; i++) {
if (i == 2)
continue;
rdfaddsegment(rdfheader, outputseg[i].length);
}
rdfwriteheader(f, rdfheader);
rdfdoneheader(rdfheader);
for (i = 0; i < nsegs; i++) {
uint16 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);
}
void usage()
{
printf("usage:\n"
" ldrdf [options] object modules ... [-llibrary ...]\n"
" ldrdf -r\n"
"options:\n"
" -v[=n] increase verbosity by 1, or set it to n\n"
" -a nn set segment alignment value (default 16)\n"
" -s strip public symbols\n"
" -dy Unix-style dynamic linking\n"
" -o name write output in file 'name'\n"
" -j path specify objects search path\n"
" -L path specify libraries search path\n"
" -g file embed 'file' as a first header record with type 'generic'\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.dynalink = 0;
options.strip = 0;
error_file = stderr;
argc--, argv++;
if (argc == 0)
usage();
while (argc && *argv && **argv == '-' && argv[0][1] != 'l') {
switch (argv[0][1]) {
case 'r':
printf("ldrdf (linker for RDF files) version " LDRDF_VERSION
"\n");
printf("RDOFF2 revision %s\n", RDOFF2_REVISION);
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 'd':
if (argv[0][2] == 'y')
options.dynalink = 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);
}
argv++, argc--;
while (fgets(buf, sizeof(buf), f) != NULL) {
char *p;
if (buf[0] == '\n')
continue;
if ((p = strchr(buf, '\n')) != NULL)
*p = '\0';
if (i >= 128) {
fprintf(stderr, "ldrdf: too many input files\n");
exit(1);
}
*(respstrings + i) = newstr(buf);
argc++, i++;
}
break;
}
case '2':
options.stderr_redir = 1;
error_file = stdout;
break;
case 'g':
generic_rec_file = argv[1];
argv++, argc--;
break;
default:
usage();
}
argv++, argc--;
}
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.dynalink)
printf(" Unix-style dynamic linking\n");
if (options.objpath)
printf(" objects search path: %s\n", objpath);
if (options.libpath)
printf(" libraries search path: %s\n", libpath);
printf("\n");
}
symtab = symtabNew();
initsegments();
if (!symtab) {
fprintf(stderr, "ldrdf: out of memory\n");
exit(1);
}
while (argc) {
if (!*argv)
argv = respstrings;
if (!*argv)
break;
if (!strncmp(*argv, "-l", 2)) {
if (libpath && (argv[0][2] != '/'))
add_library(newstrcat(libpath, *argv + 2));
else
add_library(*argv + 2);
} else {
if (objpath && (argv[0][0] != '/'))
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;
}