/* lksym.c */
/*
* Copyright (C) 1989-2009 Alan R. Baldwin
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*
* Alan R. Baldwin
* 721 Berkeley St.
* Kent, Ohio 44240
*
* With enhancements from
* John L. Hartman (JLH)
* jhartman@compuserve.com
*
*/
#include "aslink.h"
/*)Module lksym.c
*
* The module lksym.c contains the functions that operate
* on the symbol structures.
*
* lksym.c contains the following functions:
* int hash()
* sym * lkpsym()
* char * new()
* sym * newsym()
* char * strsto()
* VOID symdef()
* int symeq()
* VOID syminit()
* VOID symmod()
* a_uint symval()
*
* lksym.c contains the static variables:
* char * pnext
* int bytes
* used by the string store function.
*/
/*)Function VOID syminit()
*
* The function syminit() is called to clear the hashtable.
*
* local variables:
* sym ** spp pointer to an array of
* sym structure pointers
*
* global variables:
* sym * symhash[] array of pointers to NHASH
* linked symbol lists
*
* functions called:
* none
*
* side effects:
* (1) The symbol hash tables are cleared
*/
VOID
syminit(void)
{
struct sym **spp;
spp = &symhash[0];
while (spp < &symhash[NHASH])
*spp++ = NULL;
}
/*)Function sym * newsym()
*
* The function newsym() is called to evaluate the symbol
* definition/reference directive from the .rel file(s).
* If the symbol is not found in the symbol table a new
* symbol structure is created. Evaluation of the
* directive determines if this is a reference or a definition.
* Multiple definitions of the same variable will be flagged
* as an error if the values are not identical. A symbol
* definition places the symbol value and area extension
* into the symbols data structure. And finally, a pointer
* to the symbol structure is placed into the head structure
* symbol list. Refer to the description of the header, symbol,
* area, and areax structures in lkdata.c for structure and
* linkage details.
*
* local variables:
* int c character from input text
* int i evaluation value
* char id[] symbol name
* int nglob number of symbols in this header
* sym * tsp pointer to symbol structure
* sym ** s list of pointers to symbol structures
*
* global variables:
* areax *axp Pointer to the current
* areax structure
* head *headp The pointer to the first
* head structure of a linked list
* int lkerr error flag
*
* functions called:
* a_uint eval() lkeval.c
* VOID exit() c_library
* int fprintf() c_library
* char getSid() lklex.c
* int get() lklex.c
* int getnb() lklex.c
* sym * lkpsym() lksym.c
*
* side effects:
* A symbol structure is created and/or modified.
* If structure space allocation fails linker will abort.
* Several severe errors (these are internal errors
* indicating a corrupted .rel file or corrupted
* assembler or linker) will terminated the linker.
*/
/*
* Find/Create a global symbol entry.
*
* S xxxxxx Defnnnn
* | | |
* | | `-- sp->s_addr
* | `----- sp->s_type
* `------------ sp->s_id
*
*/
struct sym *
newsym(void)
{
a_uint ev;
int c, i, nsym;
struct sym *tsp;
struct sym **s;
char id[NCPS];
if (headp == NULL) {
fprintf(stderr, "No header defined\n");
lkexit(ER_FATAL);
}
/*
* Create symbol entry
*/
getSid(id);
tsp = lkpsym(id, 1);
c = getnb();get();get();
if (c == 'R') {
tsp->s_type |= S_REF;
if (eval()) {
fprintf(stderr, "Non zero S_REF\n");
lkerr++;
}
} else
if (c == 'D') {
ev = eval();
if (tsp->s_type & S_DEF &&
!(tsp->s_addr == ev && tsp->s_axp && tsp->s_axp->a_bap && ((tsp->s_axp->a_bap->a_flag & A3_ABS) == A3_ABS))) {
fprintf(stderr,
"Multiple definition of %s\n", id);
lkerr++;
}
/*
* Set value and area extension link.
*/
tsp->s_addr = ev;
tsp->s_axp = axp;
tsp->s_type |= S_DEF;
tsp->m_id = hp->m_id;
} else {
fprintf(stderr, "Invalid symbol type %c for %s\n", c, id);
lkexit(ER_FATAL);
}
/*
* Place pointer in header symbol list
*/
nsym = hp->h_nsym;
s = hp->s_list;
for (i=0; i < nsym ;++i) {
if (s[i] == NULL) {
s[i] = tsp;
return(tsp);
}
}
fprintf(stderr, "Header symbol list overflow\n");
lkexit(ER_FATAL);
return(NULL);
}
/*)Function sym * lkpsym(id,f)
*
* char * id symbol name string
* int f f == 0, lookup only
* f != 0, create if not found
*
* The function lookup() searches the symbol hash tables for
* a symbol name match returning a pointer to the sym structure.
* If the symbol is not found then a sym structure is created,
* initialized, and linked to the appropriate hash table if f != 0.
* A pointer to this new sym structure is returned or a NULL
* pointer is returned if f == 0.
*
* local variables:
* int h computed hash value
* sym * sp pointer to a sym structure
*
* global varaibles:
* sym * symhash[] array of pointers to NHASH
* linked symbol lists
* int zflag Disable symbol case sensitivity
*
* functions called:
* int hash() lksym.c
* char * new() lksym.c
* int symeq() lksym.c
*
* side effects:
* If the function new() fails to allocate space
* for the new sym structure the linker terminates.
*/
struct sym *
lkpsym(char *id, int f)
{
struct sym *sp;
int h;
h = hash(id, zflag);
sp = symhash[h];
while (sp != NULL) {
if (symeq(id, sp->s_id, zflag))
return (sp);
sp = sp->s_sp;
}
if (f == 0)
return (NULL);
sp = (struct sym *) new (sizeof(struct sym));
sp->s_sp = symhash[h];
symhash[h] = sp;
sp->s_id = strsto(id); /* JLH */
return (sp);
}
/*)Function a_uint symval(tsp)
*
* sym * tsp pointer to a symbol structure
*
* The function symval() returns the value of the
* relocated symbol by adding the variable definition
* value to the areax base address.
*
* local variables:
* a_uint val relocated address value
*
* global variables:
* none
*
* functions called:
* none
*
* side effects:
* none
*/
a_uint
symval(struct sym *tsp)
{
a_uint val;
val = tsp->s_addr;
if (tsp->s_axp) {
val += tsp->s_axp->a_addr;
}
return(val);
}
/*)Function VOID symdef(fp)
*
* FILE * fp file handle for output
*
* The function symdef() scans the hashed symbol table
* searching for variables referenced but not defined.
* Undefined variables are linked to the default
* area "_CODE" and reported as referenced by the
* appropriate module.
*
* local variables:
* int i hash table index loop variable
* sym * sp pointer to linked symbol structure
*
* global variables:
* area *areap The pointer to the first
* area structure of a linked list
* sym *symhash[NHASH] array of pointers to NHASH
* linked symbol lists
*
* functions called:
* symmod() lksym.c
*
* side effects:
* Undefined variables have their areas set to "_CODE".
*/
VOID
symdef(FILE *fp)
{
struct sym *sp;
int i;
for (i=0; is_axp == NULL)
sp->s_axp = areap->a_axp;
if ((sp->s_type & S_DEF) == 0)
symmod(fp, sp);
sp = sp->s_sp;
}
}
}
/*)Function VOID symmod(fp,tsp)
*
* FILE * fp output file handle
* sym * tsp pointer to a symbol structure
*
* The function symmod() scans the header structures
* searching for a reference to the symbol structure
* pointed to by tsp. The function then generates an error
* message whichs names the module having referenced the
* undefined variable.
*
* local variables:
* int i loop counter
* sym ** p pointer to a list of pointers
* to symbol structures
*
* global variables:
* head *headp The pointer to the first
* head structure of a linked list
* head *hp Pointer to the current
* head structure
* int lkerr error flag
*
* functions called:
* int fprintf() c_library
*
* side effects:
* Error output generated.
*/
VOID
symmod(FILE *fp, struct sym *tsp)
{
int i;
struct sym **p;
if ((hp = headp) != NULL) {
while(hp) {
p = hp->s_list;
for (i=0; ih_nsym; ++i) {
if (p[i] == tsp) {
fprintf(fp,
"\n?ASlink-Warning-Undefined Global '%s' ",
tsp->s_id);
fprintf(fp,
"referenced by module '%s'\n",
hp->m_id);
lkerr++;
}
}
hp = hp->h_hp;
}
}
}
/*)Function int symeq(p1, p2, cflag)
*
* int cflag case sensitive flag
* char * p1 name string
* char * p2 name string
*
* The function symeq() compares the two name strings for a match.
* The return value is 1 for a match and 0 for no match.
*
* cflag == 0 case sensitive compare
* cflag != 0 case insensitive compare
*
* local variables:
* int n loop counter
*
* global variables:
* char ccase[] an array of characters which
* perform the case translation function
*
* functions called:
* none
*
* side effects:
* none
*
*/
int
symeq(char *p1, char *p2, int cflag)
{
int n;
n = strlen(p1) + 1;
if(cflag) {
/*
* Case Insensitive Compare
*/
do {
if (ccase[*p1++ & 0x007F] != ccase[*p2++ & 0x007F])
return (0);
} while (--n);
} else {
/*
* Case Sensitive Compare
*/
do {
if (*p1++ != *p2++)
return (0);
} while (--n);
}
return (1);
}
/*)Function int hash(p, cflag)
*
* char * p pointer to string to hash
* int cflag case sensitive flag
*
* The function hash() computes a hash code using the sum
* of all characters mod table size algorithm.
*
* cflag == 0 case sensitive hash
* cflag != 0 case insensitive hash
*
* local variables:
* int h accumulated character sum
*
* global variables:
* char ccase[] an array of characters which
* perform the case translation function
*
* functions called:
* none
*
* side effects:
* none
*/
int
hash(char *p, int cflag)
{
int h;
h = 0;
while (*p) {
if(cflag) {
/*
* Case Insensitive Hash
*/
h += ccase[*p++ & 0x007F];
} else {
/*
* Case Sensitive Hash
*/
h += *p++;
}
}
return (h&HMASK);
}
#if decus
/*)Function char * strsto(str)
*
* char * str pointer to string to save
*
* Allocate space for "str", copy str into new space.
* Return a pointer to the allocated string.
*
* This function based on code by
* John L. Hartman
* jhartman@compuserve.com
*
* local variables:
* int l string length + 8
* char * p string location
*
* global variables:
* none
*
* functions called:
* char * new() assym.c
* char * strncpy() c_library
*
* side effects:
* Space allocated for string, string copied
* to space. Out of Space terminates linker.
*/
char *
strsto(char *str)
{
int l;
char *p;
/*
* What we need, including a null, and maybe some extra space for strcat().
*/
l = strlen(str) + 8;
p = (char *) new (l);
/*
* Copy the name and terminating null.
*/
strncpy(p, str, l);
return(p);
}
/*
* This code is optimized for the PDP-11 (decus)
* which has a limited program space of 56K Bytes !
* Short strings and small structures are allocated
* from a memory hunk in new() to reduce the overhead
* from allocations directly by malloc(). Longer
* allocations are made directly by malloc.
* PDP-11 addressing requires that variables
* are allocated on a word boundary, (strings donot
* have this restriction,) all allocations will have
* at most 1 extra byte to maintain the word boundary
* requirement.
*/
/*)Function char * new(n)
*
* unsigned int n allocation size in bytes
*
* The function new() allocates n bytes of space and returns
* a pointer to this memory. If no space is available the
* linker is terminated.
*
* Allocate space for "str", copy str into new space.
* Return a pointer to the allocated string.
*
* This function based on code by
* John L. Hartman
* jhartman@compuserve.com
*
* local variables:
* int bytes bytes remaining in buffer area
* int i loop counter
* char * p pointer to head of copied string
* char * pnext next location in buffer area
* char * q a general pointer
*
* global variables:
* none
*
* functions called:
* int fprintf() c_library
* VOID * malloc() c_library
*
* side effects:
* Memory is allocated, if allocation fails
* the linker is terminated.
*/
/*
* To avoid wasting memory headers on small allocations, we
* allocate a big chunk and parcel it out as required.
* These static variables remember our hunk.
*/
#define STR_SPC 1024
#define STR_MIN 16
static char * pnext = NULL;
static int bytes = 0;
char *
new(unsigned int n)
{
char *p,*q;
unsigned int i;
/*
* Always an even byte count
*/
n = (n+1) & 0xFFFE;
if (n > STR_MIN) {
/*
* For allocations larger than
* most structures and short strings
* allocate the space directly.
*/
p = (char *) malloc(n);
} else {
/*
* For smaller structures and
* strings allocate from the hunk.
*/
if (n > bytes) {
/*
* No space. Allocate a new hunk.
* We lose the pointer to any old hunk.
* We don't care, as the pieces are never deleted.
*/
pnext = (char *) malloc (STR_SPC);
bytes = STR_SPC;
}
p = pnext;
pnext += n;
bytes -= n;
}
if (p == NULL) {
fprintf(stderr, "Out of space!\n");
lkexit(ER_FATAL);
}
for (i=0,q=p; i bytes) {
/*
* No space. Allocate a new hunk.
* We lose the pointer to any old hunk.
* We don't care, as the strings are never deleted.
*/
pnext = (char *) new (STR_SPC);
bytes = STR_SPC;
}
/*
* Copy the name and terminating null.
*/
p = pnext;
strncpy(p, str, l);
pnext += l;
bytes -= l;
return(p);
}
/*)Function char * new(n)
*
* unsigned int n allocation size in bytes
*
* The function new() allocates n bytes of space and returns
* a pointer to this memory. If no space is available the
* linker is terminated.
*
* local variables:
* char * p a general pointer
* char * q a general pointer
*
* global variables:
* none
*
* functions called:
* int fprintf() c_library
* VOID * malloc() c_library
*
* side effects:
* Memory is allocated, if allocation fails
* the linker is terminated.
*/
char *
new(unsigned int n)
{
char *p,*q;
unsigned int i;
if ((p = (char *) malloc(n)) == NULL) {
fprintf(stderr, "Out of space!\n");
lkexit(ER_FATAL);
}
for (i=0,q=p; i