diff --git a/src/dbginfo/dbginfo.c b/src/dbginfo/dbginfo.c index cb46daad8..7b35224b0 100644 --- a/src/dbginfo/dbginfo.c +++ b/src/dbginfo/dbginfo.c @@ -52,6 +52,10 @@ +/* Version numbers of the debug format we understand */ +#define VER_MAJOR 1U +#define VER_MINOR 0U + /* Dynamic strings */ typedef struct StrBuf StrBuf; struct StrBuf { @@ -114,6 +118,8 @@ typedef enum { TOK_MINOR, /* MINOR keyword */ TOK_MTIME, /* MTIME keyword */ TOK_NAME, /* NAME keyword */ + TOK_OUTPUTNAME, /* OUTPUTNAME keyword */ + TOK_OUTPUTOFFS, /* OUTPUTOFFS keyword */ TOK_RANGE, /* RANGE keyword */ TOK_RO, /* RO keyword */ TOK_RW, /* RW keyword */ @@ -156,6 +162,8 @@ struct SegInfo { unsigned Id; /* Id of segment */ cc65_addr Start; /* Start address of segment */ cc65_addr Size; /* Size of segment */ + char* OutputName; /* Name of output file */ + unsigned long OutputOffs; /* Offset in output file */ char SegName[1]; /* Name of segment */ }; @@ -405,6 +413,26 @@ static void SB_AppendChar (StrBuf* B, int C) +static char* SB_StrDup (const StrBuf* B) +/* Return the contents of B as a dynamically allocated string. The string + * will always be NUL terminated. + */ +{ + /* Allocate memory */ + char* S = xmalloc (B->Len + 1); + + /* Copy the string */ + memcpy (S, B->Buf, B->Len); + + /* Terminate it */ + S[B->Len] = '\0'; + + /* And return the result */ + return S; +} + + + /*****************************************************************************/ /* Collections */ /*****************************************************************************/ @@ -614,16 +642,26 @@ void CollSort (Collection* C, int (*Compare) (const void*, const void*)) static SegInfo* NewSegInfo (const StrBuf* SegName, unsigned Id, - cc65_addr Start, cc65_addr Size) + cc65_addr Start, cc65_addr Size, + const StrBuf* OutputName, unsigned long OutputOffs) /* Create a new SegInfo struct and return it */ { /* Allocate memory */ SegInfo* S = xmalloc (sizeof (SegInfo) + SB_GetLen (SegName)); /* Initialize it */ - S->Id = Id; - S->Start = Start; - S->Size = Size; + S->Id = Id; + S->Start = Start; + S->Size = Size; + if (SB_GetLen (OutputName) > 0) { + /* Output file given */ + S->OutputName = SB_StrDup (OutputName); + S->OutputOffs = OutputOffs; + } else { + /* No output file given */ + S->OutputName = 0; + S->OutputOffs = 0; + } memcpy (S->SegName, SB_GetConstBuf (SegName), SB_GetLen (SegName) + 1); /* Return it */ @@ -853,6 +891,28 @@ static void FreeDbgInfo (DbgInfo* Info) +static void CopyLineInfo (cc65_linedata* D, const LineInfo* L) +/* Copy data from a LineInfo struct to the cc65_linedata struct returned to + * the caller. + */ +{ + D->source_name = L->File.Info->FileName; + D->source_size = L->File.Info->Size; + D->source_mtime = L->File.Info->MTime; + D->source_line = L->Line; + D->line_start = L->Start; + D->line_end = L->End; + if (L->Seg.Info->OutputName) { + D->output_name = L->Seg.Info->OutputName; + D->output_offs = L->Seg.Info->OutputOffs + L->Start - L->Seg.Info->Start; + } else { + D->output_name = 0; + D->output_offs = 0; + } +} + + + static void ParseError (InputData* D, cc65_error_severity Type, const char* Msg, ...) /* Call the user supplied parse error function */ { @@ -910,14 +970,6 @@ static void UnexpectedToken (InputData* 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); -} - - - static void UnknownKeyword (InputData* D) /* Print a warning about an unknown keyword in the file. Try to do smart * recovery, so if later versions of the debug information add additional @@ -988,7 +1040,7 @@ static void NextToken (InputData* D) /* Read the next token from the input stream */ { static const struct KeywordEntry { - const char Keyword[10]; + const char Keyword[16]; Token Tok; } KeywordTable[] = { { "absolute", TOK_ABSOLUTE }, @@ -1003,6 +1055,8 @@ static void NextToken (InputData* D) { "minor", TOK_MINOR }, { "mtime", TOK_MTIME }, { "name", TOK_NAME }, + { "outputname", TOK_OUTPUTNAME }, + { "outputoffs", TOK_OUTPUTOFFS }, { "range", TOK_RANGE }, { "ro", TOK_RO }, { "rw", TOK_RW }, @@ -1197,6 +1251,20 @@ static int ConsumeMinus (InputData* D) +static void ConsumeEOL (InputData* D) +/* Consume an end-of-line token, if we aren't at end-of-file */ +{ + if (D->Tok != TOK_EOF) { + if (D->Tok != TOK_EOL) { + ParseError (D, CC65_ERROR, "Extra tokens in line"); + SkipLine (D); + } + NextToken (D); + } +} + + + static void ParseFile (InputData* D) /* Parse a FILE line */ { @@ -1304,7 +1372,7 @@ static void ParseFile (InputData* D) } /* Check for required information */ - if (InfoBits != ibRequired) { + if ((InfoBits & ibRequired) != ibRequired) { ParseError (D, CC65_ERROR, "Required attributes missing"); goto ErrorExit; } @@ -1436,7 +1504,7 @@ static void ParseLine (InputData* D) } /* Check for required information */ - if (InfoBits != ibRequired) { + if ((InfoBits & ibRequired) != ibRequired) { ParseError (D, CC65_ERROR, "Required attributes missing"); goto ErrorExit; } @@ -1455,11 +1523,13 @@ ErrorExit: static void ParseSegment (InputData* D) /* Parse a SEGMENT line */ { - unsigned Id; - cc65_addr Start; - cc65_addr Size; - StrBuf SegName = STRBUF_INITIALIZER; - SegInfo* S; + unsigned Id; + cc65_addr Start; + cc65_addr Size; + StrBuf SegName = STRBUF_INITIALIZER; + StrBuf OutputName = STRBUF_INITIALIZER; + unsigned long OutputOffs; + SegInfo* S; enum { ibNone = 0x00, ibId = 0x01, @@ -1468,6 +1538,8 @@ static void ParseSegment (InputData* D) ibSize = 0x08, ibAddrSize = 0x10, ibType = 0x20, + ibOutputName= 0x40, + ibOutputOffs= 0x80, ibRequired = ibId | ibSegName | ibStart | ibSize | ibAddrSize | ibType, } InfoBits = ibNone; @@ -1486,9 +1558,10 @@ static void ParseSegment (InputData* D) } /* Something we know? */ - if (D->Tok != TOK_ID && D->Tok != TOK_NAME && - D->Tok != TOK_START && D->Tok != TOK_SIZE && - D->Tok != TOK_ADDRSIZE && D->Tok != TOK_TYPE) { + if (D->Tok != TOK_ADDRSIZE && D->Tok != TOK_ID && + D->Tok != TOK_NAME && D->Tok != TOK_OUTPUTNAME && + D->Tok != TOK_OUTPUTOFFS && D->Tok != TOK_SIZE && + D->Tok != TOK_START && D->Tok != TOK_TYPE) { /* Done */ break; } @@ -1522,6 +1595,25 @@ static void ParseSegment (InputData* D) NextToken (D); break; + case TOK_OUTPUTNAME: + if (!StrConstFollows (D)) { + goto ErrorExit; + } + SB_Copy (&OutputName, &D->SVal); + SB_Terminate (&OutputName); + InfoBits |= ibOutputName; + NextToken (D); + break; + + case TOK_OUTPUTOFFS: + if (!IntConstFollows (D)) { + goto ErrorExit; + } + OutputOffs = D->IVal; + NextToken (D); + InfoBits |= ibOutputOffs; + break; + case TOK_START: if (!IntConstFollows (D)) { goto ErrorExit; @@ -1572,18 +1664,19 @@ static void ParseSegment (InputData* D) } /* Check for required information */ - if (InfoBits != ibRequired) { + if ((InfoBits & ibRequired) != ibRequired) { ParseError (D, CC65_ERROR, "Required attributes missing"); goto ErrorExit; } /* Create the segment info and remember it */ - S = NewSegInfo (&SegName, Id, Start, Size); + S = NewSegInfo (&SegName, Id, Start, Size, &OutputName, OutputOffs); CollAppend (&D->Info->SegInfoByName, S); ErrorExit: /* Entry point in case of errors */ SB_Done (&SegName); + SB_Done (&OutputName); return; } @@ -1604,7 +1697,12 @@ static void ParseSym (InputData* D) static void ParseVersion (InputData* D) /* Parse a VERSION line */ { - enum { None = 0x00, Major = 0x01, Minor = 0x02 } InfoBits = None; + enum { + ibNone = 0x00, + ibMajor = 0x01, + ibMinor = 0x02, + ibRequired = ibMajor | ibMinor, + } InfoBits = ibNone; /* Skip the VERSION token */ NextToken (D); @@ -1624,7 +1722,7 @@ static void ParseVersion (InputData* D) } D->MajorVersion = D->IVal; NextToken (D); - InfoBits |= Major; + InfoBits |= ibMajor; break; case TOK_MINOR: @@ -1637,7 +1735,7 @@ static void ParseVersion (InputData* D) } D->MinorVersion = D->IVal; NextToken (D); - InfoBits |= Minor; + InfoBits |= ibMinor; break; case TOK_IDENT: @@ -1665,12 +1763,8 @@ static void ParseVersion (InputData* D) } /* Check for required information */ - if ((InfoBits & Major) == None) { - MissingAttribute (D, "major"); - goto ErrorExit; - } - if ((InfoBits & Minor) == None) { - MissingAttribute (D, "minor"); + if ((InfoBits & ibRequired) != ibRequired) { + ParseError (D, CC65_ERROR, "Required attributes missing"); goto ErrorExit; } @@ -2110,54 +2204,63 @@ cc65_dbginfo cc65_read_dbginfo (const char* FileName, cc65_errorfunc ErrFunc) /* 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; - - case TOK_IDENT: - /* Output a warning, then skip the line with the unknown - * keyword that may have been added by a later version. - */ - ParseError (&D, CC65_WARNING, - "Unknown keyword \"%s\" - skipping", - SB_GetConstBuf (&D.SVal)); - - SkipLine (&D); - break; - - default: - UnexpectedToken (&D); + /* The first line in the file must specify version information */ + if (D.Tok != TOK_VERSION) { + ParseError (&D, CC65_ERROR, + "\"version\" keyword missing in first line - this is not " + "a valid debug info file"); + } else { + /* Parse the version directive and check the version */ + ParseVersion (&D); + if (D.MajorVersion > VER_MAJOR) { + ParseError (&D, CC65_WARNING, + "The format of this debug info file is newer than what we " + "know. Will proceed but probably fail. Version found = %u, " + "version supported = %u", + D.MajorVersion, VER_MAJOR); } + ConsumeEOL (&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_IDENT: + /* Output a warning, then skip the line with the unknown + * keyword that may have been added by a later version. + */ + ParseError (&D, CC65_WARNING, + "Unknown keyword \"%s\" - skipping", + SB_GetConstBuf (&D.SVal)); + + SkipLine (&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); + + /* EOL or EOF must follow */ + ConsumeEOL (&D); } } @@ -2240,17 +2343,8 @@ cc65_lineinfo* cc65_lineinfo_byaddr (cc65_dbginfo Handle, unsigned long Addr) (CollCount (&LineInfos) - 1) * sizeof (D->data[0])); D->count = CollCount (&LineInfos); for (I = 0; I < D->count; ++I) { - - /* Pointer to this info */ - LineInfo* L = CollAt (&LineInfos, I); - /* Copy data */ - D->data[I].name = L->File.Info->FileName; - D->data[I].size = L->File.Info->Size; - D->data[I].mtime = L->File.Info->MTime; - D->data[I].line = L->Line; - D->data[I].start = L->Start; - D->data[I].end = L->End; + CopyLineInfo (D->data + I, CollAt (&LineInfos, I)); } } @@ -2299,12 +2393,7 @@ cc65_lineinfo* cc65_lineinfo_byname (cc65_dbginfo Handle, const char* FileName, D->count = 1; /* Copy data */ - D->data[0].name = L->File.Info->FileName; - D->data[0].size = L->File.Info->Size; - D->data[0].mtime = L->File.Info->MTime; - D->data[0].line = L->Line; - D->data[0].start = L->Start; - D->data[0].end = L->End; + CopyLineInfo (D->data, L); /* Return the allocated struct */ return D; @@ -2324,13 +2413,13 @@ void cc65_free_lineinfo (cc65_dbginfo Handle, cc65_lineinfo* Info) -cc65_filelist* cc65_get_filelist (cc65_dbginfo Handle) -/* Return a list of all files referenced in the debug information */ +cc65_sourceinfo* cc65_get_sourcelist (cc65_dbginfo Handle) +/* Return a list of all source files */ { - DbgInfo* Info; - Collection* FileInfoByName; - cc65_filelist* D; - unsigned I; + DbgInfo* Info; + Collection* FileInfoByName; + cc65_sourceinfo* D; + unsigned I; /* Check the parameter */ assert (Handle != 0); @@ -2353,9 +2442,9 @@ cc65_filelist* cc65_get_filelist (cc65_dbginfo Handle) FileInfo* F = CollAt (FileInfoByName, I); /* Copy the data */ - D->data[I].name = F->FileName; - D->data[I].size = F->Size; - D->data[I].mtime = F->MTime; + D->data[I].source_name = F->FileName; + D->data[I].source_size = F->Size; + D->data[I].source_mtime = F->MTime; } /* Return the result */ @@ -2364,24 +2453,24 @@ cc65_filelist* cc65_get_filelist (cc65_dbginfo Handle) -void cc65_free_filelist (cc65_dbginfo Handle, cc65_filelist* List) -/* Free a file list returned by cc65_get_filelist() */ +void cc65_free_sourceinfo (cc65_dbginfo Handle, cc65_sourceinfo* Info) +/* Free a source info record */ { /* Just for completeness, check the handle */ assert (Handle != 0); - /* Just free the memory */ - xfree (List); + /* Free the memory */ + xfree (Info); } -cc65_segmentlist* cc65_get_segmentlist (cc65_dbginfo Handle) +cc65_segmentinfo* cc65_get_segmentlist (cc65_dbginfo Handle) /* Return a list of all segments referenced in the debug information */ { DbgInfo* Info; Collection* SegInfoByName; - cc65_segmentlist* D; + cc65_segmentinfo* D; unsigned I; /* Check the parameter */ @@ -2405,9 +2494,11 @@ cc65_segmentlist* cc65_get_segmentlist (cc65_dbginfo Handle) SegInfo* S = CollAt (SegInfoByName, I); /* Copy the data */ - D->data[I].name = S->SegName; - D->data[I].start = S->Start; - D->data[I].end = S->Start + S->Size - 1; + D->data[I].segment_name = S->SegName; + D->data[I].segment_start = S->Start; + D->data[I].segment_size = S->Size; + D->data[I].output_name = S->OutputName; + D->data[I].output_offs = S->OutputOffs; } /* Return the result */ @@ -2416,14 +2507,14 @@ cc65_segmentlist* cc65_get_segmentlist (cc65_dbginfo Handle) -void cc65_free_segmentlist (cc65_dbginfo Handle, cc65_segmentlist* List) -/* Free a file list returned by cc65_get_filelist() */ +void cc65_free_segmentinfo (cc65_dbginfo Handle, cc65_segmentinfo* Info) +/* Free a segment info record */ { /* Just for completeness, check the handle */ assert (Handle != 0); - /* Just free the memory */ - xfree (List); + /* Free the memory */ + xfree (Info); } diff --git a/src/dbginfo/dbginfo.h b/src/dbginfo/dbginfo.h index 11c5ad084..8d155f5fa 100644 --- a/src/dbginfo/dbginfo.h +++ b/src/dbginfo/dbginfo.h @@ -74,42 +74,66 @@ struct cc65_parseerror { /* Function that is called in case of parse errors */ typedef void (*cc65_errorfunc) (const struct cc65_parseerror*); -/* Line information */ +/* Line information. + * Notes: + * - line_end is inclusive + * - output_name may be NULL if the data wasn't written to the output file + * (example: bss segment) + * - output_offs is invalid if there is no output_name, and may not be of + * much use in case of a relocatable output file + */ +typedef struct cc65_linedata cc65_linedata; +struct cc65_linedata { + const char* source_name; /* Name of the file */ + unsigned long source_size; /* Size of file */ + unsigned long source_mtime; /* Modification time */ + cc65_line source_line; /* Line number */ + cc65_addr line_start; /* Start address for this line */ + cc65_addr line_end; /* End address for this line */ + const char* output_name; /* Output file */ + unsigned long output_offs; /* Offset in output file */ +}; + typedef struct cc65_lineinfo cc65_lineinfo; struct cc65_lineinfo { unsigned count; /* Number of data sets that follow */ - struct { - const char* name; /* Name of the file */ - unsigned long size; /* Size of file */ - unsigned long mtime; /* Modification time */ - cc65_line line; /* Line number */ - cc65_addr start; /* Start address for this line */ - cc65_addr end; /* End address for this line */ - } data[1]; + cc65_linedata data[1]; /* Data sets, number is dynamic */ }; -/* A list of files with some information */ -typedef struct cc65_filelist cc65_filelist; -struct cc65_filelist { - unsigned count; /* Number of data sets that follow */ - struct { - const char* name; /* Name of the file */ - unsigned long size; /* Size of file */ - unsigned long mtime; /* Modification time */ - } data[1]; +/* Source file information */ +typedef struct cc65_sourcedata cc65_sourcedata; +struct cc65_sourcedata { + const char* source_name; /* Name of the file */ + unsigned long source_size; /* Size of file */ + unsigned long source_mtime; /* Modification time */ }; - - -/* A list of segments with some information */ -typedef struct cc65_segmentlist cc65_segmentlist; -struct cc65_segmentlist { +typedef struct cc65_sourceinfo cc65_sourceinfo; +struct cc65_sourceinfo { unsigned count; /* Number of data sets that follow */ - struct { - const char* name; /* Name of the file */ - cc65_addr start; /* Start address of segment */ - cc65_addr end; /* End address of segment */ - } data[1]; + cc65_sourcedata data[1]; /* Data sets, number is dynamic */ +}; + +/* Segment information. + * Notes: + * - output_name may be NULL if the data wasn't written to the output file + * (example: bss segment) + * - output_offs is invalid if there is no output_name, and may not be of + * much use in case of a relocatable output file + */ +typedef struct cc65_segmentdata cc65_segmentdata; +struct cc65_segmentdata { + const char* segment_name; /* Name of the segment */ + cc65_addr segment_start; /* Start address of segment */ + cc65_addr segment_size; /* Size of segment */ + const char* output_name; /* Output file this seg was written to */ + unsigned long output_offs; /* Offset of this seg in output file */ +}; + +typedef struct cc65_segmentinfo cc65_segmentinfo; +struct cc65_segmentinfo { + unsigned count; /* Number of data sets that follow */ + cc65_segmentdata data[1]; /* Data sets, number is dynamic */ }; @@ -145,17 +169,17 @@ cc65_lineinfo* cc65_lineinfo_byname (cc65_dbginfo handle, const char* filename, void cc65_free_lineinfo (cc65_dbginfo handle, cc65_lineinfo* info); /* Free line info returned by one of the other functions */ -cc65_filelist* cc65_get_filelist (cc65_dbginfo handle); -/* Return a list of all files referenced in the debug information */ +cc65_sourceinfo* cc65_get_sourcelist (cc65_dbginfo handle); +/* Return a list of all source files */ -void cc65_free_filelist (cc65_dbginfo handle, cc65_filelist* list); -/* free a file list returned by cc65_get_filelist() */ +void cc65_free_sourceinfo (cc65_dbginfo handle, cc65_sourceinfo* info); +/* Free a source info record */ -cc65_segmentlist* cc65_get_segmentlist (cc65_dbginfo handle); +cc65_segmentinfo* cc65_get_segmentlist (cc65_dbginfo handle); /* Return a list of all segments referenced in the debug information */ -void cc65_free_segmentlist (cc65_dbginfo handle, cc65_segmentlist* list); -/* Free a file list returned by cc65_get_filelist() */ +void cc65_free_segmentinfo (cc65_dbginfo handle, cc65_segmentinfo* info); +/* Free a segment info record */ diff --git a/src/dbginfo/dbgtest.c b/src/dbginfo/dbgtest.c index 6d118b8c8..f83a749a5 100644 --- a/src/dbginfo/dbgtest.c +++ b/src/dbginfo/dbgtest.c @@ -65,8 +65,8 @@ int main (int argc, char** argv) { const char* Input; cc65_dbginfo Info; - cc65_filelist* Files; - cc65_segmentlist* Segments; + cc65_sourceinfo* Sources; + cc65_segmentinfo* Segments; cc65_lineinfo* L; unsigned I; unsigned long Addr; @@ -87,23 +87,29 @@ int main (int argc, char** argv) printf ("Input file \"%s\" successfully read\n", Input); /* Output a list of files */ - printf ("Files used in compilation:\n"); - Files = cc65_get_filelist (Info); - for (I = 0; I < Files->count; ++I) { - printf (" %s\n", Files->data[I].name); + printf ("List of source files:\n"); + Sources = cc65_get_sourcelist (Info); + for (I = 0; I < Sources->count; ++I) { + printf (" %s\n", Sources->data[I].source_name); } - cc65_free_filelist (Info, Files); + cc65_free_sourceinfo (Info, Sources); /* Output a list of segments */ printf ("Segments processed when linking:\n"); Segments = cc65_get_segmentlist (Info); for (I = 0; I < Segments->count; ++I) { - printf (" %-20s $%06lX-$%06lX\n", - Segments->data[I].name, - (unsigned long) Segments->data[I].start, - (unsigned long) Segments->data[I].end); + printf (" %-20s $%06lX $%04lX", + Segments->data[I].segment_name, + (unsigned long) Segments->data[I].segment_start, + (unsigned long) Segments->data[I].segment_size); + if (Segments->data[I].output_name) { + printf (" %-20s $%06lX", + Segments->data[I].output_name, + Segments->data[I].output_offs); + } + putchar ('\n'); } - cc65_free_segmentlist (Info, Segments); + cc65_free_segmentinfo (Info, Segments); /* Check one line */ printf ("Requesting line info for crt0.s(59):\n"); @@ -111,12 +117,12 @@ int main (int argc, char** argv) if (L == 0) { printf (" Not found\n"); } else { - printf (" Code range is $%04X-$%04X\n", L->data[0].start, L->data[0].end); + printf (" Code range is $%04X-$%04X\n", + L->data[0].line_start, + L->data[0].line_end); cc65_free_lineinfo (Info, L); } - - /* Output debug information for all addresses in the complete 6502 address * space. This is also sort of a benchmark for the search algorithms. */ @@ -130,8 +136,15 @@ int main (int argc, char** argv) if (I > 0) { printf (", "); } - printf ("%s(%lu)", L->data[I].name, - (unsigned long) L->data[I].line); + printf ("%s(%lu)", + L->data[I].source_name, + (unsigned long) L->data[I].source_line); + if (L->data[I].output_name) { + printf (" %s($%06lX)", + L->data[I].output_name, + L->data[I].output_offs); + + } } printf ("\n"); cc65_free_lineinfo (Info, L);