1 /*****************************************************************************/
5 /* Dump subroutines for the od65 object file dump utility */
9 /* (C) 2002-2003 Ullrich von Bassewitz */
10 /* Römerstrasse 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 /*****************************************************************************/
57 /*****************************************************************************/
59 /*****************************************************************************/
63 static void DestroyStrPool (Collection* C)
64 /* Free all strings in the given pool plus the item pointers. Note: The
65 * collection may not be reused later.
69 for (I = 0; I < CollCount (C); ++I) {
70 xfree (CollAtUnchecked (C, I));
77 static const char* GetString (const Collection* C, unsigned Index)
78 /* Get a string from a collection. In fact, this function calls CollConstAt,
79 * but will print a somewhat more readable error message if the index is out
83 if (Index >= CollCount (C)) {
84 Error ("Invalid string index (%u) - file corrupt!", Index);
86 return CollConstAt (C, Index);
91 static void DumpObjHeaderSection (const char* Name,
94 /* Dump a header section */
96 printf (" %s:\n", Name);
97 printf (" Offset:%24lu\n", Offset);
98 printf (" Size: %24lu\n", Size);
103 static char* TimeToStr (unsigned long Time)
104 /* Convert the time into a string and return it */
106 /* Get the time and convert to string */
107 time_t T = (time_t) Time;
108 char* S = asctime (localtime (&T));
110 /* Remove the trailing newline */
111 unsigned Len = strlen (S);
112 if (Len > 0 && S[Len-1] == '\n') {
116 /* Return the time string */
122 static void SkipExpr (FILE* F)
123 /* Skip an expression from the given file */
125 /* Read the node tag and handle NULL nodes */
126 unsigned char Op = Read8 (F);
127 if (Op == EXPR_NULL) {
131 /* Check the tag and handle the different expression types */
132 if (EXPR_IS_LEAF (Op)) {
136 (void) Read32Signed (F);
140 /* Read the import number */
145 /* Read the segment number */
150 Error ("Invalid expression op: %02X", Op);
156 /* Not a leaf node */
165 static unsigned SkipFragment (FILE* F)
166 /* Skip a fragment from the given file and return the size */
171 /* Read the fragment type */
172 unsigned char Type = Read8 (F);
174 /* Extract the check mask */
175 unsigned char Check = Type & FRAG_CHECKMASK;
176 Type &= ~FRAG_CHECKMASK;
178 /* Handle the different fragment types */
193 Size = Type & FRAG_BYTEMASK;
201 Error ("Unknown fragment type: 0x%02X", Type);
208 /* Now read the fragment data */
209 switch (Type & FRAG_TYPEMASK) {
213 FileSeek (F, ftell (F) + Size);
224 /* Skip the check expression if we have one */
225 if (Check & FRAG_CHECK_WARN) {
228 if (Check & FRAG_CHECK_ERROR) {
232 /* Skip the file position of the fragment */
233 ReadFilePos (F, &Pos);
235 /* Skip the additional line info */
238 /* Return the size */
244 static const char* GetExportFlags (unsigned Flags, const unsigned char* ConDes)
245 /* Get the export flags as a (static) string */
248 static char TypeDesc[256];
256 switch (Flags & EXP_MASK_SIZE) {
257 case EXP_ABS: strcat (TypeDesc, "EXP_ABS"); break;
258 case EXP_ZP: strcat (TypeDesc, "EXP_ZP"); break;
261 /* Type of expression */
262 switch (Flags & EXP_MASK_VAL) {
263 case EXP_CONST: strcat (TypeDesc, ",EXP_CONST"); break;
264 case EXP_EXPR: strcat (TypeDesc, ",EXP_EXPR"); break;
267 /* Constructor/destructor declarations */
268 T = TypeDesc + strlen (TypeDesc);
269 Count = GET_EXP_CONDES_COUNT (Flags);
271 T += sprintf (T, ",EXP_CONDES=");
272 for (I = 0; I < Count; ++I) {
273 unsigned Type = CD_GET_TYPE (ConDes[I]);
274 unsigned Prio = CD_GET_PRIO (ConDes[I]);
278 T += sprintf (T, "[%u,%u]", Type, Prio);
282 /* Return the result */
288 void DumpObjHeader (FILE* F, unsigned long Offset)
289 /* Dump the header of the given object file */
293 /* Seek to the header position */
294 FileSeek (F, Offset);
296 /* Read the header */
297 ReadObjHeader (F, &H);
299 /* Now dump the information */
301 /* Output a header */
302 printf (" Header:\n");
305 printf (" Magic:%17s0x%08lX\n", "", H.Magic);
308 printf (" Version:%25u\n", H.Version);
311 printf (" Flags:%21s0x%04X (", "", H.Flags);
312 if (H.Flags & OBJ_FLAGS_DBGINFO) {
313 printf ("OBJ_FLAGS_DBGINFO");
318 DumpObjHeaderSection ("Options", H.OptionOffs, H.OptionSize);
321 DumpObjHeaderSection ("Files", H.FileOffs, H.FileSize);
324 DumpObjHeaderSection ("Segments", H.SegOffs, H.SegSize);
327 DumpObjHeaderSection ("Imports", H.ImportOffs, H.ImportSize);
330 DumpObjHeaderSection ("Exports", H.ExportOffs, H.ExportSize);
333 DumpObjHeaderSection ("Debug symbols", H.DbgSymOffs, H.DbgSymSize);
336 DumpObjHeaderSection ("Line infos", H.LineInfoOffs, H.LineInfoSize);
339 DumpObjHeaderSection ("String pool", H.StrPoolOffs, H.StrPoolSize);
344 void DumpObjOptions (FILE* F, unsigned long Offset)
345 /* Dump the file options */
348 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
352 /* Seek to the header position and read the header */
353 FileSeek (F, Offset);
354 ReadObjHeader (F, &H);
356 /* Seek to the start of the string pool and read it */
357 FileSeek (F, Offset + H.StrPoolOffs);
358 ReadStrPool (F, &StrPool);
360 /* Seek to the start of the options */
361 FileSeek (F, Offset + H.OptionOffs);
363 /* Output a header */
364 printf (" Options:\n");
366 /* Read the number of options and print it */
368 printf (" Count:%27u\n", Count);
370 /* Read and print all options */
371 for (I = 0; I < Count; ++I) {
373 unsigned long ArgNum;
377 /* Read the type of the option */
378 unsigned char Type = Read8 (F);
380 /* Get the type of the argument */
381 unsigned char ArgType = Type & OPT_ARGMASK;
383 /* Determine which option follows */
384 const char* TypeDesc;
386 case OPT_COMMENT: TypeDesc = "OPT_COMMENT"; break;
387 case OPT_AUTHOR: TypeDesc = "OPT_AUTHOR"; break;
388 case OPT_TRANSLATOR:TypeDesc = "OPT_TRANSLATOR"; break;
389 case OPT_COMPILER: TypeDesc = "OPT_COMPILER"; break;
390 case OPT_OS: TypeDesc = "OPT_OS"; break;
391 case OPT_DATETIME: TypeDesc = "OPT_DATETIME"; break;
392 default: TypeDesc = "OPT_UNKNOWN"; break;
395 /* Print the header */
396 printf (" Index:%27u\n", I);
399 printf (" Type:%22s0x%02X (%s)\n", "", Type, TypeDesc);
403 ArgStr = ReadStr (F);
404 ArgLen = strlen (ArgStr);
405 printf (" Data:%*s\"%s\"\n", 24-ArgLen, "", ArgStr);
411 printf (" Data:%26lu", ArgNum);
412 if (Type == OPT_DATETIME) {
413 /* Print the time as a string */
414 printf (" (%s)", TimeToStr (ArgNum));
420 /* Unknown argument type. This means that we cannot determine
421 * the option length, so we cannot proceed.
423 Error ("Unknown option type: 0x%02X", Type);
428 /* Destroy the string pool */
429 DestroyStrPool (&StrPool);
434 void DumpObjFiles (FILE* F, unsigned long Offset)
435 /* Dump the source files */
438 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
442 /* Seek to the header position and read the header */
443 FileSeek (F, Offset);
444 ReadObjHeader (F, &H);
446 /* Seek to the start of the string pool and read it */
447 FileSeek (F, Offset + H.StrPoolOffs);
448 ReadStrPool (F, &StrPool);
450 /* Seek to the start of the source files */
451 FileSeek (F, Offset + H.FileOffs);
453 /* Output a header */
454 printf (" Files:\n");
456 /* Read the number of files and print it */
458 printf (" Count:%27u\n", Count);
460 /* Read and print all files */
461 for (I = 0; I < Count; ++I) {
463 /* Read the data for one file */
464 unsigned long MTime = Read32 (F);
465 unsigned long Size = Read32 (F);
466 char* Name = ReadStr (F);
467 unsigned Len = strlen (Name);
469 /* Print the header */
470 printf (" Index:%27u\n", I);
473 printf (" Name:%*s\"%s\"\n", 24-Len, "", Name);
474 printf (" Size:%26lu\n", Size);
475 printf (" Modification time:%13lu (%s)\n", MTime, TimeToStr (MTime));
481 /* Destroy the string pool */
482 DestroyStrPool (&StrPool);
487 void DumpObjSegments (FILE* F, unsigned long Offset)
488 /* Dump the segments in the object file */
491 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
496 /* Seek to the header position and read the header */
497 FileSeek (F, Offset);
498 ReadObjHeader (F, &H);
500 /* Seek to the start of the string pool and read it */
501 FileSeek (F, Offset + H.StrPoolOffs);
502 ReadStrPool (F, &StrPool);
504 /* Seek to the start of the segments */
505 FileSeek (F, Offset + H.SegOffs);
507 /* Output a header */
508 printf (" Segments:\n");
510 /* Read the number of segments and print it */
512 printf (" Count:%27u\n", Count);
514 /* Read and print all segments */
515 for (I = 0; I < Count; ++I) {
517 /* Read the data for one segments */
518 char* Name = ReadStr (F);
519 unsigned Len = strlen (Name);
520 unsigned long Size = Read32 (F);
521 unsigned Align = (1U << Read8 (F));
522 unsigned char Type = Read8 (F);
524 /* Get the description for the type */
525 const char* TypeDesc;
527 case SEGTYPE_DEFAULT: TypeDesc = "SEGTYPE_DEFAULT"; break;
528 case SEGTYPE_ABS: TypeDesc = "SEGTYPE_ABS"; break;
529 case SEGTYPE_ZP: TypeDesc = "SEGTYPE_ZP"; break;
530 case SEGTYPE_FAR: TypeDesc = "SEGTYPE_FAR"; break;
531 default: TypeDesc = "SEGTYPE_UNKNOWN"; break;
534 /* Print the header */
535 printf (" Index:%27u\n", I);
538 printf (" Name:%*s\"%s\"\n", 24-Len, "", Name);
539 printf (" Size:%26lu\n", Size);
540 printf (" Alignment:%21u\n", Align);
541 printf (" Type:%22s0x%02X (%s)\n", "", Type, TypeDesc);
546 /* Skip the fragments for this segment, counting them */
549 unsigned FragSize = SkipFragment (F);
550 if (FragSize > Size) {
551 /* OOPS - file data invalid */
552 Error ("Invalid fragment data - file corrupt!");
558 /* Print the fragment count */
559 printf (" Fragment count:%16u\n", FragCount);
562 /* Destroy the string pool */
563 DestroyStrPool (&StrPool);
568 void DumpObjImports (FILE* F, unsigned long Offset)
569 /* Dump the imports in the object file */
572 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
577 /* Seek to the header position and read the header */
578 FileSeek (F, Offset);
579 ReadObjHeader (F, &H);
581 /* Seek to the start of the string pool and read it */
582 FileSeek (F, Offset + H.StrPoolOffs);
583 ReadStrPool (F, &StrPool);
585 /* Seek to the start of the imports */
586 FileSeek (F, Offset + H.ImportOffs);
588 /* Output a header */
589 printf (" Imports:\n");
591 /* Read the number of imports and print it */
593 printf (" Count:%27u\n", Count);
595 /* Read and print all imports */
596 for (I = 0; I < Count; ++I) {
598 const char* TypeDesc;
600 /* Read the data for one import */
601 unsigned char Type = Read8 (F);
602 const char* Name = GetString (&StrPool, ReadVar (F));
603 unsigned Len = strlen (Name);
604 ReadFilePos (F, &Pos);
606 /* Get a description for the type */
608 case IMP_ZP: TypeDesc = "IMP_ZP"; break;
609 case IMP_ABS: TypeDesc = "IMP_ABS"; break;
610 default: TypeDesc = "IMP_UNKNOWN"; break;
613 /* Print the header */
614 printf (" Index:%27u\n", I);
617 printf (" Type:%22s0x%02X (%s)\n", "", Type, TypeDesc);
618 printf (" Name:%*s\"%s\"\n", 24-Len, "", Name);
621 /* Destroy the string pool */
622 DestroyStrPool (&StrPool);
627 void DumpObjExports (FILE* F, unsigned long Offset)
628 /* Dump the exports in the object file */
631 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
636 /* Seek to the header position and read the header */
637 FileSeek (F, Offset);
638 ReadObjHeader (F, &H);
640 /* Seek to the start of the string pool and read it */
641 FileSeek (F, Offset + H.StrPoolOffs);
642 ReadStrPool (F, &StrPool);
644 /* Seek to the start of the exports */
645 FileSeek (F, Offset + H.ExportOffs);
647 /* Output a header */
648 printf (" Exports:\n");
650 /* Read the number of exports and print it */
652 printf (" Count:%27u\n", Count);
654 /* Read and print all exports */
655 for (I = 0; I < Count; ++I) {
657 unsigned long Value = 0;
660 unsigned char ConDes [CD_TYPE_COUNT];
665 /* Read the data for one export */
667 ReadData (F, ConDes, GET_EXP_CONDES_COUNT (Type));
668 Name = GetString (&StrPool, ReadVar (F));
670 if (IS_EXP_EXPR (Type)) {
677 ReadFilePos (F, &Pos);
679 /* Print the header */
680 printf (" Index:%27u\n", I);
683 printf (" Type:%22s0x%02X (%s)\n", "", Type, GetExportFlags (Type, ConDes));
684 printf (" Name:%*s\"%s\"\n", 24-Len, "", Name);
686 printf (" Value:%15s0x%08lX (%lu)\n", "", Value, Value);
690 /* Destroy the string pool */
691 DestroyStrPool (&StrPool);
696 void DumpObjDbgSyms (FILE* F, unsigned long Offset)
697 /* Dump the debug symbols from an object file */
700 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
705 /* Seek to the header position and read the header */
706 FileSeek (F, Offset);
707 ReadObjHeader (F, &H);
709 /* Seek to the start of the string pool and read it */
710 FileSeek (F, Offset + H.StrPoolOffs);
711 ReadStrPool (F, &StrPool);
713 /* Seek to the start of the debug syms */
714 FileSeek (F, Offset + H.DbgSymOffs);
716 /* Output a header */
717 printf (" Debug symbols:\n");
719 /* Check if the object file was compiled with debug info */
720 if ((H.Flags & OBJ_FLAGS_DBGINFO) == 0) {
721 /* Print that there no debug symbols and bail out */
722 printf (" Count:%27u\n", 0);
726 /* Read the number of exports and print it */
728 printf (" Count:%27u\n", Count);
730 /* Read and print all debug symbols */
731 for (I = 0; I < Count; ++I) {
733 unsigned long Value = 0;
736 unsigned char ConDes [CD_TYPE_COUNT];
740 /* Read the data for one symbol */
742 ReadData (F, ConDes, GET_EXP_CONDES_COUNT (Type));
743 Name = GetString (&StrPool, ReadVar (F));
745 if (IS_EXP_EXPR (Type)) {
752 ReadFilePos (F, &Pos);
754 /* Print the header */
755 printf (" Index:%27u\n", I);
758 printf (" Type:%22s0x%02X (%s)\n", "", Type, GetExportFlags (Type, ConDes));
759 printf (" Name:%*s\"%s\"\n", 24-Len, "", Name);
761 printf (" Value:%15s0x%08lX (%lu)\n", "", Value, Value);
765 /* Destroy the string pool */
766 DestroyStrPool (&StrPool);
771 void DumpObjLineInfo (FILE* F, unsigned long Offset)
772 /* Dump the line info from an object file */
775 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
779 /* Seek to the header position and read the header */
780 FileSeek (F, Offset);
781 ReadObjHeader (F, &H);
783 /* Seek to the start of the string pool and read it */
784 FileSeek (F, Offset + H.StrPoolOffs);
785 ReadStrPool (F, &StrPool);
787 /* Seek to the start of line infos */
788 FileSeek (F, Offset + H.LineInfoOffs);
790 /* Output a header */
791 printf (" Line info:\n");
793 /* Check if the object file was compiled with debug info */
794 if ((H.Flags & OBJ_FLAGS_DBGINFO) == 0) {
795 /* Print that there no line infos and bail out */
796 printf (" Count:%27u\n", 0);
800 /* Read the number of line infos and print it */
802 printf (" Count:%27u\n", Count);
804 /* Read and print all line infos */
805 for (I = 0; I < Count; ++I) {
809 /* Read one line info */
810 ReadFilePos (F, &Pos);
812 /* Print the header */
813 printf (" Index:%27u\n", I);
816 printf (" Line:%26lu\n", Pos.Line);
817 printf (" Col:%27u\n", Pos.Col);
818 printf (" Name:%26u\n", Pos.Name);
821 /* Destroy the string pool */
822 DestroyStrPool (&StrPool);
827 void DumpObjSegSize (FILE* F, unsigned long Offset)
828 /* Dump the sizes of the segment in the object file */
831 Collection StrPool = AUTO_COLLECTION_INITIALIZER;
834 /* Seek to the header position and read the header */
835 FileSeek (F, Offset);
836 ReadObjHeader (F, &H);
838 /* Seek to the start of the string pool and read it */
839 FileSeek (F, Offset + H.StrPoolOffs);
840 ReadStrPool (F, &StrPool);
842 /* Seek to the start of the segments */
843 FileSeek (F, Offset + H.SegOffs);
845 /* Output a header */
846 printf (" Segment sizes:\n");
848 /* Read the number of segments */
851 /* Read and print the sizes of all segments */
854 /* Read the data for one segments */
855 char* Name = ReadStr (F);
856 unsigned Len = strlen (Name);
857 unsigned long Size = Read32 (F);
859 /* Skip alignment and type */
863 /* Print the size for this segment */
864 printf (" %s:%*s%6lu\n", Name, 24-Len, "", Size);
869 /* Skip the fragments for this segment, counting them */
871 unsigned FragSize = SkipFragment (F);
872 if (FragSize > Size) {
873 /* OOPS - file data invalid */
874 Error ("Invalid fragment data - file corrupt!");
880 /* Destroy the string pool */
881 DestroyStrPool (&StrPool);