1 /*****************************************************************************/
5 /* Dump subroutines for the od65 object file dump utility */
9 /* (C) 2002-2011, Ullrich von Bassewitz */
10 /* Roemerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
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. */
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: */
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 */
32 /*****************************************************************************/
48 #include "scopedefs.h"
60 /*****************************************************************************/
62 /*****************************************************************************/
66 static void DestroyStrPool (Collection* C)
67 /* Free all strings in the given pool plus the item pointers. Note: The
68 * collection may not be reused later.
72 for (I = 0; I < CollCount (C); ++I) {
73 xfree (CollAtUnchecked (C, I));
80 static const char* GetString (const Collection* C, unsigned Index)
81 /* Get a string from a collection. In fact, this function calls CollConstAt,
82 * but will print a somewhat more readable error message if the index is out
86 if (Index >= CollCount (C)) {
87 Error ("Invalid string index (%u) - file corrupt!", Index);
89 return CollConstAt (C, Index);
94 static void DumpObjHeaderSection (const char* Name,
97 /* Dump a header section */
99 printf (" %s:\n", Name);
100 printf (" Offset:%24lu\n", Offset);
101 printf (" Size: %24lu\n", Size);
106 static char* TimeToStr (unsigned long Time)
107 /* Convert the time into a string and return it */
109 /* Get the time and convert to string */
110 time_t T = (time_t) Time;
111 char* S = asctime (localtime (&T));
113 /* Remove the trailing newline */
114 unsigned Len = strlen (S);
115 if (Len > 0 && S[Len-1] == '\n') {
119 /* Return the time string */
125 static void SkipLineInfoList (FILE* F)
126 /* Skip a line info list from the given file */
128 /* Count preceeds the list */
129 unsigned long Count = ReadVar (F);
139 static void SkipExpr (FILE* F)
140 /* Skip an expression from the given file */
142 /* Read the node tag and handle NULL nodes */
143 unsigned char Op = Read8 (F);
144 if (Op == EXPR_NULL) {
148 /* Check the tag and handle the different expression types */
149 if (EXPR_IS_LEAF (Op)) {
153 (void) Read32Signed (F);
157 /* Read the import number */
162 /* Read the segment number */
167 Error ("Invalid expression op: %02X", Op);
173 /* Not a leaf node */
181 static const char* GetExportFlags (unsigned Flags, const unsigned char* ConDes)
182 /* Get the export flags as a (static) string */
185 static char TypeDesc[256];
193 switch (Flags & SYM_MASK_TYPE) {
194 case SYM_STD: strcat (TypeDesc, "SYM_STD"); break;
195 case SYM_CHEAP_LOCAL: strcat (TypeDesc, "SYM_CHEAP_LOCAL"); break;
199 switch (Flags & SYM_MASK_LABEL) {
200 case SYM_EQUATE: strcat (TypeDesc, ",SYM_EQUATE"); break;
201 case SYM_LABEL: strcat (TypeDesc, ",SYM_LABEL"); break;
204 /* Type of expression */
205 switch (Flags & SYM_MASK_VAL) {
206 case SYM_CONST: strcat (TypeDesc, ",SYM_CONST"); break;
207 case SYM_EXPR: strcat (TypeDesc, ",SYM_EXPR"); break;
210 /* Size available? */
211 if (SYM_HAS_SIZE (Flags)) {
212 strcat (TypeDesc, ",SYM_SIZE");
216 /* Constructor/destructor declarations */
217 T = TypeDesc + strlen (TypeDesc);
218 Count = SYM_GET_CONDES_COUNT (Flags);
219 if (Count > 0 && ConDes) {
220 T += sprintf (T, ",SYM_CONDES=");
221 for (I = 0; I < Count; ++I) {
222 unsigned Type = CD_GET_TYPE (ConDes[I]);
223 unsigned Prio = CD_GET_PRIO (ConDes[I]);
227 T += sprintf (T, "[%u,%u]", Type, Prio);
231 /* Return the result */
237 static const char* GetScopeType (unsigned Type)
238 /* Return the name of a scope type */
241 case SCOPE_GLOBAL: return "Global scope";
242 case SCOPE_FILE: return "File scope";
243 case SCOPE_SCOPE: return ".SCOPE or .PROC";
244 case SCOPE_STRUCT: return ".STRUCT";
245 case SCOPE_ENUM: return ".ENUM";
246 case SCOPE_UNDEF: return "Undefined";
247 default: return "Unknown scope type";
253 void DumpObjHeader (FILE* F, unsigned long Offset)
254 /* Dump the header of the given object file */
258 /* Seek to the header position */
259 FileSetPos (F, Offset);
261 /* Read the header */
262 ReadObjHeader (F, &H);
264 /* Now dump the information */
266 /* Output a header */
267 printf (" Header:\n");
270 printf (" Magic:%17s0x%08lX\n", "", H.Magic);
273 printf (" Version:%25u\n", H.Version);
276 printf (" Flags:%21s0x%04X (", "", H.Flags);
277 if (H.Flags & OBJ_FLAGS_DBGINFO) {
278 printf ("OBJ_FLAGS_DBGINFO");
283 DumpObjHeaderSection ("Options", H.OptionOffs, H.OptionSize);
286 DumpObjHeaderSection ("Files", H.FileOffs, H.FileSize);
289 DumpObjHeaderSection ("Segments", H.SegOffs, H.SegSize);
292 DumpObjHeaderSection ("Imports", H.ImportOffs, H.ImportSize);
295 DumpObjHeaderSection ("Exports", H.ExportOffs, H.ExportSize);
298 DumpObjHeaderSection ("Debug symbols", H.DbgSymOffs, H.DbgSymSize);
301 DumpObjHeaderSection ("Line infos", H.LineInfoOffs, H.LineInfoSize);
304 DumpObjHeaderSection ("String pool", H.StrPoolOffs, H.StrPoolSize);
307 DumpObjHeaderSection ("Assertions", H.AssertOffs, H.AssertSize);
310 DumpObjHeaderSection ("Scopes", H.ScopeOffs, H.ScopeSize);
315 void DumpObjOptions (FILE* F, unsigned long Offset)
316 /* Dump the file options */
319 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
323 /* Seek to the header position and read the header */
324 FileSetPos (F, Offset);
325 ReadObjHeader (F, &H);
327 /* Seek to the start of the string pool and read it */
328 FileSetPos (F, Offset + H.StrPoolOffs);
329 ReadStrPool (F, &StrPool);
331 /* Seek to the start of the options */
332 FileSetPos (F, Offset + H.OptionOffs);
334 /* Output a header */
335 printf (" Options:\n");
337 /* Read the number of options and print it */
339 printf (" Count:%27u\n", Count);
341 /* Read and print all options */
342 for (I = 0; I < Count; ++I) {
347 /* Read the type of the option and the value */
348 unsigned char Type = Read8 (F);
349 unsigned long Val = ReadVar (F);
351 /* Get the type of the argument */
352 unsigned char ArgType = Type & OPT_ARGMASK;
354 /* Determine which option follows */
355 const char* TypeDesc;
357 case OPT_COMMENT: TypeDesc = "OPT_COMMENT"; break;
358 case OPT_AUTHOR: TypeDesc = "OPT_AUTHOR"; break;
359 case OPT_TRANSLATOR:TypeDesc = "OPT_TRANSLATOR"; break;
360 case OPT_COMPILER: TypeDesc = "OPT_COMPILER"; break;
361 case OPT_OS: TypeDesc = "OPT_OS"; break;
362 case OPT_DATETIME: TypeDesc = "OPT_DATETIME"; break;
363 default: TypeDesc = "OPT_UNKNOWN"; break;
366 /* Print the header */
367 printf (" Index:%27u\n", I);
370 printf (" Type:%22s0x%02X (%s)\n", "", Type, TypeDesc);
374 ArgStr = GetString (&StrPool, Val);
375 ArgLen = strlen (ArgStr);
376 printf (" Data:%*s\"%s\"\n", (int)(24-ArgLen), "", ArgStr);
380 printf (" Data:%26lu", Val);
381 if (Type == OPT_DATETIME) {
382 /* Print the time as a string */
383 printf (" (%s)", TimeToStr (Val));
389 /* Unknown argument type. This means that we cannot determine
390 * the option length, so we cannot proceed.
392 Error ("Unknown option type: 0x%02X", Type);
397 /* Destroy the string pool */
398 DestroyStrPool (&StrPool);
403 void DumpObjFiles (FILE* F, unsigned long Offset)
404 /* Dump the source files */
407 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
411 /* Seek to the header position and read the header */
412 FileSetPos (F, Offset);
413 ReadObjHeader (F, &H);
415 /* Seek to the start of the string pool and read it */
416 FileSetPos (F, Offset + H.StrPoolOffs);
417 ReadStrPool (F, &StrPool);
419 /* Seek to the start of the source files */
420 FileSetPos (F, Offset + H.FileOffs);
422 /* Output a header */
423 printf (" Files:\n");
425 /* Read the number of files and print it */
427 printf (" Count:%27u\n", Count);
429 /* Read and print all files */
430 for (I = 0; I < Count; ++I) {
432 /* Read the data for one file */
433 const char* Name = GetString (&StrPool, ReadVar (F));
434 unsigned long MTime = Read32 (F);
435 unsigned long Size = ReadVar (F);
436 unsigned Len = strlen (Name);
438 /* Print the header */
439 printf (" Index:%27u\n", I);
442 printf (" Name:%*s\"%s\"\n", (int)(24-Len), "", Name);
443 printf (" Size:%26lu\n", Size);
444 printf (" Modification time:%13lu (%s)\n", MTime, TimeToStr (MTime));
447 /* Destroy the string pool */
448 DestroyStrPool (&StrPool);
453 void DumpObjSegments (FILE* F, unsigned long Offset)
454 /* Dump the segments in the object file */
457 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
461 /* Seek to the header position and read the header */
462 FileSetPos (F, Offset);
463 ReadObjHeader (F, &H);
465 /* Seek to the start of the string pool and read it */
466 FileSetPos (F, Offset + H.StrPoolOffs);
467 ReadStrPool (F, &StrPool);
469 /* Seek to the start of the segments */
470 FileSetPos (F, Offset + H.SegOffs);
472 /* Output a header */
473 printf (" Segments:\n");
475 /* Read the number of segments and print it */
477 printf (" Count:%27u\n", Count);
479 /* Read and print all segments */
480 for (I = 0; I < Count; ++I) {
482 /* Read the data for one segments */
483 unsigned long DataSize = Read32 (F);
484 unsigned long NextSeg = ftell (F) + DataSize;
485 const char* Name = GetString (&StrPool, ReadVar (F));
486 unsigned Len = strlen (Name);
487 unsigned long Size = Read32 (F);
488 unsigned Align = (1U << Read8 (F));
489 unsigned char AddrSize = Read8 (F);
490 unsigned long FragCount = ReadVar (F);
492 /* Print the header */
493 printf (" Index:%27u\n", I);
496 printf (" Name:%*s\"%s\"\n", (int)(24-Len), "", Name);
497 printf (" Size:%26lu\n", Size);
498 printf (" Alignment:%21u\n", Align);
499 printf (" Address size:%14s0x%02X (%s)\n", "", AddrSize,
500 AddrSizeToStr (AddrSize));
501 printf (" Fragment count:%16lu\n", FragCount);
503 /* Seek to the end of the segment data (start of next) */
504 FileSetPos (F, NextSeg);
507 /* Destroy the string pool */
508 DestroyStrPool (&StrPool);
513 void DumpObjImports (FILE* F, unsigned long Offset)
514 /* Dump the imports in the object file */
517 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
521 /* Seek to the header position and read the header */
522 FileSetPos (F, Offset);
523 ReadObjHeader (F, &H);
525 /* Seek to the start of the string pool and read it */
526 FileSetPos (F, Offset + H.StrPoolOffs);
527 ReadStrPool (F, &StrPool);
529 /* Seek to the start of the imports */
530 FileSetPos (F, Offset + H.ImportOffs);
532 /* Output a header */
533 printf (" Imports:\n");
535 /* Read the number of imports and print it */
537 printf (" Count:%27u\n", Count);
539 /* Read and print all imports */
540 for (I = 0; I < Count; ++I) {
542 /* Read the data for one import */
543 unsigned char AddrSize = Read8 (F);
544 const char* Name = GetString (&StrPool, ReadVar (F));
545 unsigned Len = strlen (Name);
547 /* Skip the line infos */
548 SkipLineInfoList (F);
550 /* Print the header */
551 printf (" Index:%27u\n", I);
554 printf (" Address size:%14s0x%02X (%s)\n", "", AddrSize,
555 AddrSizeToStr (AddrSize));
556 printf (" Name:%*s\"%s\"\n", (int)(24-Len), "", Name);
559 /* Destroy the string pool */
560 DestroyStrPool (&StrPool);
565 void DumpObjExports (FILE* F, unsigned long Offset)
566 /* Dump the exports in the object file */
569 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
573 /* Seek to the header position and read the header */
574 FileSetPos (F, Offset);
575 ReadObjHeader (F, &H);
577 /* Seek to the start of the string pool and read it */
578 FileSetPos (F, Offset + H.StrPoolOffs);
579 ReadStrPool (F, &StrPool);
581 /* Seek to the start of the exports */
582 FileSetPos (F, Offset + H.ExportOffs);
584 /* Output a header */
585 printf (" Exports:\n");
587 /* Read the number of exports and print it */
589 printf (" Count:%27u\n", Count);
591 /* Read and print all exports */
592 for (I = 0; I < Count; ++I) {
594 unsigned long Value = 0;
595 unsigned long Size = 0;
596 unsigned char ConDes[CD_TYPE_COUNT];
601 /* Read the data for one export */
602 unsigned Type = ReadVar (F);
603 unsigned char AddrSize = Read8 (F);
604 ReadData (F, ConDes, SYM_GET_CONDES_COUNT (Type));
605 Name = GetString (&StrPool, ReadVar (F));
607 if (SYM_IS_CONST (Type)) {
612 if (SYM_HAS_SIZE (Type)) {
616 /* Skip the line infos */
617 SkipLineInfoList (F);
619 /* Print the header */
620 printf (" Index:%27u\n", I);
623 printf (" Type:%22s0x%02X (%s)\n", "", Type, GetExportFlags (Type, ConDes));
624 printf (" Address size:%14s0x%02X (%s)\n", "", AddrSize,
625 AddrSizeToStr (AddrSize));
626 printf (" Name:%*s\"%s\"\n", (int)(24-Len), "", Name);
627 if (SYM_IS_CONST (Type)) {
628 printf (" Value:%15s0x%08lX (%lu)\n", "", Value, Value);
630 if (SYM_HAS_SIZE (Type)) {
631 printf (" Size:%16s0x%04lX (%lu)\n", "", Size, Size);
635 /* Destroy the string pool */
636 DestroyStrPool (&StrPool);
641 void DumpObjDbgSyms (FILE* F, unsigned long Offset)
642 /* Dump the debug symbols from an object file */
645 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
649 /* Seek to the header position and read the header */
650 FileSetPos (F, Offset);
651 ReadObjHeader (F, &H);
653 /* Seek to the start of the string pool and read it */
654 FileSetPos (F, Offset + H.StrPoolOffs);
655 ReadStrPool (F, &StrPool);
657 /* Seek to the start of the debug syms */
658 FileSetPos (F, Offset + H.DbgSymOffs);
660 /* Output a header */
661 printf (" Debug symbols:\n");
663 /* Check if the object file was compiled with debug info */
664 if ((H.Flags & OBJ_FLAGS_DBGINFO) == 0) {
665 /* Print that there no debug symbols and bail out */
666 printf (" Count:%27u\n", 0);
670 /* Read the number of exports and print it */
672 printf (" Count:%27u\n", Count);
674 /* Read and print all debug symbols */
675 for (I = 0; I < Count; ++I) {
677 unsigned long Value = 0;
678 unsigned long Size = 0;
680 /* Read the data for one symbol */
681 unsigned Type = ReadVar (F);
682 unsigned char AddrSize = Read8 (F);
683 unsigned long Owner = ReadVar (F);
684 const char* Name = GetString (&StrPool, ReadVar (F));
685 unsigned Len = strlen (Name);
686 if (SYM_IS_CONST (Type)) {
691 if (SYM_HAS_SIZE (Type)) {
695 /* Skip the line infos */
696 SkipLineInfoList (F);
698 /* Print the header */
699 printf (" Index:%27u\n", I);
702 printf (" Type:%22s0x%02X (%s)\n", "", Type, GetExportFlags (Type, 0));
703 printf (" Address size:%14s0x%02X (%s)\n", "", AddrSize,
704 AddrSizeToStr (AddrSize));
705 printf (" Owner:%25lu\n", Owner);
706 printf (" Name:%*s\"%s\"\n", (int)(24-Len), "", Name);
707 if (SYM_IS_CONST (Type)) {
708 printf (" Value:%15s0x%08lX (%lu)\n", "", Value, Value);
710 if (SYM_HAS_SIZE (Type)) {
711 printf (" Size:%20s0x%04lX (%lu)\n", "", Size, Size);
715 /* Destroy the string pool */
716 DestroyStrPool (&StrPool);
721 void DumpObjLineInfo (FILE* F, unsigned long Offset)
722 /* Dump the line info from an object file */
725 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
729 /* Seek to the header position and read the header */
730 FileSetPos (F, Offset);
731 ReadObjHeader (F, &H);
733 /* Seek to the start of the string pool and read it */
734 FileSetPos (F, Offset + H.StrPoolOffs);
735 ReadStrPool (F, &StrPool);
737 /* Seek to the start of line infos */
738 FileSetPos (F, Offset + H.LineInfoOffs);
740 /* Output a header */
741 printf (" Line info:\n");
743 /* Check if the object file was compiled with debug info */
744 if ((H.Flags & OBJ_FLAGS_DBGINFO) == 0) {
745 /* Print that there no line infos and bail out */
746 printf (" Count:%27u\n", 0);
750 /* Read the number of line infos and print it */
752 printf (" Count:%27u\n", Count);
754 /* Read and print all line infos */
755 for (I = 0; I < Count; ++I) {
759 /* Type of line info */
760 unsigned Type = ReadVar (F);
762 /* File position of line info */
763 ReadFilePos (F, &Pos);
765 /* Print the header */
766 printf (" Index:%27u\n", I);
769 printf (" Type:%26u\n", LI_GET_TYPE (Type));
770 printf (" Count:%25u\n", LI_GET_COUNT (Type));
771 printf (" Line:%26u\n", Pos.Line);
772 printf (" Col:%27u\n", Pos.Col);
773 printf (" Name:%26u\n", Pos.Name);
776 /* Destroy the string pool */
777 DestroyStrPool (&StrPool);
782 void DumpObjScopes (FILE* F, unsigned long Offset)
783 /* Dump the scopes from an object file */
786 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
790 /* Seek to the header position and read the header */
791 FileSetPos (F, Offset);
792 ReadObjHeader (F, &H);
794 /* Seek to the start of the string pool and read it */
795 FileSetPos (F, Offset + H.StrPoolOffs);
796 ReadStrPool (F, &StrPool);
798 /* Seek to the start of scopes */
799 FileSetPos (F, Offset + H.ScopeOffs);
801 /* Output a header */
802 printf (" Scopes:\n");
804 /* Check if the object file was compiled with debug info */
805 if ((H.Flags & OBJ_FLAGS_DBGINFO) == 0) {
806 /* Print that there no scopes and bail out */
807 printf (" Count:%27u\n", 0);
811 /* Read the number of scopes and print it */
813 printf (" Count:%27u\n", Count);
815 /* Read and print all scopes */
816 for (I = 0; I < Count; ++I) {
824 unsigned ParentId = ReadVar (F);
825 unsigned LexicalLevel = ReadVar (F);
826 unsigned Flags = ReadVar (F);
827 const char* ScopeType = GetScopeType (ReadVar (F));
829 /* Print the header */
830 printf (" Index:%27u\n", I);
833 printf (" Parent id:%21u\n", ParentId);
834 printf (" Lexical level:%17u\n", LexicalLevel);
835 printf (" Flags:%21s0x%02X\n", "", Flags);
836 printf (" Type:%26s\n", ScopeType);
838 /* Resolve and print the name */
839 Name = GetString (&StrPool, ReadVar (F));
841 printf (" Name:%*s\"%s\"\n", (int)(24-Len), "", Name);
844 if (SCOPE_HAS_SIZE (Flags)) {
845 unsigned long Size = ReadVar (F);
846 printf (" Size:%20s0x%04lX (%lu)\n", "", Size, Size);
850 if (SCOPE_HAS_LABEL (Flags)) {
851 unsigned LabelId = ReadVar (F);
852 printf (" Label id:%22u\n", LabelId);
856 SpanCount = ReadVar (F);
857 printf (" Segment spans:\n");
858 printf (" Count:%23u\n", SpanCount);
860 for (J = 0; J < SpanCount; ++J) {
861 printf (" Index:%23u\n", J);
862 printf (" Segment:%19lu\n", ReadVar (F));
863 printf (" Start:%13s0x%06lX\n", "", ReadVar (F));
864 printf (" Size:%14s0x%06lX\n", "", ReadVar (F));
868 /* Destroy the string pool */
869 DestroyStrPool (&StrPool);
874 void DumpObjSegSize (FILE* F, unsigned long Offset)
875 /* Dump the sizes of the segment in the object file */
878 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
881 /* Seek to the header position and read the header */
882 FileSetPos (F, Offset);
883 ReadObjHeader (F, &H);
885 /* Seek to the start of the string pool and read it */
886 FileSetPos (F, Offset + H.StrPoolOffs);
887 ReadStrPool (F, &StrPool);
889 /* Seek to the start of the segments */
890 FileSetPos (F, Offset + H.SegOffs);
892 /* Output a header */
893 printf (" Segment sizes:\n");
895 /* Read the number of segments */
898 /* Read and print the sizes of all segments */
901 /* Read the data for one segments */
902 unsigned long DataSize = Read32 (F);
903 unsigned long NextSeg = ftell (F) + DataSize;
904 const char* Name = GetString (&StrPool, ReadVar (F));
905 unsigned Len = strlen (Name);
906 unsigned long Size = Read32 (F);
908 /* Skip alignment, type and fragment count */
913 /* Print the size for this segment */
914 printf (" %s:%*s%6lu\n", Name, (int)(24-Len), "", Size);
916 /* Seek to the end of the segment data (start of next) */
917 FileSetPos (F, NextSeg);
920 /* Destroy the string pool */
921 DestroyStrPool (&StrPool);