mirror of
https://github.com/cc65/cc65.git
synced 2024-09-28 10:55:43 +00:00
a6b04f6e97
Quotations that are embraced by tick marks now look better, in most fonts.
530 lines
15 KiB
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);
|
|
|
|
}
|
|
}
|