/* * link editor */ #include #include "sys/types.h" #include "sys/stat.h" /* Layout of a.out file : * * header of 8 words magic number 405, 407, 410, 411 * text size ) * data size ) in bytes but even * bss size ) * symbol table size * entry point * {unused} * flag set if no relocation * * * header: 0 * text: 16 * data: 16+textsize * relocation: 16+textsize+datasize * symbol table: 16+2*(textsize+datasize) or 16+textsize+datasize * */ #define TRUE 1 #define FALSE 0 #define ARCMAGIC 0177545 #define OMAGIC 0405 #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 NOVLY 16 #define RELFLG 01 #define NROUT 256 #define NSYM 1103 #define NSYMPR 1000 char premeof[] = "Premature EOF"; char goodnm[] = "__.SYMDEF"; /* table of contents stuff */ #define TABSZ 700 struct tab { char cname[8]; long cloc; } tab[TABSZ]; int tnum; /* overlay management */ int vindex; struct overlay { int argsav; int symsav; struct liblist *libsav; char *vname; int ctsav, cdsav, cbsav; int offt, offd, offb, offs; } vnodes[NOVLY]; /* input management */ 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 { char aname[14]; long atime; char auid, agid; int amode; long asize; } archdr; struct { int fmagic; int tsize; int dsize; int bsize; int ssize; int entry; int pad; int relflg; } filhdr; /* one entry for each archive member referenced; * set in first pass; needs restoring for overlays */ struct liblist { long loc; }; struct liblist liblist[NROUT]; struct liblist *libp = liblist; /* symbol management */ struct symbol { char sname[8]; char stype; char spare; int svalue; }; struct local { int locindex; /* index to symbol in file */ struct symbol *locsymbol; /* ptr to symbol table */ }; struct symbol cursym; /* current symbol */ struct symbol symtab[NSYM]; /* actual symbols */ struct symbol **symhash[NSYM]; /* ptr to hash table entry */ struct symbol *lastsym; /* last symbol entered */ int symindex; /* next available symbol table entry */ struct symbol *hshtab[NSYM+2]; /* hash table for symbols */ struct local local[NSYMPR]; /* internal symbols */ struct symbol *p_etext; struct symbol *p_edata; struct symbol *p_end; struct symbol *entrypt; int trace; /* flags */ int xflag; /* discard local symbols */ int Xflag; /* discard locals starting with 'L' */ int Sflag; /* discard all except locals and globals*/ 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 Oflag; /* set magic # to 0405 (overlay) */ int dflag; /* define common even with rflag */ int iflag; /* I/D space separated */ int vflag; /* overlays used */ int ofilfnd; char *ofilename = "l.out"; int infil; char *filname; /* cumulative sizes set in pass 1 */ int tsize; int dsize; int bsize; int ssize; /* symbol relocation; both passes */ int ctrel; int cdrel; int cbrel; int errlev; int delarg = 4; char tfname[] = "/tmp/ldaXXXXX"; /* output management */ struct buf { int fildes; int nleft; int *xnext; int iobuf[256]; }; struct buf toutb; struct buf doutb; struct buf troutb; struct buf droutb; struct buf soutb; struct symbol **lookup(); struct symbol **slookup(); struct symbol *lookloc(); delexit() { unlink("l.out"); if (delarg==0) chmod(ofilename, 0777 & ~umask(0)); exit(delarg); } main(argc, argv) char **argv; { register int c, i; int num; register char *ap, **p; int found; int vscan; char save; if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, delexit); if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, delexit); if (argc == 1) exit(4); p = argv+1; /* scan files once to find symdefs */ for (c=1; c= argc) error(2, "Bad output file"); ofilename = *p++; ofilfnd++; continue; case 'u': case 'e': if (++c >= argc) error(2, "Bad 'use' or 'entry'"); enter(slookup(*p++)); if (ap[i]=='e') entrypt = lastsym; continue; case 'v': if (++c >= argc) error(2, "-v: arg missing"); vflag=TRUE; vscan = vindex; found=FALSE; while (--vscan>=0 && found==FALSE) found = eq(vnodes[vscan].vname, *p); if (found) { endload(c, argv); restore(vscan); } else record(c, *p); p++; continue; case 'D': if (++c >= argc) error(2, "-D: arg missing"); num = atoi(*p++); if (dsize>num) error(2, "-D: too small"); dsize = num; continue; case 'l': save = ap[--i]; ap[i]='-'; load1arg(&ap[i]); ap[i]=save; break; case 'x': xflag++; continue; case 'X': Xflag++; continue; case 'S': Sflag++; continue; case 'r': rflag++; arflag++; continue; case 's': sflag++; xflag++; continue; case 'n': nflag++; continue; case 'd': dflag++; continue; case 'i': iflag++; continue; case 'O': Oflag++; continue; case 't': trace++; continue; default: error(2, "bad flag"); } /*endsw*/ break; } /*endfor*/ } else load1arg(ap); } endload(argc, argv); } /* used after pass 1 */ int nsym; int torigin; int dorigin; int borigin; endload(argc, argv) int argc; char **argv; { register int c, i; int dnum; register char *ap, **p; filname = 0; middle(); setupout(); p = argv+1; libp = liblist; for (c=1; cargsav = c; v->symsav = symindex; v->libsav = libp; v->vname = nam; v->offt = tsize; v->offd = dsize; v->offb = bsize; v->offs = ssize; v->ctsav = ctrel; v->cdsav = cdrel; v->cbsav = cbrel; } restore(vscan) int vscan; { register struct overlay *v; register int saved; v = &vnodes[vscan]; vindex = vscan+1; libp = v->libsav; ctrel = v->ctsav; cdrel = v->cdsav; cbrel = v->cbsav; tsize = v->offt; dsize = v->offd; bsize = v->offb; ssize = v->offs; saved = v->symsav; while (symindex>saved) *symhash[--symindex]=0; } /* scan file to find defined symbols */ load1arg(acp) char *acp; { register char *cp; long nloc; cp = acp; switch ( getfile(cp)) { case 0: load1(0, 0L); break; /* regular archive */ case 1: nloc = 1; while ( step(nloc)) nloc += (archdr.asize + sizeof(archdr) + 1) >> 1; break; /* table of contents */ case 2: tnum = archdr.asize / sizeof(struct tab); if (tnum >= TABSZ) { error(2, "fast load buffer too small"); } lseek(infil, (long)(sizeof(filhdr.fmagic)+sizeof(archdr)), 0); read(infil, (char *)tab, tnum * sizeof(struct tab)); while (ldrand()); libp->loc = -1; libp++; break; /* out of date table of contents */ case 3: error(0, "out of date (warning)"); for(nloc = 1+((archdr.asize+sizeof(archdr)+1) >> 1); step(nloc); nloc += (archdr.asize + sizeof(archdr) + 1) >> 1); break; } close(infil); } step(nloc) long nloc; { dseek(&text, nloc, sizeof archdr); if (text.size <= 0) { libp->loc = -1; libp++; return(0); } mget((int *)&archdr, sizeof archdr); if (load1(1, nloc + (sizeof archdr) / 2)) { libp->loc = nloc; libp++; } return(1); } ldrand() { int i; struct symbol *sp, **pp; struct liblist *oldp = libp; for(i = 0; istype != EXTERN+UNDEF) continue; step(tab[i].cloc >> 1); } return(oldp != libp); } add(a,b,s) int a, b; char *s; { long r; r = (long)(unsigned)a + (unsigned)b; if (r >= 0200000) error(1,s); return(r); } /* single file or archive member */ load1(libflg, loc) long loc; { register struct symbol *sp; int savindex; int ndef, nloc, type, mtype; readhdr(loc); ctrel = tsize; cdrel += dsize; cbrel += bsize; ndef = 0; nloc = sizeof cursym; savindex = symindex; if ((filhdr.relflg&RELFLG)==1) { error(1, "No relocation bits"); return(0); } loc += (sizeof filhdr)/2 + filhdr.tsize + filhdr.dsize; dseek(&text, loc, filhdr.ssize); while (text.size > 0) { mget((int *)&cursym, sizeof cursym); type = cursym.stype; if (Sflag) { mtype = type&037; if (mtype==1 || mtype>4) { continue; } } if ((type&EXTERN)==0) { if (Xflag==0 || cursym.sname[0]!='L') nloc += sizeof cursym; continue; } symreloc(); if (enter(lookup())) continue; if ((sp = lastsym)->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 = add(tsize,filhdr.tsize,"text overflow"); dsize = add(dsize,filhdr.dsize,"data overflow"); bsize = add(bsize,filhdr.bsize,"bss overflow"); ssize = add(ssize,nloc,"symbol table overflow"); return(1); } /* * No symbols defined by this library member. * Rip out the hash table entries and reset the symbol table. */ while (symindex>savindex) *symhash[--symindex]=0; return(0); } middle() { register struct symbol *sp, *symp; register t, csize; int nund, corigin; torigin=0; dorigin=0; borigin=0; p_etext = *slookup("_etext"); p_edata = *slookup("_edata"); p_end = *slookup("_end"); /* * If there are any undefined symbols, save the relocation bits. */ symp = &symtab[symindex]; if (rflag==0) { for (sp = symtab; spstype==EXTERN+UNDEF && sp->svalue==0 && sp!=p_end && sp!=p_edata && sp!=p_etext) { rflag++; dflag = 0; break; } } if (rflag) nflag = sflag = iflag = Oflag = 0; /* * Assign common locations. */ csize = 0; if (dflag || rflag==0) { ldrsym(p_etext, tsize, EXTERN+TEXT); ldrsym(p_edata, dsize, EXTERN+DATA); ldrsym(p_end, bsize, EXTERN+BSS); for (sp = symtab; spstype==EXTERN+UNDEF && (t = sp->svalue)!=0) { t = (t+1) & ~01; sp->svalue = csize; sp->stype = EXTERN+COMM; csize = add(csize, t, "bss overflow"); } } /* * 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; spstype) { 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 = add(bsize, csize, "bss overflow"); nsym = ssize / (sizeof cursym); } ldrsym(asp, val, type) struct symbol *asp; { register struct symbol *sp; if ((sp = asp) == 0) return; if (sp->stype != EXTERN+UNDEF || sp->svalue) { printf("%.8s: ", sp->sname); error(1, "Multiply defined"); return; } sp->stype = type; sp->svalue = val; } setupout() { tcreat(&toutb, 0); mktemp(tfname); tcreat(&doutb, 1); if (sflag==0 || xflag==0) tcreat(&soutb, 1); if (rflag) { tcreat(&troutb, 1); tcreat(&droutb, 1); } filhdr.fmagic = (Oflag ? OMAGIC :( iflag ? IMAGIC : ( nflag ? NMAGIC : FMAGIC ))); filhdr.tsize = tsize; filhdr.dsize = dsize; filhdr.bsize = bsize; filhdr.ssize = sflag? 0: (ssize + (sizeof cursym)*symindex); if (entrypt) { if (entrypt->stype!=EXTERN+TEXT) error(1, "Entry point not in text"); else filhdr.entry = entrypt->svalue | 01; } else filhdr.entry=0; filhdr.pad = 0; filhdr.relflg = (rflag==0); mput(&toutb, (int *)&filhdr, sizeof filhdr); } tcreat(buf, tempflg) struct buf *buf; { register int ufd; char *nam; nam = (tempflg ? tfname : ofilename); if ((ufd = creat(nam, 0666)) < 0) error(2, tempflg?"cannot create temp":"cannot create output"); close(ufd); buf->fildes = open(nam, 2); if (tempflg) unlink(tfname); buf->nleft = sizeof(buf->iobuf)/sizeof(int); buf->xnext = buf->iobuf; } 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(0L); } else { /* scan archive members referenced */ for (lp = libp; lp->loc != -1; lp++) { dseek(&text, lp->loc, sizeof archdr); mget((int *)&archdr, sizeof archdr); mkfsym(archdr.aname); load2(lp->loc + (sizeof archdr) / 2); } libp = ++lp; } close(infil); } load2(loc) long loc; { register struct symbol *sp; register struct local *lp; register int symno; int type, mtype; readhdr(loc); ctrel = torigin; cdrel += dorigin; cbrel += borigin; /* * Reread the symbol table, recording the numbering * of symbols for fixing external references. */ lp = local; symno = -1; loc += (sizeof filhdr)/2; dseek(&text, loc + filhdr.tsize + filhdr.dsize, filhdr.ssize); while (text.size > 0) { symno++; mget((int *)&cursym, sizeof cursym); symreloc(); type = cursym.stype; if (Sflag) { mtype = type&037; if (mtype==1 || mtype>4) continue; } if ((type&EXTERN) == 0) { if (!sflag&&!xflag&&(!Xflag||cursym.sname[0]!='L')) mput(&soutb, (int *)&cursym, sizeof cursym); continue; } if ((sp = *lookup()) == 0) error(2, "internal error: symbol not found"); if (cursym.stype == EXTERN+UNDEF) { if (lp >= &local[NSYMPR]) error(2, "Local symbol overflow"); lp->locindex = symno; lp++->locsymbol = sp; continue; } if (cursym.stype!=sp->stype || cursym.svalue!=sp->svalue) { printf("%.8s: ", cursym.sname); error(1, "Multiply defined"); } } dseek(&text, loc, filhdr.tsize); dseek(&reloc, loc + half(filhdr.tsize + filhdr.dsize), filhdr.tsize); load2td(lp, ctrel, &toutb, &troutb); dseek(&text, loc+half(filhdr.tsize), filhdr.dsize); dseek(&reloc, loc+filhdr.tsize+half(filhdr.dsize), filhdr.dsize); load2td(lp, cdrel, &doutb, &droutb); torigin += filhdr.tsize; dorigin += filhdr.dsize; borigin += filhdr.bsize; } load2td(lp, creloc, b1, b2) struct local *lp; struct buf *b1, *b2; { register r, t; register struct symbol *sp; for (;;) { /* * The pickup code is copied from "get" for speed. */ /* next text or data word */ 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++; /* next relocation word */ if (--reloc.size <= 0) { if (reloc.size < 0) error(2, "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); if (rflag) { copy(&troutb); copy(&droutb); } if (sflag==0) { if (xflag==0) copy(&soutb); for (p = (int *)symtab; p < (int *)&symtab[symindex];) putw(*p++, &toutb); } flush(&toutb); close(toutb.fildes); if (!ofilfnd) { unlink("a.out"); link("l.out", "a.out"); ofilename = "a.out"; } delarg = errlev; delexit(); } copy(buf) struct buf *buf; { register f, *p, n; flush(buf); lseek(f = buf->fildes, (long)0, 0); while ((n = read(f, (char *)doutb.iobuf, sizeof(doutb.iobuf))) > 1) { n >>= 1; p = (int *)doutb.iobuf; 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, (int *)&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) struct buf *buf; int *aloc; { register *loc; register n; loc = aloc; n = an>>1; do { putw(*loc++, buf); } while (--n); } dseek(asp, aloc, s) long aloc; struct stream *asp; { register struct stream *sp; register struct page *p; /* register */ long b, o; int n; b = aloc >> 8; o = aloc & 0377; sp = asp; --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; lseek(infil, (aloc & ~0377L) << 1, 0); if ((n = read(infil, (char *)p->buff, 512)>>1) < 0) n = 0; p->nibuf = n; } else error(2, "No pages"); ++p->nuser; sp->bno = b; sp->pno = p; sp->ptr = p->buff + o; if (s != -1) sp->size = half(s); if ((sp->nibuf = p->nibuf-o) <= 0) sp->size = 0; } half(i) { return((i>>1)&077777); } get(asp) struct stream *asp; { register struct stream *sp; sp = asp; if (--sp->nibuf < 0) { dseek(sp, (long)(sp->bno + 1) << 8, -1); --sp->nibuf; } if (--sp->size <= 0) { if (sp->size < 0) error(2, premeof); ++fpage.nuser; --sp->pno->nuser; sp->pno = (struct page *)&fpage; } return(*sp->ptr++); } getfile(acp) char *acp; { register char *cp; register int c; struct stat x; cp = acp; infil = -1; archdr.aname[0] = '\0'; filname = cp; if (cp[0]=='-' && cp[1]=='l') { if(cp[2] == '\0') cp = "-la"; filname = "/usr/lib/libxxxxxxxxxxxxxxx"; for(c=0; cp[c+2]; c++) filname[c+12] = cp[c+2]; filname[c+12] = '.'; filname[c+13] = 'a'; filname[c+14] = '\0'; if ((infil = open(filname+4, 0)) >= 0) { filname += 4; } } if (infil == -1 && (infil = open(filname, 0)) < 0) error(2, "cannot open"); page[0].bno = page[1].bno = -1; page[0].nuser = page[1].nuser = 0; text.pno = reloc.pno = (struct page *)&fpage; fpage.nuser = 2; dseek(&text, 0L, 2); if (text.size <= 0) error(2, premeof); if(get(&text) != ARCMAGIC) return(0); /* regualr file */ dseek(&text, 1L, sizeof archdr); /* word addressing */ if(text.size <= 0) return(1); /* regular archive */ mget((int *)&archdr, sizeof archdr); if(strncmp(archdr.aname, goodnm, 14) != 0) return(1); /* regular archive */ else { fstat(infil, &x); if(x.st_mtime > archdr.atime) { return(3); } else return(2); } } struct symbol **lookup() { int i; int clash; 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; clash=FALSE; for (cp = cursym.sname; cp < &cursym.sname[8];) if (*cp++ != *cp1++) { clash=TRUE; break; } if (clash) { if (++hp >= &hshtab[NSYM+2]) hp = hshtab; } else break; } return(hp); } struct symbol **slookup(s) char *s; { cp8c(s, cursym.sname); cursym.stype = EXTERN+UNDEF; cursym.svalue = 0; return(lookup()); } enter(hp) struct symbol **hp; { register struct symbol *sp; if (*hp==0) { if (symindex>=NSYM) error(2, "Symbol table overflow"); symhash[symindex] = hp; *hp = lastsym = sp = &symtab[symindex++]; cp8c(cursym.sname, sp->sname); sp->stype = cursym.stype; sp->svalue = cursym.svalue; return(1); } else { lastsym = *hp; return(0); } } 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 (errlev==0) printf("ld:"); if (filname) { printf("%s", filname); if (archdr.aname[0]) printf("(%.14s)", archdr.aname); printf(": "); } printf("%s\n", s); if (n > 1) delexit(); errlev = n; } struct symbol * lookloc(alp, r) struct local *alp; { register struct local *clp, *lp; register sn; lp = alp; sn = (r>>4) & 07777; for (clp = local; clplocindex == sn) return(clp->locsymbol); error(2, "Local symbol botch"); } readhdr(loc) long loc; { register st, sd; dseek(&text, loc, sizeof filhdr); mget((int *)&filhdr, sizeof filhdr); if (filhdr.fmagic != FMAGIC) error(2, "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++) && txnext)++ = w; if (--b->nleft <= 0) flush(b); } flush(b) register struct buf *b; { register n; if ((n = (char *)b->xnext - (char *)b->iobuf) > 0) if (write(b->fildes, (char *)b->iobuf, n) != n) error(2, "output error"); b->xnext = b->iobuf; b->nleft = sizeof(b->iobuf)/sizeof(int); }