1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* dbginfo.c */ |
4 | /* */ |
5 | /* Handle the .dbg commands */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 2000-2012, Ullrich von Bassewitz */ |
10 | /* Roemerstrasse 52 */ |
11 | /* D-70794 Filderstadt */ |
12 | /* EMail: uz@cc65.org */ |
13 | /* */ |
14 | /* */ |
15 | /* This software is provided 'as-is', without any expressed or implied */ |
16 | /* warranty. In no event will the authors be held liable for any damages */ |
17 | /* arising from the use of this software. */ |
18 | /* */ |
19 | /* Permission is granted to anyone to use this software for any purpose, */ |
20 | /* including commercial applications, and to alter it and redistribute it */ |
21 | /* freely, subject to the following restrictions: */ |
22 | /* */ |
23 | /* 1. The origin of this software must not be misrepresented; you must not */ |
24 | /* claim that you wrote the original software. If you use this software */ |
25 | /* in a product, an acknowledgment in the product documentation would be */ |
26 | /* appreciated but is not required. */ |
27 | /* 2. Altered source versions must be plainly marked as such, and must not */ |
28 | /* be misrepresented as being the original software. */ |
29 | /* 3. This notice may not be removed or altered from any source */ |
30 | /* distribution. */ |
31 | /* */ |
32 | /*****************************************************************************/ |
33 | |
34 | |
35 | |
36 | #include <string.h> |
37 | |
38 | /* common */ |
39 | #include "chartype.h" |
40 | #include "coll.h" |
41 | #include "filepos.h" |
42 | #include "hlldbgsym.h" |
43 | #include "scopedefs.h" |
44 | #include "strbuf.h" |
45 | |
46 | /* ca65 */ |
47 | #include "dbginfo.h" |
48 | #include "error.h" |
49 | #include "expr.h" |
50 | #include "filetab.h" |
51 | #include "global.h" |
52 | #include "lineinfo.h" |
53 | #include "objfile.h" |
54 | #include "nexttok.h" |
55 | #include "symentry.h" |
56 | #include "symtab.h" |
57 | |
58 | |
59 | |
60 | /*****************************************************************************/ |
61 | /* Data */ |
62 | /*****************************************************************************/ |
63 | |
64 | |
65 | |
66 | /* Structure used for a high level language function or symbol */ |
67 | typedef struct HLLDbgSym HLLDbgSym; |
68 | struct HLLDbgSym { |
69 | unsigned Flags; /* See above */ |
70 | unsigned Name; /* String id of name */ |
71 | unsigned AsmName; /* String id of asm symbol name */ |
72 | SymEntry* Sym; /* The assembler symbol */ |
73 | int Offs; /* Offset if any */ |
74 | unsigned Type; /* String id of type */ |
75 | SymTable* Scope; /* Parent scope */ |
76 | unsigned FuncId; /* Id of hll function if any */ |
77 | FilePos Pos; /* Position of statement */ |
78 | }; |
79 | |
80 | /* The current line info */ |
81 | static LineInfo* CurLineInfo = 0; |
82 | |
83 | /* List of high level language debug symbols */ |
84 | static Collection HLLDbgSyms = STATIC_COLLECTION_INITIALIZER; |
85 | |
86 | |
87 | |
88 | /*****************************************************************************/ |
89 | /* Code */ |
90 | /*****************************************************************************/ |
91 | |
92 | |
93 | |
94 | static HLLDbgSym* NewHLLDbgSym (unsigned Flags, unsigned Name, unsigned Type) |
95 | /* Allocate and return a new HLLDbgSym structure */ |
96 | { |
97 | /* Allocate memory */ |
98 | HLLDbgSym* S = xmalloc (sizeof (*S)); |
99 | |
100 | /* Initialize the fields as necessary */ |
101 | S->Flags = Flags; |
102 | S->Name = Name; |
103 | S->AsmName = EMPTY_STRING_ID; |
104 | S->Sym = 0; |
105 | S->Offs = 0; |
106 | S->Type = Type; |
107 | S->Scope = CurrentScope; |
108 | S->FuncId = ~0U; |
109 | S->Pos = CurTok.Pos; |
110 | |
111 | /* Return the result */ |
112 | return S; |
113 | } |
114 | |
115 | |
116 | |
117 | static unsigned HexValue (char C) |
118 | /* Convert the ascii representation of a hex nibble into the hex nibble */ |
119 | { |
120 | if (isdigit (C)) { |
121 | return C - '0'; |
122 | } else if (islower (C)) { |
123 | return C - 'a' + 10; |
124 | } else { |
125 | return C - 'A' + 10; |
126 | } |
127 | } |
128 | |
129 | |
130 | |
131 | static int ValidateType (StrBuf* Type) |
132 | /* Check if the given type is valid and if so, return a string id for it. If |
133 | ** the type isn't valid, return -1. Type is overwritten when checking. |
134 | */ |
135 | { |
136 | unsigned I; |
137 | const char* A; |
138 | char* B; |
139 | |
140 | |
141 | /* The length must not be zero and divideable by two */ |
142 | unsigned Length = SB_GetLen (Type); |
143 | if (Length < 2 || (Length & 0x01) != 0) { |
144 | ErrorSkip ("Type value has invalid length" ); |
145 | return -1; |
146 | } |
147 | |
148 | /* The string must consist completely of hex digit chars */ |
149 | A = SB_GetConstBuf (Type); |
150 | for (I = 0; I < Length; ++I) { |
151 | if (!IsXDigit (A[I])) { |
152 | ErrorSkip ("Type value contains invalid characters" ); |
153 | return -1; |
154 | } |
155 | } |
156 | |
157 | /* Convert the type to binary */ |
158 | B = SB_GetBuf (Type); |
159 | while (A < SB_GetConstBuf (Type) + Length) { |
160 | /* Since we know, there are only hex digits, there can't be any errors */ |
161 | *B++ = (HexValue (A[0]) << 4) | HexValue (A[1]); |
162 | A += 2; |
163 | } |
164 | Type->Len = (Length /= 2); |
165 | |
166 | /* Allocate the type and return it */ |
167 | return GetStrBufId (Type); |
168 | } |
169 | |
170 | |
171 | |
172 | void DbgInfoFile (void) |
173 | /* Parse and handle FILE subcommand of the .dbg pseudo instruction */ |
174 | { |
175 | StrBuf Name = STATIC_STRBUF_INITIALIZER; |
176 | unsigned long Size; |
177 | unsigned long MTime; |
178 | |
179 | /* Parameters are separated by a comma */ |
180 | ConsumeComma (); |
181 | |
182 | /* Name */ |
183 | if (CurTok.Tok != TOK_STRCON) { |
184 | ErrorSkip ("String constant expected" ); |
185 | return; |
186 | } |
187 | SB_Copy (&Name, &CurTok.SVal); |
188 | NextTok (); |
189 | |
190 | /* Comma expected */ |
191 | ConsumeComma (); |
192 | |
193 | /* Size */ |
194 | Size = ConstExpression (); |
195 | |
196 | /* Comma expected */ |
197 | ConsumeComma (); |
198 | |
199 | /* MTime */ |
200 | MTime = ConstExpression (); |
201 | |
202 | /* Insert the file into the table */ |
203 | AddFile (&Name, FT_DBGINFO, Size, MTime); |
204 | |
205 | /* Free memory used for Name */ |
206 | SB_Done (&Name); |
207 | } |
208 | |
209 | |
210 | |
211 | void DbgInfoFunc (void) |
212 | /* Parse and handle func subcommand of the .dbg pseudo instruction */ |
213 | { |
214 | static const char* const StorageKeys[] = { |
215 | "EXTERN" , |
216 | "STATIC" , |
217 | }; |
218 | |
219 | unsigned Name; |
220 | int Type; |
221 | unsigned AsmName; |
222 | unsigned Flags; |
223 | HLLDbgSym* S; |
224 | |
225 | |
226 | /* Parameters are separated by a comma */ |
227 | ConsumeComma (); |
228 | |
229 | /* Name */ |
230 | if (CurTok.Tok != TOK_STRCON) { |
231 | ErrorSkip ("String constant expected" ); |
232 | return; |
233 | } |
234 | Name = GetStrBufId (&CurTok.SVal); |
235 | NextTok (); |
236 | |
237 | /* Comma expected */ |
238 | ConsumeComma (); |
239 | |
240 | /* Type */ |
241 | if (CurTok.Tok != TOK_STRCON) { |
242 | ErrorSkip ("String constant expected" ); |
243 | return; |
244 | } |
245 | Type = ValidateType (&CurTok.SVal); |
246 | if (Type < 0) { |
247 | return; |
248 | } |
249 | NextTok (); |
250 | |
251 | /* Comma expected */ |
252 | ConsumeComma (); |
253 | |
254 | /* The storage class follows */ |
255 | if (CurTok.Tok != TOK_IDENT) { |
256 | ErrorSkip ("Storage class specifier expected" ); |
257 | return; |
258 | } |
259 | switch (GetSubKey (StorageKeys, sizeof (StorageKeys)/sizeof (StorageKeys[0]))) { |
260 | case 0: Flags = HLL_TYPE_FUNC | HLL_SC_EXTERN; break; |
261 | case 1: Flags = HLL_TYPE_FUNC | HLL_SC_STATIC; break; |
262 | default: ErrorSkip ("Storage class specifier expected" ); return; |
263 | } |
264 | NextTok (); |
265 | |
266 | /* Comma expected */ |
267 | ConsumeComma (); |
268 | |
269 | /* Assembler name follows */ |
270 | if (CurTok.Tok != TOK_STRCON) { |
271 | ErrorSkip ("String constant expected" ); |
272 | return; |
273 | } |
274 | AsmName = GetStrBufId (&CurTok.SVal); |
275 | NextTok (); |
276 | |
277 | /* There may only be one function per scope */ |
278 | if (CurrentScope == RootScope) { |
279 | ErrorSkip ("Functions may not be used in the root scope" ); |
280 | return; |
281 | } else if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label == 0) { |
282 | ErrorSkip ("Functions can only be tagged to .PROC scopes" ); |
283 | return; |
284 | } else if (CurrentScope->Label->HLLSym != 0) { |
285 | ErrorSkip ("Only one HLL symbol per asm symbol is allowed" ); |
286 | return; |
287 | } else if (CurrentScope->Label->Name != AsmName) { |
288 | ErrorSkip ("Scope label and asm name for function must match" ); |
289 | return; |
290 | } |
291 | |
292 | /* Add the function */ |
293 | S = NewHLLDbgSym (Flags, Name, Type); |
294 | S->Sym = CurrentScope->Label; |
295 | CurrentScope->Label->HLLSym = S; |
296 | CollAppend (&HLLDbgSyms, S); |
297 | } |
298 | |
299 | |
300 | |
301 | void DbgInfoLine (void) |
302 | /* Parse and handle LINE subcommand of the .dbg pseudo instruction */ |
303 | { |
304 | long Line; |
305 | FilePos Pos = STATIC_FILEPOS_INITIALIZER; |
306 | |
307 | /* Any new line info terminates the last one */ |
308 | if (CurLineInfo) { |
309 | EndLine (CurLineInfo); |
310 | CurLineInfo = 0; |
311 | } |
312 | |
313 | /* If a parameters follow, this is actual line info. If no parameters |
314 | ** follow, the last line info is terminated. |
315 | */ |
316 | if (CurTok.Tok == TOK_SEP) { |
317 | return; |
318 | } |
319 | |
320 | /* Parameters are separated by a comma */ |
321 | ConsumeComma (); |
322 | |
323 | /* The name of the file follows */ |
324 | if (CurTok.Tok != TOK_STRCON) { |
325 | ErrorSkip ("String constant expected" ); |
326 | return; |
327 | } |
328 | |
329 | /* Get the index in the file table for the name */ |
330 | Pos.Name = GetFileIndex (&CurTok.SVal); |
331 | |
332 | /* Skip the name */ |
333 | NextTok (); |
334 | |
335 | /* Comma expected */ |
336 | ConsumeComma (); |
337 | |
338 | /* Line number */ |
339 | Line = ConstExpression (); |
340 | if (Line < 0) { |
341 | ErrorSkip ("Line number is out of valid range" ); |
342 | return; |
343 | } |
344 | Pos.Line = Line; |
345 | |
346 | /* Generate a new external line info */ |
347 | CurLineInfo = StartLine (&Pos, LI_TYPE_EXT, 0); |
348 | } |
349 | |
350 | |
351 | |
352 | void DbgInfoSym (void) |
353 | /* Parse and handle SYM subcommand of the .dbg pseudo instruction */ |
354 | { |
355 | static const char* const StorageKeys[] = { |
356 | "AUTO" , |
357 | "EXTERN" , |
358 | "REGISTER" , |
359 | "STATIC" , |
360 | }; |
361 | |
362 | unsigned Name; |
363 | int Type; |
364 | unsigned AsmName = EMPTY_STRING_ID; |
365 | unsigned Flags; |
366 | int Offs = 0; |
367 | HLLDbgSym* S; |
368 | |
369 | |
370 | /* Parameters are separated by a comma */ |
371 | ConsumeComma (); |
372 | |
373 | /* Name */ |
374 | if (CurTok.Tok != TOK_STRCON) { |
375 | ErrorSkip ("String constant expected" ); |
376 | return; |
377 | } |
378 | Name = GetStrBufId (&CurTok.SVal); |
379 | NextTok (); |
380 | |
381 | /* Comma expected */ |
382 | ConsumeComma (); |
383 | |
384 | /* Type */ |
385 | if (CurTok.Tok != TOK_STRCON) { |
386 | ErrorSkip ("String constant expected" ); |
387 | return; |
388 | } |
389 | Type = ValidateType (&CurTok.SVal); |
390 | if (Type < 0) { |
391 | return; |
392 | } |
393 | NextTok (); |
394 | |
395 | /* Comma expected */ |
396 | ConsumeComma (); |
397 | |
398 | /* The storage class follows */ |
399 | if (CurTok.Tok != TOK_IDENT) { |
400 | ErrorSkip ("Storage class specifier expected" ); |
401 | return; |
402 | } |
403 | switch (GetSubKey (StorageKeys, sizeof (StorageKeys)/sizeof (StorageKeys[0]))) { |
404 | case 0: Flags = HLL_SC_AUTO; break; |
405 | case 1: Flags = HLL_SC_EXTERN; break; |
406 | case 2: Flags = HLL_SC_REG; break; |
407 | case 3: Flags = HLL_SC_STATIC; break; |
408 | default: ErrorSkip ("Storage class specifier expected" ); return; |
409 | } |
410 | |
411 | /* Skip the storage class token and the following comma */ |
412 | NextTok (); |
413 | ConsumeComma (); |
414 | |
415 | /* The next tokens depend on the storage class */ |
416 | if (Flags == HLL_SC_AUTO) { |
417 | /* Auto: Stack offset follows */ |
418 | Offs = ConstExpression (); |
419 | } else { |
420 | /* Register, extern or static: Assembler name follows */ |
421 | if (CurTok.Tok != TOK_STRCON) { |
422 | ErrorSkip ("String constant expected" ); |
423 | return; |
424 | } |
425 | AsmName = GetStrBufId (&CurTok.SVal); |
426 | NextTok (); |
427 | |
428 | /* For register, an offset follows */ |
429 | if (Flags == HLL_SC_REG) { |
430 | ConsumeComma (); |
431 | Offs = ConstExpression (); |
432 | } |
433 | } |
434 | |
435 | /* Add the function */ |
436 | S = NewHLLDbgSym (Flags | HLL_TYPE_SYM, Name, Type); |
437 | S->AsmName = AsmName; |
438 | S->Offs = Offs; |
439 | CollAppend (&HLLDbgSyms, S); |
440 | } |
441 | |
442 | |
443 | |
444 | void DbgInfoCheck (void) |
445 | /* Do checks on all hll debug info symbols when assembly is complete */ |
446 | { |
447 | /* When parsing the debug statements for HLL symbols, we have already |
448 | ** tagged the functions to their asm counterparts. This wasn't done for |
449 | ** C symbols, since we will allow forward declarations. So we have to |
450 | ** resolve the normal C symbols now. |
451 | */ |
452 | unsigned I; |
453 | for (I = 0; I < CollCount (&HLLDbgSyms); ++I) { |
454 | |
455 | /* Get the next symbol */ |
456 | HLLDbgSym* S = CollAtUnchecked (&HLLDbgSyms, I); |
457 | |
458 | /* Ignore functions and auto symbols, because the later live on the |
459 | ** stack and don't have corresponding asm symbols. |
460 | */ |
461 | if (HLL_IS_FUNC (S->Flags) || HLL_GET_SC (S->Flags) == HLL_SC_AUTO) { |
462 | continue; |
463 | } |
464 | |
465 | /* Safety */ |
466 | CHECK (S->Sym == 0 && S->Scope != 0); |
467 | |
468 | /* Search for the symbol name */ |
469 | S->Sym = SymFindAny (S->Scope, GetStrBuf (S->AsmName)); |
470 | if (S->Sym == 0) { |
471 | PError (&S->Pos, "Assembler symbol '%s' not found" , |
472 | GetString (S->AsmName)); |
473 | } else { |
474 | /* Set the backlink */ |
475 | S->Sym->HLLSym = S; |
476 | } |
477 | |
478 | } |
479 | } |
480 | |
481 | |
482 | |
483 | void WriteHLLDbgSyms (void) |
484 | /* Write a list of all high level language symbols to the object file. */ |
485 | { |
486 | unsigned I; |
487 | |
488 | /* Only if debug info is enabled */ |
489 | if (DbgSyms) { |
490 | |
491 | /* Write the symbol count to the list */ |
492 | ObjWriteVar (CollCount (&HLLDbgSyms)); |
493 | |
494 | /* Walk through list and write all symbols to the file. */ |
495 | for (I = 0; I < CollCount (&HLLDbgSyms); ++I) { |
496 | |
497 | /* Get the next symbol */ |
498 | HLLDbgSym* S = CollAtUnchecked (&HLLDbgSyms, I); |
499 | |
500 | /* Get the type of the symbol */ |
501 | unsigned SC = HLL_GET_SC (S->Flags); |
502 | |
503 | /* Remember if the symbol has debug info attached |
504 | ** ### This should go into DbgInfoCheck |
505 | */ |
506 | if (S->Sym && S->Sym->DebugSymId != ~0U) { |
507 | S->Flags |= HLL_DATA_SYM; |
508 | } |
509 | |
510 | /* Write the symbol data */ |
511 | ObjWriteVar (S->Flags); |
512 | ObjWriteVar (S->Name); |
513 | if (HLL_HAS_SYM (S->Flags)) { |
514 | ObjWriteVar (S->Sym->DebugSymId); |
515 | } |
516 | if (SC == HLL_SC_AUTO || SC == HLL_SC_REG) { |
517 | ObjWriteVar (S->Offs); |
518 | } |
519 | ObjWriteVar (S->Type); |
520 | ObjWriteVar (S->Scope->Id); |
521 | } |
522 | |
523 | } else { |
524 | |
525 | /* Write a count of zero */ |
526 | ObjWriteVar (0); |
527 | |
528 | } |
529 | } |
530 | |