76e67e2f97
string table in the object file that (currently) holds all identifiers from the import, export and debug info sections. The plan is to put all strings into this table, so we have them in a central place and don't waste memory. Apart from that, the indices are unique, so comparing strings should be a lot easier than before (as soon as the programs take advantage of this fact, which is currently not the case). git-svn-id: svn://svn.cc65.org/cc65/trunk@2169 b7a2c559-68d2-44c3-8de9-860c34a00d81
1311 lines
33 KiB
C
1311 lines
33 KiB
C
/*****************************************************************************/
|
|
/* */
|
|
/* symtab.c */
|
|
/* */
|
|
/* Symbol table for the ca65 macroassembler */
|
|
/* */
|
|
/* */
|
|
/* */
|
|
/* (C) 1998-2003 Ullrich von Bassewitz */
|
|
/* Römerstrasse 52 */
|
|
/* D-70794 Filderstadt */
|
|
/* EMail: uz@cc65.org */
|
|
/* */
|
|
/* */
|
|
/* This software is provided 'as-is', without any expressed or implied */
|
|
/* warranty. In no event will the authors be held liable for any damages */
|
|
/* arising from the use of this software. */
|
|
/* */
|
|
/* Permission is granted to anyone to use this software for any purpose, */
|
|
/* including commercial applications, and to alter it and redistribute it */
|
|
/* freely, subject to the following restrictions: */
|
|
/* */
|
|
/* 1. The origin of this software must not be misrepresented; you must not */
|
|
/* claim that you wrote the original software. If you use this software */
|
|
/* in a product, an acknowledgment in the product documentation would be */
|
|
/* appreciated but is not required. */
|
|
/* 2. Altered source versions must be plainly marked as such, and must not */
|
|
/* be misrepresented as being the original software. */
|
|
/* 3. This notice may not be removed or altered from any source */
|
|
/* distribution. */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
/* common */
|
|
#include "cddefs.h"
|
|
#include "check.h"
|
|
#include "hashstr.h"
|
|
#include "symdefs.h"
|
|
#include "xmalloc.h"
|
|
|
|
/* ca65 */
|
|
#include "global.h"
|
|
#include "error.h"
|
|
#include "expr.h"
|
|
#include "objfile.h"
|
|
#include "scanner.h"
|
|
#include "spool.h"
|
|
#include "symtab.h"
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Data */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
/* Bits for the Flags value in SymEntry */
|
|
#define SF_NONE 0x0000 /* Empty flag set */
|
|
#define SF_USER 0x0001 /* User bit */
|
|
#define SF_TRAMPOLINE 0x0002 /* Trampoline entry */
|
|
#define SF_EXPORT 0x0004 /* Export this symbol */
|
|
#define SF_IMPORT 0x0008 /* Import this symbol */
|
|
#define SF_GLOBAL 0x0010 /* Global symbol */
|
|
#define SF_ZP 0x0020 /* Declared as zeropage symbol */
|
|
#define SF_ABS 0x0040 /* Declared as absolute symbol */
|
|
#define SF_LABEL 0x0080 /* Used as a label */
|
|
#define SF_FORCED 0x0100 /* Forced import, SF_IMPORT also set */
|
|
#define SF_INDEXED 0x0800 /* Index is valid */
|
|
#define SF_CONST 0x1000 /* The symbol has a constant value */
|
|
#define SF_MULTDEF 0x2000 /* Multiply defined symbol */
|
|
#define SF_DEFINED 0x4000 /* Defined */
|
|
#define SF_REFERENCED 0x8000 /* Referenced */
|
|
|
|
/* Combined stuff */
|
|
#define SF_UNDEFMASK (SF_REFERENCED | SF_DEFINED | SF_IMPORT)
|
|
#define SF_UNDEFVAL (SF_REFERENCED)
|
|
#define SF_EXPMASK (SF_TRAMPOLINE | SF_EXPORT)
|
|
#define SF_EXPVAL (SF_EXPORT)
|
|
#define SF_DBGINFOMASK (SF_TRAMPOLINE | SF_DEFINED | SF_EXPORT | SF_IMPORT)
|
|
#define SF_DBGINFOVAL (SF_DEFINED)
|
|
|
|
/* Structure of a symbol table entry */
|
|
struct SymEntry {
|
|
SymEntry* Left; /* Lexically smaller entry */
|
|
SymEntry* Right; /* Lexically larger entry */
|
|
SymEntry* List; /* List of all entries */
|
|
SymEntry* Locals; /* Root of subtree for local symbols */
|
|
struct SymTable* SymTab; /* Table this symbol is in, 0 for locals */
|
|
FilePos Pos; /* File position for this symbol */
|
|
unsigned Flags; /* Symbol flags */
|
|
unsigned Index; /* Index of import/export entries */
|
|
union {
|
|
struct ExprNode* Expr; /* Expression if CONST not set */
|
|
long Val; /* Value (if CONST set) */
|
|
SymEntry* Sym; /* Symbol (if trampoline entry) */
|
|
} V;
|
|
unsigned char ConDesPrio[CD_TYPE_COUNT]; /* ConDes priorities... */
|
|
/* ...actually value+1 (used as flag) */
|
|
char Name [1]; /* Dynamic allocation */
|
|
};
|
|
|
|
|
|
|
|
/* Definitions for the hash table */
|
|
#define MAIN_HASHTAB_SIZE 213
|
|
#define SUB_HASHTAB_SIZE 53
|
|
typedef struct SymTable SymTable;
|
|
struct SymTable {
|
|
unsigned TableSlots; /* Number of hash table slots */
|
|
unsigned TableEntries; /* Number of entries in the table */
|
|
SymTable* BackLink; /* Link to enclosing scope if any */
|
|
SymEntry* Table [1]; /* Dynamic allocation */
|
|
};
|
|
|
|
|
|
|
|
/* Arguments for SymFind */
|
|
#define SF_FIND_EXISTING 0
|
|
#define SF_ALLOC_NEW 1
|
|
|
|
|
|
|
|
/* Symbol table variables */
|
|
static SymEntry* SymList = 0; /* List of all symbol table entries */
|
|
static SymEntry* SymLast = 0; /* Pointer to last defined symbol */
|
|
static SymTable* SymTab = 0; /* Pointer to current symbol table */
|
|
static SymTable* RootTab = 0; /* Root symbol table */
|
|
static unsigned ImportCount = 0;/* Counter for import symbols */
|
|
static unsigned ExportCount = 0;/* Counter for export symbols */
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Internally used functions */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
static int IsLocal (const char* Name)
|
|
/* Return true if Name is the name of a local symbol */
|
|
{
|
|
return (*Name == LocalStart);
|
|
}
|
|
|
|
|
|
|
|
static SymEntry* NewSymEntry (const char* Name)
|
|
/* Allocate a symbol table entry, initialize and return it */
|
|
{
|
|
SymEntry* S;
|
|
unsigned Len;
|
|
|
|
/* Get the length of the name */
|
|
Len = strlen (Name);
|
|
|
|
/* Allocate memory */
|
|
S = xmalloc (sizeof (SymEntry) + Len);
|
|
|
|
/* Initialize the entry */
|
|
S->Left = 0;
|
|
S->Right = 0;
|
|
S->Locals = 0;
|
|
S->SymTab = 0;
|
|
S->Pos = CurPos;
|
|
S->Flags = 0;
|
|
S->V.Expr = 0;
|
|
memset (S->ConDesPrio, 0, sizeof (S->ConDesPrio));
|
|
memcpy (S->Name, Name, Len+1);
|
|
|
|
/* Insert it into the list of all entries */
|
|
S->List = SymList;
|
|
SymList = S;
|
|
|
|
/* Return the initialized entry */
|
|
return S;
|
|
}
|
|
|
|
|
|
|
|
static SymTable* NewSymTable (unsigned Size)
|
|
/* Allocate a symbol table on the heap and return it */
|
|
{
|
|
SymTable* S;
|
|
|
|
/* Allocate memory */
|
|
S = xmalloc (sizeof (SymTable) + (Size-1) * sizeof (SymEntry*));
|
|
|
|
/* Set variables and clear hash table entries */
|
|
S->TableSlots = Size;
|
|
S->TableEntries = 0;
|
|
S->BackLink = 0;
|
|
while (Size--) {
|
|
S->Table [Size] = 0;
|
|
}
|
|
|
|
/* Return the prepared struct */
|
|
return S;
|
|
}
|
|
|
|
|
|
|
|
static int SearchSymTab (SymEntry* T, const char* Name, SymEntry** E)
|
|
/* Search in the given table for a name (Hash is the hash value of Name and
|
|
* is given as parameter so that it will not get calculated twice if we search
|
|
* in more than one table). If we find the symbol, the function will return 0
|
|
* and put the entry pointer into E. If we did not find the symbol, and the
|
|
* tree is empty, E is set to NULL. If the tree is not empty, E will be set to
|
|
* the last entry, and the result of the function is <0 if the entry should
|
|
* be inserted on the left side, and >0 if it should get inserted on the right
|
|
* side.
|
|
*/
|
|
{
|
|
int Cmp;
|
|
|
|
/* Is there a tree? */
|
|
if (T == 0) {
|
|
*E = 0;
|
|
return 1;
|
|
}
|
|
|
|
/* We have a table, search it */
|
|
while (1) {
|
|
/* Choose next entry */
|
|
Cmp = strcmp (Name, T->Name);
|
|
if (Cmp < 0 && T->Left) {
|
|
T = T->Left;
|
|
} else if (Cmp > 0 && T->Right) {
|
|
T = T->Right;
|
|
} else {
|
|
/* Found or end of search */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Return the search result */
|
|
*E = T;
|
|
return Cmp;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Code */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
static SymEntry* SymFind (SymTable* Tab, const char* Name, int AllocNew)
|
|
/* Find a new symbol table entry in the given table. If AllocNew is given and
|
|
* the entry is not found, create a new one. Return the entry found, or the
|
|
* new entry created, or - in case AllocNew is zero - return 0.
|
|
*/
|
|
{
|
|
SymEntry* S;
|
|
int Cmp;
|
|
unsigned Hash;
|
|
|
|
if (IsLocal (Name)) {
|
|
|
|
/* Local symbol, get the table */
|
|
if (!SymLast) {
|
|
/* No last global, so there's no local table */
|
|
Error (ERR_ILLEGAL_LOCAL_USE);
|
|
if (AllocNew) {
|
|
return NewSymEntry (Name);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Search for the symbol if we have a table */
|
|
Cmp = SearchSymTab (SymLast->Locals, Name, &S);
|
|
|
|
/* If we found an entry, return it */
|
|
if (Cmp == 0) {
|
|
return S;
|
|
}
|
|
|
|
if (AllocNew) {
|
|
|
|
/* Otherwise create a new entry, insert and return it */
|
|
SymEntry* N = NewSymEntry (Name);
|
|
if (S == 0) {
|
|
SymLast->Locals = N;
|
|
} else if (Cmp < 0) {
|
|
S->Left = N;
|
|
} else {
|
|
S->Right = N;
|
|
}
|
|
return N;
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Global symbol: Get the hash value for the name */
|
|
Hash = HashStr (Name) % Tab->TableSlots;
|
|
|
|
/* Search for the entry */
|
|
Cmp = SearchSymTab (Tab->Table [Hash], Name, &S);
|
|
|
|
/* If we found an entry, return it */
|
|
if (Cmp == 0) {
|
|
/* Check for a trampoline entry, in this case return the real
|
|
* symbol.
|
|
*/
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
return S->V.Sym;
|
|
} else {
|
|
return S;
|
|
}
|
|
}
|
|
|
|
if (AllocNew) {
|
|
|
|
/* Otherwise create a new entry, insert and return it */
|
|
SymEntry* N = NewSymEntry (Name);
|
|
if (S == 0) {
|
|
Tab->Table [Hash] = N;
|
|
} else if (Cmp < 0) {
|
|
S->Left = N;
|
|
} else {
|
|
S->Right = N;
|
|
}
|
|
N->SymTab = Tab;
|
|
++Tab->TableEntries;
|
|
return N;
|
|
|
|
}
|
|
}
|
|
|
|
/* We did not find the entry and AllocNew is false. */
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static SymEntry* SymFindAny (SymTable* Tab, const char* Name)
|
|
/* Find a symbol in any table */
|
|
{
|
|
SymEntry* Sym;
|
|
do {
|
|
/* Search in the current table */
|
|
Sym = SymFind (Tab, Name, SF_FIND_EXISTING);
|
|
if (Sym) {
|
|
/* Found, return it */
|
|
return Sym;
|
|
} else {
|
|
/* Not found, search in the backlink, if we have one */
|
|
Tab = Tab->BackLink;
|
|
}
|
|
} while (Sym == 0 && Tab != 0);
|
|
|
|
/* Not found */
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
void SymEnterLevel (void)
|
|
/* Enter a new lexical level */
|
|
{
|
|
if (RootTab == 0) {
|
|
/* Create the main symbol table */
|
|
RootTab = SymTab = NewSymTable (MAIN_HASHTAB_SIZE);
|
|
} else {
|
|
/* Create a local symbol table */
|
|
SymTable* LocalSyms;
|
|
LocalSyms = NewSymTable (SUB_HASHTAB_SIZE);
|
|
LocalSyms->BackLink = SymTab;
|
|
SymTab = LocalSyms;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void SymLeaveLevel (void)
|
|
/* Leave the current lexical level */
|
|
{
|
|
SymTab = SymTab->BackLink;
|
|
}
|
|
|
|
|
|
|
|
int SymIsLocalLevel (void)
|
|
/* Return true if we are on a local symbol table level. */
|
|
{
|
|
return (SymTab != RootTab);
|
|
}
|
|
|
|
|
|
|
|
void SymDef (const char* Name, ExprNode* Expr, int ZP, int Label)
|
|
/* Define a new symbol */
|
|
{
|
|
/* Do we have such a symbol? */
|
|
SymEntry* S = SymFind (SymTab, Name, SF_ALLOC_NEW);
|
|
if (S->Flags & SF_IMPORT) {
|
|
/* Defined symbol is marked as imported external symbol */
|
|
Error (ERR_SYM_ALREADY_IMPORT, Name);
|
|
return;
|
|
}
|
|
if (S->Flags & SF_DEFINED) {
|
|
/* Multiple definition */
|
|
Error (ERR_SYM_ALREADY_DEFINED, Name);
|
|
S->Flags |= SF_MULTDEF;
|
|
return;
|
|
}
|
|
|
|
/* Set the symbol data */
|
|
if (IsConstExpr (Expr)) {
|
|
/* Expression is const, store the value */
|
|
S->Flags |= SF_CONST;
|
|
S->V.Val = GetExprVal (Expr);
|
|
FreeExpr (Expr);
|
|
} else {
|
|
/* Not const, store the expression */
|
|
S->V.Expr = Expr;
|
|
}
|
|
S->Flags |= SF_DEFINED;
|
|
if (ZP) {
|
|
S->Flags |= SF_ZP;
|
|
}
|
|
if (Label) {
|
|
S->Flags |= SF_LABEL;
|
|
}
|
|
|
|
/* If the symbol is a ZP symbol, check if the value is in correct range */
|
|
if (S->Flags & SF_ZP) {
|
|
/* Already marked as ZP symbol by some means */
|
|
if (!IsByteExpr (Expr)) {
|
|
Error (ERR_RANGE);
|
|
}
|
|
}
|
|
|
|
/* If this is not a local symbol, remember it as the last global one */
|
|
if (!IsLocal (Name)) {
|
|
SymLast = S;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
SymEntry* SymRef (const char* Name, int Scope)
|
|
/* Search for the symbol and return it */
|
|
{
|
|
SymEntry* S;
|
|
|
|
switch (Scope) {
|
|
case SCOPE_GLOBAL: S = SymFind (RootTab, Name, SF_ALLOC_NEW); break;
|
|
case SCOPE_LOCAL: S = SymFind (SymTab, Name, SF_ALLOC_NEW); break;
|
|
|
|
/* Others are not allowed */
|
|
case SCOPE_ANY:
|
|
default:
|
|
Internal ("Invalid scope in SymRef: %d", Scope);
|
|
/* NOTREACHED */
|
|
S = 0;
|
|
}
|
|
|
|
/* Mark the symbol as referenced */
|
|
S->Flags |= SF_REFERENCED;
|
|
|
|
/* Return it */
|
|
return S;
|
|
}
|
|
|
|
|
|
|
|
static void SymImportInternal (const char* Name, unsigned Flags)
|
|
/* Mark the given symbol as an imported symbol */
|
|
{
|
|
SymEntry* S;
|
|
|
|
/* Don't accept local symbols */
|
|
if (IsLocal (Name)) {
|
|
Error (ERR_ILLEGAL_LOCAL_USE);
|
|
return;
|
|
}
|
|
|
|
/* Do we have such a symbol? */
|
|
S = SymFind (SymTab, Name, SF_ALLOC_NEW);
|
|
if (S->Flags & SF_DEFINED) {
|
|
Error (ERR_SYM_ALREADY_DEFINED, Name);
|
|
S->Flags |= SF_MULTDEF;
|
|
return;
|
|
}
|
|
if (S->Flags & SF_EXPORT) {
|
|
/* The symbol is already marked as exported symbol */
|
|
Error (ERR_SYM_ALREADY_EXPORT, Name);
|
|
return;
|
|
}
|
|
|
|
/* If the symbol is marked as global, check the symbol flags, then do
|
|
* silently remove the global flag
|
|
*/
|
|
if (S->Flags & SF_GLOBAL) {
|
|
if ((Flags & (SF_ZP | SF_FORCED)) != (S->Flags & (SF_ZP | SF_FORCED))) {
|
|
Error (ERR_SYM_REDECL_MISMATCH, Name);
|
|
}
|
|
S->Flags &= ~SF_GLOBAL;
|
|
}
|
|
|
|
/* Set the symbol data */
|
|
S->Flags |= (SF_IMPORT | Flags);
|
|
}
|
|
|
|
|
|
|
|
void SymImport (const char* Name)
|
|
/* Mark the given symbol as an imported symbol */
|
|
{
|
|
SymImportInternal (Name, SF_NONE);
|
|
}
|
|
|
|
|
|
|
|
void SymImportZP (const char* Name)
|
|
/* Mark the given symbol as a forced imported symbol */
|
|
{
|
|
SymImportInternal (Name, SF_ZP);
|
|
}
|
|
|
|
|
|
|
|
void SymImportForced (const char* Name)
|
|
/* Mark the given symbol as a forced imported symbol */
|
|
{
|
|
SymImportInternal (Name, SF_FORCED);
|
|
}
|
|
|
|
|
|
|
|
static void SymExportInternal (const char* Name, unsigned Flags)
|
|
/* Mark the given symbol as an exported symbol */
|
|
{
|
|
SymEntry* S;
|
|
|
|
/* Don't accept local symbols */
|
|
if (IsLocal (Name)) {
|
|
Error (ERR_ILLEGAL_LOCAL_USE);
|
|
return;
|
|
}
|
|
|
|
/* Do we have such a symbol? */
|
|
S = SymFind (SymTab, Name, SF_ALLOC_NEW);
|
|
if (S->Flags & SF_IMPORT) {
|
|
/* The symbol is already marked as imported external symbol */
|
|
Error (ERR_SYM_ALREADY_IMPORT, Name);
|
|
return;
|
|
}
|
|
|
|
/* If the symbol is marked as global, check the symbol size, then do
|
|
* silently remove the global flag
|
|
*/
|
|
if (S->Flags & SF_GLOBAL) {
|
|
if ((Flags & SF_ZP) != (S->Flags & SF_ZP)) {
|
|
Error (ERR_SYM_REDECL_MISMATCH, Name);
|
|
}
|
|
S->Flags &= ~SF_GLOBAL;
|
|
}
|
|
|
|
/* Set the symbol data */
|
|
S->Flags |= (SF_EXPORT | SF_REFERENCED | Flags);
|
|
}
|
|
|
|
|
|
|
|
void SymExport (const char* Name)
|
|
/* Mark the given symbol as an exported symbol */
|
|
{
|
|
SymExportInternal (Name, SF_NONE);
|
|
}
|
|
|
|
|
|
|
|
void SymExportZP (const char* Name)
|
|
/* Mark the given symbol as an exported zeropage symbol */
|
|
{
|
|
SymExportInternal (Name, SF_ZP);
|
|
}
|
|
|
|
|
|
|
|
static void SymGlobalInternal (const char* Name, unsigned Flags)
|
|
/* Mark the given symbol as a global symbol, that is, as a symbol that is
|
|
* either imported or exported.
|
|
*/
|
|
{
|
|
SymEntry* S;
|
|
|
|
/* Don't accept local symbols */
|
|
if (IsLocal (Name)) {
|
|
Error (ERR_ILLEGAL_LOCAL_USE);
|
|
return;
|
|
}
|
|
|
|
/* Search for this symbol, create a new entry if needed */
|
|
S = SymFind (SymTab, Name, SF_ALLOC_NEW);
|
|
|
|
/* If the symbol is already marked as import or export, check the
|
|
* size of the definition, then bail out. */
|
|
if (S->Flags & SF_IMPORT || S->Flags & SF_EXPORT) {
|
|
if ((Flags & SF_ZP) != (S->Flags & SF_ZP)) {
|
|
Error (ERR_SYM_REDECL_MISMATCH, Name);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Mark the symbol */
|
|
S->Flags |= (SF_GLOBAL | Flags);
|
|
}
|
|
|
|
|
|
|
|
void SymGlobal (const char* Name)
|
|
/* Mark the given symbol as a global symbol, that is, as a symbol that is
|
|
* either imported or exported.
|
|
*/
|
|
{
|
|
SymGlobalInternal (Name, SF_NONE);
|
|
}
|
|
|
|
|
|
|
|
void SymGlobalZP (const char* Name)
|
|
/* Mark the given symbol as a global zeropage symbol, that is, as a symbol
|
|
* that is either imported or exported.
|
|
*/
|
|
{
|
|
SymGlobalInternal (Name, SF_ZP);
|
|
}
|
|
|
|
|
|
|
|
void SymConDes (const char* Name, unsigned Type, unsigned Prio)
|
|
/* Mark the given symbol as a module constructor/destructor. This will also
|
|
* mark the symbol as an export. Initializers may never be zero page symbols.
|
|
*/
|
|
{
|
|
SymEntry* S;
|
|
|
|
/* Check the parameters */
|
|
#if (CD_TYPE_MIN != 0)
|
|
CHECK (Type >= CD_TYPE_MIN && Type <= CD_TYPE_MAX);
|
|
#else
|
|
CHECK (Type <= CD_TYPE_MAX);
|
|
#endif
|
|
CHECK (Prio >= CD_PRIO_MIN && Prio <= CD_PRIO_MAX);
|
|
|
|
/* Don't accept local symbols */
|
|
if (IsLocal (Name)) {
|
|
Error (ERR_ILLEGAL_LOCAL_USE);
|
|
return;
|
|
}
|
|
|
|
/* Do we have such a symbol? */
|
|
S = SymFind (SymTab, Name, SF_ALLOC_NEW);
|
|
if (S->Flags & SF_IMPORT) {
|
|
/* The symbol is already marked as imported external symbol */
|
|
Error (ERR_SYM_ALREADY_IMPORT, Name);
|
|
return;
|
|
}
|
|
|
|
/* If the symbol is marked as global, silently remove the global flag */
|
|
if (S->Flags & SF_GLOBAL) {
|
|
S->Flags &= ~SF_GLOBAL;
|
|
}
|
|
|
|
/* Check if the symbol was not already defined as ZP symbol */
|
|
if ((S->Flags & SF_ZP) != 0) {
|
|
Error (ERR_SYM_REDECL_MISMATCH, Name);
|
|
}
|
|
|
|
/* If the symbol was already declared as a condes, check if the new
|
|
* priority value is the same as the old one.
|
|
*/
|
|
if (S->ConDesPrio[Type] != CD_PRIO_NONE) {
|
|
if (S->ConDesPrio[Type] != Prio) {
|
|
Error (ERR_SYM_REDECL_MISMATCH, Name);
|
|
}
|
|
}
|
|
S->ConDesPrio[Type] = Prio;
|
|
|
|
/* Set the symbol data */
|
|
S->Flags |= SF_EXPORT | SF_REFERENCED;
|
|
}
|
|
|
|
|
|
|
|
int SymIsDef (const char* Name, int Scope)
|
|
/* Return true if the given symbol is already defined */
|
|
{
|
|
SymEntry* S = 0;
|
|
|
|
/* Search for the symbol */
|
|
switch (Scope) {
|
|
case SCOPE_ANY: S = SymFindAny (SymTab, Name); break;
|
|
case SCOPE_GLOBAL: S = SymFind (RootTab, Name, SF_FIND_EXISTING); break;
|
|
case SCOPE_LOCAL: S = SymFind (SymTab, Name, SF_FIND_EXISTING); break;
|
|
default: Internal ("Invalid scope in SymIsDef: %d", Scope);
|
|
}
|
|
|
|
/* Check if it's defined */
|
|
return S != 0 && (S->Flags & SF_DEFINED) != 0;
|
|
}
|
|
|
|
|
|
|
|
int SymIsRef (const char* Name, int Scope)
|
|
/* Return true if the given symbol has been referenced */
|
|
{
|
|
SymEntry* S = 0;
|
|
|
|
/* Search for the symbol */
|
|
switch (Scope) {
|
|
case SCOPE_ANY: S = SymFindAny (SymTab, Name); break;
|
|
case SCOPE_GLOBAL: S = SymFind (RootTab, Name, SF_FIND_EXISTING); break;
|
|
case SCOPE_LOCAL: S = SymFind (SymTab, Name, SF_FIND_EXISTING); break;
|
|
default: Internal ("Invalid scope in SymIsRef: %d", Scope);
|
|
}
|
|
|
|
/* Check if it's defined */
|
|
return S != 0 && (S->Flags & SF_REFERENCED) != 0;
|
|
}
|
|
|
|
|
|
|
|
int SymIsConst (SymEntry* S)
|
|
/* Return true if the given symbol has a constant value */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
|
|
/* Check for constness */
|
|
if (S->Flags & SF_CONST) {
|
|
return 1;
|
|
} else if ((S->Flags & SF_DEFINED) && IsConstExpr (S->V.Expr)) {
|
|
/* Constant expression, remember the value */
|
|
ExprNode* Expr = S->V.Expr;
|
|
S->Flags |= SF_CONST;
|
|
S->V.Val = GetExprVal (Expr);
|
|
FreeExpr (Expr);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int SymIsZP (SymEntry* S)
|
|
/* Return true if the symbol is explicitly marked as zeropage symbol */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
|
|
/* If the symbol is not a global symbol, was not defined before, check the
|
|
* enclosing scope for a symbol with the same name, and return the ZP
|
|
* attribute of this symbol if we find one.
|
|
*/
|
|
if (!IsLocal (S->Name) &&
|
|
(S->Flags & (SF_ZP | SF_ABS | SF_DEFINED | SF_IMPORT)) == 0 &&
|
|
S->SymTab->BackLink != 0) {
|
|
|
|
/* Try to find a symbol with the same name in the enclosing scope */
|
|
SymEntry* E = SymFindAny (S->SymTab->BackLink, S->Name);
|
|
|
|
/* If we found one, use the ZP flag */
|
|
if (E && (E->Flags & SF_ZP) != 0) {
|
|
S->Flags |= SF_ZP;
|
|
}
|
|
}
|
|
|
|
/* Check the ZP flag */
|
|
return (S->Flags & SF_ZP) != 0;
|
|
}
|
|
|
|
|
|
|
|
int SymIsImport (SymEntry* S)
|
|
/* Return true if the given symbol is marked as import */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
|
|
/* Check the import flag */
|
|
return (S->Flags & SF_IMPORT) != 0;
|
|
}
|
|
|
|
|
|
|
|
int SymHasExpr (SymEntry* S)
|
|
/* Return true if the given symbol has an associated expression */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
|
|
/* Check the expression */
|
|
return ((S->Flags & SF_DEFINED) != 0 &&
|
|
(S->Flags & SF_IMPORT) == 0 &&
|
|
(S->Flags & SF_CONST) == 0);
|
|
}
|
|
|
|
|
|
|
|
void SymFinalize (SymEntry* S)
|
|
/* Finalize a symbol expression if there is one */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
|
|
/* Check if we have an expression */
|
|
if (SymHasExpr (S)) {
|
|
S->V.Expr = FinalizeExpr (S->V.Expr);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void SymMarkUser (SymEntry* S)
|
|
/* Set a user mark on the specified symbol */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
|
|
/* Set the bit */
|
|
S->Flags |= SF_USER;
|
|
}
|
|
|
|
|
|
|
|
void SymUnmarkUser (SymEntry* S)
|
|
/* Remove a user mark from the specified symbol */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
|
|
/* Reset the bit */
|
|
S->Flags &= ~SF_USER;
|
|
}
|
|
|
|
|
|
|
|
int SymHasUserMark (SymEntry* S)
|
|
/* Return the state of the user mark for the specified symbol */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
|
|
/* Check the bit */
|
|
return (S->Flags & SF_USER) != 0;
|
|
}
|
|
|
|
|
|
|
|
long GetSymVal (SymEntry* S)
|
|
/* Return the symbol value */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
|
|
PRECONDITION ((S->Flags & SF_DEFINED) != 0 && (S->Flags & SF_CONST) != 0);
|
|
return S->V.Val;
|
|
}
|
|
|
|
|
|
|
|
ExprNode* GetSymExpr (SymEntry* S)
|
|
/* Get the expression for a non-const symbol */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
|
|
PRECONDITION (S != 0 && (S->Flags & SF_CONST) == 0);
|
|
return S->V.Expr;
|
|
}
|
|
|
|
|
|
|
|
const char* GetSymName (SymEntry* S)
|
|
/* Return the name of the symbol */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
return S->Name;
|
|
}
|
|
|
|
|
|
|
|
unsigned GetSymIndex (SymEntry* S)
|
|
/* Return the symbol index for the given symbol */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
PRECONDITION (S != 0 && (S->Flags & SF_INDEXED));
|
|
return S->Index;
|
|
}
|
|
|
|
|
|
|
|
const FilePos* GetSymPos (SymEntry* S)
|
|
/* Return the position of first occurence in the source for the given symbol */
|
|
{
|
|
/* Resolve trampoline entries */
|
|
if (S->Flags & SF_TRAMPOLINE) {
|
|
S = S->V.Sym;
|
|
}
|
|
PRECONDITION (S != 0);
|
|
return &S->Pos;
|
|
}
|
|
|
|
|
|
|
|
static void SymCheckUndefined (SymEntry* S)
|
|
/* Handle an undefined symbol */
|
|
{
|
|
/* Undefined symbol. It may be...
|
|
*
|
|
* - An undefined symbol in a nested lexical level. In this
|
|
* case, search for the symbol in the higher levels and
|
|
* make the entry a trampoline entry if we find one.
|
|
*
|
|
* - If the symbol is not found, it is a real undefined symbol.
|
|
* If the AutoImport flag is set, make it an import. If the
|
|
* AutoImport flag is not set, it's an error.
|
|
*/
|
|
SymEntry* Sym = 0;
|
|
if (S->SymTab) {
|
|
/* It's a global symbol, get the higher level table */
|
|
SymTable* Tab = S->SymTab->BackLink;
|
|
while (Tab) {
|
|
Sym = SymFindAny (Tab, S->Name);
|
|
if (Sym) {
|
|
if (Sym->Flags & (SF_DEFINED | SF_IMPORT)) {
|
|
/* We've found a symbol in a higher level that is
|
|
* either defined in the source, or an import.
|
|
*/
|
|
break;
|
|
} else {
|
|
/* The symbol found is undefined itself. Look further */
|
|
Tab = Sym->SymTab->BackLink;
|
|
}
|
|
} else {
|
|
/* No symbol found */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (Sym) {
|
|
/* We found the symbol in a higher level. Make S a trampoline
|
|
* symbol. Beware: We have to transfer the symbol attributes to
|
|
* the real symbol and check for any conflicts.
|
|
*/
|
|
S->Flags |= SF_TRAMPOLINE;
|
|
S->V.Sym = Sym;
|
|
|
|
/* Transfer the flags. Note: S may not be imported, since in that
|
|
* case it wouldn't be undefined.
|
|
*/
|
|
if (S->Flags & SF_EXPORT) {
|
|
if (Sym->Flags & SF_IMPORT) {
|
|
/* The symbol is already marked as imported external symbol */
|
|
PError (&S->Pos, ERR_SYM_ALREADY_IMPORT, S->Name);
|
|
}
|
|
Sym->Flags |= S->Flags & (SF_EXPORT | SF_ZP);
|
|
}
|
|
|
|
/* Transfer the referenced flag */
|
|
Sym->Flags |= (S->Flags & SF_REFERENCED);
|
|
|
|
} else {
|
|
/* The symbol is definitely undefined */
|
|
if (S->Flags & SF_EXPORT) {
|
|
/* We will not auto-import an export */
|
|
PError (&S->Pos, ERR_EXPORT_UNDEFINED, S->Name);
|
|
} else {
|
|
if (AutoImport) {
|
|
/* Mark as import, will be indexed later */
|
|
S->Flags |= SF_IMPORT;
|
|
} else {
|
|
/* Error */
|
|
PError (&S->Pos, ERR_SYM_UNDEFINED, S->Name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void SymCheck (void)
|
|
/* Run through all symbols and check for anomalies and errors */
|
|
{
|
|
SymEntry* S;
|
|
|
|
/* Check for open lexical levels */
|
|
if (SymTab->BackLink != 0) {
|
|
Error (ERR_OPEN_PROC);
|
|
}
|
|
|
|
/* First pass: Walk through all symbols, checking for undefined's and
|
|
* changing them to trampoline symbols or make them imports.
|
|
*/
|
|
S = SymList;
|
|
while (S) {
|
|
/* If the symbol is marked as global, mark it as export, if it is
|
|
* already defined, otherwise mark it as import.
|
|
*/
|
|
if (S->Flags & SF_GLOBAL) {
|
|
S->Flags &= ~SF_GLOBAL;
|
|
if (S->Flags & SF_DEFINED) {
|
|
S->Flags |= SF_EXPORT;
|
|
} else {
|
|
S->Flags |= SF_IMPORT;
|
|
}
|
|
}
|
|
|
|
/* Handle undefined symbols */
|
|
if ((S->Flags & SF_UNDEFMASK) == SF_UNDEFVAL) {
|
|
/* This is an undefined symbol. Handle it. */
|
|
SymCheckUndefined (S);
|
|
}
|
|
|
|
/* Next symbol */
|
|
S = S->List;
|
|
}
|
|
|
|
/* Second pass: Walk again through the symbols. Ignore undefined's, since
|
|
* we handled them in the last pass, and ignore trampoline symbols, since
|
|
* we handled them in the last pass, too.
|
|
*/
|
|
S = SymList;
|
|
while (S) {
|
|
if ((S->Flags & SF_TRAMPOLINE) == 0 &&
|
|
(S->Flags & SF_UNDEFMASK) != SF_UNDEFVAL) {
|
|
if ((S->Flags & SF_DEFINED) != 0 && (S->Flags & SF_REFERENCED) == 0) {
|
|
/* Symbol was defined but never referenced */
|
|
PWarning (&S->Pos, WARN_SYM_NOT_REFERENCED, S->Name);
|
|
}
|
|
if (S->Flags & SF_IMPORT) {
|
|
if ((S->Flags & (SF_REFERENCED | SF_FORCED)) == SF_NONE) {
|
|
/* Imported symbol is not referenced */
|
|
PWarning (&S->Pos, WARN_IMPORT_NOT_REFERENCED, S->Name);
|
|
} else {
|
|
/* Give the import an index, count imports */
|
|
S->Index = ImportCount++;
|
|
S->Flags |= SF_INDEXED;
|
|
}
|
|
}
|
|
if (S->Flags & SF_EXPORT) {
|
|
/* Give the export an index, count exports */
|
|
S->Index = ExportCount++;
|
|
S->Flags |= SF_INDEXED;
|
|
}
|
|
}
|
|
|
|
/* Next symbol */
|
|
S = S->List;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void SymDump (FILE* F)
|
|
/* Dump the symbol table */
|
|
{
|
|
SymEntry* S = SymList;
|
|
|
|
while (S) {
|
|
/* Ignore trampoline symbols */
|
|
if ((S->Flags & SF_TRAMPOLINE) != 0) {
|
|
fprintf (F,
|
|
"%-24s %s %s %s %s %s\n",
|
|
S->Name,
|
|
(S->Flags & SF_DEFINED)? "DEF" : "---",
|
|
(S->Flags & SF_REFERENCED)? "REF" : "---",
|
|
(S->Flags & SF_IMPORT)? "IMP" : "---",
|
|
(S->Flags & SF_EXPORT)? "EXP" : "---",
|
|
(S->Flags & SF_ZP)? "ZP" : "--");
|
|
}
|
|
/* Next symbol */
|
|
S = S->List;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void WriteImports (void)
|
|
/* Write the imports list to the object file */
|
|
{
|
|
SymEntry* S;
|
|
|
|
/* Tell the object file module that we're about to start the imports */
|
|
ObjStartImports ();
|
|
|
|
/* Write the import count to the list */
|
|
ObjWriteVar (ImportCount);
|
|
|
|
/* Walk throught list and write all valid imports to the file. An import
|
|
* is considered valid, if it is either referenced, or the forced bit is
|
|
* set. Otherwise, the import is ignored (no need to link in something
|
|
* that isn't used).
|
|
*/
|
|
S = SymList;
|
|
while (S) {
|
|
if ((S->Flags & (SF_TRAMPOLINE | SF_IMPORT)) == SF_IMPORT &&
|
|
(S->Flags & (SF_REFERENCED | SF_FORCED)) != 0) {
|
|
|
|
if (S->Flags & SF_ZP) {
|
|
ObjWrite8 (IMP_ZP);
|
|
} else {
|
|
ObjWrite8 (IMP_ABS);
|
|
}
|
|
ObjWriteVar (GetStringId (S->Name));
|
|
ObjWritePos (&S->Pos);
|
|
}
|
|
S = S->List;
|
|
}
|
|
|
|
/* Done writing imports */
|
|
ObjEndImports ();
|
|
}
|
|
|
|
|
|
|
|
static unsigned char GetExprMask (SymEntry* S)
|
|
/* Return the expression bits for the given symbol table entry */
|
|
{
|
|
unsigned char ExprMask;
|
|
|
|
/* Check if the symbol is const */
|
|
ExprMask = (SymIsConst (S))? EXP_CONST : EXP_EXPR;
|
|
|
|
/* Add zeropage/abs bits */
|
|
ExprMask |= (S->Flags & SF_ZP)? EXP_ZP : EXP_ABS;
|
|
|
|
/* Add the label/equate bits */
|
|
ExprMask |= (S->Flags & SF_LABEL)? EXP_LABEL : EXP_EQUATE;
|
|
|
|
/* Return the mask */
|
|
return ExprMask;
|
|
}
|
|
|
|
|
|
|
|
void WriteExports (void)
|
|
/* Write the exports list to the object file */
|
|
{
|
|
SymEntry* S;
|
|
unsigned Type;
|
|
|
|
/* Tell the object file module that we're about to start the exports */
|
|
ObjStartExports ();
|
|
|
|
/* Write the export count to the list */
|
|
ObjWriteVar (ExportCount);
|
|
|
|
/* Walk throught list and write all exports to the file */
|
|
S = SymList;
|
|
while (S) {
|
|
if ((S->Flags & SF_EXPMASK) == SF_EXPVAL) {
|
|
unsigned char ExprMask;
|
|
|
|
/* Finalize an associated expression if we have one */
|
|
SymFinalize (S);
|
|
|
|
/* Get the expression bits */
|
|
ExprMask = GetExprMask (S);
|
|
|
|
/* Count the number of ConDes types */
|
|
for (Type = 0; Type < CD_TYPE_COUNT; ++Type) {
|
|
if (S->ConDesPrio[Type] != CD_PRIO_NONE) {
|
|
INC_EXP_CONDES_COUNT (ExprMask);
|
|
}
|
|
}
|
|
|
|
/* Write the type */
|
|
ObjWrite8 (ExprMask);
|
|
|
|
/* Write any ConDes declarations */
|
|
if (GET_EXP_CONDES_COUNT (ExprMask) > 0) {
|
|
for (Type = 0; Type < CD_TYPE_COUNT; ++Type) {
|
|
unsigned char Prio = S->ConDesPrio[Type];
|
|
if (Prio != CD_PRIO_NONE) {
|
|
ObjWrite8 (CD_BUILD (Type, Prio));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Write the name */
|
|
ObjWriteVar (GetStringId (S->Name));
|
|
|
|
/* Write the value */
|
|
if ((ExprMask & EXP_MASK_VAL) == EXP_CONST) {
|
|
/* Constant value */
|
|
ObjWrite32 (S->V.Val);
|
|
} else {
|
|
/* Expression involved */
|
|
WriteExpr (S->V.Expr);
|
|
}
|
|
|
|
/* Write the source file position */
|
|
ObjWritePos (&S->Pos);
|
|
}
|
|
S = S->List;
|
|
}
|
|
|
|
/* Done writing exports */
|
|
ObjEndExports ();
|
|
}
|
|
|
|
|
|
|
|
void WriteDbgSyms (void)
|
|
/* Write a list of all symbols to the object file */
|
|
{
|
|
unsigned Count;
|
|
SymEntry* S;
|
|
|
|
/* Tell the object file module that we're about to start the debug info */
|
|
ObjStartDbgSyms ();
|
|
|
|
/* Check if debug info is requested */
|
|
if (DbgSyms) {
|
|
|
|
/* Walk through the list and count the symbols */
|
|
Count = 0;
|
|
S = SymList;
|
|
while (S) {
|
|
if ((S->Flags & SF_DBGINFOMASK) == SF_DBGINFOVAL) {
|
|
++Count;
|
|
}
|
|
S = S->List;
|
|
}
|
|
|
|
/* Write the symbol count to the list */
|
|
ObjWriteVar (Count);
|
|
|
|
/* Walk through list and write all symbols to the file */
|
|
S = SymList;
|
|
while (S) {
|
|
if ((S->Flags & SF_DBGINFOMASK) == SF_DBGINFOVAL) {
|
|
unsigned char ExprMask;
|
|
|
|
/* Finalize an associated expression if we have one */
|
|
SymFinalize (S);
|
|
|
|
/* Get the expression bits */
|
|
ExprMask = GetExprMask (S);
|
|
|
|
/* Write the type */
|
|
ObjWrite8 (ExprMask);
|
|
|
|
/* Write the name */
|
|
ObjWriteVar (GetStringId (S->Name));
|
|
|
|
/* Write the value */
|
|
if ((ExprMask & EXP_MASK_VAL) == EXP_CONST) {
|
|
/* Constant value */
|
|
ObjWrite32 (S->V.Val);
|
|
} else {
|
|
/* Expression involved */
|
|
WriteExpr (S->V.Expr);
|
|
}
|
|
|
|
/* Write the source file position */
|
|
ObjWritePos (&S->Pos);
|
|
}
|
|
S = S->List;
|
|
}
|
|
|
|
} else {
|
|
|
|
/* No debug symbols */
|
|
ObjWriteVar (0);
|
|
|
|
}
|
|
|
|
/* Done writing debug symbols */
|
|
ObjEndDbgSyms ();
|
|
}
|
|
|
|
|
|
|