cc65/src/ca65/dbginfo.c

530 lines
15 KiB
C

/*****************************************************************************/
/* */
/* dbginfo.c */
/* */
/* Handle the .dbg commands */
/* */
/* */
/* */
/* (C) 2000-2012, Ullrich von Bassewitz */
/* Roemerstrasse 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 "chartype.h"
#include "coll.h"
#include "filepos.h"
#include "hlldbgsym.h"
#include "scopedefs.h"
#include "strbuf.h"
/* ca65 */
#include "dbginfo.h"
#include "error.h"
#include "expr.h"
#include "filetab.h"
#include "global.h"
#include "lineinfo.h"
#include "objfile.h"
#include "nexttok.h"
#include "symentry.h"
#include "symtab.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Structure used for a high level language function or symbol */
typedef struct HLLDbgSym HLLDbgSym;
struct HLLDbgSym {
unsigned Flags; /* See above */
unsigned Name; /* String id of name */
unsigned AsmName; /* String id of asm symbol name */
SymEntry* Sym; /* The assembler symbol */
int Offs; /* Offset if any */
unsigned Type; /* String id of type */
SymTable* Scope; /* Parent scope */
unsigned FuncId; /* Id of hll function if any */
FilePos Pos; /* Position of statement */
};
/* The current line info */
static LineInfo* CurLineInfo = 0;
/* List of high level language debug symbols */
static Collection HLLDbgSyms = STATIC_COLLECTION_INITIALIZER;
/*****************************************************************************/
/* Code */
/*****************************************************************************/
static HLLDbgSym* NewHLLDbgSym (unsigned Flags, unsigned Name, unsigned Type)
/* Allocate and return a new HLLDbgSym structure */
{
/* Allocate memory */
HLLDbgSym* S = xmalloc (sizeof (*S));
/* Initialize the fields as necessary */
S->Flags = Flags;
S->Name = Name;
S->AsmName = EMPTY_STRING_ID;
S->Sym = 0;
S->Offs = 0;
S->Type = Type;
S->Scope = CurrentScope;
S->FuncId = ~0U;
S->Pos = CurTok.Pos;
/* Return the result */
return S;
}
static unsigned HexValue (char C)
/* Convert the ascii representation of a hex nibble into the hex nibble */
{
if (isdigit (C)) {
return C - '0';
} else if (islower (C)) {
return C - 'a' + 10;
} else {
return C - 'A' + 10;
}
}
static int ValidateType (StrBuf* Type)
/* Check if the given type is valid and if so, return a string id for it. If
** the type isn't valid, return -1. Type is overwritten when checking.
*/
{
unsigned I;
const char* A;
char* B;
/* The length must not be zero and divideable by two */
unsigned Length = SB_GetLen (Type);
if (Length < 2 || (Length & 0x01) != 0) {
ErrorSkip ("Type value has invalid length");
return -1;
}
/* The string must consist completely of hex digit chars */
A = SB_GetConstBuf (Type);
for (I = 0; I < Length; ++I) {
if (!IsXDigit (A[I])) {
ErrorSkip ("Type value contains invalid characters");
return -1;
}
}
/* Convert the type to binary */
B = SB_GetBuf (Type);
while (A < SB_GetConstBuf (Type) + Length) {
/* Since we know, there are only hex digits, there can't be any errors */
*B++ = (HexValue (A[0]) << 4) | HexValue (A[1]);
A += 2;
}
Type->Len = (Length /= 2);
/* Allocate the type and return it */
return GetStrBufId (Type);
}
void DbgInfoFile (void)
/* Parse and handle FILE subcommand of the .dbg pseudo instruction */
{
StrBuf Name = STATIC_STRBUF_INITIALIZER;
unsigned long Size;
unsigned long MTime;
/* Parameters are separated by a comma */
ConsumeComma ();
/* Name */
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
return;
}
SB_Copy (&Name, &CurTok.SVal);
NextTok ();
/* Comma expected */
ConsumeComma ();
/* Size */
Size = ConstExpression ();
/* Comma expected */
ConsumeComma ();
/* MTime */
MTime = ConstExpression ();
/* Insert the file into the table */
AddFile (&Name, FT_DBGINFO, Size, MTime);
/* Free memory used for Name */
SB_Done (&Name);
}
void DbgInfoFunc (void)
/* Parse and handle func subcommand of the .dbg pseudo instruction */
{
static const char* const StorageKeys[] = {
"EXTERN",
"STATIC",
};
unsigned Name;
int Type;
unsigned AsmName;
unsigned Flags;
HLLDbgSym* S;
/* Parameters are separated by a comma */
ConsumeComma ();
/* Name */
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
return;
}
Name = GetStrBufId (&CurTok.SVal);
NextTok ();
/* Comma expected */
ConsumeComma ();
/* Type */
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
return;
}
Type = ValidateType (&CurTok.SVal);
if (Type < 0) {
return;
}
NextTok ();
/* Comma expected */
ConsumeComma ();
/* The storage class follows */
if (CurTok.Tok != TOK_IDENT) {
ErrorSkip ("Storage class specifier expected");
return;
}
switch (GetSubKey (StorageKeys, sizeof (StorageKeys)/sizeof (StorageKeys[0]))) {
case 0: Flags = HLL_TYPE_FUNC | HLL_SC_EXTERN; break;
case 1: Flags = HLL_TYPE_FUNC | HLL_SC_STATIC; break;
default: ErrorSkip ("Storage class specifier expected"); return;
}
NextTok ();
/* Comma expected */
ConsumeComma ();
/* Assembler name follows */
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
return;
}
AsmName = GetStrBufId (&CurTok.SVal);
NextTok ();
/* There may only be one function per scope */
if (CurrentScope == RootScope) {
ErrorSkip ("Functions may not be used in the root scope");
return;
} else if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label == 0) {
ErrorSkip ("Functions can only be tagged to .PROC scopes");
return;
} else if (CurrentScope->Label->HLLSym != 0) {
ErrorSkip ("Only one HLL symbol per asm symbol is allowed");
return;
} else if (CurrentScope->Label->Name != AsmName) {
ErrorSkip ("Scope label and asm name for function must match");
return;
}
/* Add the function */
S = NewHLLDbgSym (Flags, Name, Type);
S->Sym = CurrentScope->Label;
CurrentScope->Label->HLLSym = S;
CollAppend (&HLLDbgSyms, S);
}
void DbgInfoLine (void)
/* Parse and handle LINE subcommand of the .dbg pseudo instruction */
{
long Line;
FilePos Pos = STATIC_FILEPOS_INITIALIZER;
/* Any new line info terminates the last one */
if (CurLineInfo) {
EndLine (CurLineInfo);
CurLineInfo = 0;
}
/* If a parameters follow, this is actual line info. If no parameters
** follow, the last line info is terminated.
*/
if (CurTok.Tok == TOK_SEP) {
return;
}
/* Parameters are separated by a comma */
ConsumeComma ();
/* The name of the file follows */
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
return;
}
/* Get the index in the file table for the name */
Pos.Name = GetFileIndex (&CurTok.SVal);
/* Skip the name */
NextTok ();
/* Comma expected */
ConsumeComma ();
/* Line number */
Line = ConstExpression ();
if (Line < 0) {
ErrorSkip ("Line number is out of valid range");
return;
}
Pos.Line = Line;
/* Generate a new external line info */
CurLineInfo = StartLine (&Pos, LI_TYPE_EXT, 0);
}
void DbgInfoSym (void)
/* Parse and handle SYM subcommand of the .dbg pseudo instruction */
{
static const char* const StorageKeys[] = {
"AUTO",
"EXTERN",
"REGISTER",
"STATIC",
};
unsigned Name;
int Type;
unsigned AsmName = EMPTY_STRING_ID;
unsigned Flags;
int Offs = 0;
HLLDbgSym* S;
/* Parameters are separated by a comma */
ConsumeComma ();
/* Name */
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
return;
}
Name = GetStrBufId (&CurTok.SVal);
NextTok ();
/* Comma expected */
ConsumeComma ();
/* Type */
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
return;
}
Type = ValidateType (&CurTok.SVal);
if (Type < 0) {
return;
}
NextTok ();
/* Comma expected */
ConsumeComma ();
/* The storage class follows */
if (CurTok.Tok != TOK_IDENT) {
ErrorSkip ("Storage class specifier expected");
return;
}
switch (GetSubKey (StorageKeys, sizeof (StorageKeys)/sizeof (StorageKeys[0]))) {
case 0: Flags = HLL_SC_AUTO; break;
case 1: Flags = HLL_SC_EXTERN; break;
case 2: Flags = HLL_SC_REG; break;
case 3: Flags = HLL_SC_STATIC; break;
default: ErrorSkip ("Storage class specifier expected"); return;
}
/* Skip the storage class token and the following comma */
NextTok ();
ConsumeComma ();
/* The next tokens depend on the storage class */
if (Flags == HLL_SC_AUTO) {
/* Auto: Stack offset follows */
Offs = ConstExpression ();
} else {
/* Register, extern or static: Assembler name follows */
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
return;
}
AsmName = GetStrBufId (&CurTok.SVal);
NextTok ();
/* For register, an offset follows */
if (Flags == HLL_SC_REG) {
ConsumeComma ();
Offs = ConstExpression ();
}
}
/* Add the function */
S = NewHLLDbgSym (Flags | HLL_TYPE_SYM, Name, Type);
S->AsmName = AsmName;
S->Offs = Offs;
CollAppend (&HLLDbgSyms, S);
}
void DbgInfoCheck (void)
/* Do checks on all hll debug info symbols when assembly is complete */
{
/* When parsing the debug statements for HLL symbols, we have already
** tagged the functions to their asm counterparts. This wasn't done for
** C symbols, since we will allow forward declarations. So we have to
** resolve the normal C symbols now.
*/
unsigned I;
for (I = 0; I < CollCount (&HLLDbgSyms); ++I) {
/* Get the next symbol */
HLLDbgSym* S = CollAtUnchecked (&HLLDbgSyms, I);
/* Ignore functions and auto symbols, because the later live on the
** stack and don't have corresponding asm symbols.
*/
if (HLL_IS_FUNC (S->Flags) || HLL_GET_SC (S->Flags) == HLL_SC_AUTO) {
continue;
}
/* Safety */
CHECK (S->Sym == 0 && S->Scope != 0);
/* Search for the symbol name */
S->Sym = SymFindAny (S->Scope, GetStrBuf (S->AsmName));
if (S->Sym == 0) {
PError (&S->Pos, "Assembler symbol '%s' not found",
GetString (S->AsmName));
} else {
/* Set the backlink */
S->Sym->HLLSym = S;
}
}
}
void WriteHLLDbgSyms (void)
/* Write a list of all high level language symbols to the object file. */
{
unsigned I;
/* Only if debug info is enabled */
if (DbgSyms) {
/* Write the symbol count to the list */
ObjWriteVar (CollCount (&HLLDbgSyms));
/* Walk through list and write all symbols to the file. */
for (I = 0; I < CollCount (&HLLDbgSyms); ++I) {
/* Get the next symbol */
HLLDbgSym* S = CollAtUnchecked (&HLLDbgSyms, I);
/* Get the type of the symbol */
unsigned SC = HLL_GET_SC (S->Flags);
/* Remember if the symbol has debug info attached
** ### This should go into DbgInfoCheck
*/
if (S->Sym && S->Sym->DebugSymId != ~0U) {
S->Flags |= HLL_DATA_SYM;
}
/* Write the symbol data */
ObjWriteVar (S->Flags);
ObjWriteVar (S->Name);
if (HLL_HAS_SYM (S->Flags)) {
ObjWriteVar (S->Sym->DebugSymId);
}
if (SC == HLL_SC_AUTO || SC == HLL_SC_REG) {
ObjWriteVar (S->Offs);
}
ObjWriteVar (S->Type);
ObjWriteVar (S->Scope->Id);
}
} else {
/* Write a count of zero */
ObjWriteVar (0);
}
}