Written 1980 by Scott W. Layson
This code is in the public domain.
This is an improved linker for BDS C CRL format. It eliminates the
jump table at the beginning of each function in the object code,
thus saving up to 10% or so in code space with a slight improvement
in speed.
Compile: A>cc nl2.c -e4800 -o (use -e4a00 if using CLINK instead of L2)
A>cc portio.c
link: A>l2 nl2 portio
[81.4.27] Overlay capability now exists. It works a bit
differently from CLINK: the option
-ovl <base name> <origin>
causes symbols to be read from <base name>.sym
and produces a segment linked at <origin>. It
also defaults the name of the overlay function to
that of the first .crl file (to override, put -m
after the -ovl), and replaces c.ccc with a jump
to that function. Also note that the base
symbols are loaded at the very beginning, so name
conflicts are always resolved in favor of the
base.
Changes made by EBM:
1) added -2 and -d flags
2) fixed so that duplicates work right when two passes are made
3) changed to use hashing for symbols
4) added -f flag
5) change "two-pass" processing to write file as it goes and go
back through for fixups (this removes the -2 flag)
6) added -o flag for choosing output file name
7) made symbols grow down from top so table size is variable;
also added more checks for various tables overflowing
*/
/**************** Globals ****************/
/* #define SDOS /* comment this out for CP/M */*/
/* #define OVERLAYS /* comment this out for shorter version */*/
/* #define MARC /* for MARC cross-linker version */*/
#include "bdscio.h" /* for i/o buffer defs */
#define NUL 0
#define FLAG char
#define STDOUT 1
int needfixups; /* set if we need to go back through */
/* Sizes */
#define FNAMESIZE 15 /* length of file names */
#define MAXPROGS 30 /* max number of program files */
#define MAXLIBS 20 /* max number of library files */
/* function table */
struct funct {
char fname[9];
FLAG flinkedp; /* indicates whether found or external */
unsigned faddr; /* address of function (initially 0) */
unsigned fchain; /* chain of fixups */
struct funct *next; /* next one in same hash bucket */
} *ftab, *ftabend;
/* Hash table */
struct funct *hashtab[256];
#define LINKED 1 /* (flinkedp) function really here */
#define EXTERNAL 2 /* function defined in separate symbol table */
char fdir[512]; /* CRL file function directory */
/* command line parameters etc. */
int nprogs, nlibs;
char progfiles [MAXPROGS][FNAMESIZE]; /* program file names */
char libfiles [MAXLIBS] [FNAMESIZE]; /* library file names */
FLAG symsp, /* write symbols to .sym file? */
appstatsp, /* append stats to .sym file? */
sepstatsp; /* write stats to .lnk file? */
FLAG maxmemp, /* punt MARC shell? */
marcp; /* uses CM.CCC */
unsigned cccsize; /* for fixup processing */
char mainfunct[10];
char objname[FNAMESIZE]; /* name for output file */
FLAG objset; /* was -O specified ? */
FLAG ovlp; /* make overlay? */
char symsfile[FNAMESIZE]; /* file with symbols (for overlays) */
FLAG debug; /* extra printouts */
/* useful things to have defined */
struct inst {
char opcode;
char *address;
};
union ptr {
unsigned u; /* an int */
unsigned *w; /* a word ptr */
char *b; /* a byte ptr */
struct inst *i; /* an instruction ptr */
};
/* Link control variables */
union ptr codend; /* last used byte of code buffer + 1 */
union ptr exts; /* start of externals */
union ptr acodend; /* actual code-end address */
unsigned extspc; /* size of externals */
unsigned origin; /* origin of code */
unsigned buforg; /* origin of code buffer */
unsigned jtsaved; /* bytes of jump table saved */
char *lspace; /* space to link in */
char *lspcend; /* end of link area */
char *lodstart; /* beginning of current file */
char *firstfree; /* address of first byte above good stuff */
cmdline (argc, argv) /* process command line */
int argc;
char **argv;
{
int i, progp;
if (argc < 1) {
puts ("Usage is:\n");
puts (" l2 {program files} -l {library files}\n");
puts ("Optional flags are:\n");
puts (" -w to write symbol table (without stats)\n");
puts (" -wa write symbol table with stats appended\n");
puts (" -ws write symbol table and separate stats file\n");
puts (" -d print extra, debugging info about each function\n");
puts (" -m take next argument as name of main function\n");
puts (" -f next argument is the name of a file of file names,\n");
puts (" one per line; those files will be included as if\n");
puts (" they were on the command line in the same place\n");
puts (" -o take next argument as first name of output file\n");
puts (" -ovl symbol-file origin\n");
puts (" links an overlay where the main program symbols\n");
puts (" are in symbol-file and the overlay starts at the\n");
puts (" specified origin (origin is expressed in hex)\n");
puts (" -marc link for the MARC operating system\n");
exit (1);
}
progp = TRUE;
for (i = 1; i < argc; ++i) {
if (argv[i][0] == '-') {
if (!strcmp (argv[i], "-L")) progp = FALSE;
else if (!strcmp (argv[i], "-W")) symsp = TRUE;
else if (!strcmp (argv[i], "-WA")) symsp = appstatsp = TRUE;
else if (!strcmp (argv[i], "-WS")) symsp = sepstatsp = TRUE;
else if (!strcmp (argv[i], "-M")) {
if (++i >= argc) Fatal ("-m argument missing.\n");
strcpy (&mainfunct, argv[i]);
}
else if (!strcmp (argv[i], "-D")) debug = TRUE;
else if (!strcmp (argv[i], "-F")) {
if (++i >= argc) Fatal ("-f argument missing.\n");
getfiles (argv[i], progp);
}
else if (!strcmp (argv[i], "-O")) {
if (++i >= argc) Fatal ("-o argument missing.\n");
strcpy (&objname, argv[i]);
objset = TRUE;
}
#ifdef OVERLAYS
else if (!strcmp (argv[i], "-OVL")) {
ovlp = TRUE;
if (i + 2 >= argc) Fatal ("-ovl argument missing.\n");
strcpy (&symsfile, argv[++i]);
sscanf (argv[++i], "%x", &origin);
}
#endif
#ifdef MARC
else if (!strcmp (argv[i], "-MARC")) {
maxmemp = TRUE;
marcp = TRUE;
}
#endif
else printf ("Unknown option: '%s'\n", argv[i]);
}
else {
if (progp) {
if (++nprogs >= MAXPROGS)
Fatal ("Too many program files.\n");
strcpy (&progfiles[nprogs - 1], argv[i]);
}
else {
if (++nlibs > (MAXLIBS - 2)) /* leave room for defaults */
Fatal ("Too many library files.\n");
strcpy (&libfiles[nlibs - 1], argv[i]);
}
}
}
if (ovlp) strcpy (&mainfunct, &progfiles[0]);
if (!objset) strcpy (&objname, &progfiles[0]);
strcpy (&libfiles[nlibs++], marcp ? "DEFFM" : "DEFF");
strcpy (&libfiles[nlibs++], marcp ? "DEFF2M" : "DEFF2");
}
getfiles(ffname, progp) /* read a file of filenames */
char *ffname;
int progp;
{
char buffer[FNAMESIZE], *p, *q;
if (fopen (ffname, &symbuf) < 0)
{printf ("Could not open %s.\n", ffname);
return;
}
while (fgets (&buffer, &symbuf)) {
if (progp ? (++nprogs >= MAXPROGS) : (++nlibs > (MAXLIBS - 2)))
Fatal ("Too many %s files.", (progp ? "program" : "library"));
p = (progp ? &progfiles[nprogs - 1] : &libfiles[nlibs - 1]);
q = &buffer;
while (*q != '\n') *p++ = *q++;
*p = '\0';
}
fclose (&symbuf);
}
codend.b = lspace;
if (!ovlp) {
if (copen (&ibuf, marcp ? "CM.CCC" : "C.CCC") < 0)
Fatal ("Can't open C.CCC\n");
if (cread (&ibuf, lspace, 128) < 128) /* read a sector */
Fatal ("C.CCC: read error!\n");
temp.b = lspace + 0x17;
len = *temp.w; /* how long is it? */
setmem (lspace + 128, len - 128, 0); /* for file compares */
cread (&ibuf, lspace + 128, len - 128);
codend.b += len;
cclose (&ibuf);
}
else codend.i++->opcode = JMP;
cccsize = codend.b - lspace;
firstfree = codend.b;
}
linkprog() /* link in all program files */
{
int i;
union ptr dirtmp;
struct funct *fnct;
for (i = 0; i < nprogs; ++i) {
makeext (&progfiles[i], "CRL");
if (copen (&ibuf, progfiles[i]) < 0) {
printf ("Can't open %s\n", progfiles[i]);
continue;
}
printf ("<< Loading %s >>\n", &progfiles[i]);
readprog (i == 0);
for (dirtmp.b = &fdir; *dirtmp.b != 0x80;) {
fnct = intern (dirtmp.b); /* for each module */
skip7 (&dirtmp); /* in directory */
if (fnct->flinkedp) {
puts ("Duplicate program function '");
puts (&fnct->fname);
puts ("', not linked.\n");
}
else linkmod (fnct, *dirtmp.w - 0x205);
dirtmp.w++;
} /* intern & link it */
cclose (&ibuf);
}
}
linklibs() /* link in library files */
{
int ifile;
for (ifile = 0; ifile < nlibs; ++ifile) scanlib (ifile);
while (missingp ()) {
puts ("Enter the name of a file to be searched: ");
gets (&libfiles[nlibs]);
scanlib (nlibs++);
}
acodend.b = codend.b - lspace + buforg; /* save that number! */
if (!exts.b) exts.b = acodend.b;
}
missingp() /* are any functions missing? print them out */
{
int foundp;
struct funct *fptr;
compar (f1, f2) /* compare two symbol table entries by name */
struct funct *f1, *f2;
{
/* return (strcmp (&f1->fname, &f2->fname)); alphabetical order */
return (f1->faddr > f2->faddr); /* memory order */
}
#ifdef OVERLAYS
loadsyms() /* load base symbol table (for overlay) */
{ /* symbol table must be empty! */
int nread;
FLAG done;
char *c;
makeext (&symsfile, "SYM");
if (fopen (&symsfile, &symbuf) < 0)
Fatal ("Can't open %s.\n", &symsfile);
done = FALSE;
while (!done) {
if (lspcend - lspace < 0x80) Fatal ("No more room for symbols.");
nread = fscanf (&symbuf, "%x%s\t%x%s\t%x%s\t%x%s\n",
&(ftabend[-1].faddr), &(ftabend[-1].fname),
&(ftabend[-2].faddr), &(ftabend[-2].fname),
&(ftabend[-3].faddr), &(ftabend[-3].fname),
&(ftabend[-4].faddr), &(ftabend[-4].fname));
nread /= 2;
if (nread < 4) done = TRUE;
while (nread-- > 0) (--ftabend)->flinkedp = EXTERNAL;
lspcend = ftabend;
}
fclose (&symbuf);
}
#endif
stats (chan) /* print statistics on chan */
int chan;
{
unsigned temp;
fprintf (chan, "\n\nLink statistics:\n");
fprintf (chan, " Number of functions: %d\n", ftab - ftabend);
fprintf (chan, " Code ends at: 0x%x\n", acodend.u);
fprintf (chan, " Externals begin at: 0x%x\n", exts.u);
fprintf (chan, " Externals end at: 0x%x\n", exts.u + extspc);
fprintf (chan, " Jump table bytes saved: 0x%x\n", jtsaved);
temp = lspcend;
if (!needfixups)
fprintf (chan,
" Link space remaining: %dK\n", (temp - codend.u) / 1024);
}
makeext (fname, ext) /* force a file extension to ext */
char *fname, *ext;
{
while (*fname && (*fname != '.')) {
*fname = toupper (*fname); /* upcase as well */
++fname;
}
*fname++ = '.';
strcpy (fname, ext);
}
strcmp7 (s1, s2) /* compare two bit-7-terminated strings */
char *s1, *s2; /* also works for non-null NUL-term strings */
{
/* char c1, c2, end1, end2; (These are now global for speed) */