string table in the object file that (currently) holds all identifiers from the import, export and debug info sections. The plan is to put all strings into this table, so we have them in a central place and don't waste memory. Apart from that, the indices are unique, so comparing strings should be a lot easier than before (as soon as the programs take advantage of this fact, which is currently not the case). git-svn-id: svn://svn.cc65.org/cc65/trunk@2169 b7a2c559-68d2-44c3-8de9-860c34a00d81
886 lines
22 KiB
C
886 lines
22 KiB
C
/*****************************************************************************/
|
|
/* */
|
|
/* dump.c */
|
|
/* */
|
|
/* Dump subroutines for the od65 object file dump utility */
|
|
/* */
|
|
/* */
|
|
/* */
|
|
/* (C) 2002-2003 Ullrich von Bassewitz */
|
|
/* Römerstrasse 52 */
|
|
/* D-70794 Filderstadt */
|
|
/* EMail: uz@cc65.org */
|
|
/* */
|
|
/* */
|
|
/* This software is provided 'as-is', without any expressed or implied */
|
|
/* warranty. In no event will the authors be held liable for any damages */
|
|
/* arising from the use of this software. */
|
|
/* */
|
|
/* Permission is granted to anyone to use this software for any purpose, */
|
|
/* including commercial applications, and to alter it and redistribute it */
|
|
/* freely, subject to the following restrictions: */
|
|
/* */
|
|
/* 1. The origin of this software must not be misrepresented; you must not */
|
|
/* claim that you wrote the original software. If you use this software */
|
|
/* in a product, an acknowledgment in the product documentation would be */
|
|
/* appreciated but is not required. */
|
|
/* 2. Altered source versions must be plainly marked as such, and must not */
|
|
/* be misrepresented as being the original software. */
|
|
/* 3. This notice may not be removed or altered from any source */
|
|
/* distribution. */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
/* common */
|
|
#include "cddefs.h"
|
|
#include "coll.h"
|
|
#include "exprdefs.h"
|
|
#include "filepos.h"
|
|
#include "objdefs.h"
|
|
#include "optdefs.h"
|
|
#include "segdefs.h"
|
|
#include "symdefs.h"
|
|
#include "xmalloc.h"
|
|
|
|
/* od65 */
|
|
#include "error.h"
|
|
#include "fileio.h"
|
|
#include "dump.h"
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Code */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
static void DestroyStrPool (Collection* C)
|
|
/* Free all strings in the given pool plus the item pointers. Note: The
|
|
* collection may not be reused later.
|
|
*/
|
|
{
|
|
unsigned I;
|
|
for (I = 0; I < CollCount (C); ++I) {
|
|
xfree (CollAtUnchecked (C, I));
|
|
}
|
|
DoneCollection (C);
|
|
}
|
|
|
|
|
|
|
|
static const char* GetString (const Collection* C, unsigned Index)
|
|
/* Get a string from a collection. In fact, this function calls CollConstAt,
|
|
* but will print a somewhat more readable error message if the index is out
|
|
* of bounds.
|
|
*/
|
|
{
|
|
if (Index >= CollCount (C)) {
|
|
Error ("Invalid string index (%u) - file corrupt!", Index);
|
|
}
|
|
return CollConstAt (C, Index);
|
|
}
|
|
|
|
|
|
|
|
static void DumpObjHeaderSection (const char* Name,
|
|
unsigned long Offset,
|
|
unsigned long Size)
|
|
/* Dump a header section */
|
|
{
|
|
printf (" %s:\n", Name);
|
|
printf (" Offset:%24lu\n", Offset);
|
|
printf (" Size: %24lu\n", Size);
|
|
}
|
|
|
|
|
|
|
|
static char* TimeToStr (unsigned long Time)
|
|
/* Convert the time into a string and return it */
|
|
{
|
|
/* Get the time and convert to string */
|
|
time_t T = (time_t) Time;
|
|
char* S = asctime (localtime (&T));
|
|
|
|
/* Remove the trailing newline */
|
|
unsigned Len = strlen (S);
|
|
if (Len > 0 && S[Len-1] == '\n') {
|
|
S[Len-1 ] = '\0';
|
|
}
|
|
|
|
/* Return the time string */
|
|
return S;
|
|
}
|
|
|
|
|
|
|
|
static void SkipExpr (FILE* F)
|
|
/* Skip an expression from the given file */
|
|
{
|
|
/* Read the node tag and handle NULL nodes */
|
|
unsigned char Op = Read8 (F);
|
|
if (Op == EXPR_NULL) {
|
|
return;
|
|
}
|
|
|
|
/* Check the tag and handle the different expression types */
|
|
if (EXPR_IS_LEAF (Op)) {
|
|
switch (Op) {
|
|
|
|
case EXPR_LITERAL:
|
|
(void) Read32Signed (F);
|
|
break;
|
|
|
|
case EXPR_SYMBOL:
|
|
/* Read the import number */
|
|
(void) ReadVar (F);
|
|
break;
|
|
|
|
case EXPR_SECTION:
|
|
/* Read the segment number */
|
|
(void) Read8 (F);
|
|
break;
|
|
|
|
default:
|
|
Error ("Invalid expression op: %02X", Op);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Not a leaf node */
|
|
SkipExpr (F);
|
|
SkipExpr (F);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static unsigned SkipFragment (FILE* F)
|
|
/* Skip a fragment from the given file and return the size */
|
|
{
|
|
FilePos Pos;
|
|
unsigned long Size;
|
|
|
|
/* Read the fragment type */
|
|
unsigned char Type = Read8 (F);
|
|
|
|
/* Extract the check mask */
|
|
unsigned char Check = Type & FRAG_CHECKMASK;
|
|
Type &= ~FRAG_CHECKMASK;
|
|
|
|
/* Handle the different fragment types */
|
|
switch (Type) {
|
|
|
|
case FRAG_LITERAL:
|
|
Size = ReadVar (F);
|
|
break;
|
|
|
|
case FRAG_EXPR8:
|
|
case FRAG_EXPR16:
|
|
case FRAG_EXPR24:
|
|
case FRAG_EXPR32:
|
|
case FRAG_SEXPR8:
|
|
case FRAG_SEXPR16:
|
|
case FRAG_SEXPR24:
|
|
case FRAG_SEXPR32:
|
|
Size = Type & FRAG_BYTEMASK;
|
|
break;
|
|
|
|
case FRAG_FILL:
|
|
Size = ReadVar (F);
|
|
break;
|
|
|
|
default:
|
|
Error ("Unknown fragment type: 0x%02X", Type);
|
|
/* NOTREACHED */
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/* Now read the fragment data */
|
|
switch (Type & FRAG_TYPEMASK) {
|
|
|
|
case FRAG_LITERAL:
|
|
/* Literal data */
|
|
FileSeek (F, ftell (F) + Size);
|
|
break;
|
|
|
|
case FRAG_EXPR:
|
|
case FRAG_SEXPR:
|
|
/* An expression */
|
|
SkipExpr (F);
|
|
break;
|
|
|
|
}
|
|
|
|
/* Skip the check expression if we have one */
|
|
if (Check & FRAG_CHECK_WARN) {
|
|
SkipExpr (F);
|
|
}
|
|
if (Check & FRAG_CHECK_ERROR) {
|
|
SkipExpr (F);
|
|
}
|
|
|
|
/* Skip the file position of the fragment */
|
|
ReadFilePos (F, &Pos);
|
|
|
|
/* Skip the additional line info */
|
|
(void) ReadVar (F);
|
|
|
|
/* Return the size */
|
|
return Size;
|
|
}
|
|
|
|
|
|
|
|
static const char* GetExportFlags (unsigned Flags, const unsigned char* ConDes)
|
|
/* Get the export flags as a (static) string */
|
|
{
|
|
/* Static buffer */
|
|
static char TypeDesc[256];
|
|
static char* T;
|
|
|
|
unsigned Count;
|
|
unsigned I;
|
|
|
|
/* Adressing mode */
|
|
TypeDesc[0] = '\0';
|
|
switch (Flags & EXP_MASK_SIZE) {
|
|
case EXP_ABS: strcat (TypeDesc, "EXP_ABS"); break;
|
|
case EXP_ZP: strcat (TypeDesc, "EXP_ZP"); break;
|
|
}
|
|
|
|
/* Type of expression */
|
|
switch (Flags & EXP_MASK_VAL) {
|
|
case EXP_CONST: strcat (TypeDesc, ",EXP_CONST"); break;
|
|
case EXP_EXPR: strcat (TypeDesc, ",EXP_EXPR"); break;
|
|
}
|
|
|
|
/* Constructor/destructor declarations */
|
|
T = TypeDesc + strlen (TypeDesc);
|
|
Count = GET_EXP_CONDES_COUNT (Flags);
|
|
if (Count > 0) {
|
|
T += sprintf (T, ",EXP_CONDES=");
|
|
for (I = 0; I < Count; ++I) {
|
|
unsigned Type = CD_GET_TYPE (ConDes[I]);
|
|
unsigned Prio = CD_GET_PRIO (ConDes[I]);
|
|
if (I > 0) {
|
|
*T++ = ',';
|
|
}
|
|
T += sprintf (T, "[%u,%u]", Type, Prio);
|
|
}
|
|
}
|
|
|
|
/* Return the result */
|
|
return TypeDesc;
|
|
}
|
|
|
|
|
|
|
|
void DumpObjHeader (FILE* F, unsigned long Offset)
|
|
/* Dump the header of the given object file */
|
|
{
|
|
ObjHeader H;
|
|
|
|
/* Seek to the header position */
|
|
FileSeek (F, Offset);
|
|
|
|
/* Read the header */
|
|
ReadObjHeader (F, &H);
|
|
|
|
/* Now dump the information */
|
|
|
|
/* Output a header */
|
|
printf (" Header:\n");
|
|
|
|
/* Magic */
|
|
printf (" Magic:%17s0x%08lX\n", "", H.Magic);
|
|
|
|
/* Version */
|
|
printf (" Version:%25u\n", H.Version);
|
|
|
|
/* Flags */
|
|
printf (" Flags:%21s0x%04X (", "", H.Flags);
|
|
if (H.Flags & OBJ_FLAGS_DBGINFO) {
|
|
printf ("OBJ_FLAGS_DBGINFO");
|
|
}
|
|
printf (")\n");
|
|
|
|
/* Options */
|
|
DumpObjHeaderSection ("Options", H.OptionOffs, H.OptionSize);
|
|
|
|
/* Files */
|
|
DumpObjHeaderSection ("Files", H.FileOffs, H.FileSize);
|
|
|
|
/* Segments */
|
|
DumpObjHeaderSection ("Segments", H.SegOffs, H.SegSize);
|
|
|
|
/* Imports */
|
|
DumpObjHeaderSection ("Imports", H.ImportOffs, H.ImportSize);
|
|
|
|
/* Exports */
|
|
DumpObjHeaderSection ("Exports", H.ExportOffs, H.ExportSize);
|
|
|
|
/* Debug symbols */
|
|
DumpObjHeaderSection ("Debug symbols", H.DbgSymOffs, H.DbgSymSize);
|
|
|
|
/* Line infos */
|
|
DumpObjHeaderSection ("Line infos", H.LineInfoOffs, H.LineInfoSize);
|
|
|
|
/* String pool */
|
|
DumpObjHeaderSection ("String pool", H.StrPoolOffs, H.StrPoolSize);
|
|
}
|
|
|
|
|
|
|
|
void DumpObjOptions (FILE* F, unsigned long Offset)
|
|
/* Dump the file options */
|
|
{
|
|
ObjHeader H;
|
|
Collection StrPool = AUTO_COLLECTION_INITIALIZER;
|
|
unsigned Count;
|
|
unsigned I;
|
|
|
|
/* Seek to the header position and read the header */
|
|
FileSeek (F, Offset);
|
|
ReadObjHeader (F, &H);
|
|
|
|
/* Seek to the start of the string pool and read it */
|
|
FileSeek (F, Offset + H.StrPoolOffs);
|
|
ReadStrPool (F, &StrPool);
|
|
|
|
/* Seek to the start of the options */
|
|
FileSeek (F, Offset + H.OptionOffs);
|
|
|
|
/* Output a header */
|
|
printf (" Options:\n");
|
|
|
|
/* Read the number of options and print it */
|
|
Count = ReadVar (F);
|
|
printf (" Count:%27u\n", Count);
|
|
|
|
/* Read and print all options */
|
|
for (I = 0; I < Count; ++I) {
|
|
|
|
unsigned long ArgNum;
|
|
char* ArgStr;
|
|
unsigned ArgLen;
|
|
|
|
/* Read the type of the option */
|
|
unsigned char Type = Read8 (F);
|
|
|
|
/* Get the type of the argument */
|
|
unsigned char ArgType = Type & OPT_ARGMASK;
|
|
|
|
/* Determine which option follows */
|
|
const char* TypeDesc;
|
|
switch (Type) {
|
|
case OPT_COMMENT: TypeDesc = "OPT_COMMENT"; break;
|
|
case OPT_AUTHOR: TypeDesc = "OPT_AUTHOR"; break;
|
|
case OPT_TRANSLATOR:TypeDesc = "OPT_TRANSLATOR"; break;
|
|
case OPT_COMPILER: TypeDesc = "OPT_COMPILER"; break;
|
|
case OPT_OS: TypeDesc = "OPT_OS"; break;
|
|
case OPT_DATETIME: TypeDesc = "OPT_DATETIME"; break;
|
|
default: TypeDesc = "OPT_UNKNOWN"; break;
|
|
}
|
|
|
|
/* Print the header */
|
|
printf (" Index:%27u\n", I);
|
|
|
|
/* Print the data */
|
|
printf (" Type:%22s0x%02X (%s)\n", "", Type, TypeDesc);
|
|
switch (ArgType) {
|
|
|
|
case OPT_ARGSTR:
|
|
ArgStr = ReadStr (F);
|
|
ArgLen = strlen (ArgStr);
|
|
printf (" Data:%*s\"%s\"\n", 24-ArgLen, "", ArgStr);
|
|
xfree (ArgStr);
|
|
break;
|
|
|
|
case OPT_ARGNUM:
|
|
ArgNum = Read32 (F);
|
|
printf (" Data:%26lu", ArgNum);
|
|
if (Type == OPT_DATETIME) {
|
|
/* Print the time as a string */
|
|
printf (" (%s)", TimeToStr (ArgNum));
|
|
}
|
|
printf ("\n");
|
|
break;
|
|
|
|
default:
|
|
/* Unknown argument type. This means that we cannot determine
|
|
* the option length, so we cannot proceed.
|
|
*/
|
|
Error ("Unknown option type: 0x%02X", Type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Destroy the string pool */
|
|
DestroyStrPool (&StrPool);
|
|
}
|
|
|
|
|
|
|
|
void DumpObjFiles (FILE* F, unsigned long Offset)
|
|
/* Dump the source files */
|
|
{
|
|
ObjHeader H;
|
|
Collection StrPool = AUTO_COLLECTION_INITIALIZER;
|
|
unsigned Count;
|
|
unsigned I;
|
|
|
|
/* Seek to the header position and read the header */
|
|
FileSeek (F, Offset);
|
|
ReadObjHeader (F, &H);
|
|
|
|
/* Seek to the start of the string pool and read it */
|
|
FileSeek (F, Offset + H.StrPoolOffs);
|
|
ReadStrPool (F, &StrPool);
|
|
|
|
/* Seek to the start of the source files */
|
|
FileSeek (F, Offset + H.FileOffs);
|
|
|
|
/* Output a header */
|
|
printf (" Files:\n");
|
|
|
|
/* Read the number of files and print it */
|
|
Count = ReadVar (F);
|
|
printf (" Count:%27u\n", Count);
|
|
|
|
/* Read and print all files */
|
|
for (I = 0; I < Count; ++I) {
|
|
|
|
/* Read the data for one file */
|
|
unsigned long MTime = Read32 (F);
|
|
unsigned long Size = Read32 (F);
|
|
char* Name = ReadStr (F);
|
|
unsigned Len = strlen (Name);
|
|
|
|
/* Print the header */
|
|
printf (" Index:%27u\n", I);
|
|
|
|
/* Print the data */
|
|
printf (" Name:%*s\"%s\"\n", 24-Len, "", Name);
|
|
printf (" Size:%26lu\n", Size);
|
|
printf (" Modification time:%13lu (%s)\n", MTime, TimeToStr (MTime));
|
|
|
|
/* Free the Name */
|
|
xfree (Name);
|
|
}
|
|
|
|
/* Destroy the string pool */
|
|
DestroyStrPool (&StrPool);
|
|
}
|
|
|
|
|
|
|
|
void DumpObjSegments (FILE* F, unsigned long Offset)
|
|
/* Dump the segments in the object file */
|
|
{
|
|
ObjHeader H;
|
|
Collection StrPool = AUTO_COLLECTION_INITIALIZER;
|
|
unsigned Count;
|
|
unsigned I;
|
|
unsigned FragCount;
|
|
|
|
/* Seek to the header position and read the header */
|
|
FileSeek (F, Offset);
|
|
ReadObjHeader (F, &H);
|
|
|
|
/* Seek to the start of the string pool and read it */
|
|
FileSeek (F, Offset + H.StrPoolOffs);
|
|
ReadStrPool (F, &StrPool);
|
|
|
|
/* Seek to the start of the segments */
|
|
FileSeek (F, Offset + H.SegOffs);
|
|
|
|
/* Output a header */
|
|
printf (" Segments:\n");
|
|
|
|
/* Read the number of segments and print it */
|
|
Count = ReadVar (F);
|
|
printf (" Count:%27u\n", Count);
|
|
|
|
/* Read and print all segments */
|
|
for (I = 0; I < Count; ++I) {
|
|
|
|
/* Read the data for one segments */
|
|
char* Name = ReadStr (F);
|
|
unsigned Len = strlen (Name);
|
|
unsigned long Size = Read32 (F);
|
|
unsigned Align = (1U << Read8 (F));
|
|
unsigned char Type = Read8 (F);
|
|
|
|
/* Get the description for the type */
|
|
const char* TypeDesc;
|
|
switch (Type) {
|
|
case SEGTYPE_DEFAULT: TypeDesc = "SEGTYPE_DEFAULT"; break;
|
|
case SEGTYPE_ABS: TypeDesc = "SEGTYPE_ABS"; break;
|
|
case SEGTYPE_ZP: TypeDesc = "SEGTYPE_ZP"; break;
|
|
case SEGTYPE_FAR: TypeDesc = "SEGTYPE_FAR"; break;
|
|
default: TypeDesc = "SEGTYPE_UNKNOWN"; break;
|
|
}
|
|
|
|
/* Print the header */
|
|
printf (" Index:%27u\n", I);
|
|
|
|
/* Print the data */
|
|
printf (" Name:%*s\"%s\"\n", 24-Len, "", Name);
|
|
printf (" Size:%26lu\n", Size);
|
|
printf (" Alignment:%21u\n", Align);
|
|
printf (" Type:%22s0x%02X (%s)\n", "", Type, TypeDesc);
|
|
|
|
/* Free the Name */
|
|
xfree (Name);
|
|
|
|
/* Skip the fragments for this segment, counting them */
|
|
FragCount = 0;
|
|
while (Size > 0) {
|
|
unsigned FragSize = SkipFragment (F);
|
|
if (FragSize > Size) {
|
|
/* OOPS - file data invalid */
|
|
Error ("Invalid fragment data - file corrupt!");
|
|
}
|
|
Size -= FragSize;
|
|
++FragCount;
|
|
}
|
|
|
|
/* Print the fragment count */
|
|
printf (" Fragment count:%16u\n", FragCount);
|
|
}
|
|
|
|
/* Destroy the string pool */
|
|
DestroyStrPool (&StrPool);
|
|
}
|
|
|
|
|
|
|
|
void DumpObjImports (FILE* F, unsigned long Offset)
|
|
/* Dump the imports in the object file */
|
|
{
|
|
ObjHeader H;
|
|
Collection StrPool = AUTO_COLLECTION_INITIALIZER;
|
|
unsigned Count;
|
|
unsigned I;
|
|
FilePos Pos;
|
|
|
|
/* Seek to the header position and read the header */
|
|
FileSeek (F, Offset);
|
|
ReadObjHeader (F, &H);
|
|
|
|
/* Seek to the start of the string pool and read it */
|
|
FileSeek (F, Offset + H.StrPoolOffs);
|
|
ReadStrPool (F, &StrPool);
|
|
|
|
/* Seek to the start of the imports */
|
|
FileSeek (F, Offset + H.ImportOffs);
|
|
|
|
/* Output a header */
|
|
printf (" Imports:\n");
|
|
|
|
/* Read the number of imports and print it */
|
|
Count = ReadVar (F);
|
|
printf (" Count:%27u\n", Count);
|
|
|
|
/* Read and print all imports */
|
|
for (I = 0; I < Count; ++I) {
|
|
|
|
const char* TypeDesc;
|
|
|
|
/* Read the data for one import */
|
|
unsigned char Type = Read8 (F);
|
|
const char* Name = GetString (&StrPool, ReadVar (F));
|
|
unsigned Len = strlen (Name);
|
|
ReadFilePos (F, &Pos);
|
|
|
|
/* Get a description for the type */
|
|
switch (Type) {
|
|
case IMP_ZP: TypeDesc = "IMP_ZP"; break;
|
|
case IMP_ABS: TypeDesc = "IMP_ABS"; break;
|
|
default: TypeDesc = "IMP_UNKNOWN"; break;
|
|
}
|
|
|
|
/* Print the header */
|
|
printf (" Index:%27u\n", I);
|
|
|
|
/* Print the data */
|
|
printf (" Type:%22s0x%02X (%s)\n", "", Type, TypeDesc);
|
|
printf (" Name:%*s\"%s\"\n", 24-Len, "", Name);
|
|
}
|
|
|
|
/* Destroy the string pool */
|
|
DestroyStrPool (&StrPool);
|
|
}
|
|
|
|
|
|
|
|
void DumpObjExports (FILE* F, unsigned long Offset)
|
|
/* Dump the exports in the object file */
|
|
{
|
|
ObjHeader H;
|
|
Collection StrPool = AUTO_COLLECTION_INITIALIZER;
|
|
unsigned Count;
|
|
unsigned I;
|
|
FilePos Pos;
|
|
|
|
/* Seek to the header position and read the header */
|
|
FileSeek (F, Offset);
|
|
ReadObjHeader (F, &H);
|
|
|
|
/* Seek to the start of the string pool and read it */
|
|
FileSeek (F, Offset + H.StrPoolOffs);
|
|
ReadStrPool (F, &StrPool);
|
|
|
|
/* Seek to the start of the exports */
|
|
FileSeek (F, Offset + H.ExportOffs);
|
|
|
|
/* Output a header */
|
|
printf (" Exports:\n");
|
|
|
|
/* Read the number of exports and print it */
|
|
Count = ReadVar (F);
|
|
printf (" Count:%27u\n", Count);
|
|
|
|
/* Read and print all exports */
|
|
for (I = 0; I < Count; ++I) {
|
|
|
|
unsigned long Value = 0;
|
|
int HaveValue;
|
|
unsigned char Type;
|
|
unsigned char ConDes [CD_TYPE_COUNT];
|
|
const char* Name;
|
|
unsigned Len;
|
|
|
|
|
|
/* Read the data for one export */
|
|
Type = Read8 (F);
|
|
ReadData (F, ConDes, GET_EXP_CONDES_COUNT (Type));
|
|
Name = GetString (&StrPool, ReadVar (F));
|
|
Len = strlen (Name);
|
|
if (IS_EXP_EXPR (Type)) {
|
|
SkipExpr (F);
|
|
HaveValue = 0;
|
|
} else {
|
|
Value = Read32 (F);
|
|
HaveValue = 1;
|
|
}
|
|
ReadFilePos (F, &Pos);
|
|
|
|
/* Print the header */
|
|
printf (" Index:%27u\n", I);
|
|
|
|
/* Print the data */
|
|
printf (" Type:%22s0x%02X (%s)\n", "", Type, GetExportFlags (Type, ConDes));
|
|
printf (" Name:%*s\"%s\"\n", 24-Len, "", Name);
|
|
if (HaveValue) {
|
|
printf (" Value:%15s0x%08lX (%lu)\n", "", Value, Value);
|
|
}
|
|
}
|
|
|
|
/* Destroy the string pool */
|
|
DestroyStrPool (&StrPool);
|
|
}
|
|
|
|
|
|
|
|
void DumpObjDbgSyms (FILE* F, unsigned long Offset)
|
|
/* Dump the debug symbols from an object file */
|
|
{
|
|
ObjHeader H;
|
|
Collection StrPool = AUTO_COLLECTION_INITIALIZER;
|
|
unsigned Count;
|
|
unsigned I;
|
|
FilePos Pos;
|
|
|
|
/* Seek to the header position and read the header */
|
|
FileSeek (F, Offset);
|
|
ReadObjHeader (F, &H);
|
|
|
|
/* Seek to the start of the string pool and read it */
|
|
FileSeek (F, Offset + H.StrPoolOffs);
|
|
ReadStrPool (F, &StrPool);
|
|
|
|
/* Seek to the start of the debug syms */
|
|
FileSeek (F, Offset + H.DbgSymOffs);
|
|
|
|
/* Output a header */
|
|
printf (" Debug symbols:\n");
|
|
|
|
/* Check if the object file was compiled with debug info */
|
|
if ((H.Flags & OBJ_FLAGS_DBGINFO) == 0) {
|
|
/* Print that there no debug symbols and bail out */
|
|
printf (" Count:%27u\n", 0);
|
|
return;
|
|
}
|
|
|
|
/* Read the number of exports and print it */
|
|
Count = ReadVar (F);
|
|
printf (" Count:%27u\n", Count);
|
|
|
|
/* Read and print all debug symbols */
|
|
for (I = 0; I < Count; ++I) {
|
|
|
|
unsigned long Value = 0;
|
|
int HaveValue;
|
|
unsigned char Type;
|
|
unsigned char ConDes [CD_TYPE_COUNT];
|
|
const char* Name;
|
|
unsigned Len;
|
|
|
|
/* Read the data for one symbol */
|
|
Type = Read8 (F);
|
|
ReadData (F, ConDes, GET_EXP_CONDES_COUNT (Type));
|
|
Name = GetString (&StrPool, ReadVar (F));
|
|
Len = strlen (Name);
|
|
if (IS_EXP_EXPR (Type)) {
|
|
SkipExpr (F);
|
|
HaveValue = 0;
|
|
} else {
|
|
Value = Read32 (F);
|
|
HaveValue = 1;
|
|
}
|
|
ReadFilePos (F, &Pos);
|
|
|
|
/* Print the header */
|
|
printf (" Index:%27u\n", I);
|
|
|
|
/* Print the data */
|
|
printf (" Type:%22s0x%02X (%s)\n", "", Type, GetExportFlags (Type, ConDes));
|
|
printf (" Name:%*s\"%s\"\n", 24-Len, "", Name);
|
|
if (HaveValue) {
|
|
printf (" Value:%15s0x%08lX (%lu)\n", "", Value, Value);
|
|
}
|
|
}
|
|
|
|
/* Destroy the string pool */
|
|
DestroyStrPool (&StrPool);
|
|
}
|
|
|
|
|
|
|
|
void DumpObjLineInfo (FILE* F, unsigned long Offset)
|
|
/* Dump the line info from an object file */
|
|
{
|
|
ObjHeader H;
|
|
Collection StrPool = AUTO_COLLECTION_INITIALIZER;
|
|
unsigned Count;
|
|
unsigned I;
|
|
|
|
/* Seek to the header position and read the header */
|
|
FileSeek (F, Offset);
|
|
ReadObjHeader (F, &H);
|
|
|
|
/* Seek to the start of the string pool and read it */
|
|
FileSeek (F, Offset + H.StrPoolOffs);
|
|
ReadStrPool (F, &StrPool);
|
|
|
|
/* Seek to the start of line infos */
|
|
FileSeek (F, Offset + H.LineInfoOffs);
|
|
|
|
/* Output a header */
|
|
printf (" Line info:\n");
|
|
|
|
/* Check if the object file was compiled with debug info */
|
|
if ((H.Flags & OBJ_FLAGS_DBGINFO) == 0) {
|
|
/* Print that there no line infos and bail out */
|
|
printf (" Count:%27u\n", 0);
|
|
return;
|
|
}
|
|
|
|
/* Read the number of line infos and print it */
|
|
Count = ReadVar (F);
|
|
printf (" Count:%27u\n", Count);
|
|
|
|
/* Read and print all line infos */
|
|
for (I = 0; I < Count; ++I) {
|
|
|
|
FilePos Pos;
|
|
|
|
/* Read one line info */
|
|
ReadFilePos (F, &Pos);
|
|
|
|
/* Print the header */
|
|
printf (" Index:%27u\n", I);
|
|
|
|
/* Print the data */
|
|
printf (" Line:%26lu\n", Pos.Line);
|
|
printf (" Col:%27u\n", Pos.Col);
|
|
printf (" Name:%26u\n", Pos.Name);
|
|
}
|
|
|
|
/* Destroy the string pool */
|
|
DestroyStrPool (&StrPool);
|
|
}
|
|
|
|
|
|
|
|
void DumpObjSegSize (FILE* F, unsigned long Offset)
|
|
/* Dump the sizes of the segment in the object file */
|
|
{
|
|
ObjHeader H;
|
|
Collection StrPool = AUTO_COLLECTION_INITIALIZER;
|
|
unsigned Count;
|
|
|
|
/* Seek to the header position and read the header */
|
|
FileSeek (F, Offset);
|
|
ReadObjHeader (F, &H);
|
|
|
|
/* Seek to the start of the string pool and read it */
|
|
FileSeek (F, Offset + H.StrPoolOffs);
|
|
ReadStrPool (F, &StrPool);
|
|
|
|
/* Seek to the start of the segments */
|
|
FileSeek (F, Offset + H.SegOffs);
|
|
|
|
/* Output a header */
|
|
printf (" Segment sizes:\n");
|
|
|
|
/* Read the number of segments */
|
|
Count = ReadVar (F);
|
|
|
|
/* Read and print the sizes of all segments */
|
|
while (Count--) {
|
|
|
|
/* Read the data for one segments */
|
|
char* Name = ReadStr (F);
|
|
unsigned Len = strlen (Name);
|
|
unsigned long Size = Read32 (F);
|
|
|
|
/* Skip alignment and type */
|
|
(void) Read8 (F);
|
|
(void) Read8 (F);
|
|
|
|
/* Print the size for this segment */
|
|
printf (" %s:%*s%6lu\n", Name, 24-Len, "", Size);
|
|
|
|
/* Free the Name */
|
|
xfree (Name);
|
|
|
|
/* Skip the fragments for this segment, counting them */
|
|
while (Size > 0) {
|
|
unsigned FragSize = SkipFragment (F);
|
|
if (FragSize > Size) {
|
|
/* OOPS - file data invalid */
|
|
Error ("Invalid fragment data - file corrupt!");
|
|
}
|
|
Size -= FragSize;
|
|
}
|
|
}
|
|
|
|
/* Destroy the string pool */
|
|
DestroyStrPool (&StrPool);
|
|
}
|
|
|
|
|
|
|