MiniUnix/usr/source/s1/ld.c
#
/*
* link editor
*/
#define SIGINT 2
#define ARCMAGIC 0177555
#define FMAGIC 0407
#define NMAGIC 0410
#define IMAGIC 0411
#define EXTERN 040
#define UNDEF 00
#define ABS 01
#define TEXT 02
#define DATA 03
#define BSS 04
#define COMM 05 /* internal use only */
#define RABS 00
#define RTEXT 02
#define RDATA 04
#define RBSS 06
#define REXT 010
#define RELFLG 01
#define NROUT 256
#define NSYM 501
#define NSYMPR 500
#define RONLY 0400
char premeof[] "Premature EOF on %s";
struct page {
int nuser;
int bno;
int nibuf;
int buff[256];
} page[2];
struct {
int nuser;
int bno;
} fpage;
struct stream {
int *ptr;
int bno;
int nibuf;
int size;
struct page *pno;
};
struct stream text;
struct stream reloc;
struct archdr {
char aname[8];
int atime[2];
char auid, amode;
int asize;
} archdr;
struct filhdr {
int fmagic;
int tsize;
int dsize;
int bsize;
int ssize;
int entry;
int pad;
int relflg;
} filhdr;
struct liblist {
int off;
int bno;
};
struct liblist liblist[NROUT];
struct liblist *libp { &liblist[0] };
struct symbol {
char sname[8];
char stype;
char spad;
int svalue;
};
struct symbol cursym;
struct symbol symtab[NSYM];
struct symbol *hshtab[NSYM+2];
struct symbol *symp { symtab };
struct symbol **local[NSYMPR];
struct symbol *p_etext;
struct symbol *p_edata;
struct symbol *p_end;
int xflag; /* discard local symbols */
int Xflag; /* discard locals starting with 'L' */
int rflag; /* preserve relocation bits, don't define common */
int arflag; /* original copy of rflag */
int sflag; /* discard all symbols */
int nflag; /* pure procedure */
int dflag; /* define common even with rflag */
int iflag; /* I/D space separated */
int infil;
char *filname;
int tsize;
int dsize;
int bsize;
int ssize;
int nsym;
int torigin;
int dorigin;
int borigin;
int ctrel;
int cdrel;
int cbrel;
int errlev;
int delarg 4;
char tfname[] "/tmp/lxyyyyy";
int toutb[259];
int doutb[259];
int troutb[259];
int droutb[259];
int soutb[259];
struct symbol **lookup();
struct symbol **slookup();
main(argc, argv)
char **argv;
{
extern int delexit();
register c;
register char *ap, **p;
struct symbol **hp;
if ((signal(SIGINT, 1) & 01) == 0)
signal(SIGINT, delexit);
if (argc == 1)
exit(4);
p = argv + 1;
for (c = 1; c<argc; c++) {
filname = 0;
ap = *p++;
if (*ap == '-') switch (ap[1]) {
case 'u':
if (++c >= argc)
error(1, "Bad 'use'");
if (*(hp = slookup(*p++)) == 0) {
*hp = symp;
enter();
}
continue;
case 'l':
break;
case 'x':
xflag++;
continue;
case 'X':
Xflag++;
continue;
case 'r':
rflag++;
arflag++;
continue;
case 's':
sflag++;
xflag++;
continue;
case 'n':
nflag++;
continue;
case 'd':
dflag++;
continue;
case 'i':
iflag++;
continue;
}
load1arg(ap);
close(infil);
}
middle();
setupout();
p = argv+1;
libp = liblist;
for (c=1; c<argc; c++) {
ap = *p++;
if (*ap == '-') switch (ap[1]) {
case 'u':
++c;
++p;
default:
continue;
case 'l':
break;
}
load2arg(ap);
close(infil);
}
finishout();
}
load1arg(acp)
char *acp;
{
register char *cp;
register noff, nbno;
cp = acp;
if (getfile(cp)==0) {
load1(0, 0, 0);
return;
}
nbno = 0;
noff = 1;
for (;;) {
dseek(&text, nbno, noff, sizeof archdr);
if (text.size <= 0) {
libp->bno = -1;
libp++;
return;
}
mget(&archdr, sizeof archdr);
if (load1(1, nbno, noff + (sizeof archdr) / 2)) {
libp->bno = nbno;
libp->off = noff;
libp++;
}
noff =+ (archdr.asize + sizeof archdr)>>1;
nbno =+ (noff >> 8) & 0377;
noff =& 0377;
}
}
load1(libflg, bno, off)
{
register struct symbol *sp, **hp, ***cp;
struct symbol *ssymp;
int ndef, nloc;
readhdr(bno, off);
ctrel = tsize;
cdrel =+ dsize;
cbrel =+ bsize;
ndef = 0;
nloc = sizeof cursym;
cp = local;
ssymp = symp;
if ((filhdr.relflg&RELFLG)==1) {
error(0, "No relocation bits");
return(0);
}
off =+ (sizeof filhdr)/2 + filhdr.tsize + filhdr.dsize;
dseek(&text, bno, off, filhdr.ssize);
while (text.size > 0) {
mget(&cursym, sizeof cursym);
if ((cursym.stype&EXTERN)==0) {
if (Xflag==0 || cursym.sname[0]!='L')
nloc =+ sizeof cursym;
continue;
}
symreloc();
hp = lookup();
if ((sp = *hp) == 0) {
*hp = enter();
*cp++ = hp;
continue;
}
if (sp->stype != EXTERN+UNDEF)
continue;
if (cursym.stype == EXTERN+UNDEF) {
if (cursym.svalue > sp->svalue)
sp->svalue = cursym.svalue;
continue;
}
if (sp->svalue != 0 && cursym.stype == EXTERN+TEXT)
continue;
ndef++;
sp->stype = cursym.stype;
sp->svalue = cursym.svalue;
}
if (libflg==0 || ndef) {
tsize =+ filhdr.tsize;
dsize =+ filhdr.dsize;
bsize =+ filhdr.bsize;
ssize =+ nloc;
return(1);
}
/*
* No symbols defined by this library member.
* Rip out the hash table entries and reset the symbol table.
*/
symp = ssymp;
while (cp > local)
**--cp = 0;
return(0);
}
middle()
{
register struct symbol *sp;
register t, csize;
int nund, corigin;
p_etext = *slookup("_etext");
p_edata = *slookup("_edata");
p_end = *slookup("_end");
/*
* If there are any undefined symbols, save the relocation bits.
*/
if (rflag==0) for (sp=symtab; sp<symp; sp++)
if (sp->stype==EXTERN+UNDEF && sp->svalue==0
&& sp!=p_end && sp!=p_edata && sp!=p_etext) {
rflag++;
dflag = 0;
nflag = 0;
iflag = 0;
sflag = 0;
break;
}
/*
* Assign common locations.
*/
csize = 0;
if (dflag || rflag==0) {
for (sp=symtab; sp<symp; sp++)
if (sp->stype==EXTERN+UNDEF && (t=sp->svalue)!=0) {
t = (t+1) & ~01;
sp->svalue = csize;
sp->stype = EXTERN+COMM;
csize =+ t;
}
if (p_etext && p_etext->stype==EXTERN+UNDEF) {
p_etext->stype = EXTERN+TEXT;
p_etext->svalue = tsize;
}
if (p_edata && p_edata->stype==EXTERN+UNDEF) {
p_edata->stype = EXTERN+DATA;
p_edata->svalue = dsize;
}
if (p_end && p_end->stype==EXTERN+UNDEF) {
p_end->stype = EXTERN+BSS;
p_end->svalue = bsize;
}
}
/*
* Now set symbols to their final value
*/
if (nflag || iflag)
tsize = (tsize + 077) & ~077;
dorigin = tsize;
if (nflag)
dorigin = (tsize+017777) & ~017777;
if (iflag)
dorigin = 0;
corigin = dorigin + dsize;
borigin = corigin + csize;
nund = 0;
for (sp=symtab; sp<symp; sp++) switch (sp->stype) {
case EXTERN+UNDEF:
errlev =| 01;
if (arflag==0 && sp->svalue==0) {
if (nund==0)
printf("Undefined:\n");
nund++;
printf("%.8s\n", sp->sname);
}
continue;
case EXTERN+ABS:
default:
continue;
case EXTERN+TEXT:
sp->svalue =+ torigin;
continue;
case EXTERN+DATA:
sp->svalue =+ dorigin;
continue;
case EXTERN+BSS:
sp->svalue =+ borigin;
continue;
case EXTERN+COMM:
sp->stype = EXTERN+BSS;
sp->svalue =+ corigin;
continue;
}
if (sflag || xflag)
ssize = 0;
bsize =+ csize;
nsym = ssize / (sizeof cursym);
}
setupout()
{
register char *p;
register pid;
if ((toutb[0] = creat("l.out", 0666)) < 0)
error(1, "Can't create l.out");
pid = getpid();
for (p = &tfname[12]; p > &tfname[7];) {
*--p = (pid&07) + '0';
pid =>> 3;
}
tcreat(doutb, 'a');
if (sflag==0 || xflag==0)
tcreat(soutb, 'b');
if (rflag) {
tcreat(troutb, 'c');
tcreat(droutb, 'd');
}
filhdr.fmagic = FMAGIC;
if (nflag)
filhdr.fmagic = NMAGIC;
if (iflag)
filhdr.fmagic = IMAGIC;
filhdr.tsize = tsize;
filhdr.dsize = dsize;
filhdr.bsize = bsize;
filhdr.ssize = sflag? 0: (ssize + (sizeof cursym)*(symp-symtab));
filhdr.entry = 0;
filhdr.pad = 0;
filhdr.relflg = (rflag==0);
mput(toutb, &filhdr, sizeof filhdr);
return;
}
tcreat(buf, letter)
int *buf;
{
tfname[6] = letter;
if ((buf[0] = creat(tfname, RONLY)) < 0)
error(1, "Can't create temp");
}
load2arg(acp)
char *acp;
{
register char *cp;
register struct liblist *lp;
cp = acp;
if (getfile(cp) == 0) {
while (*cp)
cp++;
while (cp >= acp && *--cp != '/');
mkfsym(++cp);
load2(0, 0);
return;
}
for (lp = libp; lp->bno != -1; lp++) {
dseek(&text, lp->bno, lp->off, sizeof archdr);
mget(&archdr, sizeof archdr);
mkfsym(archdr.aname);
load2(lp->bno, lp->off + (sizeof archdr) / 2);
}
libp = ++lp;
}
load2(bno, off)
{
register struct symbol *sp;
register int *lp, symno;
readhdr(bno, off);
ctrel = torigin;
cdrel =+ dorigin;
cbrel =+ borigin;
/*
* Reread the symbol table, recording the numbering
* of symbols for fixing external references.
*/
lp = local;
symno = -1;
off =+ (sizeof filhdr)/2;
dseek(&text, bno, off+filhdr.tsize+filhdr.dsize, filhdr.ssize);
while (text.size > 0) {
symno++;
mget(&cursym, sizeof cursym);
symreloc();
if ((cursym.stype&EXTERN) == 0) {
if (!sflag&&!xflag&&(!Xflag||cursym.sname[0]!='L'))
mput(soutb, &cursym, sizeof cursym);
continue;
}
if ((sp = *lookup()) == 0)
error(1, "internal error: symbol not found");
if (cursym.stype == EXTERN+UNDEF) {
if (lp >= &local[NSYMPR])
error(1, "Local symbol overflow");
*lp++ = symno;
*lp++ = sp;
continue;
}
if (cursym.stype!=sp->stype || cursym.svalue!=sp->svalue) {
printf("%.8s: ", cursym.sname);
error(0, "Multiply defined");
}
}
dseek(&text, bno, off, filhdr.tsize);
dseek(&reloc, bno, off+(filhdr.tsize+filhdr.dsize)/2, filhdr.tsize);
load2td(lp, ctrel, toutb, troutb);
dseek(&text, bno, off+(filhdr.tsize/2), filhdr.dsize);
dseek(&reloc, bno, off+filhdr.tsize+(filhdr.dsize/2), filhdr.dsize);
load2td(lp, cdrel, doutb, droutb);
torigin =+ filhdr.tsize;
dorigin =+ filhdr.dsize;
borigin =+ filhdr.bsize;
}
load2td(lp, creloc, b1, b2)
int *lp;
{
register r, t;
register struct symbol *sp;
for (;;) {
/*
* The pickup code is copied from "get" for speed.
*/
if (--text.size <= 0) {
if (text.size < 0)
break;
text.size++;
t = get(&text);
} else if (--text.nibuf < 0) {
text.nibuf++;
text.size++;
t = get(&text);
} else
t = *text.ptr++;
if (--reloc.size <= 0) {
if (reloc.size < 0)
error(1, "Relocation error");
reloc.size++;
r = get(&reloc);
} else if (--reloc.nibuf < 0) {
reloc.nibuf++;
reloc.size++;
r = get(&reloc);
} else
r = *reloc.ptr++;
switch (r&016) {
case RTEXT:
t =+ ctrel;
break;
case RDATA:
t =+ cdrel;
break;
case RBSS:
t =+ cbrel;
break;
case REXT:
sp = lookloc(lp, r);
if (sp->stype==EXTERN+UNDEF) {
r = (r&01) + ((nsym+(sp-symtab))<<4) + REXT;
break;
}
t =+ sp->svalue;
r = (r&01) + ((sp->stype-(EXTERN+ABS))<<1);
break;
}
if (r&01)
t =- creloc;
putw(t, b1);
if (rflag)
putw(r, b2);
}
}
finishout()
{
register n, *p;
if (nflag||iflag) {
n = torigin;
while (n&077) {
n =+ 2;
putw(0, toutb);
if (rflag)
putw(0, troutb);
}
}
copy(doutb, 'a');
if (rflag) {
copy(troutb, 'c');
copy(droutb, 'd');
}
if (sflag==0) {
if (xflag==0)
copy(soutb, 'b');
for (p=symtab; p < symp;)
putw(*p++, toutb);
}
fflush(toutb);
close(toutb[0]);
unlink("a.out");
link("l.out", "a.out");
delarg = errlev;
delexit();
}
delexit()
{
register c;
unlink("l.out");
for (c = 'a'; c <= 'd'; c++) {
tfname[6] = c;
unlink(tfname);
}
if (delarg==0)
chmod("a.out", 0777);
exit(delarg);
}
copy(buf, c)
int *buf;
{
register f, *p, n;
fflush(buf);
close(buf[0]);
tfname[6] = c;
f = open(tfname, 0);
while ((n = read(f, doutb, 512)) > 1) {
n =>> 1;
p = doutb;
do
putw(*p++, toutb);
while (--n);
}
close(f);
}
mkfsym(s)
char *s;
{
if (sflag || xflag)
return;
cp8c(s, cursym.sname);
cursym.stype = 037;
cursym.svalue = torigin;
mput(soutb, &cursym, sizeof cursym);
}
mget(aloc, an)
int *aloc;
{
register *loc, n;
register *p;
n = an;
n =>> 1;
loc = aloc;
if ((text.nibuf =- n) >= 0) {
if ((text.size =- n) > 0) {
p = text.ptr;
do
*loc++ = *p++;
while (--n);
text.ptr = p;
return;
} else
text.size =+ n;
}
text.nibuf =+ n;
do {
*loc++ = get(&text);
} while (--n);
}
mput(buf, aloc, an)
int *aloc;
{
register *loc;
register n;
loc = aloc;
n = an>>1;
do {
putw(*loc++, buf);
} while (--n);
}
dseek(asp, ab, o, s)
{
register struct stream *sp;
register struct page *p;
register b;
int n;
sp = asp;
b = ab + ((o>>8) & 0377);
o =& 0377;
--sp->pno->nuser;
if ((p = &page[0])->bno!=b && (p = &page[1])->bno!=b)
if (p->nuser==0 || (p = &page[0])->nuser==0) {
if (page[0].nuser==0 && page[1].nuser==0)
if (page[0].bno < page[1].bno)
p = &page[0];
p->bno = b;
seek(infil, b, 3);
if ((n = read(infil, p->buff, 512)>>1) < 0)
n = 0;
p->nibuf = n;
} else
error(1, "No pages");
++p->nuser;
sp->bno = b;
sp->pno = p;
sp->ptr = p->buff + o;
if (s != -1)
sp->size = (s>>1) & 077777;
if ((sp->nibuf = p->nibuf-o) <= 0)
sp->size = 0;
}
get(asp)
struct stream *asp;
{
register struct stream *sp;
sp = asp;
if (--sp->nibuf < 0) {
dseek(sp, sp->bno+1, 0, -1);
--sp->nibuf;
}
if (--sp->size <= 0) {
if (sp->size < 0)
error(1, premeof);
++fpage.nuser;
--sp->pno->nuser;
sp->pno = &fpage;
}
return(*sp->ptr++);
}
getfile(acp)
char *acp;
{
register char *cp;
register c;
cp = acp;
archdr.aname[0] = '\0';
if (cp[0]=='-' && cp[1]=='l') {
if ((c = cp[2]) == '\0')
c = 'a';
cp = "/lib/lib?.a";
cp[8] = c;
}
filname = cp;
if ((infil = open(cp, 0)) < 0)
error(1, "cannot open");
page[0].bno = page[1].bno = -1;
page[0].nuser = page[1].nuser = 0;
text.pno = reloc.pno = &fpage;
fpage.nuser = 2;
dseek(&text, 0, 0, 2);
if (text.size <= 0)
error(1, premeof);
return(get(&text) == ARCMAGIC);
}
struct symbol **lookup()
{
int i;
register struct symbol **hp;
register char *cp, *cp1;
i = 0;
for (cp=cursym.sname; cp < &cursym.sname[8];)
i = (i<<1) + *cp++;
for (hp = &hshtab[(i&077777)%NSYM+2]; *hp!=0;) {
cp1 = (*hp)->sname;
for (cp=cursym.sname; cp < &cursym.sname[8];)
if (*cp++ != *cp1++)
goto no;
break;
no:
if (++hp >= &hshtab[NSYM+2])
hp = hshtab;
}
return(hp);
}
struct symbol **slookup(s)
char *s;
{
cp8c(s, cursym.sname);
cursym.stype = EXTERN+UNDEF;
cursym.svalue = 0;
return(lookup());
}
enter()
{
register struct symbol *sp;
if ((sp=symp) >= &symtab[NSYM])
error(1, "Symbol table overflow");
cp8c(cursym.sname, sp->sname);
sp->stype = cursym.stype;
sp->svalue = cursym.svalue;
symp++;
return(sp);
}
symreloc()
{
switch (cursym.stype) {
case TEXT:
case EXTERN+TEXT:
cursym.svalue =+ ctrel;
return;
case DATA:
case EXTERN+DATA:
cursym.svalue =+ cdrel;
return;
case BSS:
case EXTERN+BSS:
cursym.svalue =+ cbrel;
return;
case EXTERN+UNDEF:
return;
}
if (cursym.stype&EXTERN)
cursym.stype = EXTERN+ABS;
}
error(n, s)
char *s;
{
if (filname) {
printf("%s", filname);
if (archdr.aname[0])
printf("(%.8s)", archdr.aname);
printf(": ");
}
printf("%s\n", s);
if (n)
delexit();
errlev = 2;
}
lookloc(alp, r)
{
register int *clp, *lp;
register sn;
lp = alp;
sn = (r>>4) & 07777;
for (clp=local; clp<lp; clp =+ 2)
if (clp[0] == sn)
return(clp[1]);
error(1, "Local symbol botch");
}
readhdr(bno, off)
{
register st, sd;
dseek(&text, bno, off, sizeof filhdr);
mget(&filhdr, sizeof filhdr);
if (filhdr.fmagic != FMAGIC)
error(1, "Bad format");
st = (filhdr.tsize+01) & ~01;
filhdr.tsize = st;
cdrel = -st;
sd = (filhdr.dsize+01) & ~01;
cbrel = - (st+sd);
filhdr.bsize = (filhdr.bsize+01) & ~01;
}
cp8c(from, to)
char *from, *to;
{
register char *f, *t, *te;
f = from;
t = to;
te = t+8;
while ((*t++ = *f++) && t<te);
while (t<te)
*t++ = 0;
}