ack/util/led/extract.c
David Given 4cb4bdc85f There are two places where names are added to the global symbol table; one via
the -U command line option, and one via file scanning. Turns out only the
second would increment the number of global names, so adding names with -U
would cause names found via scanning to fall off the end of the list! This
wouldn't cause linker errors because fixups don't use the list, but would cause
the generated symbol table in the output to be incorrect.
2018-03-11 12:37:23 +01:00

256 lines
6.3 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
#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 "scan.h"
#include "sym.h"
static get_names();
static process();
static getexternal();
static redefine();
static transfer();
extern ind_t savechar();
void namerelocate();
/*
* Get section sizes and symboltable information from present module.
*/
extract()
{
struct outhead head;
get_modul();
/*
* Copy head because we need it so often but it can change place,
* so we can't trust a pointer to it.
*/
head = *(struct outhead *)modulptr(IND_HEAD);
get_names(&head);
process(&head);
skip_modul(&head);
}
/*
* Walk through the nametable of this module, counting the locals that must
* appear in the final output file if this module is linked.
* That number will be returned.
*/
static
get_names(head)
register struct outhead *head;
{
register int nnames;
register ind_t nameindex, charindex;
register ind_t charoff;
extern int flagword;
nnames = head->oh_nname;
nameindex = IND_NAME(*head);
charindex = IND_CHAR(*head);
charoff = OFF_CHAR(*head);
while (nnames--) {
struct outname name; /* A local copy. */
/*
* Because savelocal/getexternal might relocate the modules
* we have to compute the core addresses again.
*/
name = *(struct outname *)modulptr(nameindex);
/*
* Change the offset in file into an offset in the memory area.
* There will always be at least a header before the string
* area, so we don't have to be afraid to confuse "no name"
* with "the first name".
*/
if (name.on_foff) {
if (name.on_foff < charoff ||
name.on_foff >= charoff+head->oh_nchar) {
fatal("illegal offset in name");
}
name.on_foff += charindex - charoff;
}
namerelocate(&name);
if ((name.on_type & S_TYP) == S_CRS) {
name.on_valu += charindex - charoff;
name.on_valu = savechar(ALLOGCHR, (ind_t)name.on_valu);
}
if (name.on_type & S_EXT) {
getexternal(&name);
} else {
/*
* The only thing we want to know about locals is
* whether they must appear in the output file.
*/
if (!(flagword & SFLAG) && mustsavelocal(&name)) {
NLocals++;
savelocal(&name);
}
}
nameindex += sizeof(struct outname);
}
}
extern struct orig relorig[];
static
process(head)
register struct outhead *head;
{
register struct outsect *sects;
register struct outsect *outsp;
register int nsect;
register struct orig *orig = relorig;
extern struct outhead outhead;
extern struct outsect outsect[];
outhead.oh_nrelo += head->oh_nrelo;
outhead.oh_nemit += head->oh_nemit;
if (head->oh_nsect > outhead.oh_nsect)
outhead.oh_nsect = head->oh_nsect;
sects = (struct outsect *)modulptr(IND_SECT(*head));
nsect = head->oh_nsect;
outsp = outsect;
while (nsect--) {
if (sects->os_flen) {
/* contains non-zero stuff */
outhead.oh_nemit += outsp->os_size - outsp->os_flen;
outsp->os_flen = outsp->os_size + sects->os_flen;
}
else {
outsp->os_flen += sects->os_flen;
}
outsp->os_size += sects->os_size;
/*
* Add all flen's and all (size - flen == zero)'s of
* preceding sections with the same number.
*/
orig->org_size = outsp->os_size;
orig++; outsp++; sects++;
}
}
/*
* Add relocation constant for names in user defined sections.
* The value of a common name indicates a size instead of an offset,
* and hence shouldn't be relocated.
* Otherwise we just add the accumulated size of all normal parts in preceding
* sections with the same size.
*/
void
namerelocate(name)
register struct outname *name;
{
register int type = name->on_type;
register int sct = type & S_TYP;
if (sct == S_UND || sct == S_ABS || sct == S_CRS)
return;
if (type & S_COM) {
if ( ! (type&S_EXT) ) fatal("local commons should be handled by the assembler") ;
return;
}
name->on_valu += relorig[(type & S_TYP) - S_MIN].org_size;
}
/*
* If we see this name for the first time, we must remember it for
* we might need it later on. Otherwise it must confirm to what we already
* know about it, and eventually add to that knowledge.
*/
static
getexternal(name)
register struct outname *name;
{
register char *string;
register int h;
register struct outname *old;
extern int hash();
extern struct outname *searchname();
string = modulptr((ind_t)name->on_foff);
h = hash(string);
old = searchname(string, h);
if (old == (struct outname *)0) {
entername(name, h);
if (ISUNDEFINED(name)) {
verbose("requires %s", string, 0, 0, 0);
}
} else if (!ISUNDEFINED(name)) {
if (ISUNDEFINED(old)) {
name->on_mptr = string; /* Just for convenience. */
transfer(name, old);
} else {
name->on_mptr = string; /* Just for convenience. */
redefine(name, old);
}
}
}
/*
* Handle the redefinition of `new' in the current module.
* A name can be defined in three ways, in increasing priority:
* undefined,
* common,
* defined in a section.
* A name may become "higher" when defined, but not "lower".
* A redefinition as common is allowed. It is ignored, but a warning is given
* when the desired section of `new' doesn't correspond with the section of
* `old'. If a common definition is given again for a name, we take the
* greatest value so that the common declared name always has enough space.
* If a common is defined as a not-common, the old definition is ignored.
*/
static
redefine(new, old)
register struct outname *new, *old;
{
if (!ISCOMMON(old)) {
if (!ISCOMMON(new))
error("%s: multiply defined", new->on_mptr);
/*
else if ((new->on_type & S_TYP) != (old->on_type & S_TYP))
warning("%s: sections differ", new->on_mptr);
*/
} else {
/* `Old' is common. */
if (ISCOMMON(new)) {
if ((new->on_type & S_TYP) != (old->on_type & S_TYP))
warning("%s: sections differ", new->on_mptr);
if (new->on_valu > old->on_valu)
old->on_valu = new->on_valu;
} else {
transfer(new, old);
}
}
}
/*
* Transfer things we want to know from `src' to `dst'.
*/
static
transfer(src, dst)
register struct outname *src, *dst;
{
debug("%s defined here\n", src->on_mptr, 0, 0, 0);
dst->on_valu = src->on_valu;
dst->on_type = src->on_type;
dst->on_desc = src->on_desc;
}