From 54e22eb53ca6d9fab0dc688e8e5ea087338e33db Mon Sep 17 00:00:00 2001 From: uz Date: Tue, 3 Aug 2010 18:46:50 +0000 Subject: [PATCH] First version of a module that reads debug files generated by ld65. git-svn-id: svn://svn.cc65.org/cc65/trunk@4779 b7a2c559-68d2-44c3-8de9-860c34a00d81 --- src/dbginfo/dbginfo.c | 1479 ++++++++++++++++++++++++++++++++++++++ src/dbginfo/dbginfo.h | 113 +++ src/dbginfo/dbgtest.c | 85 +++ src/dbginfo/make/gcc.mak | 58 ++ 4 files changed, 1735 insertions(+) create mode 100644 src/dbginfo/dbginfo.c create mode 100644 src/dbginfo/dbginfo.h create mode 100644 src/dbginfo/dbgtest.c create mode 100644 src/dbginfo/make/gcc.mak diff --git a/src/dbginfo/dbginfo.c b/src/dbginfo/dbginfo.c new file mode 100644 index 000000000..0b6b720d9 --- /dev/null +++ b/src/dbginfo/dbginfo.c @@ -0,0 +1,1479 @@ +/*****************************************************************************/ +/* */ +/* dbginfo.h */ +/* */ +/* cc65 debug info handling */ +/* */ +/* */ +/* */ +/* (C) 2010, 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 +#include +#include +#include +#include +#include +#include + +#include "dbginfo.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Dynamic strings */ +typedef struct StrBuf StrBuf; +struct StrBuf { + char* Buf; /* Pointer to buffer */ + unsigned Len; /* Length of the string */ + unsigned Allocated; /* Size of allocated memory */ +}; + +/* Initializer for a string buffer */ +#define STRBUF_INITIALIZER { 0, 0, 0 } + +/* An empty string buf */ +static const StrBuf EmptyStrBuf = STRBUF_INITIALIZER; + +/* An array of pointers that grows if needed */ +typedef struct Collection Collection; +struct Collection { + unsigned Count; /* Number of items in the list */ + unsigned Size; /* Size of allocated array */ + void** Items; /* Array with dynamic size */ +}; + +/* Initializer for static collections */ +#define COLLECTION_INITIALIZER { 0, 0, 0 } + +/* An empty collection */ +static const Collection EmptyCollection = COLLECTION_INITIALIZER; + + + +/* ### Parseerror */ +enum { + CC65_WARNING, + CC65_ERROR, +}; + +/* Data structure containing information from the debug info file. A pointer + * to this structure is passed as handle to callers from the outside. + */ +typedef struct DbgInfo DbgInfo; +struct DbgInfo { + Collection FileInfo; /* Collection with file infos */ + Collection LineInfo; /* Collection with line infos */ + +}; + +/* Input tokens */ +typedef enum Token Token; +enum Token { + + TOK_INVALID, /* Invalid token */ + TOK_EOF, /* End of file reached */ + + TOK_INTCON, /* Integer constant */ + TOK_STRCON, /* String constant */ + + TOK_EQUAL, /* = */ + TOK_COMMA, /* , */ + TOK_MINUS, /* - */ + TOK_PLUS, /* + */ + TOK_EOL, /* \n */ + + TOK_ABSOLUTE, /* ABSOLUTE keyword */ + TOK_ADDRSIZE, /* ADDRSIZE keyword */ + TOK_EQUATE, /* EQUATE keyword */ + TOK_FILE, /* FILE keyword */ + TOK_LABEL, /* LABEL keyword */ + TOK_LINE, /* LINE keyword */ + TOK_MTIME, /* MTIME keyword */ + TOK_RANGE, /* RANGE keyword */ + TOK_RO, /* RO keyword */ + TOK_RW, /* RW keyword */ + TOK_SEGMENT, /* SEGMENT keyword */ + TOK_SIZE, /* SIZE keyword */ + TOK_START, /* START keyword */ + TOK_SYM, /* SYM keyword */ + TOK_TYPE, /* TYPE keyword */ + TOK_VALUE, /* VALUE keyword */ + TOK_VERSION, /* VERSION keyword */ + TOK_ZEROPAGE, /* ZEROPAGE keyword */ +}; + +/* Data used when parsing the debug info file */ +typedef struct InputData InputData; +struct InputData { + const char* FileName; /* Name of input file */ + unsigned long Line; /* Current line number */ + unsigned Col; /* Current column number */ + unsigned long SLine; /* Line number at start of token */ + unsigned SCol; /* Column number at start of token */ + unsigned Errors; /* Number of errors */ + FILE* F; /* Input file */ + int C; /* Input character */ + Token Tok; /* Token from input stream */ + unsigned long IVal; /* Integer constant */ + StrBuf SVal; /* String constant */ + cc65_errorfunc Error; /* Function called in case of errors */ + DbgInfo* Info; /* Pointer to debug info */ +}; + +/* Internally used file info struct */ +typedef struct FileInfo FileInfo; +struct FileInfo { + unsigned long Size; /* Size of file */ + unsigned long MTime; /* Modification time */ + char FileName[1]; /* Name of file with full path */ +}; + +/* Internally used line info struct */ +typedef struct LineInfo LineInfo; +struct LineInfo { + unsigned long Start; /* Start of data range */ + unsigned long End; /* End of data range */ + unsigned long Line; /* Line number */ + FileInfo* FileInfo; /* Pointer to file info */ + char FileName[1]; /* Name of file */ +}; + + + +/*****************************************************************************/ +/* Forwards */ +/*****************************************************************************/ + + + +static void NextToken (InputData* D); +/* Read the next token from the input stream */ + + + +/*****************************************************************************/ +/* Memory allocation */ +/*****************************************************************************/ + + + +static void* xmalloc (size_t Size) +/* Allocate memory, check for out of memory condition. Do some debugging */ +{ + void* P = 0; + + /* Allow zero sized requests and return NULL in this case */ + if (Size) { + + /* Allocate memory */ + P = malloc (Size); + + /* Check for errors */ + if (P == 0) { + /* ####### */ + } + } + + /* Return a pointer to the block */ + return P; +} + + + +static void* xrealloc (void* P, size_t Size) +/* Reallocate a memory block, check for out of memory */ +{ + /* Reallocate the block */ + void* N = realloc (P, Size); + + /* Check for errors */ + assert (N != 0 || Size == 0); + + /* Return the pointer to the new block */ + return N; +} + + + +static void xfree (void* Block) +/* Free the block, do some debugging */ +{ + free (Block); +} + + + +/*****************************************************************************/ +/* Dynamic strings */ +/*****************************************************************************/ + + + +static void SB_Done (StrBuf* B) +/* Free the data of a string buffer (but not the struct itself) */ +{ + if (B->Allocated) { + xfree (B->Buf); + } +} + + + +static void SB_Realloc (StrBuf* B, unsigned NewSize) +/* Reallocate the string buffer space, make sure at least NewSize bytes are + * available. + */ +{ + /* Get the current size, use a minimum of 8 bytes */ + unsigned NewAllocated = B->Allocated; + if (NewAllocated == 0) { + NewAllocated = 8; + } + + /* Round up to the next power of two */ + while (NewAllocated < NewSize) { + NewAllocated *= 2; + } + + /* Reallocate the buffer. Beware: The allocated size may be zero while the + * length is not. This means that we have a buffer that wasn't allocated + * on the heap. + */ + if (B->Allocated) { + /* Just reallocate the block */ + B->Buf = xrealloc (B->Buf, NewAllocated); + } else { + /* Allocate a new block and copy */ + B->Buf = memcpy (xmalloc (NewAllocated), B->Buf, B->Len); + } + + /* Remember the new block size */ + B->Allocated = NewAllocated; +} + + + +static unsigned SB_GetLen (const StrBuf* B) +/* Return the length of the buffer contents */ +{ + return B->Len; +} + + + +static const char* SB_GetConstBuf (const StrBuf* B) +/* Return a buffer pointer */ +{ + return B->Buf; +} + + + +static void SB_Terminate (StrBuf* B) +/* Zero terminate the given string buffer. NOTE: The terminating zero is not + * accounted for in B->Len, if you want that, you have to use AppendChar! + */ +{ + unsigned NewLen = B->Len + 1; + if (NewLen > B->Allocated) { + SB_Realloc (B, NewLen); + } + B->Buf[B->Len] = '\0'; +} + + + +static void SB_Clear (StrBuf* B) +/* Clear the string buffer (make it empty) */ +{ + B->Len = 0; +} + + + +static void SB_AppendChar (StrBuf* B, int C) +/* Append a character to a string buffer */ +{ + unsigned NewLen = B->Len + 1; + if (NewLen > B->Allocated) { + SB_Realloc (B, NewLen); + } + B->Buf[B->Len] = (char) C; + B->Len = NewLen; +} + + + +/*****************************************************************************/ +/* Collections */ +/*****************************************************************************/ + + + +static Collection* InitCollection (Collection* C) +/* Initialize a collection and return it. */ +{ + /* Intialize the fields. */ + C->Count = 0; + C->Size = 0; + C->Items = 0; + + /* Return the new struct */ + return C; +} + + + +static void DoneCollection (Collection* C) +/* Free the data for a collection. This will not free the data contained in + * the collection. + */ +{ + /* Free the pointer array */ + xfree (C->Items); +} + + + +static unsigned CollCount (const Collection* C) +/* Return the number of items in the collection */ +{ + return C->Count; +} + + + +static void CollGrow (Collection* C, unsigned Size) +/* Grow the collection C so it is able to hold Size items without a resize + * being necessary. This can be called for performance reasons if the number + * of items to be placed in the collection is known in advance. + */ +{ + void** NewItems; + + /* Ignore the call if the collection is already large enough */ + if (Size <= C->Size) { + return; + } + + /* Grow the collection */ + C->Size = Size; + NewItems = xmalloc (C->Size * sizeof (void*)); + memcpy (NewItems, C->Items, C->Count * sizeof (void*)); + xfree (C->Items); + C->Items = NewItems; +} + + + +static void CollInsert (Collection* C, void* Item, unsigned Index) +/* Insert the data at the given position in the collection */ +{ + /* Check for invalid indices */ + assert (Index <= C->Count); + + /* Grow the array if necessary */ + if (C->Count >= C->Size) { + /* Must grow */ + CollGrow (C, (C->Size == 0)? 8 : C->Size * 2); + } + + /* Move the existing elements if needed */ + if (C->Count != Index) { + memmove (C->Items+Index+1, C->Items+Index, (C->Count-Index) * sizeof (void*)); + } + ++C->Count; + + /* Store the new item */ + C->Items[Index] = Item; +} + + + +static void CollAppend (Collection* C, void* Item) +/* Append an item to the end of the collection */ +{ + /* Insert the item at the end of the current list */ + CollInsert (C, Item, C->Count); +} + + + +static void* CollAt (Collection* C, unsigned Index) +/* Return the item at the given index */ +{ + /* Check the index */ + assert (Index < C->Count); + + /* Return the element */ + return C->Items[Index]; +} + + + +static void CollDelete (Collection* C, unsigned Index) +/* Remove the item with the given index from the collection. This will not + * free the item itself, just the pointer. All items with higher indices + * will get moved to a lower position. + */ +{ + /* Check the index */ + assert (Index < C->Count); + + /* Remove the item pointer */ + --C->Count; + memmove (C->Items+Index, C->Items+Index+1, (C->Count-Index) * sizeof (void*)); +} + + + +static void CollReplace (Collection* C, void* Item, unsigned Index) +/* Replace the item at the given position. The old item will not be freed, + * just the pointer will get replaced. + */ +{ + /* Check the index */ + assert (Index < C->Count); + + /* Replace the item pointer */ + C->Items[Index] = Item; +} + + + +static void CollQuickSort (Collection* C, int Lo, int Hi, + int (*Compare) (const void*, const void*)) +/* Internal recursive sort function. */ +{ + /* Get a pointer to the items */ + void** Items = C->Items; + + /* Quicksort */ + while (Hi > Lo) { + int I = Lo + 1; + int J = Hi; + while (I <= J) { + while (I <= J && Compare (Items[Lo], Items[I]) >= 0) { + ++I; + } + while (I <= J && Compare (Items[Lo], Items[J]) < 0) { + --J; + } + if (I <= J) { + /* Swap I and J */ + void* Tmp = Items[I]; + Items[I] = Items[J]; + Items[J] = Tmp; + ++I; + --J; + } + } + if (J != Lo) { + /* Swap J and Lo */ + void* Tmp = Items[J]; + Items[J] = Items[Lo]; + Items[Lo] = Tmp; + } + if (J > (Hi + Lo) / 2) { + CollQuickSort (C, J + 1, Hi, Compare); + Hi = J - 1; + } else { + CollQuickSort (C, Lo, J - 1, Compare); + Lo = J + 1; + } + } +} + + + +void CollSort (Collection* C, int (*Compare) (const void*, const void*)) +/* Sort the collection using the given compare function. */ +{ + if (C->Count > 1) { + CollQuickSort (C, 0, C->Count-1, Compare); + } +} + + + +/*****************************************************************************/ +/* File info */ +/*****************************************************************************/ + + + +static FileInfo* NewFileInfo (const StrBuf* FileName) +/* Create a new FileInfo struct and return it */ +{ + /* Allocate memory */ + FileInfo* F = xmalloc (sizeof (FileInfo) + SB_GetLen (FileName)); + + /* Initialize it */ + F->Size = 0; + F->MTime = 0; + memcpy (F->FileName, SB_GetConstBuf (FileName), SB_GetLen (FileName) + 1); + + /* Return it */ + return F; +} + + + +static void FreeFileInfo (FileInfo* F) +/* Free a FileInfo struct */ +{ + xfree (F); +} + + + +static int CompareFileInfo (const void* L, const void* R) +/* Helper function to sort file infos in a collection */ +{ + /* Sort by file name */ + return strcmp (((const FileInfo*) L)->FileName, + ((const FileInfo*) R)->FileName); +} + + + +/*****************************************************************************/ +/* Line info */ +/*****************************************************************************/ + + + +static LineInfo* NewLineInfo (const StrBuf* FileName) +/* Create a new LineInfo struct and return it */ +{ + /* Allocate memory */ + LineInfo* L = xmalloc (sizeof (LineInfo) + SB_GetLen (FileName)); + + /* Initialize it */ + L->Start = 0; + L->End = 0; + L->Line = 0; + L->FileInfo = 0; + memcpy (L->FileName, SB_GetConstBuf (FileName), SB_GetLen (FileName) + 1); + + /* Return it */ + return L; +} + + + +static void FreeLineInfo (LineInfo* L) +/* Free a LineInfo struct */ +{ + xfree (L); +} + + + +static LineInfo* PreenLineInfo (LineInfo* L, FileInfo* F) +/* Replace the name by file information */ +{ + /* Shrink the LineInfo struct removing the FfileName field */ + L = xrealloc (L, sizeof (*L) - 1); + + /* Set the FileInfo pointer instead */ + L->FileInfo = F; + + /* Return the result */ + return L; +} + + + +/*****************************************************************************/ +/* Debug info */ +/*****************************************************************************/ + + + +static DbgInfo* NewDbgInfo (void) +/* Create a new DbgInfo struct and return it */ +{ + /* Allocate memory */ + DbgInfo* Info = xmalloc (sizeof (DbgInfo)); + + /* Initialize it */ + InitCollection (&Info->FileInfo); + InitCollection (&Info->LineInfo); + + /* Return it */ + return Info; +} + + + +static void FreeDbgInfo (DbgInfo* Info) +/* Free a DbgInfo struct */ +{ + unsigned I; + + /* Free file info */ + for (I = 0; I < CollCount (&Info->FileInfo); ++I) { + FreeFileInfo (CollAt (&Info->FileInfo, I)); + } + DoneCollection (&Info->FileInfo); + + /* Free line info */ + for (I = 0; I < CollCount (&Info->LineInfo); ++I) { + FreeLineInfo (CollAt (&Info->LineInfo, I)); + } + DoneCollection (&Info->LineInfo); + + /* Free the structure itself */ + xfree (Info); +} + + + +/*****************************************************************************/ +/* Helper functions */ +/*****************************************************************************/ + + + +static void ParseError (InputData* D, unsigned Type, const char* Msg, ...) +/* Call the user supplied parse error function */ +{ + va_list ap; + int MsgSize; + cc65_parseerror* E; + + /* Test-format the error message so we know how much space to allocate */ + va_start (ap, Msg); + MsgSize = vsnprintf (0, 0, Msg, ap); + va_end (ap); + + /* Allocate memory */ + E = xmalloc (sizeof (*E) + MsgSize); + + /* Write data to E */ + E->type = Type; + E->name = D->FileName; + E->line = D->SLine; + E->column = D->SCol; + va_start (ap, Msg); + vsnprintf (E->errormsg, MsgSize+1, Msg, ap); + va_end (ap); + + /* Call the caller:-) */ + D->Error (E); + + /* Free the data structure */ + xfree (E); + + /* Count errors */ + if (Type == CC65_ERROR) { + ++D->Errors; + } +} + + + +static void SkipLine (InputData* D) +/* Error recovery routine. Skip tokens until EOL or EOF is reached */ +{ + while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) { + NextToken (D); + } +} + + + +static void UnexpectedToken (InputData* D) +/* Call ParseError with a message about an unexpected input token */ +{ + ParseError (D, CC65_ERROR, "Unexpected input token %d", D->Tok); + SkipLine (D); +} + + + +static void MissingAttribute (InputData* D, const char* AttrName) +/* Print an error about a missing attribute */ +{ + ParseError (D, CC65_ERROR, "Attribute \"%s\" is mising", AttrName); +} + + + +/*****************************************************************************/ +/* Scanner and parser */ +/*****************************************************************************/ + + + +static int DigitVal (int C) +/* Return the value for a numeric digit. Return -1 if C is invalid */ +{ + if (isdigit (C)) { + return C - '0'; + } else if (isxdigit (C)) { + return toupper (C) - 'A' + 10; + } else { + return -1; + } +} + + + +static void NextChar (InputData* D) +/* Read the next character from the input. Count lines and columns */ +{ + /* Check if we've encountered EOF before */ + if (D->C >= 0) { + D->C = fgetc (D->F); + if (D->C == '\n') { + ++D->Line; + D->Col = 0; + } else { + ++D->Col; + } + } +} + + + +static void NextToken (InputData* D) +/* Read the next token from the input stream */ +{ + static const struct KeywordEntry { + const char Keyword[10]; + Token Tok; + } KeywordTable[] = { + { "absolute", TOK_ABSOLUTE }, + { "addrsize", TOK_ADDRSIZE }, + { "equate", TOK_EQUATE }, + { "file", TOK_FILE }, + { "label", TOK_LABEL }, + { "line", TOK_LINE }, + { "mtime", TOK_MTIME }, + { "range", TOK_RANGE }, + { "ro", TOK_RO }, + { "rw", TOK_RW }, + { "segment", TOK_SEGMENT }, + { "size", TOK_SIZE }, + { "start", TOK_START }, + { "sym", TOK_SYM }, + { "type", TOK_TYPE }, + { "value", TOK_VALUE }, + { "version", TOK_VERSION }, + { "zeropage", TOK_ZEROPAGE }, + }; + + + /* Skip whitespace */ + while (D->C == ' ' || D->C == '\t') { + NextChar (D); + } + + /* Remember the current position as start of the next token */ + D->SLine = D->Line; + D->SCol = D->Col; + + /* Identifier? */ + if (D->C == '_' || isalpha (D->C)) { + + const struct KeywordEntry* Entry; + + /* Read the identifier */ + SB_Clear (&D->SVal); + while (D->C == '_' || isalnum (D->C)) { + SB_AppendChar (&D->SVal, D->C); + NextChar (D); + } + SB_Terminate (&D->SVal); + + /* Search the identifier in the keyword table */ + Entry = bsearch (SB_GetConstBuf (&D->SVal), + KeywordTable, + sizeof (KeywordTable) / sizeof (KeywordTable[0]), + sizeof (KeywordTable[0]), + (int (*)(const void*, const void*)) strcmp); + if (Entry == 0) { + ParseError (D, CC65_ERROR, "Unknown keyword `%s'", SB_GetConstBuf (&D->SVal)); + D->Tok = TOK_INVALID; + } else { + D->Tok = Entry->Tok; + } + return; + } + + /* Number? */ + if (isdigit (D->C)) { + int Base = 10; + int Val; + if (D->C == '0') { + NextChar (D); + if (toupper (D->C) == 'X') { + NextChar (D); + Base = 16; + } else { + Base = 8; + } + } else { + Base = 10; + } + D->IVal = 0; + while ((Val = DigitVal (D->C)) >= 0 && Val < Base) { + D->IVal = D->IVal * Base + Val; + NextChar (D); + } + D->Tok = TOK_INTCON; + return; + } + + /* Other characters */ + switch (D->C) { + + case '-': + NextChar (D); + D->Tok = TOK_MINUS; + break; + + case '+': + NextChar (D); + D->Tok = TOK_PLUS; + break; + + case ',': + NextChar (D); + D->Tok = TOK_COMMA; + break; + + case '=': + NextChar (D); + D->Tok = TOK_EQUAL; + break; + + case '\"': + SB_Clear (&D->SVal); + NextChar (D); + while (1) { + if (D->C == '\n' || D->C == EOF) { + ParseError (D, CC65_ERROR, "Unterminated string constant"); + break; + } + if (D->C == '\"') { + NextChar (D); + break; + } + SB_AppendChar (&D->SVal, D->C); + NextChar (D); + } + SB_Terminate (&D->SVal); + D->Tok = TOK_STRCON; + break; + + case '\n': + NextChar (D); + D->Tok = TOK_EOL; + break; + + case EOF: + D->Tok = TOK_EOF; + break; + + default: + ParseError (D, CC65_ERROR, "Invalid input character `%c'", D->C); + + } +} + + + +static int TokenFollows (InputData* D, Token Tok, const char* Name) +/* Check for a comma */ +{ + if (D->Tok != Tok) { + ParseError (D, CC65_ERROR, "%s expected", Name); + SkipLine (D); + return 0; + } else { + return 1; + } +} + + + +static int IntConstFollows (InputData* D) +/* Check for an integer constant */ +{ + return TokenFollows (D, TOK_INTCON, "Integer constant"); +} + + + +static int StringConstFollows (InputData* D) +/* Check for a string literal */ +{ + return TokenFollows (D, TOK_STRCON, "String literal"); +} + + + +static int Consume (InputData* D, Token Tok, const char* Name) +/* Check for a token and consume it. Return true if the token was comsumed, + * return false otherwise. + */ +{ + if (TokenFollows (D, Tok, Name)) { + NextToken (D); + return 1; + } else { + return 0; + } +} + + + +static int ConsumeComma (InputData* D) +/* Consume a comma */ +{ + return Consume (D, TOK_COMMA, "','"); +} + + + +static int ConsumeEqual (InputData* D) +/* Consume an equal sign */ +{ + return Consume (D, TOK_EQUAL, "'='"); +} + + + +static int ConsumeMinus (InputData* D) +/* Consume a minus sign */ +{ + return Consume (D, TOK_MINUS, "'-'"); +} + + + +static void ParseFile (InputData* D) +/* Parse a FILE line */ +{ + FileInfo* F; + enum { None = 0x00, Size = 0x01, MTime = 0x02 } InfoBits = None; + + /* Skip the FILE token */ + NextToken (D); + + /* Name follows */ + if (!StringConstFollows (D)) { + return; + } + + /* Allocate a new file info */ + F = NewFileInfo (&D->SVal); + + /* Skip the file name */ + NextToken (D); + + /* More stuff follows */ + while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) { + + /* Comma follows before next attribute */ + if (!ConsumeComma (D)) { + goto ErrorExit; + } + + switch (D->Tok) { + + case TOK_SIZE: + NextToken (D); + if (!ConsumeEqual (D)) { + goto ErrorExit; + } + if (!IntConstFollows (D)) { + goto ErrorExit; + } + F->Size = D->IVal; + NextToken (D); + InfoBits |= Size; + break; + + case TOK_MTIME: + NextToken (D); + if (!ConsumeEqual (D)) { + goto ErrorExit; + } + if (!IntConstFollows (D)) { + goto ErrorExit; + } + F->MTime = D->IVal; + NextToken (D); + InfoBits |= MTime; + break; + + default: + UnexpectedToken (D); + SkipLine (D); + goto ErrorExit; + } + + } + + /* Check for required information */ + if ((InfoBits & Size) == None) { + MissingAttribute (D, "size"); + goto ErrorExit; + } + if ((InfoBits & MTime) == None) { + MissingAttribute (D, "mtime"); + goto ErrorExit; + } + + /* Remember the file info */ + CollAppend (&D->Info->FileInfo, F); + + /* Done */ + return; + +ErrorExit: + /* Entry point in case of errors */ + FreeFileInfo (F); +} + + + +static void ParseLine (InputData* D) +/* Parse a LINE line */ +{ + LineInfo* L; + enum { None = 0x00, Line = 0x01, Range = 0x02 } InfoBits = None; + + /* Skip the LINE token */ + NextToken (D); + + /* File name follows */ + if (!StringConstFollows (D)) { + return; + } + + /* Allocate a new line info */ + L = NewLineInfo (&D->SVal); + + /* Skip the file name */ + NextToken (D); + + /* More stuff follows */ + while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) { + + /* Comma follows before next attribute */ + if (!ConsumeComma (D)) { + goto ErrorExit; + } + + switch (D->Tok) { + + case TOK_LINE: + NextToken (D); + if (!ConsumeEqual (D)) { + goto ErrorExit; + } + if (!IntConstFollows (D)) { + goto ErrorExit; + } + L->Line = D->IVal; + NextToken (D); + InfoBits |= Line; + break; + + case TOK_RANGE: + NextToken (D); + if (!ConsumeEqual (D)) { + goto ErrorExit; + } + if (!IntConstFollows (D)) { + goto ErrorExit; + } + L->Start = D->IVal; + NextToken (D); + if (!ConsumeMinus (D)) { + goto ErrorExit; + } + if (!IntConstFollows (D)) { + goto ErrorExit; + } + L->Start = D->IVal; + NextToken (D); + InfoBits |= Range; + break; + + default: + UnexpectedToken (D); + SkipLine (D); + goto ErrorExit; + } + + } + + /* Check for required information */ + if ((InfoBits & Line) == None) { + MissingAttribute (D, "line"); + goto ErrorExit; + } + if ((InfoBits & Range) == None) { + MissingAttribute (D, "range"); + goto ErrorExit; + } + + /* Remember the line info */ + CollAppend (&D->Info->LineInfo, L); + + /* Done */ + return; + +ErrorExit: + /* Entry point in case of errors */ + FreeLineInfo (L); +} + + + +static void ParseSegment (InputData* D) +/* Parse a SEGMENT line */ +{ + /* Skip the SEGMENT token */ + NextToken (D); + + /* ### */ + SkipLine (D); +} + + + +static void ParseSym (InputData* D) +/* Parse a SYM line */ +{ + /* Skip the SYM token */ + NextToken (D); + + /* ### */ + SkipLine (D); +} + + + +static void ParseVersion (InputData* D) +/* Parse a VERSION line */ +{ + /* Skip the VERSION token */ + NextToken (D); + + /* ### */ + SkipLine (D); +} + + + +/*****************************************************************************/ +/* Data processing */ +/*****************************************************************************/ + + + +static FileInfo* FindFileInfo (InputData* D, const char* FileName) +/* Find the FileInfo for a given file name */ +{ + /* Get a pointer to the file info collection */ + Collection* FileInfos = &D->Info->FileInfo; + + /* Do a binary search */ + int Lo = 0; + int Hi = (int) CollCount (FileInfos) - 1; + while (Lo <= Hi) { + + /* Mid of range */ + int Cur = (Lo + Hi) / 2; + + /* Get item */ + FileInfo* CurItem = CollAt (FileInfos, Cur); + + /* Compare */ + int Res = strcmp (CurItem->FileName, FileName); + + /* Found? */ + if (Res < 0) { + Lo = Cur + 1; + } else if (Res > 0) { + Hi = Cur - 1; + } else { + /* Found! */ + return CurItem; + } + } + + /* Not found */ + return 0; +} + + + +static void ProcessFileInfo (InputData* D) +/* Postprocess file infos */ +{ + /* Get a pointer to the file info collection */ + Collection* FileInfos = &D->Info->FileInfo; + + /* First, sort the file infos, so we can check for duplicates and do + * binary search. + */ + CollSort (FileInfos, CompareFileInfo); + + /* Cannot work on an empty collection */ + if (CollCount (FileInfos) > 0) { + + /* Walk through the file infos and check for duplicates. If we find + * some, remove all but the first, so the file infos are unique after + * that step. + */ + FileInfo* F = CollAt (FileInfos, 0); + unsigned I = 1; + while (I < CollCount (FileInfos)) { + FileInfo* Next = CollAt (FileInfos, I); + if (strcmp (F->FileName, Next->FileName) == 0) { + /* Warn only if time stamp and/or size is different */ + if (F->Size != Next->Size || F->MTime != Next->MTime) { + ParseError (D, + CC65_WARNING, + "Duplicate file entry for \"%s\"", + F->FileName); + } + /* Remove the duplicate entry */ + FreeFileInfo (Next); + CollDelete (FileInfos, I); + } else { + /* This one is ok, check the next entry */ + F = Next; + ++I; + } + } + } +} + + + +static void ProcessLineInfo (InputData* D) +/* Postprocess line infos */ +{ + /* Get a pointer to the line info collection */ + Collection* LineInfos = &D->Info->LineInfo; + + /* Walk over the line infos and replace the name by a pointer to the + * corresponding file info struct. The LineInfo structs will get shrinked + * in this process. + */ + unsigned I = 0; + while (I < CollCount (LineInfos)) { + + /* Get LineInfo struct */ + LineInfo* L = CollAt (LineInfos, I); + + /* Find FileInfo that corresponds to name */ + FileInfo* F = FindFileInfo (D, L->FileName); + + /* If we have no corresponding file info, print a warning and remove + * the line info. + */ + if (F == 0) { + ParseError (D, + CC65_ERROR, + "No file info for file \"%s\"", + L->FileName); + FreeLineInfo (L); + CollDelete (LineInfos, I); + continue; + } + + /* Shrink the line info struct effectively removing the file name + * but set the pointer to the file info now. + */ + CollReplace (LineInfos, PreenLineInfo (L, F), I); + + /* Next one */ + ++I; + } +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +cc65_dbginfo cc65_read_dbginfo (const char* filename, cc65_errorfunc errorfunc) +/* Parse the debug info file with the given name. On success, the function + * will return a pointer to an opaque cc65_dbginfo structure, that must be + * passed to the other functions in this module to retrieve information. + * errorfunc is called in case of warnings and errors. If the file cannot be + * read successfully, NULL is returned. + */ +{ + /* Data structure used to control scanning and parsing */ + InputData D = { + filename, /* Name of input file */ + 1, /* Line number */ + 0, /* Input file */ + 0, /* Line at start of current token */ + 0, /* Column at start of current token */ + 0, /* Number of errors */ + 0, /* Input file */ + ' ', /* Input character */ + TOK_INVALID, /* Input token */ + 0, /* Integer constant */ + STRBUF_INITIALIZER, /* String constant */ + errorfunc, /* Function called in case of errors */ + 0, /* Pointer to debug info */ + }; + + /* Open the input file */ + D.F = fopen (D.FileName, "r"); + if (D.F == 0) { + /* Cannot open */ + ParseError (&D, CC65_ERROR, + "Cannot open input file \"%s\": %s", + D.FileName, strerror (errno)); + return 0; + } + + /* Create a new debug info struct */ + D.Info = NewDbgInfo (); + + /* Prime the pump */ + NextToken (&D); + + /* Parse lines */ + while (D.Tok != TOK_EOF) { + + switch (D.Tok) { + + case TOK_FILE: + ParseFile (&D); + break; + + case TOK_LINE: + ParseLine (&D); + break; + + case TOK_SEGMENT: + ParseSegment (&D); + break; + + case TOK_SYM: + ParseSym (&D); + break; + + case TOK_VERSION: + ParseVersion (&D); + break; + + default: + UnexpectedToken (&D); + + } + + /* EOL or EOF must follow */ + if (D.Tok != TOK_EOF) { + if (D.Tok != TOK_EOL) { + ParseError (&D, 1, "Extra tokens in line"); + SkipLine (&D); + } + NextToken (&D); + } + } + + /* Close the file */ + fclose (D.F); + + /* Free memory allocated for SVal */ + SB_Done (&D.SVal); + + /* In case of errors, delete the debug info already allocated and + * return NULL + */ + if (D.Errors > 0) { + /* Free the allocated debug info */ + FreeDbgInfo (D.Info); + return 0; + } + + /* We do now have all the information from the file. Do postprocessing. */ + ProcessFileInfo (&D); + ProcessLineInfo (&D); + + /* Return the debug info struct that was created */ + return D.Info; +} + + + +cc65_lineinfo* cc65_get_lineinfo (cc65_dbginfo handle, unsigned long addr) +/* Return line information for the given address */ +{ + /* The passed handle is actually a pointer to a DbgInfo struct */ + DbgInfo* Info = handle; + assert (Info != 0); + + +} + + + +void cc65_free_lineinfo (cc65_dbginfo handle, cc65_lineinfo* info) +/* Free line info returned by cc65_get_lineinfo() */ +{ + /* The passed handle is actually a pointer to a DbgInfo struct */ + DbgInfo* Info = handle; + assert (Info != 0); + +} + + + + + + + + + + diff --git a/src/dbginfo/dbginfo.h b/src/dbginfo/dbginfo.h new file mode 100644 index 000000000..1f7f60349 --- /dev/null +++ b/src/dbginfo/dbginfo.h @@ -0,0 +1,113 @@ +/*****************************************************************************/ +/* */ +/* dbginfo.h */ +/* */ +/* cc65 debug info handling */ +/* */ +/* */ +/* */ +/* (C) 2010, 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. */ +/* */ +/*****************************************************************************/ + + + +#ifndef DBGINFO_H +#define DBGINFO_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Pointer to an opaque data structure containing information from the debug + * info file. Actually a handle to the data in the file. + */ +typedef void* cc65_dbginfo; + +/* Warnings/errors in cc65_read_dbginfo are passed via this struct */ +typedef struct cc65_parseerror cc65_parseerror; +struct cc65_parseerror { + unsigned type; /* 0 = warning, 1 = error */ + const char* name; /* Name of input file */ + unsigned long line; /* Error line */ + unsigned column; /* Error column */ + char errormsg[1]; /* Error message */ +}; + +/* Function that is called in case of parse errors */ +typedef void (*cc65_errorfunc) (const struct cc65_parseerror*); + +/* File information */ +typedef struct cc65_fileinfo cc65_fileinfo; +struct cc65_fileinfo { + char* name; /* Name of file with full path */ + unsigned long size; /* Size of file */ + unsigned long mtime; /* Modification time */ +}; + +/* Line information */ +typedef struct cc65_lineinfo cc65_lineinfo; +struct cc65_lineinfo { + unsigned count; /* Count of data sets that follow */ + struct { + cc65_fileinfo* fileinfo; /* File information including name */ + unsigned long line; /* Line number */ + } data[1]; +}; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +cc65_dbginfo cc65_read_dbginfo (const char* filename, cc65_errorfunc errorfunc); +/* Parse the debug info file with the given name. On success, the function + * will return a pointer to an opaque cc65_dbginfo structure, that must be + * passed to the other functions in this module to retrieve information. + * errorfunc is called in case of warnings and errors. If the file cannot be + * read successfully, NULL is returned. + */ + +cc65_lineinfo* cc65_get_lineinfo (cc65_dbginfo handle, unsigned long addr); +/* Return line information for the given address */ + +void cc65_free_lineinfo (cc65_dbginfo handle, cc65_lineinfo* info); +/* Free line info returned by cc65_get_lineinfo() */ + + + +/* End of dbginfo.h */ + +#endif + + + + diff --git a/src/dbginfo/dbgtest.c b/src/dbginfo/dbgtest.c new file mode 100644 index 000000000..ac4e52c50 --- /dev/null +++ b/src/dbginfo/dbgtest.c @@ -0,0 +1,85 @@ +/*****************************************************************************/ +/* */ +/* dbgtest.c */ +/* */ +/* Test file for the cc65 dbginfo module */ +/* */ +/* */ +/* */ +/* (C) 2010, 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 +#include +#include "dbginfo.h" + + + +static void ErrorFunc (const struct cc65_parseerror* E) +/* Handle errors or warnings that occur while parsing a debug file */ +{ + fprintf (stderr, + "%s:%s(%lu): %s\n", + E->type? "Error" : "Warning", + E->name, + E->line, + E->errormsg); +} + + + +static void Usage (void) +/* Print usage information and exit */ +{ + printf ("Usage: dbgtest debug-file\n"); + exit (1); +} + + + +int main (int argc, char** argv) +{ + const char* Input; + cc65_dbginfo Handle; + + + /* Input file is argument */ + if (argc != 2) { + Usage (); + } + Input = argv[1]; + + + Handle = cc65_read_dbginfo (Input, ErrorFunc); + if (Handle == 0) { + fprintf (stderr, "No handle\n"); + } + return 0; +} + + + diff --git a/src/dbginfo/make/gcc.mak b/src/dbginfo/make/gcc.mak new file mode 100644 index 000000000..06618cf06 --- /dev/null +++ b/src/dbginfo/make/gcc.mak @@ -0,0 +1,58 @@ +# +# Makefile for debug info test executable +# + + + + +# ------------------------------------------------------------------------------ + +# The executable to build +EXE = dbgtest + +# +CC = gcc +CFLAGS = -O2 -g -Wall -W +EBIND = emxbind +LDFLAGS = + +# ------------------------------------------------------------------------------ +# Object files and libraries to link + +OBJS = dbginfo.o \ + dbgtest.o + + +# ------------------------------------------------------------------------------ +# Makefile targets + +# Main target - must be first +.PHONY: all +ifeq (.depend,$(wildcard .depend)) +all: $(EXE) +include .depend +else +all: depend + @$(MAKE) -f make/gcc.mak all +endif + +$(EXE): $(OBJS) + $(CC) $(OBJS) $(LDFLAGS) -o $@ + @if [ $(OS2_SHELL) ] ; then $(EBIND) $(EXE) ; fi + +clean: + $(RM) *~ core.* *.map + +zap: clean + $(RM) $(OBJS) $(EXE) .depend + +# ------------------------------------------------------------------------------ +# Make the dependencies + +.PHONY: depend dep +depend dep: $(OBJS:.o=.c) + @echo "Creating dependency information" + $(CC) -I$(COMMON) -MM $^ > .depend + + +