630 lines
14 KiB
C
630 lines
14 KiB
C
/*
|
|
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
*/
|
|
#ifndef lint
|
|
static char rcsid[] = "$Id$";
|
|
#endif
|
|
|
|
/*
|
|
* led - linkage editor for ACK assemblers output format
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <out.h>
|
|
#include "const.h"
|
|
#include "debug.h"
|
|
#include "defs.h"
|
|
#include "memory.h"
|
|
#include "orig.h"
|
|
#include "sym.h"
|
|
|
|
extern bool incore;
|
|
#ifndef NOSTATISTICS
|
|
int statistics;
|
|
#endif
|
|
#ifndef NDEBUG
|
|
int DEB = 0;
|
|
#endif
|
|
int Verbose = 0;
|
|
|
|
static initializations();
|
|
static first_pass();
|
|
static uint32_t number(const char *);
|
|
static void setlign(int, uint32_t);
|
|
static void setbase(int, uint32_t);
|
|
static struct outname *makename();
|
|
static pass1();
|
|
static evaluate();
|
|
static void norm_commons();
|
|
static complete_sections();
|
|
static void change_names();
|
|
static bool setbit();
|
|
static bool tstbit();
|
|
static second_pass();
|
|
static pass2();
|
|
#ifndef NOSTATISTICS
|
|
static do_statistics();
|
|
#endif
|
|
|
|
void addbase();
|
|
|
|
main(argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
initializations(argc, argv);
|
|
first_pass(argv);
|
|
#ifndef NOSTATISTICS
|
|
if (statistics) do_statistics();
|
|
#endif
|
|
freeze_core();
|
|
evaluate();
|
|
beginoutput();
|
|
second_pass(argv);
|
|
endoutput();
|
|
stop();
|
|
}
|
|
|
|
#ifndef NOSTATISTICS
|
|
static
|
|
do_statistics()
|
|
{
|
|
register struct memory *m = mems;
|
|
|
|
while (m <= &mems[NMEMS-1]) {
|
|
fprintf(stderr, "mem %d: full %lx, free %lx\n",
|
|
(int)(m - mems),
|
|
(long) m->mem_full,
|
|
(long) m->mem_left);
|
|
m++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
char *progname; /* Name this program was invoked with. */
|
|
int passnumber; /* Pass we are in. */
|
|
struct outhead outhead; /* Header of final output file. */
|
|
struct outsect outsect[MAXSECT];/* Its section table. */
|
|
|
|
/* ARGSUSED */
|
|
static
|
|
initializations(argc, argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
/*
|
|
* Avoid malloc()s.
|
|
*/
|
|
setbuf(stdin, (char *)NULL);
|
|
setbuf(stdout, (char *)NULL);
|
|
setbuf(stderr, (char *)NULL);
|
|
|
|
progname = argv[0];
|
|
passnumber = FIRST;
|
|
init_core();
|
|
init_symboltable();
|
|
outhead.oh_magic = O_MAGIC;
|
|
outhead.oh_stamp = O_STAMP;
|
|
}
|
|
|
|
/* ------------------------ ROUTINES OF FIRST PASS ------------------------- */
|
|
|
|
int flagword = 0; /* To store command-line options. */
|
|
char *outputname = "a.out"; /* Name of the resulting object file. */
|
|
int exitstatus = 0;
|
|
|
|
/*
|
|
* Scan the arguments.
|
|
* If the argument starts with a '-', it's a flag, else it is either
|
|
* a plain file to be loaded, or an archive.
|
|
*/
|
|
static
|
|
first_pass(argv)
|
|
register char **argv;
|
|
{
|
|
register char *argp;
|
|
int sectno;
|
|
int h;
|
|
extern int atoi();
|
|
extern char *strchr();
|
|
extern int hash();
|
|
extern struct outname *searchname();
|
|
|
|
while (*++argv) {
|
|
argp = *argv;
|
|
if (*argp != '-') {
|
|
pass1(argp);
|
|
continue;
|
|
}
|
|
/* It's a flag. */
|
|
switch (*++argp) {
|
|
case 'a':
|
|
/*
|
|
* The rest of the argument must be of the form
|
|
* `<section number>:<alignment>', where
|
|
* <section number> and <alignment> are numbers.
|
|
* <alignment> will be the alignment in the machine of
|
|
* section <section number>.
|
|
*/
|
|
sectno = atoi(++argp);
|
|
if ((argp = strchr(argp, ':')) == (char *)0)
|
|
fatal("usage: -a<section number>:<alignment>");
|
|
setlign(sectno, number(++argp));
|
|
break;
|
|
case 'b':
|
|
/*
|
|
* The rest of the argument must be of the form
|
|
* `<section number>:<base>', where <section number>
|
|
* and base are decimal numbers. <base> will be
|
|
* the base address in the machine of section
|
|
* <section number>.
|
|
*/
|
|
sectno = atoi(++argp);
|
|
if ((argp = strchr(argp, ':')) == (char *)0)
|
|
fatal("usage: -b<section number>:<base>");
|
|
setbase(sectno, number(++argp));
|
|
break;
|
|
case 'c':
|
|
/*
|
|
* Leave relocation information in the output, so that
|
|
* a next pass can see where relocation was done. The
|
|
* resulting output however is no longer relocatable.
|
|
*/
|
|
flagword &= ~RFLAG;
|
|
flagword |= CFLAG;
|
|
break;
|
|
#ifndef NDEBUG
|
|
case 'd':
|
|
DEB = 1;
|
|
break;
|
|
#endif
|
|
case 'n':
|
|
/* In the resulting name list, leave offsets with
|
|
respect to the beginning of the section instead
|
|
of absolute addresses.
|
|
*/
|
|
flagword |= NFLAG;
|
|
break;
|
|
|
|
case 'o':
|
|
/*
|
|
* The `name' argument after -o is used as name
|
|
* of the led output file, instead of "a.out".
|
|
*/
|
|
if ((outputname = *++argv) == (char *)0)
|
|
fatal("-o needs filename");
|
|
break;
|
|
case 'r':
|
|
/*
|
|
* Generate relocation information in the output file
|
|
* so that it can be the subject of another led run.
|
|
* This flag also prevents final definitions from being
|
|
* given to common symbols, and suppresses the
|
|
* `Undefined:' diagnostic.
|
|
*/
|
|
if (flagword & CFLAG) break;
|
|
if (flagword & SFLAG)
|
|
warning("-r contradicts -s: -s ignored");
|
|
flagword |= RFLAG;
|
|
break;
|
|
case 's':
|
|
/*
|
|
* `Strip' the output, that is, remove the symbol table
|
|
* and relocation table to save space (but impair the
|
|
* usefullness of the debuggers). This information can
|
|
* also be removed by astrip(1).
|
|
*/
|
|
if (flagword & RFLAG)
|
|
warning("-s contradicts -r: -s ignored");
|
|
else
|
|
flagword |= SFLAG;
|
|
break;
|
|
case 'u':
|
|
/*
|
|
* Take the following argument as a symbol and enter it
|
|
* as undefined in the symbol table. This is useful for
|
|
* loading wholly from a library, since initially the
|
|
* symbol table is empty and an unresolved reference is
|
|
* needed to force the loading of the first routine.
|
|
*/
|
|
if (*++argv == (char *)0)
|
|
fatal("-u needs symbol name");
|
|
h = hash(*argv);
|
|
if (searchname(*argv, h) == (struct outname *)0)
|
|
entername(makename(*argv), h);
|
|
break;
|
|
case 'v':
|
|
Verbose = 1;
|
|
break;
|
|
case 'S':
|
|
statistics = 1;
|
|
break;
|
|
default:
|
|
warning("bad flag letter %c", *argp);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If `s' starts with 0x/0X, it's hexadecimal,
|
|
* else if it starts with 0b/0B, it's binary,
|
|
* else if it starts with 0, it's octal,
|
|
* else it's decimal.
|
|
*/
|
|
static uint32_t
|
|
number(const char *s)
|
|
{
|
|
register int digit;
|
|
register uint32_t value = 0;
|
|
register int radix = 10;
|
|
|
|
if (*s == '0') {
|
|
radix = 8;
|
|
s++;
|
|
if (*s == 'x' || *s == 'X') {
|
|
radix = 16;
|
|
s++;
|
|
} else if (*s == 'b' || *s == 'B') {
|
|
radix = 2;
|
|
s++;
|
|
}
|
|
}
|
|
while (digit = *s++) {
|
|
if (digit >= 'A' && digit <= 'F')
|
|
digit = digit - 'A' + 10;
|
|
else if (digit >= 'a' && digit <= 'f')
|
|
digit = digit - 'a' + 10;
|
|
else if (digit >= '0' && digit <= '9')
|
|
digit = digit - '0';
|
|
else
|
|
fatal("wrong digit %c", digit);
|
|
if (digit >= radix)
|
|
fatal("digit %c exceeds radix %d", digit, radix);
|
|
value = radix * value + digit;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
/*
|
|
* We use one bit per section to indicate whether a base was already given or
|
|
* not. Only one base may be given. The same applies for alignments.
|
|
*/
|
|
static char basemap[MAXSECT / WIDTH];
|
|
static uint32_t sect_base[MAXSECT];
|
|
static char lignmap[MAXSECT / WIDTH];
|
|
static uint32_t sect_lign[MAXSECT];
|
|
|
|
/*
|
|
/*
|
|
* Set the alignment of section `sectno' to `lign', if this doesn't
|
|
* conflict with earlier alignment.
|
|
*/
|
|
static void
|
|
setlign(int sectno, uint32_t lign)
|
|
{
|
|
extern bool setbit();
|
|
|
|
if (setbit(sectno, lignmap) && sect_lign[sectno] != lign)
|
|
fatal("section has different alignments");
|
|
if (lign == (long)0)
|
|
fatal("alignment cannot be zero");
|
|
sect_lign[sectno] = lign;
|
|
}
|
|
|
|
/*
|
|
* Set the base of section `sectno' to `base', if no other base has been
|
|
* given yet.
|
|
*/
|
|
static void
|
|
setbase(int sectno, uint32_t base)
|
|
{
|
|
extern bool setbit();
|
|
|
|
if (setbit(sectno, basemap) && sect_base[sectno] != base)
|
|
fatal("section has different bases");
|
|
sect_base[sectno] = base;
|
|
}
|
|
|
|
static struct outname *
|
|
makename(string)
|
|
char *string;
|
|
{
|
|
static struct outname namebuf;
|
|
|
|
namebuf.on_foff = string - core_position - mems[ALLOMODL].mem_base;
|
|
namebuf.on_type = S_UND + S_EXT;
|
|
namebuf.on_valu = (long)0;
|
|
|
|
return &namebuf;
|
|
}
|
|
|
|
/*
|
|
* If `file' is a plain file, symboltable information and section sizes are
|
|
* extracted. If it is an archive it is examined to see if it defines any
|
|
* undefined symbols.
|
|
*/
|
|
static
|
|
pass1(file)
|
|
char *file;
|
|
{
|
|
if (getfile(file) == PLAIN) {
|
|
debug("%s: plain file\n", file, 0, 0, 0);
|
|
extract();
|
|
} else {
|
|
/* It must be an archive. */
|
|
debug("%s: archive\n", file, 0, 0, 0);
|
|
arch();
|
|
}
|
|
closefile(file);
|
|
}
|
|
|
|
/* ---------------- ROUTINES BETWEEN FIRST AND SECOND PASS ----------------- */
|
|
|
|
/*
|
|
* After pass 1 we know the sizes of all commons so we can give each common
|
|
* name an address within its section and we can compute the sizes of all
|
|
* sections in the machine. After this we can compute the bases of all
|
|
* sections. We then add the section bases to the values of names in
|
|
* corresponding sections.
|
|
*/
|
|
static
|
|
evaluate()
|
|
{
|
|
norm_commons();
|
|
complete_sections();
|
|
if (!(flagword&(RFLAG|NFLAG)))
|
|
change_names();
|
|
}
|
|
|
|
/*
|
|
* Sect_comm[N] is the number of common bytes in section N.
|
|
* It is computed after pass 1.
|
|
*/
|
|
long sect_comm[MAXSECT];
|
|
|
|
/*
|
|
* If there are undefined names, we print them and we set a flag so that
|
|
* the output can be subject to another led run and we return.
|
|
* We now know how much space each common name needs. We change the value
|
|
* of the common name from the size to the address within its section,
|
|
* just like "normal" names. We also count the total size of common names
|
|
* within each section to be able to compute the final size in the machine.
|
|
*/
|
|
static void
|
|
norm_commons()
|
|
{
|
|
register struct outname *name;
|
|
register int cnt;
|
|
register int und = FALSE;
|
|
|
|
name = (struct outname *)address(ALLOGLOB, (ind_t)0);
|
|
cnt = NGlobals;
|
|
while (cnt-- > 0) {
|
|
if (ISUNDEFINED(name)) {
|
|
if (!und) {
|
|
und = TRUE;
|
|
if (!(flagword & RFLAG)) {
|
|
exitstatus = 1;
|
|
fprintf(stderr, "Undefined:\n");
|
|
}
|
|
outhead.oh_flags |= HF_LINK;
|
|
if (flagword & RFLAG) break;
|
|
flagword = (flagword & ~SFLAG) | RFLAG;
|
|
}
|
|
fprintf(stderr, "\t%s\n",
|
|
address(ALLOGCHR, (ind_t)name->on_foff)
|
|
);
|
|
}
|
|
name++;
|
|
}
|
|
if (flagword & RFLAG) return;
|
|
|
|
/*
|
|
* RFLAG is off, so we need not produce relocatable output.
|
|
* We can now assign an address to common names.
|
|
* It also means that there are no undefined names.
|
|
*/
|
|
name = (struct outname *)address(ALLOGLOB, (ind_t)0);
|
|
cnt = NGlobals;
|
|
while (cnt-- > 0) {
|
|
if (!ISABSOLUTE(name) && ISCOMMON(name)) {
|
|
register long size;
|
|
register int sectindex;
|
|
|
|
size = name->on_valu; /* XXX rounding? */
|
|
sectindex = (name->on_type & S_TYP) - S_MIN;
|
|
name->on_valu =
|
|
outsect[sectindex].os_size +
|
|
sect_comm[sectindex];
|
|
sect_comm[sectindex] += size;
|
|
name->on_type &= ~S_COM;
|
|
}
|
|
name++;
|
|
}
|
|
}
|
|
|
|
struct orig relorig[MAXSECT];
|
|
|
|
/*
|
|
* Compute the offsets in file and machine that the sections will have.
|
|
* Also set the origins to 0.
|
|
*/
|
|
static
|
|
complete_sections()
|
|
{
|
|
register uint32_t base = 0;
|
|
register uint32_t foff;
|
|
register struct outsect *sc;
|
|
register int sectindex;
|
|
|
|
foff = SZ_HEAD + outhead.oh_nsect * SZ_SECT;
|
|
for (sectindex = 0; sectindex < outhead.oh_nsect; sectindex++) {
|
|
relorig[sectindex].org_size = (long)0;
|
|
sc = &outsect[sectindex];
|
|
sc->os_foff = foff;
|
|
foff += sc->os_flen;
|
|
|
|
if (flagword & RFLAG)
|
|
continue;
|
|
|
|
sc->os_size += sect_comm[sectindex];
|
|
sc->os_lign =
|
|
tstbit(sectindex, lignmap) ? sect_lign[sectindex] : 1;
|
|
if (tstbit(sectindex, basemap)) {
|
|
base = sect_base[sectindex];
|
|
if (sc->os_lign && base % sc->os_lign)
|
|
fatal("base not aligned");
|
|
} else if (sc->os_lign) {
|
|
base += sc->os_lign - 1;
|
|
base -= base % sc->os_lign;
|
|
}
|
|
sc->os_base = base;
|
|
base += sc->os_size;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* For each name we add the base of its section to its value, unless
|
|
* the output has to be able to be linked again, as indicated by RFLAG.
|
|
*/
|
|
static void
|
|
change_names()
|
|
{
|
|
register int cnt;
|
|
register struct outname *name;
|
|
|
|
name = (struct outname *)address(ALLOGLOB, (ind_t)0);
|
|
cnt = NGlobals;
|
|
while (cnt-- > 0) {
|
|
addbase(name);
|
|
name++;
|
|
}
|
|
if (!incore)
|
|
return;
|
|
/*
|
|
* Do the same with the local names.
|
|
*/
|
|
name = (struct outname *)address(ALLOLOCL, (ind_t)0);
|
|
cnt = NLocals;
|
|
while (cnt-- > 0) {
|
|
addbase(name);
|
|
name++;
|
|
}
|
|
}
|
|
|
|
#define BIT 0x01
|
|
|
|
/*
|
|
* This function sets a bit with index `indx' in string.
|
|
* It returns whether it was already set.
|
|
*/
|
|
bool
|
|
setbit(indx, string)
|
|
int indx;
|
|
char string[];
|
|
{
|
|
register int byte_index, bit_index;
|
|
register int byte;
|
|
|
|
byte_index = indx / WIDTH; /* Index of byte with bit we need. */
|
|
bit_index = indx % WIDTH; /* Index of bit we need. */
|
|
byte = string[byte_index];
|
|
byte >>= bit_index;
|
|
if (byte & BIT) return TRUE;
|
|
|
|
byte = BIT;
|
|
byte <<= bit_index;
|
|
string[byte_index] |= byte;
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* This function returns whether the bit given by `indx' is set in `string'.
|
|
*/
|
|
static bool
|
|
tstbit(indx, string)
|
|
int indx;
|
|
char string[];
|
|
{
|
|
register int byte_index, bit_index;
|
|
register int byte;
|
|
|
|
byte_index = indx / WIDTH; /* Index of byte with bit we need. */
|
|
bit_index = indx % WIDTH; /* Index of bit we need. */
|
|
byte = string[byte_index];
|
|
byte >>= bit_index;
|
|
|
|
return byte & BIT;
|
|
}
|
|
|
|
/*
|
|
* Add the base of the section of a name to its value.
|
|
*/
|
|
void
|
|
addbase(name)
|
|
struct outname *name;
|
|
{
|
|
register int type = name->on_type & S_TYP;
|
|
register int sectindex = type - S_MIN;
|
|
|
|
if (type == S_UND || type == S_ABS || type == S_CRS)
|
|
return;
|
|
if (name->on_type & S_COM)
|
|
return;
|
|
|
|
name->on_valu += outsect[sectindex].os_base;
|
|
debug( "%s: type 0x%x, value 0x%lx\n",
|
|
address((name->on_type & S_EXT) ? ALLOGCHR : ALLOLCHR,
|
|
(ind_t)name->on_foff
|
|
),
|
|
name->on_type, name->on_valu, 0
|
|
);
|
|
}
|
|
|
|
/* ------------------------ ROUTINES OF SECOND PASS ------------------------ */
|
|
|
|
/*
|
|
* Flags have already been processed, so we ignore them here.
|
|
*/
|
|
static
|
|
second_pass(argv)
|
|
char **argv;
|
|
{
|
|
passnumber = SECOND;
|
|
while (*++argv) {
|
|
if ((*argv)[0] != '-') {
|
|
pass2(*argv);
|
|
continue;
|
|
}
|
|
switch ((*argv)[1]) {
|
|
case 'o':
|
|
case 'u':
|
|
++argv;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
pass2(file)
|
|
char *file;
|
|
{
|
|
if (getfile(file) == PLAIN) {
|
|
debug("%s: plain file\n", file, 0, 0, 0);
|
|
finish();
|
|
} else {
|
|
/* It must be an archive. */
|
|
debug("%s: archive\n", file, 0, 0, 0);
|
|
arch2();
|
|
}
|
|
closefile(file);
|
|
}
|