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 SkipSpanList (FILE* F)
140 /* Skip a span list from the given file */
142 /* Count preceeds the list */
143 unsigned long Count = ReadVar (F);
153 static void SkipExpr (FILE* F)
154 /* Skip an expression from the given file */
156 /* Read the node tag and handle NULL nodes */
157 unsigned char Op = Read8 (F);
158 if (Op == EXPR_NULL) {
162 /* Check the tag and handle the different expression types */
163 if (EXPR_IS_LEAF (Op)) {
167 (void) Read32Signed (F);
171 /* Read the import number */
176 /* Read the segment number */
181 Error ("Invalid expression op: %02X", Op);
187 /* Not a leaf node */
195 static const char* GetExportFlags (unsigned Flags, const unsigned char* ConDes)
196 /* Get the export flags as a (static) string */
199 static char TypeDesc[256];
207 switch (Flags & SYM_MASK_TYPE) {
208 case SYM_STD: strcat (TypeDesc, "SYM_STD"); break;
209 case SYM_CHEAP_LOCAL: strcat (TypeDesc, "SYM_CHEAP_LOCAL"); break;
213 switch (Flags & SYM_MASK_LABEL) {
214 case SYM_EQUATE: strcat (TypeDesc, ",SYM_EQUATE"); break;
215 case SYM_LABEL: strcat (TypeDesc, ",SYM_LABEL"); break;
218 /* Type of expression */
219 switch (Flags & SYM_MASK_VAL) {
220 case SYM_CONST: strcat (TypeDesc, ",SYM_CONST"); break;
221 case SYM_EXPR: strcat (TypeDesc, ",SYM_EXPR"); break;
224 /* Size available? */
225 if (SYM_HAS_SIZE (Flags)) {
226 strcat (TypeDesc, ",SYM_SIZE");
230 /* Constructor/destructor declarations */
231 T = TypeDesc + strlen (TypeDesc);
232 Count = SYM_GET_CONDES_COUNT (Flags);
233 if (Count > 0 && ConDes) {
234 T += sprintf (T, ",SYM_CONDES=");
235 for (I = 0; I < Count; ++I) {
236 unsigned Type = CD_GET_TYPE (ConDes[I]);
237 unsigned Prio = CD_GET_PRIO (ConDes[I]);
241 T += sprintf (T, "[%u,%u]", Type, Prio);
245 /* Return the result */
251 static const char* GetScopeType (unsigned Type)
252 /* Return the name of a scope type */
255 case SCOPE_GLOBAL: return "Global scope";
256 case SCOPE_FILE: return "File scope";
257 case SCOPE_SCOPE: return ".SCOPE or .PROC";
258 case SCOPE_STRUCT: return ".STRUCT";
259 case SCOPE_ENUM: return ".ENUM";
260 case SCOPE_UNDEF: return "Undefined";
261 default: return "Unknown scope type";
267 void DumpObjHeader (FILE* F, unsigned long Offset)
268 /* Dump the header of the given object file */
272 /* Seek to the header position */
273 FileSetPos (F, Offset);
275 /* Read the header */
276 ReadObjHeader (F, &H);
278 /* Now dump the information */
280 /* Output a header */
281 printf (" Header:\n");
284 printf (" Magic:%17s0x%08lX\n", "", H.Magic);
287 printf (" Version:%25u\n", H.Version);
290 printf (" Flags:%21s0x%04X (", "", H.Flags);
291 if (H.Flags & OBJ_FLAGS_DBGINFO) {
292 printf ("OBJ_FLAGS_DBGINFO");
297 DumpObjHeaderSection ("Options", H.OptionOffs, H.OptionSize);
300 DumpObjHeaderSection ("Files", H.FileOffs, H.FileSize);
303 DumpObjHeaderSection ("Segments", H.SegOffs, H.SegSize);
306 DumpObjHeaderSection ("Imports", H.ImportOffs, H.ImportSize);
309 DumpObjHeaderSection ("Exports", H.ExportOffs, H.ExportSize);
312 DumpObjHeaderSection ("Debug symbols", H.DbgSymOffs, H.DbgSymSize);
315 DumpObjHeaderSection ("Line infos", H.LineInfoOffs, H.LineInfoSize);
318 DumpObjHeaderSection ("String pool", H.StrPoolOffs, H.StrPoolSize);
321 DumpObjHeaderSection ("Assertions", H.AssertOffs, H.AssertSize);
324 DumpObjHeaderSection ("Scopes", H.ScopeOffs, H.ScopeSize);
329 void DumpObjOptions (FILE* F, unsigned long Offset)
330 /* Dump the file options */
333 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
337 /* Seek to the header position and read the header */
338 FileSetPos (F, Offset);
339 ReadObjHeader (F, &H);
341 /* Seek to the start of the string pool and read it */
342 FileSetPos (F, Offset + H.StrPoolOffs);
343 ReadStrPool (F, &StrPool);
345 /* Seek to the start of the options */
346 FileSetPos (F, Offset + H.OptionOffs);
348 /* Output a header */
349 printf (" Options:\n");
351 /* Read the number of options and print it */
353 printf (" Count:%27u\n", Count);
355 /* Read and print all options */
356 for (I = 0; I < Count; ++I) {
361 /* Read the type of the option and the value */
362 unsigned char Type = Read8 (F);
363 unsigned long Val = ReadVar (F);
365 /* Get the type of the argument */
366 unsigned char ArgType = Type & OPT_ARGMASK;
368 /* Determine which option follows */
369 const char* TypeDesc;
371 case OPT_COMMENT: TypeDesc = "OPT_COMMENT"; break;
372 case OPT_AUTHOR: TypeDesc = "OPT_AUTHOR"; break;
373 case OPT_TRANSLATOR:TypeDesc = "OPT_TRANSLATOR"; break;
374 case OPT_COMPILER: TypeDesc = "OPT_COMPILER"; break;
375 case OPT_OS: TypeDesc = "OPT_OS"; break;
376 case OPT_DATETIME: TypeDesc = "OPT_DATETIME"; break;
377 default: TypeDesc = "OPT_UNKNOWN"; break;
380 /* Print the header */
381 printf (" Index:%27u\n", I);
384 printf (" Type:%22s0x%02X (%s)\n", "", Type, TypeDesc);
388 ArgStr = GetString (&StrPool, Val);
389 ArgLen = strlen (ArgStr);
390 printf (" Data:%*s\"%s\"\n", (int)(24-ArgLen), "", ArgStr);
394 printf (" Data:%26lu", Val);
395 if (Type == OPT_DATETIME) {
396 /* Print the time as a string */
397 printf (" (%s)", TimeToStr (Val));
403 /* Unknown argument type. This means that we cannot determine
404 * the option length, so we cannot proceed.
406 Error ("Unknown option type: 0x%02X", Type);
411 /* Destroy the string pool */
412 DestroyStrPool (&StrPool);
417 void DumpObjFiles (FILE* F, unsigned long Offset)
418 /* Dump the source files */
421 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
425 /* Seek to the header position and read the header */
426 FileSetPos (F, Offset);
427 ReadObjHeader (F, &H);
429 /* Seek to the start of the string pool and read it */
430 FileSetPos (F, Offset + H.StrPoolOffs);
431 ReadStrPool (F, &StrPool);
433 /* Seek to the start of the source files */
434 FileSetPos (F, Offset + H.FileOffs);
436 /* Output a header */
437 printf (" Files:\n");
439 /* Read the number of files and print it */
441 printf (" Count:%27u\n", Count);
443 /* Read and print all files */
444 for (I = 0; I < Count; ++I) {
446 /* Read the data for one file */
447 const char* Name = GetString (&StrPool, ReadVar (F));
448 unsigned long MTime = Read32 (F);
449 unsigned long Size = ReadVar (F);
450 unsigned Len = strlen (Name);
452 /* Print the header */
453 printf (" Index:%27u\n", I);
456 printf (" Name:%*s\"%s\"\n", (int)(24-Len), "", Name);
457 printf (" Size:%26lu\n", Size);
458 printf (" Modification time:%13lu (%s)\n", MTime, TimeToStr (MTime));
461 /* Destroy the string pool */
462 DestroyStrPool (&StrPool);
467 void DumpObjSegments (FILE* F, unsigned long Offset)
468 /* Dump the segments in the object file */
471 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
475 /* Seek to the header position and read the header */
476 FileSetPos (F, Offset);
477 ReadObjHeader (F, &H);
479 /* Seek to the start of the string pool and read it */
480 FileSetPos (F, Offset + H.StrPoolOffs);
481 ReadStrPool (F, &StrPool);
483 /* Seek to the start of the segments */
484 FileSetPos (F, Offset + H.SegOffs);
486 /* Output a header */
487 printf (" Segments:\n");
489 /* Read the number of segments and print it */
491 printf (" Count:%27u\n", Count);
493 /* Read and print all segments */
494 for (I = 0; I < Count; ++I) {
496 /* Read the data for one segments */
497 unsigned long DataSize = Read32 (F);
498 unsigned long NextSeg = ftell (F) + DataSize;
499 const char* Name = GetString (&StrPool, ReadVar (F));
500 unsigned Len = strlen (Name);
501 unsigned long Size = ReadVar (F);
502 unsigned long Align = ReadVar (F);
503 unsigned char AddrSize = Read8 (F);
504 unsigned long FragCount = ReadVar (F);
506 /* Print the header */
507 printf (" Index:%27u\n", I);
510 printf (" Name:%*s\"%s\"\n", (int)(24-Len), "", Name);
511 printf (" Size:%26lu\n", Size);
512 printf (" Alignment:%21lu\n", Align);
513 printf (" Address size:%14s0x%02X (%s)\n", "", AddrSize,
514 AddrSizeToStr (AddrSize));
515 printf (" Fragment count:%16lu\n", FragCount);
517 /* Seek to the end of the segment data (start of next) */
518 FileSetPos (F, NextSeg);
521 /* Destroy the string pool */
522 DestroyStrPool (&StrPool);
527 void DumpObjImports (FILE* F, unsigned long Offset)
528 /* Dump the imports in the object file */
531 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
535 /* Seek to the header position and read the header */
536 FileSetPos (F, Offset);
537 ReadObjHeader (F, &H);
539 /* Seek to the start of the string pool and read it */
540 FileSetPos (F, Offset + H.StrPoolOffs);
541 ReadStrPool (F, &StrPool);
543 /* Seek to the start of the imports */
544 FileSetPos (F, Offset + H.ImportOffs);
546 /* Output a header */
547 printf (" Imports:\n");
549 /* Read the number of imports and print it */
551 printf (" Count:%27u\n", Count);
553 /* Read and print all imports */
554 for (I = 0; I < Count; ++I) {
556 /* Read the data for one import */
557 unsigned char AddrSize = Read8 (F);
558 const char* Name = GetString (&StrPool, ReadVar (F));
559 unsigned Len = strlen (Name);
561 /* Skip both line info lists */
562 SkipLineInfoList (F);
563 SkipLineInfoList (F);
565 /* Print the header */
566 printf (" Index:%27u\n", I);
569 printf (" Address size:%14s0x%02X (%s)\n", "", AddrSize,
570 AddrSizeToStr (AddrSize));
571 printf (" Name:%*s\"%s\"\n", (int)(24-Len), "", Name);
574 /* Destroy the string pool */
575 DestroyStrPool (&StrPool);
580 void DumpObjExports (FILE* F, unsigned long Offset)
581 /* Dump the exports in the object file */
584 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
588 /* Seek to the header position and read the header */
589 FileSetPos (F, Offset);
590 ReadObjHeader (F, &H);
592 /* Seek to the start of the string pool and read it */
593 FileSetPos (F, Offset + H.StrPoolOffs);
594 ReadStrPool (F, &StrPool);
596 /* Seek to the start of the exports */
597 FileSetPos (F, Offset + H.ExportOffs);
599 /* Output a header */
600 printf (" Exports:\n");
602 /* Read the number of exports and print it */
604 printf (" Count:%27u\n", Count);
606 /* Read and print all exports */
607 for (I = 0; I < Count; ++I) {
609 unsigned long Value = 0;
610 unsigned long Size = 0;
611 unsigned char ConDes[CD_TYPE_COUNT];
616 /* Read the data for one export */
617 unsigned Type = ReadVar (F);
618 unsigned char AddrSize = Read8 (F);
619 ReadData (F, ConDes, SYM_GET_CONDES_COUNT (Type));
620 Name = GetString (&StrPool, ReadVar (F));
622 if (SYM_IS_CONST (Type)) {
627 if (SYM_HAS_SIZE (Type)) {
631 /* Skip both line infos lists */
632 SkipLineInfoList (F);
633 SkipLineInfoList (F);
635 /* Print the header */
636 printf (" Index:%27u\n", I);
639 printf (" Type:%22s0x%02X (%s)\n", "", Type, GetExportFlags (Type, ConDes));
640 printf (" Address size:%14s0x%02X (%s)\n", "", AddrSize,
641 AddrSizeToStr (AddrSize));
642 printf (" Name:%*s\"%s\"\n", (int)(24-Len), "", Name);
643 if (SYM_IS_CONST (Type)) {
644 printf (" Value:%15s0x%08lX (%lu)\n", "", Value, Value);
646 if (SYM_HAS_SIZE (Type)) {
647 printf (" Size:%16s0x%04lX (%lu)\n", "", Size, Size);
651 /* Destroy the string pool */
652 DestroyStrPool (&StrPool);
657 void DumpObjDbgSyms (FILE* F, unsigned long Offset)
658 /* Dump the debug symbols from an object file */
661 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
665 /* Seek to the header position and read the header */
666 FileSetPos (F, Offset);
667 ReadObjHeader (F, &H);
669 /* Seek to the start of the string pool and read it */
670 FileSetPos (F, Offset + H.StrPoolOffs);
671 ReadStrPool (F, &StrPool);
673 /* Seek to the start of the debug syms */
674 FileSetPos (F, Offset + H.DbgSymOffs);
676 /* Output a header */
677 printf (" Debug symbols:\n");
679 /* Check if the object file was compiled with debug info */
680 if ((H.Flags & OBJ_FLAGS_DBGINFO) == 0) {
681 /* Print that there no debug symbols and bail out */
682 printf (" Count:%27u\n", 0);
686 /* Read the number of exports and print it */
688 printf (" Count:%27u\n", Count);
690 /* Read and print all debug symbols */
691 for (I = 0; I < Count; ++I) {
693 unsigned long Value = 0;
694 unsigned long Size = 0;
695 unsigned ImportId = 0;
696 unsigned ExportId = 0;
698 /* Read the data for one symbol */
699 unsigned Type = ReadVar (F);
700 unsigned char AddrSize = Read8 (F);
701 unsigned long Owner = ReadVar (F);
702 const char* Name = GetString (&StrPool, ReadVar (F));
703 unsigned Len = strlen (Name);
704 if (SYM_IS_CONST (Type)) {
709 if (SYM_HAS_SIZE (Type)) {
712 if (SYM_IS_IMPORT (Type)) {
713 ImportId = ReadVar (F);
715 if (SYM_IS_EXPORT (Type)) {
716 ExportId = ReadVar (F);
719 /* Skip both line info lists */
720 SkipLineInfoList (F);
721 SkipLineInfoList (F);
723 /* Print the header */
724 printf (" Index:%27u\n", I);
727 printf (" Type:%22s0x%02X (%s)\n", "", Type, GetExportFlags (Type, 0));
728 printf (" Address size:%14s0x%02X (%s)\n", "", AddrSize,
729 AddrSizeToStr (AddrSize));
730 printf (" Owner:%25lu\n", Owner);
731 printf (" Name:%*s\"%s\"\n", (int)(24-Len), "", Name);
732 if (SYM_IS_CONST (Type)) {
733 printf (" Value:%15s0x%08lX (%lu)\n", "", Value, Value);
735 if (SYM_HAS_SIZE (Type)) {
736 printf (" Size:%20s0x%04lX (%lu)\n", "", Size, Size);
738 if (SYM_IS_IMPORT (Type)) {
739 printf (" Import:%24u\n", ImportId);
741 if (SYM_IS_EXPORT (Type)) {
742 printf (" Export:%24u\n", ExportId);
746 /* Destroy the string pool */
747 DestroyStrPool (&StrPool);
752 void DumpObjLineInfo (FILE* F, unsigned long Offset)
753 /* Dump the line info from an object file */
756 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
760 /* Seek to the header position and read the header */
761 FileSetPos (F, Offset);
762 ReadObjHeader (F, &H);
764 /* Seek to the start of the string pool and read it */
765 FileSetPos (F, Offset + H.StrPoolOffs);
766 ReadStrPool (F, &StrPool);
768 /* Seek to the start of line infos */
769 FileSetPos (F, Offset + H.LineInfoOffs);
771 /* Output a header */
772 printf (" Line info:\n");
774 /* Check if the object file was compiled with debug info */
775 if ((H.Flags & OBJ_FLAGS_DBGINFO) == 0) {
776 /* Print that there no line infos and bail out */
777 printf (" Count:%27u\n", 0);
781 /* Read the number of line infos and print it */
783 printf (" Count:%27u\n", Count);
785 /* Read and print all line infos */
786 for (I = 0; I < Count; ++I) {
791 /* File position of line info */
792 ReadFilePos (F, &Pos);
794 /* Type of line info */
800 /* Print the header */
801 printf (" Index:%27u\n", I);
804 printf (" Type:%26u\n", LI_GET_TYPE (Type));
805 printf (" Count:%25u\n", LI_GET_COUNT (Type));
806 printf (" Line:%26u\n", Pos.Line);
807 printf (" Col:%27u\n", Pos.Col);
808 printf (" Name:%26u\n", Pos.Name);
811 /* Destroy the string pool */
812 DestroyStrPool (&StrPool);
817 void DumpObjScopes (FILE* F, unsigned long Offset)
818 /* Dump the scopes from an object file */
821 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
825 /* Seek to the header position and read the header */
826 FileSetPos (F, Offset);
827 ReadObjHeader (F, &H);
829 /* Seek to the start of the string pool and read it */
830 FileSetPos (F, Offset + H.StrPoolOffs);
831 ReadStrPool (F, &StrPool);
833 /* Seek to the start of scopes */
834 FileSetPos (F, Offset + H.ScopeOffs);
836 /* Output a header */
837 printf (" Scopes:\n");
839 /* Check if the object file was compiled with debug info */
840 if ((H.Flags & OBJ_FLAGS_DBGINFO) == 0) {
841 /* Print that there no scopes and bail out */
842 printf (" Count:%27u\n", 0);
846 /* Read the number of scopes and print it */
848 printf (" Count:%27u\n", Count);
850 /* Read and print all scopes */
851 for (I = 0; I < Count; ++I) {
857 unsigned ParentId = ReadVar (F);
858 unsigned LexicalLevel = ReadVar (F);
859 unsigned Flags = ReadVar (F);
860 const char* ScopeType = GetScopeType (ReadVar (F));
862 /* Print the header */
863 printf (" Index:%27u\n", I);
866 printf (" Parent id:%21u\n", ParentId);
867 printf (" Lexical level:%17u\n", LexicalLevel);
868 printf (" Flags:%21s0x%02X\n", "", Flags);
869 printf (" Type:%26s\n", ScopeType);
871 /* Resolve and print the name */
872 Name = GetString (&StrPool, ReadVar (F));
874 printf (" Name:%*s\"%s\"\n", (int)(24-Len), "", Name);
877 if (SCOPE_HAS_SIZE (Flags)) {
878 unsigned long Size = ReadVar (F);
879 printf (" Size:%20s0x%04lX (%lu)\n", "", Size, Size);
883 if (SCOPE_HAS_LABEL (Flags)) {
884 unsigned LabelId = ReadVar (F);
885 printf (" Label id:%22u\n", LabelId);
892 /* Destroy the string pool */
893 DestroyStrPool (&StrPool);
898 void DumpObjSegSize (FILE* F, unsigned long Offset)
899 /* Dump the sizes of the segment in the object file */
902 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
905 /* Seek to the header position and read the header */
906 FileSetPos (F, Offset);
907 ReadObjHeader (F, &H);
909 /* Seek to the start of the string pool and read it */
910 FileSetPos (F, Offset + H.StrPoolOffs);
911 ReadStrPool (F, &StrPool);
913 /* Seek to the start of the segments */
914 FileSetPos (F, Offset + H.SegOffs);
916 /* Output a header */
917 printf (" Segment sizes:\n");
919 /* Read the number of segments */
922 /* Read and print the sizes of all segments */
925 /* Read the data for one segments */
926 unsigned long DataSize = Read32 (F);
927 unsigned long NextSeg = ftell (F) + DataSize;
928 const char* Name = GetString (&StrPool, ReadVar (F));
929 unsigned Len = strlen (Name);
930 unsigned long Size = ReadVar (F);
932 /* Skip alignment, type and fragment count */
937 /* Print the size for this segment */
938 printf (" %s:%*s%6lu\n", Name, (int)(24-Len), "", Size);
940 /* Seek to the end of the segment data (start of next) */
941 FileSetPos (F, NextSeg);
944 /* Destroy the string pool */
945 DestroyStrPool (&StrPool);