|
|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - download
Length: 12172 (0x2f8c)
Types: TextFile
Notes: UNIX file
Names: »main.c«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
└─⟦f4b8d8c84⟧ UNIX Filesystem
└─⟦this⟧ »cmd/ld/main.c«
static flag_t memld; /* do in-memory load */
static flag_t dcomm; /* Define commons even if reloc out */
static flag_t nosym; /* Don't output symbol table */
static flag_t reloc; /* Output relocation info */
static flag_t lmodel; /* Large model mode */
static flag_t segoff; /* Segment + offset format LR_LONG */
static uaddr_t base; /* Relocation base of output */
static char *cbase; /* Set by -b option */
static struct stat statbuf;
static char *ofname = "l.out";
static char *entrys; /* Entry point symbol */
static int digit();
FILE *setoutput();
main(argc, argv)
int argc;
char *argv[];
{
seg_t *sgp;
sym_t *sp;
mod_t *mp;
FILE *ofp;
int i, segn;
unsigned int symno;
size_t daddr;
uaddr_t addr;
scanargs(argc, argv);
if (machine == 0)
fatal("no input found");
/*
* all modules have been read
* resolve meanings of various flags
*/
if (nundef) { /* undefined symbols */
nosym=0; /* requires symbol table be retained */
oldh.l_flag &= ~LF_SEP; /* and cannot separate */
} else if (!reloc) { /* no relocation */
dcomm++; /* must define commons */
if (oseg[L_REL].size!=0) { /* discarding reloc info */
oldh.l_flag |= LF_NRB; /* make note */
oseg[L_REL].size = 0;
}
}
if (dcomm) /* to define common symbols... */
oseg[L_BSSD].size += commons; /* we need this much room */
if (nosym)
oseg[L_SYM].size = 0;
if (watch)
message("# undefined=%d", nundef);
/*
* set segment bases
* Finding the base here might not work for symbols
* as the symbol table might be not quite right yet.
*/
if (cbase != NULL)
base = lentry(cbase, "relocation base");
else if (oldh.l_flag & LF_KER)
base = drvbase[machine];
else
base = userbase[machine];
baseall(oseg, &oldh);
ofp = setoutput();
/*
* set disk offsets of segments
* if not bigger than a breadbox, build output file in memory;
* otherwise, open the file for each segment,
* to get independent buffering
*/
daddr = segoffs(oseg, 0L);
if (watch)
message("size: %ld", daddr);
#if BREADBOX
if (memld||daddr<BREADBOX)
outbuf = malloc((int)daddr);
#endif
for (i=0; i<NLSEG; i++) {
if (i==L_BSSI || i==L_BSSD)
continue;
sgp = &oseg[i];
if (sgp->size==0)
continue;
#if BREADBOX
if (outbuf!=NULL) {
int abort();
if ((outputf[i] = malloc(sizeof(FILE)))==NULL)
fatal("out of space");
outputf[i]->_cc = sgp->size;
outputf[i]->_cp = &outbuf[sgp->daddr];
outputf[i]->_pt = &abort;
} else
#endif
{
if ((outputf[i] = fopen(ofname, "r+w"))==NULL)
fatal("cannot open %s (seg %d)", ofname, i);
fseek(outputf[i], sgp->daddr, 0);
}
}
/*
* define internal symbols which have been referenced
*/
if (dcomm) {
i = oldh.l_flag&LF_KER;
endbind(etext_s, L_PRVI, i);
endbind(edata_s, L_PRVD, i);
endbind(end_s, L_BSSD, i);
}
/*
* run through symbol table fixing everything up
*/
symno = 0;
for (i=0; i<NHASH; i++) {
for (sp=symtable[i]; sp!=NULL; sp=sp->next) {
if ((segn=sp->s.ls_type&~L_GLOBAL) < L_SYM) {
/* make defined symbol relative to virtual 0 */
sp->s.ls_addr += oseg[segn].vbase;
} else if (segn==L_SYM) {
spmsg(sp, "references symbol segment");
} else if (sp->s.ls_type!=(L_GLOBAL|L_REF)) {
; /* leave absolutes alone */
} else if (sp->s.ls_addr==0) {
/* undefined global */
if (!reloc)
spmsg(sp, "undefined");
} else if (dcomm) {
/* define commons */
addr = sp->s.ls_addr;
sp->s.ls_addr = oseg[L_BSSD].vbase
+oseg[L_BSSD].size
-commons;
commons -= addr;
sp->s.ls_type = L_GLOBAL|L_BSSD;
}
/* assign symbol number */
sp->symno = symno++;
if (!nosym) {
/* output to symbol segment */
sp->s.ls_addr = ptov(sp->s.ls_addr);
canshort(sp->s.ls_type);
canlong(sp->s.ls_addr);
putstruc(&sp->s, sizeof sp->s,
outputf[L_SYM], &oseg[L_SYM]);
canshort(sp->s.ls_type);
canlong(sp->s.ls_addr);
sp->s.ls_addr = vtop(sp->s.ls_addr);
}
}
}
/*
* get entry point (cannot precede symbol table fixup)
*/
oldh.l_entry = entrys != NULL
? lentry(entrys, "entry point")
: oldh.l_flag&LF_KER ? drvbase[machine]
: userbase[machine];
oldh.l_entry = ptov(oldh.l_entry);
/*
* Pass 2
*/
for (mp = modhead; mp!=NULL; mp=mp->next)
loadmod(mp);
/*
* All over but the flushing
*/
canshort(oldh.l_magic);
canshort(oldh.l_flag);
canshort(oldh.l_machine);
canshort(oldh.l_tbase);
canlong(oldh.l_entry);
for (i=0; i<NLSEG; i++) {
oldh.l_ssize[i] = oseg[i].size;
cansize(oldh.l_ssize[i]);
}
if (outbuf==NULL)
fwrite(&oldh, sizeof oldh, 1, ofp);
else {
for (i=0; i<sizeof oldh; i++)
outbuf[i] = *((char *)&oldh + i);
fwrite(outbuf, 1, (int)daddr, ofp);
}
if (nundef==0 || dcomm) {
chmod(ofname, statbuf.st_mode|S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6));
return(0);
} else if (reloc)
return(0);
else
return(nundef);
}
/*
* Process all arguments, looking for
* options, objects, and libraries.
*/
scanargs(ac, av)
int ac;
register char *av[];
{
register int i;
register char *lp;
if (ac <= 1)
usage("no args");
for (i=1; i<ac; i++) {
if (*av[i]!='-') {
/* not -option; attempt to read file */
if (!rdfile(av[i]))
fatal("can't open %s", av[i]);
} else switch (av[i][1]) {
/* decode options */
default:
usage("unknown option %s", av[i]);
continue;
case 'd':
dcomm++;
continue;
case 'e':
if (++i >= ac)
usage("Bad -e option");
else
entrys = av[i];
continue;
case 'i':
oldh.l_flag |= LF_SEP;
continue;
case 'k':
oldh.l_flag |= LF_KER;
if (av[i][2]!='\0')
rdsymbol(&av[i][2]);
else
rdsymbol("/coherent");
continue;
case 'l':
lp = malloc(strlen(av[i])+16);
sprintf(lp, "/lib/lib%s.a", &av[i][2]);
if (rdfile(lp))
;
else {
sprintf(lp, "/usr/lib/lib%s.a", &av[i][2]);
if (rdfile(lp))
;
else
fatal("can't open lib%s.a", &av[i][2]);
}
continue;
case 'L':
lmodel++;
continue;
case 'm':
memld++;
continue;
case 'n':
oldh.l_flag |= LF_SHR;
continue;
case 'o':
if (++i >= ac)
usage("bad -o option");
else
ofname = av[i];
continue;
case 'r':
reloc++;
continue;
case 'R': /* Relocation base */
if (++i >= ac)
usage("Bad -b option");
else
cbase = av[i];
continue;
case 's':
nosym++;
continue;
case 'u':
if (++i >= ac)
usage("bad -u option");
else
undef(av[i]);
continue;
case 'w':
watch++;
message("watch!");
continue;
case 'X':
noilcl++;
continue;
case 'x':
nolcl++;
continue;
}
}
}
/*
* Open output file (forcefully
* if it is necessary)
*/
static FILE *
setoutput()
{
register FILE *fp;
if ((fp = fopen(ofname, "w"))==NULL
&& (unlink(ofname)!=0
|| (fp = fopen(ofname, "w"))==NULL))
fatal("cannot create %s", ofname);
stat(ofname, &statbuf);
chmod(ofname, statbuf.st_mode&~(S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)));
setbuf(fp, NULL);
return (fp);
}
/*
* Set virtual bases in array of segments
* according to flags in header.
* Bases are here stored as physical address
* offsets. This is important for seg/offset machines.
*/
void
baseall(sega, ldhp)
register seg_t sega[];
register ldh_t *ldhp;
{
register uaddr_t addr;
addr = ldhp->l_flag&LF_KER ? drvbase[ldhp->l_machine] : base;
addr = setbase(&sega[L_SHRI], ldhp, addr);
if ((ldhp->l_flag&(LF_SHR|LF_SEP))==LF_SHR)
addr = setbase(&sega[L_SHRD], ldhp, addr);
if (ldhp->l_flag&LF_SHR)
addr = newpage(addr);
addr = setbase(&sega[L_PRVI], ldhp, addr);
if ((ldhp->l_flag&(LF_SHR|LF_SEP))==LF_SHR)
addr = setbase(&sega[L_PRVD], ldhp, addr);
addr = setbase(&sega[L_BSSI], ldhp, addr);
if (ldhp->l_flag&LF_SEP)
addr = seppage(ldhp, addr);
if ((ldhp->l_flag&(LF_SHR|LF_SEP))!=LF_SHR)
addr = setbase(&sega[L_SHRD], ldhp, addr);
if ((ldhp->l_flag&(LF_SHR|LF_SEP))==(LF_SHR|LF_SEP))
addr = newpage(addr);
if ((ldhp->l_flag&(LF_SHR|LF_SEP))!=LF_SHR)
addr = setbase(&sega[L_PRVD], ldhp, addr);
setbase(&sega[L_BSSD], ldhp, addr);
}
/*
* Set virtual base in given segment and return next address
* Check for wraparound and driver space overflow
*/
uaddr_t
setbase(segp, ldhp, addr)
register seg_t *segp;
register ldh_t *ldhp;
register uaddr_t addr;
{
register int m;
m = ldhp->l_machine;
segp->vbase = addr;
addr += segp->size;
if (segoff) {
if (!lmodel && (addr-segp->vbase) > segmax[m])
fatal("address wraparound, use `-L'");
if (ldhp!=&oldh && segp->size>segmax[m])
fatal("segment larger than %DKb", segmax[m]/1024);
} else if ((unsigned)addr < (unsigned)segp->vbase)
fatal("address wraparound");
if ((ldhp->l_flag & LF_KER)!=0 && addr>drvtop[m])
fatal("driver larger than %DKb", (drvtop[m]-drvbase[m])/1024);
return (addr);
}
/*
* Returns where to start Data
* in a separated I/D image.
*/
uaddr_t
seppage(ldhp, addr)
register ldh_t *ldhp;
register uaddr_t addr;
{
switch (ldhp->l_machine) {
case M_Z8001:
return (newpage(addr));
default:
return (ldhp->l_flag&LF_KER ? drvbase[ldhp->l_machine] : 0);
}
/* NOTREACHED */
}
/*
* Set segment disk offsets, given sizes
* return size of module
*/
size_t
segoffs(sega, offs)
seg_t sega[];
register size_t offs;
{
register seg_t *sgp;
offs += sizeof(ldh_t);
for (sgp = &sega[0]; sgp<&sega[NLSEG]; sgp++) {
if (sgp==&sega[L_BSSI] || sgp==&sega[L_BSSD])
continue;
else {
sgp->daddr = offs;
offs += sgp->size;
}
}
return (offs);
}
/*
* Bump address to next hardware segment boundary
* [Note: assumption that segsize table contains powers of 2]
*/
uaddr_t
newpage(addr)
uaddr_t addr;
{
register uaddr_t inc = segsize[machine]-1;
return ((addr+inc)&~inc);
}
/*
* Determine entry point; either octal address or symbol name.
* Symbol table is already fixed-up (canonicalised) so must
* undo it here.
*/
uaddr_t
lentry(cp, m)
register char *cp;
char *m;
{
char id[NCPLN], *s=&id[0];
uaddr_t oaddr = 0;
int notoct = 0;
int base = 10;
int d;
register sym_t *sp;
if (*cp == '0') {
if (*++cp == 'x') {
cp++;
base = 16;
}
}
for (; *cp; cp++) {
if (s < &id[NCPLN])
*s++ = *cp;
if ((d = digit(*cp)) < base)
oaddr = oaddr*base + d;
else
++notoct;
}
if (!notoct)
return (vtop(oaddr));
while (s < &id[NCPLN])
*s++ = 0;
for (sp=symtable[hash(id)]; sp!=NULL; sp=sp->next) {
if (sp->s.ls_type&L_GLOBAL
&& sp->s.ls_type!=(L_GLOBAL|L_REF)
&& eq(sp->s.ls_id, id)) {
oaddr = sp->s.ls_addr;
return (oaddr);
}
}
message("%s %.*s undefined", m, NCPLN, id);
return (0);
}
/*
* Return the number digit for the character
* that forms part of a number. e.g. Input
* of 'f' or 'F' would return 15.
*/
static int
digit(d)
register int d;
{
if (d>='0' && d<='9')
return (d-'0');
if (d>='a' && d<='f')
return (d-'a');
if (d>='A' && d<='F')
return (d-'F');
return (16);
}
/*
* Define referenced internal symbol (as end of given segment)
*/
void
endbind(sp, sn, ldrv)
register sym_t *sp;
int sn, ldrv;
{
if (sp==NULL) {
return;
} else if (sp->s.ls_type==(L_GLOBAL|L_REF) && sp->s.ls_addr==0) {
sp->s.ls_type = L_GLOBAL|sn;
sp->s.ls_addr = oseg[sn].size;
} else if (ldrv) {
return;
} else {
spmsg(sp, "redefines builtin");
}
}
/*
* Add reference to symbol table
*/
void
undef(s)
char *s;
{
lds_t lds;
int i;
for (i=0; i<NCPLN; i++) {
lds.ls_id[i] = *s;
if (*s!='\0')
s++;
}
lds.ls_type = L_GLOBAL|L_REF;
lds.ls_addr = 0;
addsym(&lds, NULL);
}
/*
* Routine to convert physical address offset (number of bytes relative
* to base) into a virtual address.
*/
uaddr_t
ptov(a)
uaddr_t a;
{
register short unsigned t1, t2;
switch (machine) {
case 0:
fatal("Machine not set in call to ptov()");
case M_Z8001:
t1 = (a>>8) & 0xFF00;
t2 = a;
return (((long)t1<<16) + (unsigned)t2);
default:
return (a);
}
/* NOTREACHED */
}
/*
* Routine to convert virtual address to a byte-offset
* physical address form.
*/
uaddr_t
vtop(a)
uaddr_t a;
{
register short unsigned t1, t2;
switch (machine) {
case 0:
fatal("Machine not set in vtop()");
case M_Z8001:
t1 = a>>24;
t2 = a;
return (((long)t1<<16) + t2);
default:
return (a);
}
/* NOTREACHED */
}