Files
retrobsd/src/cmd/ld/ld.c
T
Serge Vakulenko 3a38f291a7 Merged kernel support for ELF executable format from karolina.lindqvist.
Now kernel can directly run executables generated by gcc.
2014-04-23 21:43:09 -07:00

1579 lines
35 KiB
C

/*
* Linker for RetroBSD, MIPS32 architecture.
*
* Copyright (C) 2011 Serge Vakulenko, <serge@vak.ru>
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for any purpose and without fee is hereby
* granted, provided that the above copyright notice appear in all
* copies and that both that the copyright notice and this
* permission notice and warranty disclaimer appear in supporting
* documentation, and that the name of the author not be used in
* advertising or publicity pertaining to distribution of the
* software without specific, written prior permission.
*
* The author disclaim all warranties with regard to this
* software, including all implied warranties of merchantability
* and fitness. In no event shall the author be liable for any
* special, indirect or consequential damages or any damages
* whatsoever resulting from loss of use, data or profits, whether
* in an action of contract, negligence or other tortious action,
* arising out of or in connection with the use or performance of
* this software.
*/
#ifdef CROSS
# include <sys/types.h>
# include <sys/select.h>
# include <sys/stat.h>
# include <sys/time.h>
# include <sys/fcntl.h>
# include <sys/signal.h>
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
# include <unistd.h>
# define MAXNAMLEN 63
#else
# include <sys/types.h>
# include <sys/stat.h>
# include <sys/dir.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <signal.h>
# include <unistd.h>
#endif
#include <stdarg.h>
#include <a.out.h>
#include <ar.h>
#include <ranlib.h>
#define W 4 /* word size in bytes */
#define BADDR 0x7f008000 /* start address in memory */
#define SYMDEF "__.SYMDEF"
#define IS_LOCSYM(s) ((s)->n_name[0] == 'L' || \
(s)->n_name[0] == '.')
#define hexdig(c) ((c)<='9' ? (c) - '0' : ((c)&7) + 9)
struct exec filhdr; /* aout header */
#define HDRSZ sizeof(struct exec)
struct archdr { /* archive header */
char * ar_name;
long ar_date;
int ar_uid;
int ar_gid;
int ar_mode;
long ar_size;
} archdr;
FILE *text, *reloc; /* input management */
/* output management */
FILE *outb, *toutb, *doutb, *troutb, *droutb, *soutb;
/* symbol management */
struct local {
unsigned locindex; /* index to symbol in file */
struct nlist *locsymbol; /* ptr to symbol table */
};
#define NSYM 1500
#define NSYMPR 500
#define NLIBS 256
#define RANTABSZ 500
struct nlist cursym; /* current symbol */
struct nlist symtab [NSYM]; /* table of symbols */
struct nlist **symhash [NSYM]; /* pointers to hash table */
struct nlist *lastsym; /* last entered symbol */
struct nlist *hshtab [NSYM+2]; /* hash table for symbols */
struct local local [NSYMPR];
int symindex; /* next free entry of symbol table */
unsigned basaddr = BADDR; /* base address of loading */
struct ranlib rantab [RANTABSZ];
int rancount; /* number of elements in rantab */
/*
* library management
*/
unsigned liblist [NLIBS], *libp;
/*
* internal symbols
*/
struct nlist *p_etext, *p_edata, *p_end, *p_gp, *entrypt;
/*
* options
*/
int trace; /* internal trace flag */
int xflag; /* discard local symbols */
int Xflag; /* discard locals starting with 'L' or '.' */
int Sflag; /* discard all except locals and globals*/
int rflag; /* preserve relocation bits, don't define commons */
int output_relinfo;
int sflag; /* discard all symbols */
int dflag; /* define common even with rflag */
int verbose; /* verbose mode */
/*
* cumulative sizes set in pass 1
*/
unsigned tsize, dsize, bsize, ssize, nsym;
/*
* symbol relocation; both passes
*/
unsigned ctrel, cdrel, cbrel;
/*
* used after pass 1
*/
unsigned torigin, dorigin, borigin;
/* gp control, MIPS specific */
unsigned gpoffset = 0x8000; /* offset from data start */
unsigned gp; /* allocated address */
int ofilfnd;
char *ofilename = "l.out";
char *filname;
int errlev;
int delarg = 4;
char tfname [] = "/tmp/ldaXXXXXX";
#define ALIGN(x,y) ((x)+(y)-1-((x)+(y)-1)%(y))
unsigned int fgetword (f)
register FILE *f;
{
register unsigned int h;
h = getc (f);
h |= getc (f) << 8;
h |= getc (f) << 16;
h |= getc (f) << 24;
return h;
}
void fputword (h, f)
register unsigned int h;
register FILE *f;
{
putc (h, f);
putc (h >> 8, f);
putc (h >> 16, f);
putc (h >> 24, f);
}
int fgethdr (text, h)
register FILE *text;
register struct exec *h;
{
h->a_midmag = fgetword (text);
h->a_text = fgetword (text);
h->a_data = fgetword (text);
h->a_bss = fgetword (text);
h->a_reltext = fgetword (text);
h->a_reldata = fgetword (text);
h->a_syms = fgetword (text);
h->a_entry = fgetword (text);
return (1);
}
void fputhdr (hdr, coutb)
register struct exec *hdr;
register FILE *coutb;
{
fputword (hdr->a_magic, coutb);
fputword (hdr->a_text, coutb);
fputword (hdr->a_data, coutb);
fputword (hdr->a_bss, coutb);
fputword (hdr->a_reltext, coutb);
fputword (hdr->a_reldata, coutb);
fputword (hdr->a_syms, coutb);
fputword (hdr->a_entry, coutb);
}
/*
* Read a relocation record: 1 to 6 bytes.
*/
void fgetrel (f, r)
register FILE *f;
register struct reloc *r;
{
r->flags = getc (f);
if ((r->flags & RSMASK) == REXT) {
r->index = getc (f);
r->index |= getc (f) << 8;
r->index |= getc (f) << 16;
}
if ((r->flags & RFMASK) == RHIGH16 ||
(r->flags & RFMASK) == RHIGH16S) {
r->offset = getc (f);
r->offset |= getc (f) << 8;
}
}
/*
* Emit a relocation record: 1 to 6 bytes.
* Return a written length.
*/
unsigned fputrel (r, f)
register struct reloc *r;
register FILE *f;
{
register unsigned nbytes = 1;
putc (r->flags, f);
if ((r->flags & RSMASK) == REXT) {
putc (r->index, f);
putc (r->index >> 8, f);
putc (r->index >> 16, f);
nbytes += 3;
}
if ((r->flags & RFMASK) == RHIGH16 ||
(r->flags & RFMASK) == RHIGH16S) {
putc (r->offset, f);
putc (r->offset >> 8, f);
nbytes += 2;
}
return nbytes;
}
void delexit (int sig)
{
unlink ("l.out");
if (! delarg && ! rflag)
chmod (ofilename, 0777 & ~umask(0));
exit (delarg);
}
void error (int n, const char *s, ...)
{
va_list ap;
va_start (ap, s);
if (! errlev)
printf ("ld: ");
if (filname)
printf ("%s: ", filname);
vprintf (s, ap);
va_end (ap);
printf ("\n");
if (n > 1)
delexit (0);
errlev = n;
}
int fgetsym (text, sym)
register FILE *text;
register struct nlist *sym;
{
register int c;
c = getc (text);
if (c <= 0)
return 0;
sym->n_len = c;
sym->n_name = malloc (sym->n_len + 1);
if (! sym->n_name)
error (2, "out of memory");
sym->n_type = getc (text);
sym->n_value = fgetword (text);
for (c=0; c<sym->n_len; c++)
sym->n_name [c] = getc (text);
sym->n_name [sym->n_len] = '\0';
return sym->n_len + 6;
}
void fputsym (s, file)
register struct nlist *s;
register FILE *file;
{
register int i;
putc (s->n_len, file);
putc (s->n_type, file);
fputword (s->n_value, file);
for (i=0; i<s->n_len; i++)
putc (s->n_name[i], file);
}
/*
* Read the file header of the archive.
*/
int fgetarhdr (fd, h)
register FILE *fd;
register struct archdr *h;
{
struct ar_hdr hdr;
register int len, nr;
register char *p;
char buf[20];
/* Read arhive name. Spaces should never happen. */
nr = fread (buf, 1, sizeof (hdr.ar_name), fd);
if (nr != sizeof (hdr.ar_name) || buf[0] == ' ')
return 0;
buf[nr] = 0;
/* Long name support. Set the "real" size of the file,
* and the long name flag/size. */
h->ar_size = 0;
if (strncmp (buf, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0) {
len = atoi (buf + sizeof(AR_EFMT1) - 1);
if (len <= 0 || len > MAXNAMLEN)
return 0;
h->ar_name = malloc (len + 1);
if (! h->ar_name)
return 0;
nr = fread (h->ar_name, 1, len, fd);
if (nr != len) {
failed: free (h->ar_name);
return 0;
}
h->ar_name[len] = 0;
h->ar_size -= len;
} else {
/* Strip trailing spaces, null terminate. */
p = buf + nr - 1;
while (*p == ' ')
--p;
*++p = '\0';
len = p - buf;
h->ar_name = malloc (len + 1);
if (! h->ar_name)
return 0;
strcpy (h->ar_name, buf);
}
/* Read arhive date. */
nr = fread (buf, 1, sizeof (hdr.ar_date), fd);
if (nr != sizeof (hdr.ar_date))
goto failed;
buf[nr] = 0;
h->ar_date = strtol (buf, 0, 10);
/* Read user id. */
nr = fread (buf, 1, sizeof (hdr.ar_uid), fd);
if (nr != sizeof (hdr.ar_uid))
goto failed;
buf[nr] = 0;
h->ar_uid = strtol (buf, 0, 10);
/* Read group id. */
nr = fread (buf, 1, sizeof (hdr.ar_gid), fd);
if (nr != sizeof (hdr.ar_gid))
goto failed;
buf[nr] = 0;
h->ar_gid = strtol (buf, 0, 10);
/* Read mode (octal). */
nr = fread (buf, 1, sizeof (hdr.ar_mode), fd);
if (nr != sizeof (hdr.ar_mode))
goto failed;
buf[nr] = 0;
h->ar_mode = strtol (buf, 0, 8);
/* Read archive size. */
nr = fread (buf, 1, sizeof (hdr.ar_size), fd);
if (nr != sizeof (hdr.ar_size))
goto failed;
buf[nr] = 0;
h->ar_size = strtol (buf, 0, 10);
/* Check secondary magic. */
nr = fread (buf, 1, sizeof (hdr.ar_fmag), fd);
if (nr != sizeof (hdr.ar_fmag))
goto failed;
buf[nr] = 0;
if (strcmp (buf, ARFMAG) != 0)
goto failed;
return 1;
}
void freerantab ()
{
register struct ranlib *p;
for (p=rantab; p<rantab+rancount; ++p)
free (p->ran_name);
}
int fgetran (text, sym)
register FILE *text;
register struct ranlib *sym;
{
register int c;
/* read struct ranlib from file */
/* 1 byte - length of name */
/* 4 bytes - seek in archive */
/* 'len' bytes - symbol name */
/* if len == 0 then eof */
/* return 1 if ok, 0 on eof */
sym->ran_len = getc (text);
if (sym->ran_len <= 0)
return (0);
sym->ran_name = malloc (sym->ran_len + 1);
if (! sym->ran_name)
error (2, "out of memory");
sym->ran_off = fgetword (text);
for (c=0; c<sym->ran_len; c++)
sym->ran_name [c] = getc (text);
sym->ran_name [sym->ran_len] = '\0';
return (1);
}
void getrantab ()
{
register struct ranlib *p;
for (p=rantab; p<rantab+RANTABSZ; ++p) {
if (! fgetran (text, p)) {
rancount = p - rantab;
return;
}
}
error (2, "ranlib buffer overflow");
}
void ldrsym (sp, val, type)
register struct nlist *sp;
unsigned val;
{
if (sp == 0)
return;
if (sp->n_type != N_EXT+N_UNDF) {
printf ("%s: ", sp->n_name);
error (1, "name redefined");
return;
}
sp->n_type = type;
sp->n_value = val;
}
void tcreat (buf, tempflg)
register FILE **buf;
register int tempflg;
{
*buf = fopen (tempflg ? tfname : ofilename, "w+");
if (! *buf)
error (2, tempflg ?
"cannot create temporary file" :
"cannot create output file");
if (tempflg)
unlink (tfname);
}
int reltype (stype)
int stype;
{
switch (stype & N_TYPE) {
case N_UNDF: return (0);
case N_ABS: return (RABS);
case N_TEXT: return (RTEXT);
case N_DATA: return (RDATA);
case N_BSS: return (RBSS);
case N_STRNG: return (RDATA);
case N_COMM: return (RBSS);
case N_FN: return (0);
default: return (0);
}
}
struct nlist *lookloc (lp, sn)
register struct local *lp;
register int sn;
{
register struct local *clp;
for (clp=local; clp<lp; clp++)
if (clp->locindex == sn)
return (clp->locsymbol);
if (trace) {
fprintf (stderr, "*** %d ***\n", sn);
for (clp=local; clp<lp; clp++)
fprintf (stderr, "%u, ", clp->locindex);
fprintf (stderr, "\n");
}
error (2, "bad symbol reference");
return 0;
}
void printrel (word, rel)
register unsigned word;
register struct reloc *rel;
{
printf ("%08x %02x ", word, rel->flags);
if ((rel->flags & RSMASK) == REXT)
printf ("%-3d ", rel->index);
else
printf (" ");
if ((rel->flags & RFMASK) == RHIGH16 ||
(rel->flags & RFMASK) == RHIGH16S)
printf ("%08x", rel->offset);
else
printf (" ");
}
/*
* Relocate the word by a given offset.
* Return the new value of word and update rel.
*/
unsigned relword (lp, word, rel, offset)
struct local *lp;
register unsigned word;
register struct reloc *rel;
unsigned offset;
{
register unsigned addr, delta;
register struct nlist *sp = 0;
if (trace > 2)
printrel (word, rel);
/*
* Extract an address field from the instruction.
*/
switch (rel->flags & RFMASK) {
case RBYTE16:
addr = word & 0xffff;
break;
case RBYTE32:
addr = word;
break;
case RWORD16:
addr = (word & 0xffff) << 2;
break;
case RWORD26:
addr = (word & 0x3ffffff) << 2;
break;
case RHIGH16:
addr = (word & 0xffff) << 16;
addr += rel->offset;
break;
case RHIGH16S:
addr = (word & 0xffff) << 16;
addr += (signed short) rel->offset;
break;
default:
addr = 0;
break;
}
/*
* Compute a delta for address.
* Update the relocation info, if needed.
*/
switch (rel->flags & RSMASK) {
case RTEXT:
delta = ctrel;
break;
case RDATA:
delta = cdrel;
break;
case RBSS:
delta = cbrel;
break;
case REXT:
sp = lookloc (lp, rel->index);
if (sp->n_type == N_EXT+N_UNDF ||
sp->n_type == N_EXT+N_COMM) {
rel->index = nsym + (sp - symtab);
sp = 0;
delta = 0;
} else {
rel->flags &= RFMASK | RGPREL;
rel->flags |= reltype (sp->n_type);
delta = sp->n_value;
}
break;
default:
delta = 0;
break;
}
if ((rel->flags & RGPREL) && ! output_relinfo) {
/*
* GP relative address.
*/
delta -= gp;
rel->flags &= ~RGPREL;
}
/*
* Update the address field of the instruction.
* Update the relocation info, if needed.
*/
switch (rel->flags & RFMASK) {
case RBYTE16:
addr += delta;
word &= ~0xffff;
word |= addr & 0xffff;
break;
case RBYTE32:
word = addr + delta;
break;
case RWORD16:
if (! sp)
break;
addr += delta - offset - 4;
word &= ~0xffff;
word |= (addr >> 2) & 0xffff;
rel->flags = RABS;
break;
case RWORD26:
addr += delta;
word &= ~0x3ffffff;
word |= (addr >> 2) & 0x3ffffff;
break;
case RHIGH16:
addr += delta;
word &= ~0xffff;
word |= (addr >> 16) & 0xffff;
break;
case RHIGH16S:
addr += delta;
word &= ~0xffff;
word |= ((addr + 0x8000) >> 16) & 0xffff;
break;
}
if (trace > 2) {
//printf (" +%#x ", delta);
printf (" -> ");
printrel (word, rel);
printf ("\n");
}
return word;
}
void relocate (lp, b1, b2, len, origin)
struct local *lp;
FILE *b1, *b2;
unsigned len, origin;
{
unsigned word, offset;
struct reloc rel;
for (offset=0; offset<len; offset+=W) {
word = fgetword (text);
fgetrel (reloc, &rel);
word = relword (lp, word, &rel, offset + origin);
fputword (word, b1);
if (output_relinfo)
fputrel (&rel, b2);
}
}
unsigned copy_and_close (buf)
register FILE *buf;
{
register int c;
unsigned nbytes;
rewind (buf);
nbytes = 0;
while ((c = getc (buf)) != EOF) {
putc (c, outb);
nbytes++;
}
fclose (buf);
return nbytes;
}
int mkfsym (s, wflag)
register char *s;
{
register char *p;
if (sflag || xflag)
return (0);
for (p=s; *p;)
if (*p++ == '/')
s = p;
if (! wflag)
return (p - s + 6);
cursym.n_len = p - s;
cursym.n_name = malloc (cursym.n_len + 1);
if (! cursym.n_name)
error (2, "out of memory");
for (p=cursym.n_name; *s; p++, s++)
*p = *s;
cursym.n_type = N_FN;
cursym.n_value = torigin;
fputsym (&cursym, soutb);
free (cursym.n_name);
return (cursym.n_len + 6);
}
int getfile (cp)
register char *cp;
{
int c;
struct stat x;
static char libname [] = "/lib/libxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
char magic [SARMAG];
text = 0;
filname = cp;
if (cp[0] == '-' && cp[1] == 'l') {
if (cp[2] == '\0')
cp = "-la";
filname = libname;
for (c = 0; cp [c+2]; c++)
filname [c + 8] = cp [c+2];
filname [c + 8] = '.';
filname [c + 8 + 1] = 'a';
filname [c + 8 + 2] = '\0';
}
text = fopen (filname, "r");
if (! text)
error (2, "cannot open");
reloc = fopen (filname, "r");
if (! reloc)
error (2, "cannot open");
/* Read file magic. */
if (fread(magic, 1, SARMAG, text) != SARMAG)
return (0); /* regular file */
if (strncmp (magic, ARMAG, SARMAG) != 0)
return (0); /* regular file */
if (! fgetarhdr (text, &archdr))
return (0); /* regular file */
if (strncmp (archdr.ar_name, SYMDEF, sizeof (SYMDEF)) != 0) {
free (archdr.ar_name);
return (1); /* regular archive */
}
free (archdr.ar_name);
fstat (fileno (text), &x);
if (x.st_mtime > archdr.ar_date + 2)
return (3); /* out of date archive */
return (2); /* randomized archive */
}
int enter (hp)
register struct nlist **hp;
{
register struct nlist *sp;
if (*hp) {
lastsym = *hp;
return (0);
}
if (symindex >= NSYM)
error (2, "symbol table overflow");
symhash [symindex] = hp;
*hp = lastsym = sp = &symtab[symindex++];
sp->n_len = cursym.n_len;
sp->n_name = cursym.n_name;
sp->n_type = cursym.n_type;
sp->n_value = cursym.n_value;
return (1);
}
void symreloc()
{
switch (cursym.n_type) {
case N_TEXT:
case N_EXT+N_TEXT:
cursym.n_value += ctrel;
return;
case N_DATA:
case N_EXT+N_DATA:
cursym.n_value += cdrel;
return;
case N_BSS:
case N_EXT+N_BSS:
cursym.n_value += cbrel;
return;
case N_EXT+N_UNDF:
case N_EXT+N_COMM:
return;
}
if (cursym.n_type & N_EXT)
cursym.n_type = N_EXT+N_ABS;
}
/*
* Suboptimal 32-bit hash function.
* Copyright (C) 2006 Serge Vakulenko.
*/
unsigned hash_rot13 (s)
register const char *s;
{
register unsigned hash, c;
hash = 0;
while ((c = (unsigned char) *s++) != 0) {
hash += c;
hash -= (hash << 13) | (hash >> 19);
}
return hash;
}
struct nlist **lookup()
{
int clash;
register char *cp, *cp1;
register struct nlist **hp;
hp = &hshtab[hash_rot13 (cursym.n_name) % NSYM + 2];
while (*hp != 0) {
cp1 = (*hp)->n_name;
clash = 0;
for (cp = cursym.n_name; *cp;) {
if (*cp++ != *cp1++) {
clash = 1;
break;
}
}
if (! clash)
break;
if (++hp >= &hshtab[NSYM+2])
hp = hshtab;
}
return (hp);
}
struct nlist **slookup (s)
char *s;
{
cursym.n_len = strlen (s) + 1;
cursym.n_name = s;
cursym.n_type = N_EXT+N_UNDF;
cursym.n_value = 0;
return (lookup ());
}
void readhdr (loc)
unsigned loc;
{
fseek (text, loc, 0);
if (! fgethdr (text, &filhdr))
error (2, "bad format");
if (N_GETMAGIC(filhdr) != RMAGIC)
error (2, "bad magic");
if (filhdr.a_text % W)
error (2, "bad length of text");
if (filhdr.a_data % W)
error (2, "bad length of data");
if (filhdr.a_bss % W)
error (2, "bad length of bss");
}
/*
* single file
*/
int load1 (loc, libflg, nloc)
unsigned loc;
{
register struct nlist *sp;
int savindex, ndef, type, symlen, nsymbol;
readhdr (loc);
if (N_GETMAGIC(filhdr) != RMAGIC) {
error (1, "file not relocatable");
return (0);
}
fseek (reloc, loc + N_SYMOFF (filhdr), 0);
ctrel = tsize;
cdrel = dsize - filhdr.a_text;
cbrel = bsize - (filhdr.a_text + filhdr.a_data);
loc += HDRSZ + filhdr.a_text + filhdr.a_data +
filhdr.a_reltext + filhdr.a_reldata;
fseek (text, loc, 0);
ndef = 0;
savindex = symindex;
if (nloc)
nsymbol = 1;
else
nsymbol = 0;
for (;;) {
symlen = fgetsym (text, &cursym);
if (symlen == 0)
break;
type = cursym.n_type;
if (Sflag && ((type & N_TYPE) == N_ABS ||
(type & N_TYPE) > N_COMM))
{
free (cursym.n_name);
continue;
}
if (! (type & N_EXT)) {
if (! (sflag || xflag ||
(Xflag && IS_LOCSYM(&cursym))))
{
nsymbol++;
nloc += symlen;
}
free (cursym.n_name);
continue;
}
symreloc ();
if (enter (lookup ()))
continue;
free (cursym.n_name);
if (cursym.n_type == N_EXT+N_UNDF)
continue;
sp = lastsym;
if (sp->n_type == N_EXT+N_UNDF ||
sp->n_type == N_EXT+N_COMM)
{
if (cursym.n_type == N_EXT+N_COMM) {
sp->n_type = cursym.n_type;
if (cursym.n_value > sp->n_value)
sp->n_value = cursym.n_value;
}
else if (sp->n_type==N_EXT+N_UNDF ||
cursym.n_type==N_EXT+N_DATA ||
cursym.n_type==N_EXT+N_BSS)
{
ndef++;
sp->n_type = cursym.n_type;
sp->n_value = cursym.n_value;
}
}
}
if (! libflg || ndef) {
tsize += filhdr.a_text;
dsize += filhdr.a_data;
bsize += filhdr.a_bss;
ssize += nloc;
nsym += nsymbol;
/* Alignment. */
tsize = (tsize + 3) & ~3;
dsize = (dsize + 3) & ~3;
bsize = (bsize + 3) & ~3;
return (1);
}
/*
* No symbols defined by this library member.
* Rip out the hash table entries and reset the symbol table.
*/
while (symindex > savindex) {
register struct nlist **p;
p = symhash[--symindex];
free ((*p)->n_name);
*p = 0;
}
return (0);
}
void addlibp (nloc)
register unsigned nloc;
{
*libp++ = nloc;
if (libp >= &liblist[NLIBS])
error (2, "library table overflow");
}
int step (nloc)
register unsigned nloc;
{
fseek (text, nloc, 0);
if (! fgetarhdr (text, &archdr)) {
return (0);
}
if (load1 (nloc + ARHDRSZ, 1, mkfsym (archdr.ar_name, 0))) {
addlibp (nloc);
if (trace)
printf ("load '%s' offset %08x\n", archdr.ar_name, nloc);
}
free (archdr.ar_name);
return (1);
}
int ldrand ()
{
register struct ranlib *p;
struct nlist **pp;
unsigned *oldp = libp;
for (p=rantab; p<rantab+rancount; ++p) {
pp = slookup (p->ran_name);
if (! *pp)
continue;
if ((*pp)->n_type == N_EXT+N_UNDF)
step (p->ran_off);
}
return (oldp != libp);
}
/*
* scan a library to find defined symbols
*/
void load1lib (off0)
unsigned off0;
{
register unsigned offset;
register unsigned *oldp;
/* repeat while any symbols found */
do {
oldp = libp;
offset = off0;
while (step (offset))
offset += archdr.ar_size + ARHDRSZ;
} while (libp != oldp);
addlibp (-1);
}
/*
* scan file to find defined symbols
*/
void load1arg (cp)
register char *cp;
{
switch (getfile (cp)) {
case 0: /* regular file */
load1 (0L, 0, mkfsym (cp, 0));
break;
case 1: /* regular archive */
load1lib (SARMAG);
break;
case 2: /* archive with table of contents */
getrantab ();
while (ldrand ())
continue;
freerantab ();
addlibp (-1);
break;
case 3: /* out of date table of contents */
error (0, "out of date (warning)");
load1lib (SARMAG + archdr.ar_size + ARHDRSZ);
break;
}
fclose (text);
fclose (reloc);
}
void pass1 (argc, argv)
char **argv;
{
register int c, i;
register char *ap, **p;
char save;
/* scan files once to find symdefs */
p = argv + 1;
libp = liblist;
for (c=1; c<argc; ++c) {
filname = 0;
ap = *p++;
if (*ap != '-') {
load1arg (ap);
continue;
}
for (i=1; ap[i]; i++) {
switch (ap [i]) {
/* output file name */
case 'o':
if (++c >= argc)
error (2, "-o: argument missing");
ofilename = *p++;
ofilfnd++;
continue;
/* 'use' */
case 'u':
if (++c >= argc)
error (2, "-u: argument missing");
enter (slookup (*p++));
continue;
/* 'entry' */
case 'e':
if (++c >= argc)
error (2, "-e: argument missing");
enter (slookup (*p++));
entrypt = lastsym;
continue;
/* base address of loading */
case 'T':
basaddr = atol (ap+i+1);
break;
/* library */
case 'l':
save = ap [--i];
ap [i] = '-';
load1arg (&ap[i]);
ap [i] = save;
break;
/* discard local symbols */
case 'x':
xflag++;
continue;
/* discard locals starting with 'L' or '.' */
case 'X':
Xflag++;
continue;
/* discard all except locals and globals*/
case 'S':
Sflag++;
continue;
/* preserve rel. bits, don't define common */
case 'r':
rflag++;
output_relinfo++;
continue;
/* discard all symbols */
case 's':
sflag++;
xflag++;
continue;
/* define common even with rflag */
case 'd':
dflag++;
continue;
/* tracing */
case 't':
trace++;
continue;
/* verbose */
case 'v':
verbose++;
continue;
default:
error (2, "unknown flag");
}
break;
}
}
}
void middle()
{
register struct nlist *sp, *symp;
register unsigned t, cmsize;
int nund;
unsigned cmorigin;
p_etext = *slookup ("_etext");
p_edata = *slookup ("_edata");
p_end = *slookup ("_end");
p_gp = *slookup ("_gp");
/*
* If there are any undefined symbols, save the relocation bits.
*/
symp = &symtab[symindex];
if (! output_relinfo) {
for (sp=symtab; sp<symp; sp++)
if (sp->n_type == N_EXT+N_UNDF &&
sp != p_end && sp != p_edata &&
sp != p_etext && sp != p_gp)
{
output_relinfo++;
dflag = 0;
break;
}
}
if (output_relinfo)
sflag = 0;
/*
* Assign common locations.
* Align text size to 16 bytes.
*/
cmsize = 0;
tsize = (tsize + 15) & ~15;
if (dflag || ! output_relinfo) {
ldrsym (p_etext, tsize, N_EXT+N_TEXT);
ldrsym (p_edata, dsize, N_EXT+N_DATA);
ldrsym (p_end, bsize, N_EXT+N_BSS);
/* Set GP as offset from the start of data segment. */
ldrsym (p_gp, gpoffset, N_EXT+N_DATA);
for (sp=symtab; sp<symp; sp++) {
if ((sp->n_type & N_TYPE) == N_COMM) {
t = sp->n_value;
sp->n_value = cmsize;
cmsize += t;
cmsize = (cmsize + 3) & ~3;
}
}
}
/*
* Now set symbols to their final value.
*/
torigin = basaddr;
dorigin = torigin + tsize;
gp = dorigin + gpoffset;
cmorigin = dorigin + dsize;
borigin = cmorigin + cmsize;
nund = 0;
for (sp=symtab; sp<symp; sp++) {
switch (sp->n_type) {
case N_EXT+N_UNDF:
if (! rflag) {
errlev |= 1;
if (sp == p_end || sp == p_edata ||
sp == p_etext || sp == p_gp)
break;
if (! nund)
printf ("Undefined:\n");
nund++;
printf ("\t%s\n", sp->n_name);
}
break;
default:
case N_EXT+N_ABS:
break;
case N_EXT+N_TEXT:
sp->n_value += torigin;
break;
case N_EXT+N_DATA:
sp->n_value += dorigin;
break;
case N_EXT+N_BSS:
sp->n_value += borigin;
break;
case N_COMM:
case N_EXT+N_COMM:
sp->n_type = N_EXT+N_BSS;
sp->n_value += cmorigin;
break;
}
}
if (sflag || xflag)
ssize = 0;
bsize += cmsize;
/*
* Compute ssize; add length of local symbols, if need,
* and one more zero byte. Alignment will be taken at setupout.
*/
if (sflag)
ssize = 0;
else {
if (xflag)
ssize = 0;
for (sp = symtab; sp < &symtab[symindex]; sp++)
ssize += sp->n_len + 6;
ssize++;
}
}
void setupout ()
{
tcreat (&outb, 0);
int fd = mkstemp (tfname);
if (fd == -1) {
error(2, "internal error: unable to create temporary file %s", tfname);
} else {
close(fd);
}
tcreat (&toutb, 1);
tcreat (&doutb, 1);
if (! sflag || ! xflag)
tcreat (&soutb, 1);
if (output_relinfo) {
tcreat (&troutb, 1);
tcreat (&droutb, 1);
}
fseek (outb, sizeof(filhdr), 0);
}
void load2 (loc)
unsigned loc;
{
register struct nlist *sp;
register struct local *lp;
register int symno;
int type;
unsigned count;
readhdr (loc);
ctrel = torigin;
cdrel = dorigin - filhdr.a_text;
cbrel = borigin - (filhdr.a_text + filhdr.a_data);
if (trace > 1)
printf ("ctrel=%08x, cdrel=%08x, cbrel=%08x\n",
ctrel, cdrel, cbrel);
/*
* Reread the symbol table, recording the numbering
* of symbols for fixing external references.
*/
lp = local;
symno = -1;
loc += HDRSZ;
fseek (text, loc + filhdr.a_text + filhdr.a_data +
filhdr.a_reltext + filhdr.a_reldata, 0);
for (;;) {
symno++;
count = fgetsym (text, &cursym);
if (count == 0)
break;
symreloc ();
type = cursym.n_type;
if (Sflag && ((type & N_TYPE) == N_ABS ||
(type & N_TYPE) > N_COMM))
{
free (cursym.n_name);
continue;
}
if (! (type & N_EXT)) {
if (! (sflag || xflag ||
(Xflag && IS_LOCSYM(&cursym))))
fputsym (&cursym, soutb);
free (cursym.n_name);
continue;
}
if (! (sp = *lookup()))
error (2, "internal error: symbol not found");
free (cursym.n_name);
if (cursym.n_type == N_EXT+N_UNDF ||
cursym.n_type == N_EXT+N_COMM)
{
if (lp >= &local [NSYMPR])
error (2, "local symbol table overflow");
lp->locindex = symno;
lp++->locsymbol = sp;
continue;
}
if (cursym.n_type != sp->n_type ||
cursym.n_value != sp->n_value)
{
printf ("%s: ", cursym.n_name);
error (1, "name redefined");
}
}
count = loc + filhdr.a_text + filhdr.a_data;
if (trace > 1)
printf ("-- text --\n");
fseek (text, loc, 0);
fseek (reloc, count, 0);
relocate (lp, toutb, troutb, filhdr.a_text, torigin);
if (trace > 1)
printf ("-- data --\n");
fseek (text, loc + filhdr.a_text, 0);
fseek (reloc, count + filhdr.a_reltext, 0);
relocate (lp, doutb, droutb, filhdr.a_data, dorigin);
torigin += filhdr.a_text;
dorigin += filhdr.a_data;
borigin += filhdr.a_bss;
/* Alignment. */
torigin = (torigin + 3) & ~3;
dorigin = (dorigin + 3) & ~3;
borigin = (borigin + 3) & ~3;
}
void load2arg (arname)
register char *arname;
{
register unsigned *lp;
if (getfile (arname) == 0) {
if (trace || verbose)
printf ("%s:\n", arname);
mkfsym (arname, 1);
load2 (0L);
} else {
/* scan archive members referenced */
for (lp = libp; *lp != -1; lp++) {
fseek (text, *lp, 0);
fgetarhdr (text, &archdr);
if (trace || verbose)
printf ("%s(%s):\n", arname, archdr.ar_name);
mkfsym (archdr.ar_name, 1);
free (archdr.ar_name);
load2 (*lp + ARHDRSZ);
}
libp = ++lp;
}
fclose (text);
fclose (reloc);
}
void pass2 (argc, argv)
int argc;
char **argv;
{
register int c, i;
register char *ap, **p;
p = argv + 1;
libp = liblist;
for (c=1; c<argc; c++) {
ap = *p++;
if (*ap != '-') {
load2arg (ap);
continue;
}
for (i=1; ap[i]; i++) {
switch (ap[i]) {
case 'u':
case 'e':
case 'o':
++c;
++p;
default:
continue;
case 'l':
ap [--i] = '-';
load2arg (&ap[i]);
break;
}
break;
}
}
}
void finishout ()
{
register struct nlist *p;
unsigned rtsize = 0, rdsize = 0;
register unsigned n;
n = copy_and_close (toutb);
while (n++ < tsize) {
/* Align text size. */
putc (0, outb);
}
copy_and_close (doutb);
if (output_relinfo) {
rtsize = copy_and_close (troutb);
while (rtsize % W) {
putc (0, outb);
rtsize++;
}
rdsize = copy_and_close (droutb);
while (rdsize % W) {
putc (0, outb);
rdsize++;
}
}
if (! sflag) {
if (! xflag)
copy_and_close (soutb);
for (p=symtab; p<&symtab[symindex]; ++p)
fputsym (p, outb);
putc (0, outb);
while (ssize++ % W)
putc (0, outb);
}
filhdr.a_midmag = output_relinfo ? RMAGIC : OMAGIC;
filhdr.a_text = tsize;
filhdr.a_data = dsize;
filhdr.a_bss = bsize;
filhdr.a_reltext = rtsize;
filhdr.a_reldata = rdsize;
filhdr.a_syms = ALIGN (ssize, W);
if (entrypt) {
if (entrypt->n_type != N_EXT+N_TEXT &&
entrypt->n_type != N_EXT+N_UNDF)
error (1, "entry out of text");
else filhdr.a_entry = entrypt->n_value;
} else
filhdr.a_entry = basaddr;
fseek (outb, 0, 0);
fputhdr (&filhdr, outb);
fclose (outb);
}
int main (argc, argv)
char **argv;
{
if (argc == 1) {
printf ("Usage:\n");
printf (" ld [-sSxXrdt] [-o file] [-lname] [-u name] [-e name] [-T num] file...\n");
printf ("Options:\n");
printf (" -o filename Set output file name, default a.out\n");
printf (" -llibname Search for library libname\n");
printf (" -u symbol Start with undefined reference to symbol\n");
printf (" -e symbol Set start address\n");
printf (" -T address Set address of .text segment, default %#x\n", basaddr);
printf (" -s Discard all symbols\n");
printf (" -S Discard all symbols except locals and globals\n");
printf (" -x Discard local symbols\n");
printf (" -X Discard locals starting with 'L' or '.'\n");
printf (" -r Generate relocatable output\n");
printf (" -d Force common symbols to be defined\n");
printf (" -t Increase trace verbosity (up to 3)\n");
exit (4);
}
if (signal (SIGINT, SIG_IGN) != SIG_IGN)
signal (SIGINT, delexit);
if (signal (SIGTERM, SIG_IGN) != SIG_IGN)
signal (SIGTERM, delexit);
/*
* First pass: compute lengths of segments, symbol name table
* and entry address.
*/
pass1 (argc, argv);
filname = 0;
/*
* Compute name table.
*/
middle ();
/*
* Create temporary files.
*/
setupout ();
/*
* Second pass: relocation.
*/
pass2 (argc, argv);
/*
* Flush buffers, write a header.
*/
finishout ();
if (! ofilfnd) {
unlink ("a.out");
link ("l.out", "a.out");
ofilename = "a.out";
}
delarg = errlev;
delexit (0);
return (0);
}