1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* dbgsyms.c */ |
4 | /* */ |
5 | /* Debug symbol handling for the ld65 linker */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 1998-2011, 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 "addrsize.h" |
40 | #include "attrib.h" |
41 | #include "check.h" |
42 | #include "hlldbgsym.h" |
43 | #include "symdefs.h" |
44 | #include "xmalloc.h" |
45 | |
46 | /* ld65 */ |
47 | #include "dbgsyms.h" |
48 | #include "error.h" |
49 | #include "exports.h" |
50 | #include "expr.h" |
51 | #include "fileio.h" |
52 | #include "global.h" |
53 | #include "lineinfo.h" |
54 | #include "objdata.h" |
55 | #include "spool.h" |
56 | #include "tpool.h" |
57 | |
58 | |
59 | |
60 | /*****************************************************************************/ |
61 | /* Data */ |
62 | /*****************************************************************************/ |
63 | |
64 | |
65 | |
66 | /* Definition of the debug symbol structure */ |
67 | struct DbgSym { |
68 | unsigned Id; /* Id of debug symbol */ |
69 | DbgSym* Next; /* Pool linear list link */ |
70 | ObjData* Obj; /* Object file that exports the name */ |
71 | Collection DefLines; /* Line infos for definition */ |
72 | Collection RefLines; /* Line infos for references */ |
73 | ExprNode* Expr; /* Expression (0 if not def'd) */ |
74 | unsigned Size; /* Symbol size if any */ |
75 | unsigned OwnerId; /* Id of parent/owner */ |
76 | unsigned ImportId; /* Id of import if this is one */ |
77 | unsigned Name; /* Name */ |
78 | unsigned short Type; /* Type of symbol */ |
79 | unsigned short AddrSize; /* Address size of symbol */ |
80 | }; |
81 | |
82 | /* Structure used for a high level language function or symbol */ |
83 | typedef struct HLLDbgSym HLLDbgSym; |
84 | struct HLLDbgSym { |
85 | unsigned Flags; /* See above */ |
86 | unsigned Name; /* String id of name */ |
87 | DbgSym* Sym; /* Assembler symbol */ |
88 | int Offs; /* Offset if any */ |
89 | unsigned Type; /* String id of type */ |
90 | unsigned ScopeId; /* Parent scope */ |
91 | }; |
92 | |
93 | /* We will collect all debug symbols in the following array and remove |
94 | ** duplicates before outputing them into a label file. |
95 | */ |
96 | static DbgSym* DbgSymPool[256]; |
97 | |
98 | |
99 | |
100 | /*****************************************************************************/ |
101 | /* Code */ |
102 | /*****************************************************************************/ |
103 | |
104 | |
105 | |
106 | static DbgSym* NewDbgSym (unsigned Id, unsigned Type, unsigned char AddrSize, |
107 | ObjData* O) |
108 | /* Create a new DbgSym and return it */ |
109 | { |
110 | /* Allocate memory */ |
111 | DbgSym* D = xmalloc (sizeof (DbgSym)); |
112 | |
113 | /* Initialize the fields */ |
114 | D->Id = Id; |
115 | D->Next = 0; |
116 | D->Obj = O; |
117 | D->DefLines = EmptyCollection; |
118 | D->RefLines = EmptyCollection; |
119 | D->Expr = 0; |
120 | D->Size = 0; |
121 | D->OwnerId = ~0U; |
122 | D->ImportId = ~0U; |
123 | D->Name = 0; |
124 | D->Type = Type; |
125 | D->AddrSize = AddrSize; |
126 | |
127 | /* Return the new entry */ |
128 | return D; |
129 | } |
130 | |
131 | |
132 | |
133 | static HLLDbgSym* NewHLLDbgSym (void) |
134 | /* Create a new HLLDbgSym and return it */ |
135 | { |
136 | /* Allocate memory and return it */ |
137 | return xmalloc (sizeof (HLLDbgSym)); |
138 | } |
139 | |
140 | |
141 | |
142 | static DbgSym* GetDbgSym (DbgSym* D, long Val) |
143 | /* Check if we find the same debug symbol in the table. If we find it, return |
144 | ** a pointer to the other occurrence, if we didn't find it, return NULL. |
145 | */ |
146 | { |
147 | /* Create the hash. We hash over the symbol value */ |
148 | unsigned Hash = ((Val >> 24) & 0xFF) ^ |
149 | ((Val >> 16) & 0xFF) ^ |
150 | ((Val >> 8) & 0xFF) ^ |
151 | ((Val >> 0) & 0xFF); |
152 | |
153 | /* Check for this symbol */ |
154 | DbgSym* Sym = DbgSymPool[Hash]; |
155 | while (Sym) { |
156 | /* Is this symbol identical? */ |
157 | if (Sym->Name == D->Name && EqualExpr (Sym->Expr, D->Expr)) { |
158 | /* Found */ |
159 | return Sym; |
160 | } |
161 | |
162 | /* Next symbol */ |
163 | Sym = Sym->Next; |
164 | } |
165 | |
166 | /* This is the first symbol of it's kind */ |
167 | return 0; |
168 | } |
169 | |
170 | |
171 | |
172 | static void InsertDbgSym (DbgSym* D, long Val) |
173 | /* Insert the symbol into the hashed symbol pool */ |
174 | { |
175 | /* Create the hash. We hash over the symbol value */ |
176 | unsigned Hash = ((Val >> 24) & 0xFF) ^ |
177 | ((Val >> 16) & 0xFF) ^ |
178 | ((Val >> 8) & 0xFF) ^ |
179 | ((Val >> 0) & 0xFF); |
180 | |
181 | /* Insert the symbol */ |
182 | D->Next = DbgSymPool [Hash]; |
183 | DbgSymPool [Hash] = D; |
184 | } |
185 | |
186 | |
187 | |
188 | DbgSym* ReadDbgSym (FILE* F, ObjData* O, unsigned Id) |
189 | /* Read a debug symbol from a file, insert and return it */ |
190 | { |
191 | /* Read the type and address size */ |
192 | unsigned Type = ReadVar (F); |
193 | unsigned char AddrSize = Read8 (F); |
194 | |
195 | /* Create a new debug symbol */ |
196 | DbgSym* D = NewDbgSym (Id, Type, AddrSize, O); |
197 | |
198 | /* Read the id of the owner scope/symbol */ |
199 | D->OwnerId = ReadVar (F); |
200 | |
201 | /* Read and assign the name */ |
202 | D->Name = MakeGlobalStringId (O, ReadVar (F)); |
203 | |
204 | /* Read the value */ |
205 | if (SYM_IS_EXPR (D->Type)) { |
206 | D->Expr = ReadExpr (F, O); |
207 | } else { |
208 | D->Expr = LiteralExpr (Read32 (F), O); |
209 | } |
210 | |
211 | /* Read the size */ |
212 | if (SYM_HAS_SIZE (D->Type)) { |
213 | D->Size = ReadVar (F); |
214 | } |
215 | |
216 | /* If this is an import, the file contains its id */ |
217 | if (SYM_IS_IMPORT (D->Type)) { |
218 | D->ImportId = ReadVar (F); |
219 | } |
220 | |
221 | /* If its an exports, there's also the export id, but we don't remember |
222 | ** it but use it to let the export point back to us. |
223 | */ |
224 | if (SYM_IS_EXPORT (D->Type)) { |
225 | /* Get the export from the export id, then set the our id */ |
226 | GetObjExport (O, ReadVar (F))->DbgSymId = Id; |
227 | } |
228 | |
229 | /* Last is the list of line infos for this symbol */ |
230 | ReadLineInfoList (F, O, &D->DefLines); |
231 | ReadLineInfoList (F, O, &D->RefLines); |
232 | |
233 | /* Return the new DbgSym */ |
234 | return D; |
235 | } |
236 | |
237 | |
238 | |
239 | HLLDbgSym* ReadHLLDbgSym (FILE* F, ObjData* O, unsigned Id attribute ((unused))) |
240 | /* Read a hll debug symbol from a file, insert and return it */ |
241 | { |
242 | unsigned SC; |
243 | |
244 | /* Create a new HLLDbgSym */ |
245 | HLLDbgSym* S = NewHLLDbgSym (); |
246 | |
247 | /* Read the data */ |
248 | S->Flags = ReadVar (F); |
249 | SC = HLL_GET_SC (S->Flags); |
250 | S->Name = MakeGlobalStringId (O, ReadVar (F)); |
251 | if (HLL_HAS_SYM (S->Flags)) { |
252 | S->Sym = GetObjDbgSym (O, ReadVar (F)); |
253 | } else { |
254 | /* Auto variables aren't attached to asm symbols */ |
255 | S->Sym = 0; |
256 | } |
257 | if (SC == HLL_SC_AUTO || SC == HLL_SC_REG) { |
258 | S->Offs = ReadVar (F); |
259 | } else { |
260 | S->Offs = 0; |
261 | } |
262 | S->Type = GetTypeId (GetObjString (O, ReadVar (F))); |
263 | S->ScopeId = ReadVar (F); |
264 | |
265 | /* Return the (now initialized) hll debug symbol */ |
266 | return S; |
267 | } |
268 | |
269 | |
270 | |
271 | static void ClearDbgSymTable (void) |
272 | /* Clear the debug symbol table */ |
273 | { |
274 | unsigned I; |
275 | for (I = 0; I < sizeof (DbgSymPool) / sizeof (DbgSymPool[0]); ++I) { |
276 | DbgSym* Sym = DbgSymPool[I]; |
277 | DbgSymPool[I] = 0; |
278 | while (Sym) { |
279 | DbgSym* NextSym = Sym->Next; |
280 | Sym->Next = 0; |
281 | Sym = NextSym; |
282 | } |
283 | } |
284 | } |
285 | |
286 | |
287 | |
288 | static long GetDbgSymVal (const DbgSym* D) |
289 | /* Get the value of this symbol */ |
290 | { |
291 | CHECK (D->Expr != 0); |
292 | return GetExprVal (D->Expr); |
293 | } |
294 | |
295 | |
296 | |
297 | static void PrintLineInfo (FILE* F, const Collection* LineInfos, const char* Format) |
298 | /* Output an attribute with line infos */ |
299 | { |
300 | if (CollCount (LineInfos) > 0) { |
301 | unsigned I; |
302 | const LineInfo* LI = CollConstAt (LineInfos, 0); |
303 | fprintf (F, Format, LI->Id); |
304 | for (I = 1; I < CollCount (LineInfos); ++I) { |
305 | LI = CollConstAt (LineInfos, I); |
306 | fprintf (F, "+%u" , LI->Id); |
307 | } |
308 | } |
309 | } |
310 | |
311 | |
312 | |
313 | unsigned DbgSymCount (void) |
314 | /* Return the total number of debug symbols */ |
315 | { |
316 | /* Walk over all object files */ |
317 | unsigned I; |
318 | unsigned Count = 0; |
319 | for (I = 0; I < CollCount (&ObjDataList); ++I) { |
320 | |
321 | /* Get this object file */ |
322 | const ObjData* O = CollAtUnchecked (&ObjDataList, I); |
323 | |
324 | /* Count debug symbols */ |
325 | Count += CollCount (&O->DbgSyms); |
326 | } |
327 | return Count; |
328 | } |
329 | |
330 | |
331 | |
332 | unsigned HLLDbgSymCount (void) |
333 | /* Return the total number of high level language debug symbols */ |
334 | { |
335 | /* Walk over all object files */ |
336 | unsigned I; |
337 | unsigned Count = 0; |
338 | for (I = 0; I < CollCount (&ObjDataList); ++I) { |
339 | |
340 | /* Get this object file */ |
341 | const ObjData* O = CollAtUnchecked (&ObjDataList, I); |
342 | |
343 | /* Count debug symbols */ |
344 | Count += CollCount (&O->HLLDbgSyms); |
345 | } |
346 | return Count; |
347 | } |
348 | |
349 | |
350 | |
351 | void PrintDbgSyms (FILE* F) |
352 | /* Print the debug symbols in a debug file */ |
353 | { |
354 | unsigned I, J; |
355 | |
356 | for (I = 0; I < CollCount (&ObjDataList); ++I) { |
357 | |
358 | /* Get the object file */ |
359 | ObjData* O = CollAtUnchecked (&ObjDataList, I); |
360 | |
361 | /* Walk through all debug symbols in this module */ |
362 | for (J = 0; J < CollCount (&O->DbgSyms); ++J) { |
363 | |
364 | /* Get the next debug symbol */ |
365 | const DbgSym* S = CollConstAt (&O->DbgSyms, J); |
366 | |
367 | /* Emit the base data for the entry */ |
368 | fprintf (F, |
369 | "sym\tid=%u,name=\"%s\",addrsize=%s" , |
370 | O->SymBaseId + J, |
371 | GetString (S->Name), |
372 | AddrSizeToStr ((unsigned char) S->AddrSize)); |
373 | |
374 | /* Emit the size only if we know it */ |
375 | if (S->Size != 0) { |
376 | fprintf (F, ",size=%u" , S->Size); |
377 | } |
378 | |
379 | /* For cheap local symbols, add the owner symbol, for others, |
380 | ** add the owner scope. |
381 | */ |
382 | if (SYM_IS_STD (S->Type)) { |
383 | fprintf (F, ",scope=%u" , O->ScopeBaseId + S->OwnerId); |
384 | } else { |
385 | fprintf (F, ",parent=%u" , O->SymBaseId + S->OwnerId); |
386 | } |
387 | |
388 | /* Output line infos */ |
389 | PrintLineInfo (F, &S->DefLines, ",def=%u" ); |
390 | PrintLineInfo (F, &S->RefLines, ",ref=%u" ); |
391 | |
392 | /* If this is an import, output the id of the matching export. |
393 | ** If this is not an import, output its value and - if we have |
394 | ** it - the segment. |
395 | */ |
396 | if (SYM_IS_IMPORT (S->Type)) { |
397 | |
398 | /* Get the import */ |
399 | const Import* Imp = GetObjImport (O, S->ImportId); |
400 | |
401 | /* Get the export from the import */ |
402 | const Export* Exp = Imp->Exp; |
403 | |
404 | /* Output the type */ |
405 | fputs (",type=imp" , F); |
406 | |
407 | /* If this is not a linker generated symbol, and the module |
408 | ** that contains the export has debug info, output the debug |
409 | ** symbol id for the export |
410 | */ |
411 | if (Exp->Obj && OBJ_HAS_DBGINFO (Exp->Obj->Header.Flags)) { |
412 | fprintf (F, ",exp=%u" , Exp->Obj->SymBaseId + Exp->DbgSymId); |
413 | } |
414 | |
415 | } else { |
416 | |
417 | SegExprDesc D; |
418 | |
419 | /* Get the symbol value */ |
420 | long Val = GetDbgSymVal (S); |
421 | |
422 | /* Output it */ |
423 | fprintf (F, ",val=0x%lX" , Val); |
424 | |
425 | /* Check for a segmented expression and add the segment id to |
426 | ** the debug info if we have one. |
427 | */ |
428 | GetSegExprVal (S->Expr, &D); |
429 | if (!D.TooComplex && D.Seg != 0) { |
430 | fprintf (F, ",seg=%u" , D.Seg->Id); |
431 | } |
432 | |
433 | /* Output the type */ |
434 | fprintf (F, ",type=%s" , SYM_IS_LABEL (S->Type)? "lab" : "equ" ); |
435 | } |
436 | |
437 | /* Terminate the output line */ |
438 | fputc ('\n', F); |
439 | } |
440 | } |
441 | } |
442 | |
443 | |
444 | |
445 | void PrintHLLDbgSyms (FILE* F) |
446 | /* Print the high level language debug symbols in a debug file */ |
447 | { |
448 | unsigned I, J; |
449 | |
450 | for (I = 0; I < CollCount (&ObjDataList); ++I) { |
451 | |
452 | /* Get the object file */ |
453 | ObjData* O = CollAtUnchecked (&ObjDataList, I); |
454 | |
455 | /* Walk through all hll debug symbols in this module */ |
456 | for (J = 0; J < CollCount (&O->HLLDbgSyms); ++J) { |
457 | |
458 | /* Get the next debug symbol */ |
459 | const HLLDbgSym* S = CollConstAt (&O->HLLDbgSyms, J); |
460 | |
461 | /* Get the storage class */ |
462 | unsigned SC = HLL_GET_SC (S->Flags); |
463 | |
464 | /* Output the base info */ |
465 | fprintf (F, "csym\tid=%u,name=\"%s\",scope=%u,type=%u,sc=" , |
466 | O->HLLSymBaseId + J, |
467 | GetString (S->Name), |
468 | O->ScopeBaseId + S->ScopeId, |
469 | S->Type); |
470 | switch (SC) { |
471 | case HLL_SC_AUTO: fputs ("auto" , F); break; |
472 | case HLL_SC_REG: fputs ("reg" , F); break; |
473 | case HLL_SC_STATIC: fputs ("static" , F); break; |
474 | case HLL_SC_EXTERN: fputs ("ext" , F); break; |
475 | default: |
476 | Error ("Invalid storage class %u for hll symbol" , SC); |
477 | break; |
478 | } |
479 | |
480 | /* Output the offset if it is not zero */ |
481 | if (S->Offs) { |
482 | fprintf (F, ",offs=%d" , S->Offs); |
483 | } |
484 | |
485 | /* For non auto symbols output the debug symbol id of the asm sym */ |
486 | if (HLL_HAS_SYM (S->Flags)) { |
487 | fprintf (F, ",sym=%u" , O->SymBaseId + S->Sym->Id); |
488 | } |
489 | |
490 | /* Terminate the output line */ |
491 | fputc ('\n', F); |
492 | } |
493 | } |
494 | } |
495 | |
496 | |
497 | |
498 | void PrintDbgSymLabels (FILE* F) |
499 | /* Print the debug symbols in a VICE label file */ |
500 | { |
501 | unsigned I, J; |
502 | |
503 | /* Clear the symbol table */ |
504 | ClearDbgSymTable (); |
505 | |
506 | /* Create labels from all modules we have linked into the output file */ |
507 | for (I = 0; I < CollCount (&ObjDataList); ++I) { |
508 | |
509 | /* Get the object file */ |
510 | ObjData* O = CollAtUnchecked (&ObjDataList, I); |
511 | |
512 | /* Walk through all debug symbols in this module */ |
513 | for (J = 0; J < CollCount (&O->DbgSyms); ++J) { |
514 | |
515 | long Val; |
516 | |
517 | /* Get the next debug symbol */ |
518 | DbgSym* D = CollAt (&O->DbgSyms, J); |
519 | |
520 | /* Emit this symbol only if it is a label (ignore equates and imports) */ |
521 | if (SYM_IS_EQUATE (D->Type) || SYM_IS_IMPORT (D->Type)) { |
522 | continue; |
523 | } |
524 | |
525 | /* Get the symbol value */ |
526 | Val = GetDbgSymVal (D); |
527 | |
528 | /* Lookup this symbol in the table. If it is found in the table, it was |
529 | ** already written to the file, so don't emit it twice. If it is not in |
530 | ** the table, insert and output it. |
531 | */ |
532 | if (GetDbgSym (D, Val) == 0) { |
533 | |
534 | /* Emit the VICE label line */ |
535 | fprintf (F, "al %06lX .%s\n" , Val, GetString (D->Name)); |
536 | |
537 | /* Insert the symbol into the table */ |
538 | InsertDbgSym (D, Val); |
539 | } |
540 | } |
541 | } |
542 | } |
543 | |