2001-09-07 18:04:38 +02:00
|
|
|
/*
|
|
|
|
* Made after:
|
|
|
|
* CVDump - Parses through a Visual Studio .DBG file in CodeView 4 format
|
|
|
|
* and dumps the info to STDOUT in a human-readable format
|
|
|
|
*
|
|
|
|
* Copyright 2000 John R. Sheets
|
2002-03-10 00:29:33 +01:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
2006-05-18 14:49:52 +02:00
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
2001-09-07 18:04:38 +02:00
|
|
|
*/
|
|
|
|
|
2002-05-16 20:29:11 +02:00
|
|
|
#include "config.h"
|
|
|
|
|
2001-09-07 18:04:38 +02:00
|
|
|
#include <stdlib.h>
|
2003-09-06 01:08:26 +02:00
|
|
|
#include <stdarg.h>
|
2001-09-07 18:04:38 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
2021-09-29 11:14:01 +02:00
|
|
|
#include "../tools.h"
|
2002-12-10 23:56:43 +01:00
|
|
|
#include "windef.h"
|
2003-09-06 01:08:26 +02:00
|
|
|
#include "winbase.h"
|
2001-09-07 18:04:38 +02:00
|
|
|
#include "winedump.h"
|
2007-01-05 21:42:39 +01:00
|
|
|
#include "wine/mscvpdb.h"
|
2001-09-07 18:04:38 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* .DBG File Layout:
|
|
|
|
*
|
|
|
|
* IMAGE_SEPARATE_DEBUG_HEADER
|
|
|
|
* IMAGE_SECTION_HEADER[]
|
|
|
|
* IMAGE_DEBUG_DIRECTORY[]
|
|
|
|
* OMFSignature
|
|
|
|
* debug data (typical example)
|
|
|
|
* - IMAGE_DEBUG_TYPE_MISC
|
|
|
|
* - IMAGE_DEBUG_TYPE_FPO
|
|
|
|
* - IMAGE_DEBUG_TYPE_CODEVIEW
|
|
|
|
* OMFDirHeader
|
|
|
|
* OMFDirEntry[]
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Descriptions:
|
|
|
|
*
|
2002-06-01 01:06:46 +02:00
|
|
|
* (hdr) IMAGE_SEPARATE_DEBUG_HEADER - .DBG-specific file header; holds info that
|
2001-09-07 18:04:38 +02:00
|
|
|
* applies to the file as a whole, including # of COFF sections, file offsets, etc.
|
|
|
|
* (hdr) IMAGE_SECTION_HEADER - list of COFF sections copied verbatim from .EXE;
|
|
|
|
* although this directory contains file offsets, these offsets are meaningless
|
|
|
|
* in the context of the .DBG file, because only the section headers are copied
|
2008-11-25 17:42:38 +01:00
|
|
|
* to the .DBG file... not the binary data it points to.
|
2001-09-07 18:04:38 +02:00
|
|
|
* (hdr) IMAGE_DEBUG_DIRECTORY - list of different formats of debug info contained in file
|
|
|
|
* (see IMAGE_DEBUG_TYPE_* descriptions below); tells where each section starts
|
|
|
|
* (hdr) OMFSignature (CV) - Contains "NBxx" signature, plus file offset telling how far
|
|
|
|
* into the IMAGE_DEBUG_TYPE_CODEVIEW section the OMFDirHeader and OMFDirEntry's sit
|
|
|
|
* (data) IMAGE_DEBUG_TYPE_MISC - usually holds name of original .EXE file
|
|
|
|
* (data) IMAGE_DEBUG_TYPE_FPO - Frame Pointer Optimization data; used for dealing with
|
|
|
|
* optimized stack frames (optional)
|
|
|
|
* (data) IMAGE_DEBUG_TYPE_CODEVIEW - *** THE GOOD STUFF ***
|
|
|
|
* This block of data contains all the symbol tables, line number info, etc.,
|
|
|
|
* that the Visual C++ debugger needs.
|
2002-06-01 01:06:46 +02:00
|
|
|
* (hdr) OMFDirHeader (CV) -
|
2001-09-07 18:04:38 +02:00
|
|
|
* (hdr) OMFDirEntry (CV) - list of subsections within CodeView debug data section
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The .DBG file typically has three arrays of directory entries, which tell
|
|
|
|
* the OS or debugger where in the file to look for the actual data
|
|
|
|
*
|
|
|
|
* IMAGE_SECTION_HEADER - number of entries determined by:
|
|
|
|
* (IMAGE_SEPARATE_DEBUG_HEADER.NumberOfSections)
|
|
|
|
*
|
|
|
|
* IMAGE_DEBUG_DIRECTORY - number of entries determined by:
|
|
|
|
* (IMAGE_SEPARATE_DEBUG_HEADER.DebugDirectorySize / sizeof (IMAGE_DEBUG_DIRECTORY))
|
|
|
|
*
|
|
|
|
* OMFDirEntry - number of entries determined by:
|
|
|
|
* (OMFDirHeader.cDir)
|
|
|
|
*/
|
|
|
|
|
2007-01-05 21:42:45 +01:00
|
|
|
extern const IMAGE_NT_HEADERS* PE_nt_headers;
|
|
|
|
static const void* cv_base /* = 0 */;
|
2002-09-11 02:49:48 +02:00
|
|
|
|
2013-12-03 23:36:09 +01:00
|
|
|
static BOOL dump_cv_sst_module(const OMFDirEntry* omfde)
|
2001-09-07 18:04:38 +02:00
|
|
|
{
|
2006-04-10 20:53:50 +02:00
|
|
|
const OMFModule* module;
|
|
|
|
const OMFSegDesc* segDesc;
|
2001-09-07 18:04:38 +02:00
|
|
|
int i;
|
|
|
|
|
|
|
|
module = PRD(Offset(cv_base) + omfde->lfo, sizeof(OMFModule));
|
|
|
|
if (!module) {printf("Can't get the OMF-Module, aborting\n"); return FALSE;}
|
|
|
|
|
|
|
|
printf(" olvNumber: %u\n", module->ovlNumber);
|
|
|
|
printf(" iLib: %u\n", module->iLib);
|
|
|
|
printf(" cSeg: %u\n", module->cSeg);
|
|
|
|
printf(" Style: %c%c\n", module->Style[0], module->Style[1]);
|
2002-06-01 01:06:46 +02:00
|
|
|
printf(" Name: %.*s\n",
|
2006-08-23 22:29:21 +02:00
|
|
|
*(const BYTE*)((const char*)(module + 1) + sizeof(OMFSegDesc) * module->cSeg),
|
|
|
|
(const char*)(module + 1) + sizeof(OMFSegDesc) * module->cSeg + 1);
|
2001-09-07 18:04:38 +02:00
|
|
|
|
|
|
|
segDesc = PRD(Offset(module + 1), sizeof(OMFSegDesc) * module->cSeg);
|
|
|
|
if (!segDesc) {printf("Can't get the OMF-SegDesc, aborting\n"); return FALSE;}
|
|
|
|
|
|
|
|
for (i = 0; i < module->cSeg; i++)
|
|
|
|
{
|
2007-01-05 21:42:45 +01:00
|
|
|
printf (" segment #%2d: offset = [0x%8x], size = [0x%8x]\n",
|
2001-09-07 18:04:38 +02:00
|
|
|
segDesc->Seg, segDesc->Off, segDesc->cbSeg);
|
|
|
|
segDesc++;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-12-03 23:36:09 +01:00
|
|
|
static BOOL dump_cv_sst_global_pub(const OMFDirEntry* omfde)
|
2001-09-07 18:04:38 +02:00
|
|
|
{
|
2006-04-10 20:53:50 +02:00
|
|
|
long fileoffset;
|
|
|
|
const OMFSymHash* header;
|
|
|
|
const BYTE* symbols;
|
2001-09-07 18:04:38 +02:00
|
|
|
|
|
|
|
fileoffset = Offset(cv_base) + omfde->lfo;
|
|
|
|
printf (" GlobalPub section starts at file offset 0x%lx\n", fileoffset);
|
|
|
|
printf (" Symbol table starts at 0x%lx\n", fileoffset + sizeof (OMFSymHash));
|
|
|
|
|
|
|
|
printf ("\n ----- Begin Symbol Table -----\n");
|
|
|
|
|
|
|
|
header = PRD(fileoffset, sizeof(OMFSymHash));
|
|
|
|
if (!header) {printf("Can't get OMF-SymHash, aborting\n");return FALSE;}
|
|
|
|
|
|
|
|
symbols = PRD(fileoffset + sizeof(OMFSymHash), header->cbSymbol);
|
|
|
|
if (!symbols) {printf("Can't OMF-SymHash details, aborting\n"); return FALSE;}
|
|
|
|
|
2007-01-05 21:42:53 +01:00
|
|
|
codeview_dump_symbols(symbols, header->cbSymbol);
|
2001-09-07 18:04:38 +02:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-12-03 23:36:09 +01:00
|
|
|
static BOOL dump_cv_sst_global_sym(const OMFDirEntry* omfde)
|
2001-09-07 18:04:38 +02:00
|
|
|
{
|
|
|
|
/*** NOT YET IMPLEMENTED ***/
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-12-03 23:36:09 +01:00
|
|
|
static BOOL dump_cv_sst_static_sym(const OMFDirEntry* omfde)
|
2001-09-07 18:04:38 +02:00
|
|
|
{
|
|
|
|
/*** NOT YET IMPLEMENTED ***/
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-12-03 23:36:09 +01:00
|
|
|
static BOOL dump_cv_sst_libraries(const OMFDirEntry* omfde)
|
2001-09-07 18:04:38 +02:00
|
|
|
{
|
|
|
|
/*** NOT YET IMPLEMENTED ***/
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-12-03 23:36:09 +01:00
|
|
|
static BOOL dump_cv_sst_global_types(const OMFDirEntry* omfde)
|
2001-09-07 18:04:38 +02:00
|
|
|
{
|
2007-01-05 21:43:06 +01:00
|
|
|
long fileoffset;
|
|
|
|
const OMFGlobalTypes*types;
|
|
|
|
const BYTE* data;
|
|
|
|
unsigned sz;
|
|
|
|
|
|
|
|
fileoffset = Offset(cv_base) + omfde->lfo;
|
|
|
|
printf (" GlobalTypes section starts at file offset 0x%lx\n", fileoffset);
|
|
|
|
|
|
|
|
printf ("\n ----- Begin Global Types Table -----\n");
|
|
|
|
|
|
|
|
types = PRD(fileoffset, sizeof(OMFGlobalTypes));
|
|
|
|
if (!types) {printf("Can't get OMF-GlobalTypes, aborting\n");return FALSE;}
|
|
|
|
|
|
|
|
sz = omfde->cb - sizeof(OMFGlobalTypes) - sizeof(DWORD) * types->cTypes;
|
|
|
|
data = PRD(fileoffset + sizeof(OMFGlobalTypes) + sizeof(DWORD) * types->cTypes, sz);
|
|
|
|
if (!data) {printf("Can't OMF-SymHash details, aborting\n"); return FALSE;}
|
|
|
|
|
2007-01-05 21:43:23 +01:00
|
|
|
/* doc says:
|
|
|
|
* - for NB07 & NB08 (that we don't support yet), offsets are from types
|
|
|
|
* - for NB09, offsets are from data
|
2008-01-16 02:28:45 +01:00
|
|
|
* For now, we only support the latter
|
2007-01-05 21:43:23 +01:00
|
|
|
*/
|
|
|
|
codeview_dump_types_from_offsets(data, (const DWORD*)(types + 1), types->cTypes);
|
2007-01-05 21:43:06 +01:00
|
|
|
|
2001-09-07 18:04:38 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-12-03 23:36:09 +01:00
|
|
|
static BOOL dump_cv_sst_seg_map(const OMFDirEntry* omfde)
|
2001-09-07 18:04:38 +02:00
|
|
|
{
|
2006-04-10 20:53:50 +02:00
|
|
|
const OMFSegMap* segMap;
|
|
|
|
const OMFSegMapDesc* segMapDesc;
|
2001-09-07 18:04:38 +02:00
|
|
|
int i;
|
|
|
|
|
|
|
|
segMap = PRD(Offset(cv_base) + omfde->lfo, sizeof(OMFSegMap));
|
|
|
|
if (!segMap) {printf("Can't get SegMap, aborting\n");return FALSE;}
|
|
|
|
|
|
|
|
printf(" cSeg: %u\n", segMap->cSeg);
|
|
|
|
printf(" cSegLog: %u\n", segMap->cSegLog);
|
|
|
|
|
|
|
|
segMapDesc = PRD(Offset(segMap + 1), segMap->cSeg * sizeof(OMFSegDesc));
|
|
|
|
if (!segMapDesc) {printf("Can't get SegDescr array, aborting\n");return FALSE;}
|
2002-06-01 01:06:46 +02:00
|
|
|
|
2001-09-07 18:04:38 +02:00
|
|
|
for (i = 0; i < segMap->cSeg; i++)
|
|
|
|
{
|
|
|
|
printf(" SegDescr #%2d\n", i + 1);
|
|
|
|
printf(" flags: %04X\n", segMapDesc[i].flags);
|
|
|
|
printf(" ovl: %u\n", segMapDesc[i].ovl);
|
|
|
|
printf(" group: %u\n", segMapDesc[i].group);
|
|
|
|
printf(" frame: %u\n", segMapDesc[i].frame);
|
|
|
|
printf(" iSegName: %u\n", segMapDesc[i].iSegName);
|
|
|
|
printf(" iClassName: %u\n", segMapDesc[i].iClassName);
|
|
|
|
printf(" offset: %lu\n", segMapDesc[i].offset);
|
|
|
|
printf(" cbSeg: %lu\n", segMapDesc[i].cbSeg);
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-12-03 23:36:09 +01:00
|
|
|
static BOOL dump_cv_sst_file_index(const OMFDirEntry* omfde)
|
2001-09-07 18:04:38 +02:00
|
|
|
{
|
|
|
|
/*** NOT YET IMPLEMENTED ***/
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-12-03 23:36:09 +01:00
|
|
|
static BOOL dump_cv_sst_src_module(const OMFDirEntry* omfde)
|
2001-09-07 18:04:38 +02:00
|
|
|
{
|
2006-04-10 20:53:50 +02:00
|
|
|
int i, j;
|
|
|
|
const BYTE* rawdata;
|
|
|
|
const unsigned long* seg_info_dw;
|
|
|
|
const unsigned short* seg_info_w;
|
|
|
|
unsigned ofs;
|
|
|
|
const OMFSourceModule* sourceModule;
|
|
|
|
const OMFSourceFile* sourceFile;
|
|
|
|
const OMFSourceLine* sourceLine;
|
2001-09-07 18:04:38 +02:00
|
|
|
|
|
|
|
rawdata = PRD(Offset(cv_base) + omfde->lfo, omfde->cb);
|
|
|
|
if (!rawdata) {printf("Can't get srcModule subsection details, aborting\n");return FALSE;}
|
|
|
|
|
|
|
|
/* FIXME: check ptr validity */
|
2006-08-23 22:29:21 +02:00
|
|
|
sourceModule = (const void*)rawdata;
|
2002-06-01 01:06:46 +02:00
|
|
|
printf (" Module table: Found %d file(s) and %d segment(s)\n",
|
2001-09-07 18:04:38 +02:00
|
|
|
sourceModule->cFile, sourceModule->cSeg);
|
|
|
|
for (i = 0; i < sourceModule->cFile; i++)
|
|
|
|
{
|
|
|
|
printf (" File #%2d begins at an offset of 0x%lx in this section\n",
|
|
|
|
i + 1, sourceModule->baseSrcFile[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* FIXME: check ptr validity */
|
2006-08-23 22:29:21 +02:00
|
|
|
seg_info_dw = (const void*)((const char*)(sourceModule + 1) +
|
2001-09-07 18:04:38 +02:00
|
|
|
sizeof(unsigned long) * (sourceModule->cFile - 1));
|
2006-08-23 22:29:21 +02:00
|
|
|
seg_info_w = (const unsigned short*)(&seg_info_dw[sourceModule->cSeg * 2]);
|
2001-09-07 18:04:38 +02:00
|
|
|
for (i = 0; i < sourceModule->cSeg; i++)
|
|
|
|
{
|
|
|
|
printf (" Segment #%2d start = 0x%lx, end = 0x%lx, seg index = %u\n",
|
2002-06-01 01:06:46 +02:00
|
|
|
i + 1, seg_info_dw[i * 2], seg_info_dw[(i * 2) + 1],
|
2001-09-07 18:04:38 +02:00
|
|
|
seg_info_w[i]);
|
|
|
|
}
|
2002-06-01 01:06:46 +02:00
|
|
|
ofs = sizeof(OMFSourceModule) + sizeof(unsigned long) * (sourceModule->cFile - 1) +
|
2001-09-07 18:04:38 +02:00
|
|
|
sourceModule->cSeg * (2 * sizeof(unsigned long) + sizeof(unsigned short));
|
|
|
|
ofs = (ofs + 3) & ~3;
|
|
|
|
|
|
|
|
/* the OMFSourceFile is quite unpleasant to use:
|
|
|
|
* we have first:
|
|
|
|
* unsigned short number of segments
|
2008-01-16 02:28:45 +01:00
|
|
|
* unsigned short reserved
|
2001-09-07 18:04:38 +02:00
|
|
|
* unsigned long baseSrcLn[# segments]
|
|
|
|
* unsigned long offset[2 * #segments]
|
|
|
|
* odd indices are start offsets
|
|
|
|
* even indices are end offsets
|
|
|
|
* unsigned char string length for file name
|
|
|
|
* char file name (length is previous field)
|
|
|
|
*/
|
|
|
|
/* FIXME: check ptr validity */
|
2006-08-23 22:29:21 +02:00
|
|
|
sourceFile = (const void*)(rawdata + ofs);
|
|
|
|
seg_info_dw = (const void*)((const char*)sourceFile + 2 * sizeof(unsigned short) +
|
2001-09-07 18:04:38 +02:00
|
|
|
sourceFile->cSeg * sizeof(unsigned long));
|
|
|
|
|
|
|
|
ofs += 2 * sizeof(unsigned short) + 3 * sourceFile->cSeg * sizeof(unsigned long);
|
|
|
|
|
2002-06-01 01:06:46 +02:00
|
|
|
printf(" File table: %.*s\n",
|
2006-08-23 22:29:21 +02:00
|
|
|
*(const BYTE*)((const char*)sourceModule + ofs), (const char*)sourceModule + ofs + 1);
|
2001-09-07 18:04:38 +02:00
|
|
|
|
|
|
|
for (i = 0; i < sourceFile->cSeg; i++)
|
|
|
|
{
|
|
|
|
printf (" Segment #%2d start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
|
|
|
|
i + 1, seg_info_dw[i * 2], seg_info_dw[(i * 2) + 1], sourceFile->baseSrcLn[i]);
|
|
|
|
}
|
|
|
|
/* add file name length */
|
2006-08-23 22:29:21 +02:00
|
|
|
ofs += *(const BYTE*)((const char*)sourceModule + ofs) + 1;
|
2001-09-07 18:04:38 +02:00
|
|
|
ofs = (ofs + 3) & ~3;
|
|
|
|
|
2002-06-01 01:06:46 +02:00
|
|
|
for (i = 0; i < sourceModule->cSeg; i++)
|
2001-09-07 18:04:38 +02:00
|
|
|
{
|
2006-08-23 22:29:21 +02:00
|
|
|
sourceLine = (const void*)(rawdata + ofs);
|
|
|
|
seg_info_dw = (const void*)((const char*)sourceLine + 2 * sizeof(unsigned short));
|
|
|
|
seg_info_w = (const void*)(&seg_info_dw[sourceLine->cLnOff]);
|
2002-06-01 01:06:46 +02:00
|
|
|
|
2001-09-07 18:04:38 +02:00
|
|
|
printf (" Line table #%2d: Found %d line numbers for segment index %d\n",
|
|
|
|
i, sourceLine->cLnOff, sourceLine->Seg);
|
2002-06-01 01:06:46 +02:00
|
|
|
|
2001-09-07 18:04:38 +02:00
|
|
|
for (j = 0; j < sourceLine->cLnOff; j++)
|
|
|
|
{
|
|
|
|
printf (" Pair #%2d: offset = [0x%8lx], linenumber = %d\n",
|
|
|
|
j + 1, seg_info_dw[j], seg_info_w[j]);
|
|
|
|
}
|
2002-06-01 01:06:46 +02:00
|
|
|
ofs += 2 * sizeof(unsigned short) +
|
2001-09-07 18:04:38 +02:00
|
|
|
sourceLine->cLnOff * (sizeof(unsigned long) + sizeof(unsigned short));
|
|
|
|
ofs = (ofs + 3) & ~3;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-12-03 23:36:09 +01:00
|
|
|
static BOOL dump_cv_sst_align_sym(const OMFDirEntry* omfde)
|
2001-09-07 18:04:38 +02:00
|
|
|
{
|
2007-01-05 21:43:06 +01:00
|
|
|
const char* rawdata = PRD(Offset(cv_base) + omfde->lfo, omfde->cb);
|
|
|
|
|
|
|
|
if (!rawdata) {printf("Can't get srcAlignSym subsection details, aborting\n");return FALSE;}
|
|
|
|
if (omfde->cb < sizeof(DWORD)) return TRUE;
|
|
|
|
codeview_dump_symbols(rawdata + sizeof(DWORD), omfde->cb - sizeof(DWORD));
|
2001-09-07 18:04:38 +02:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2006-04-10 20:53:50 +02:00
|
|
|
static void dump_codeview_all_modules(const OMFDirHeader *omfdh)
|
2001-09-07 18:04:38 +02:00
|
|
|
{
|
2001-09-21 23:03:53 +02:00
|
|
|
unsigned i;
|
2006-04-10 20:53:50 +02:00
|
|
|
const OMFDirEntry* dirEntry;
|
2001-09-07 18:04:38 +02:00
|
|
|
const char* str;
|
|
|
|
|
|
|
|
if (!omfdh || !omfdh->cDir) return;
|
|
|
|
|
|
|
|
dirEntry = PRD(Offset(omfdh + 1), omfdh->cDir * sizeof(OMFDirEntry));
|
|
|
|
if (!dirEntry) {printf("Can't read DirEntry array, aborting\n"); return;}
|
|
|
|
|
|
|
|
for (i = 0; i < omfdh->cDir; i++)
|
|
|
|
{
|
|
|
|
switch (dirEntry[i].SubSection)
|
|
|
|
{
|
|
|
|
case sstModule: str = "sstModule"; break;
|
|
|
|
case sstAlignSym: str = "sstAlignSym"; break;
|
|
|
|
case sstSrcModule: str = "sstSrcModule"; break;
|
|
|
|
case sstLibraries: str = "sstLibraries"; break;
|
|
|
|
case sstGlobalSym: str = "sstGlobalSym"; break;
|
|
|
|
case sstGlobalPub: str = "sstGlobalPub"; break;
|
|
|
|
case sstGlobalTypes: str = "sstGlobalTypes"; break;
|
|
|
|
case sstSegMap: str = "sstSegMap"; break;
|
|
|
|
case sstFileIndex: str = "sstFileIndex"; break;
|
|
|
|
case sstStaticSym: str = "sstStaticSym"; break;
|
|
|
|
default: str = "<undefined>"; break;
|
|
|
|
}
|
|
|
|
printf("Module #%2d (%p)\n", i + 1, &dirEntry[i]);
|
|
|
|
printf(" SubSection: %04X (%s)\n", dirEntry[i].SubSection, str);
|
|
|
|
printf(" iMod: %d\n", dirEntry[i].iMod);
|
2007-01-05 21:42:45 +01:00
|
|
|
printf(" lfo: %d\n", dirEntry[i].lfo);
|
|
|
|
printf(" cb: %u\n", dirEntry[i].cb);
|
2001-09-07 18:04:38 +02:00
|
|
|
|
|
|
|
switch (dirEntry[i].SubSection)
|
|
|
|
{
|
|
|
|
case sstModule: dump_cv_sst_module(&dirEntry[i]); break;
|
|
|
|
case sstAlignSym: dump_cv_sst_align_sym(&dirEntry[i]); break;
|
|
|
|
case sstSrcModule: dump_cv_sst_src_module(&dirEntry[i]); break;
|
|
|
|
case sstLibraries: dump_cv_sst_libraries(&dirEntry[i]); break;
|
|
|
|
case sstGlobalSym: dump_cv_sst_global_sym(&dirEntry[i]); break;
|
|
|
|
case sstGlobalPub: dump_cv_sst_global_pub(&dirEntry[i]); break;
|
|
|
|
case sstGlobalTypes: dump_cv_sst_global_types(&dirEntry[i]); break;
|
|
|
|
case sstSegMap: dump_cv_sst_seg_map(&dirEntry[i]); break;
|
|
|
|
case sstFileIndex: dump_cv_sst_file_index(&dirEntry[i]); break;
|
|
|
|
case sstStaticSym: dump_cv_sst_static_sym(&dirEntry[i]); break;
|
|
|
|
default: printf("unsupported type %x\n", dirEntry[i].SubSection); break;
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dump_codeview_headers(unsigned long base, unsigned long len)
|
|
|
|
{
|
2006-04-10 20:53:50 +02:00
|
|
|
const OMFDirHeader* dirHeader;
|
2007-01-05 21:42:45 +01:00
|
|
|
const char* signature;
|
2006-04-10 20:53:50 +02:00
|
|
|
const OMFDirEntry* dirEntry;
|
2007-01-05 21:42:45 +01:00
|
|
|
const OMFSignature* sig;
|
2001-09-21 23:03:53 +02:00
|
|
|
unsigned i;
|
2001-09-07 18:04:38 +02:00
|
|
|
int modulecount = 0, alignsymcount = 0, srcmodulecount = 0, librariescount = 0;
|
|
|
|
int globalsymcount = 0, globalpubcount = 0, globaltypescount = 0;
|
|
|
|
int segmapcount = 0, fileindexcount = 0, staticsymcount = 0;
|
2002-06-01 01:06:46 +02:00
|
|
|
|
2001-09-07 18:04:38 +02:00
|
|
|
cv_base = PRD(base, len);
|
|
|
|
if (!cv_base) {printf("Can't get full debug content, aborting\n");return;}
|
|
|
|
|
|
|
|
signature = cv_base;
|
|
|
|
|
|
|
|
printf(" CodeView Data\n");
|
2007-01-05 21:42:45 +01:00
|
|
|
printf(" Signature: %.4s\n", signature);
|
2001-09-07 18:04:38 +02:00
|
|
|
|
2007-01-05 21:42:45 +01:00
|
|
|
if (memcmp(signature, "NB10", 4) == 0)
|
2001-09-07 18:04:38 +02:00
|
|
|
{
|
2007-01-05 21:42:39 +01:00
|
|
|
const CODEVIEW_PDB_DATA* pdb_data;
|
2019-03-01 21:08:20 +01:00
|
|
|
pdb_data = cv_base;
|
2001-09-07 18:04:38 +02:00
|
|
|
|
2007-01-05 21:42:45 +01:00
|
|
|
printf(" Filepos: 0x%08lX\n", pdb_data->filepos);
|
|
|
|
printf(" TimeStamp: %08X (%s)\n",
|
2007-01-05 21:42:39 +01:00
|
|
|
pdb_data->timestamp, get_time_str(pdb_data->timestamp));
|
2008-08-29 21:50:33 +02:00
|
|
|
printf(" Age: %08X\n", pdb_data->age);
|
2007-01-05 21:42:45 +01:00
|
|
|
printf(" Filename: %s\n", pdb_data->name);
|
2007-01-05 21:42:39 +01:00
|
|
|
return;
|
|
|
|
}
|
2007-01-05 21:42:45 +01:00
|
|
|
if (memcmp(signature, "RSDS", 4) == 0)
|
2007-01-05 21:42:39 +01:00
|
|
|
{
|
2007-01-05 21:42:45 +01:00
|
|
|
const OMFSignatureRSDS* rsds_data;
|
2007-01-05 21:42:39 +01:00
|
|
|
|
2019-03-01 21:08:20 +01:00
|
|
|
rsds_data = cv_base;
|
2007-02-10 09:10:13 +01:00
|
|
|
printf(" Guid: %s\n", get_guid_str(&rsds_data->guid));
|
2008-08-29 21:50:33 +02:00
|
|
|
printf(" Age: %08X\n", rsds_data->age);
|
2007-01-05 21:42:45 +01:00
|
|
|
printf(" Filename: %s\n", rsds_data->name);
|
2001-09-07 18:04:38 +02:00
|
|
|
return;
|
2002-06-01 01:06:46 +02:00
|
|
|
}
|
2001-09-07 18:04:38 +02:00
|
|
|
|
2007-01-05 21:42:45 +01:00
|
|
|
if (memcmp(signature, "NB09", 4) != 0 && memcmp(signature, "NB11", 4) != 0)
|
2001-09-07 18:04:38 +02:00
|
|
|
{
|
2007-01-05 21:42:45 +01:00
|
|
|
printf("Unsupported signature (%.4s), aborting\n", signature);
|
2001-09-07 18:04:38 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-01-05 21:42:45 +01:00
|
|
|
sig = cv_base;
|
|
|
|
|
|
|
|
printf(" Filepos: 0x%08lX\n", sig->filepos);
|
|
|
|
|
|
|
|
dirHeader = PRD(Offset(cv_base) + sig->filepos, sizeof(OMFDirHeader));
|
2001-09-07 18:04:38 +02:00
|
|
|
if (!dirHeader) {printf("Can't get debug header, aborting\n"); return;}
|
|
|
|
|
|
|
|
printf(" Size of header: 0x%4X\n", dirHeader->cbDirHeader);
|
|
|
|
printf(" Size per entry: 0x%4X\n", dirHeader->cbDirEntry);
|
2007-01-05 21:42:45 +01:00
|
|
|
printf(" # of entries: 0x%8X (%d)\n", dirHeader->cDir, dirHeader->cDir);
|
|
|
|
printf(" Offset to NextDir: 0x%8X\n", dirHeader->lfoNextDir);
|
|
|
|
printf(" Flags: 0x%8X\n", dirHeader->flags);
|
2001-09-07 18:04:38 +02:00
|
|
|
|
|
|
|
if (!dirHeader->cDir) return;
|
|
|
|
|
|
|
|
dirEntry = PRD(Offset(dirHeader + 1), sizeof(OMFDirEntry) * dirHeader->cDir);
|
|
|
|
if (!dirEntry) {printf("Can't get DirEntry array, aborting\n");return;}
|
|
|
|
|
|
|
|
for (i = 0; i < dirHeader->cDir; i++)
|
|
|
|
{
|
|
|
|
switch (dirEntry[i].SubSection)
|
|
|
|
{
|
|
|
|
case sstModule: modulecount++; break;
|
|
|
|
case sstAlignSym: alignsymcount++; break;
|
|
|
|
case sstSrcModule: srcmodulecount++; break;
|
|
|
|
case sstLibraries: librariescount++; break;
|
|
|
|
case sstGlobalSym: globalsymcount++; break;
|
|
|
|
case sstGlobalPub: globalpubcount++; break;
|
|
|
|
case sstGlobalTypes: globaltypescount++; break;
|
|
|
|
case sstSegMap: segmapcount++; break;
|
|
|
|
case sstFileIndex: fileindexcount++; break;
|
|
|
|
case sstStaticSym: staticsymcount++; break;
|
2002-06-01 01:06:46 +02:00
|
|
|
}
|
2001-09-07 18:04:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* This one has to be > 0
|
|
|
|
*/
|
|
|
|
printf ("\nFound: %d sstModule subsections\n", modulecount);
|
|
|
|
|
|
|
|
if (alignsymcount > 0) printf (" %d sstAlignSym subsections\n", alignsymcount);
|
|
|
|
if (srcmodulecount > 0) printf (" %d sstSrcModule subsections\n", srcmodulecount);
|
|
|
|
if (librariescount > 0) printf (" %d sstLibraries subsections\n", librariescount);
|
|
|
|
if (globalsymcount > 0) printf (" %d sstGlobalSym subsections\n", globalsymcount);
|
|
|
|
if (globalpubcount > 0) printf (" %d sstGlobalPub subsections\n", globalpubcount);
|
|
|
|
if (globaltypescount > 0) printf (" %d sstGlobalTypes subsections\n", globaltypescount);
|
|
|
|
if (segmapcount > 0) printf (" %d sstSegMap subsections\n", segmapcount);
|
|
|
|
if (fileindexcount > 0) printf (" %d sstFileIndex subsections\n", fileindexcount);
|
|
|
|
if (staticsymcount > 0) printf (" %d sstStaticSym subsections\n", staticsymcount);
|
|
|
|
|
|
|
|
dump_codeview_all_modules(dirHeader);
|
|
|
|
}
|
|
|
|
|
2006-08-23 22:29:21 +02:00
|
|
|
static const char *get_coff_name( const IMAGE_SYMBOL *coff_sym, const char *coff_strtab )
|
2002-09-11 02:49:48 +02:00
|
|
|
{
|
|
|
|
static char namebuff[9];
|
|
|
|
const char* nampnt;
|
|
|
|
|
|
|
|
if( coff_sym->N.Name.Short )
|
|
|
|
{
|
|
|
|
memcpy(namebuff, coff_sym->N.ShortName, 8);
|
|
|
|
namebuff[8] = '\0';
|
2012-07-23 10:37:06 +02:00
|
|
|
nampnt = namebuff;
|
2002-09-11 02:49:48 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
nampnt = coff_strtab + coff_sym->N.Name.Long;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( nampnt[0] == '_' )
|
|
|
|
nampnt++;
|
|
|
|
return nampnt;
|
|
|
|
}
|
|
|
|
|
2009-12-09 20:55:07 +01:00
|
|
|
static const char* storage_class(BYTE sc)
|
|
|
|
{
|
|
|
|
static char tmp[7];
|
|
|
|
switch (sc)
|
|
|
|
{
|
|
|
|
case IMAGE_SYM_CLASS_STATIC: return "static";
|
|
|
|
case IMAGE_SYM_CLASS_EXTERNAL: return "extrnl";
|
|
|
|
case IMAGE_SYM_CLASS_LABEL: return "label ";
|
|
|
|
}
|
|
|
|
sprintf(tmp, "#%d", sc);
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
void dump_coff_symbol_table(const IMAGE_SYMBOL *coff_symbols, unsigned num_sym,
|
|
|
|
const IMAGE_SECTION_HEADER *sectHead)
|
2002-09-11 02:49:48 +02:00
|
|
|
{
|
2006-08-23 22:29:21 +02:00
|
|
|
const IMAGE_SYMBOL *coff_sym;
|
2009-12-09 20:55:07 +01:00
|
|
|
const char *coff_strtab = (const char *) (coff_symbols + num_sym);
|
2006-08-23 22:29:21 +02:00
|
|
|
unsigned int i;
|
|
|
|
const char *nampnt;
|
|
|
|
int naux;
|
2002-09-11 02:49:48 +02:00
|
|
|
|
2009-12-09 20:55:07 +01:00
|
|
|
printf("\nDebug table: COFF format.\n");
|
|
|
|
printf(" ID | seg:offs [ abs ] | Kind | symbol/function name\n");
|
|
|
|
for (i=0; i < num_sym; i++)
|
2002-09-11 02:49:48 +02:00
|
|
|
{
|
2009-12-09 20:55:07 +01:00
|
|
|
coff_sym = coff_symbols + i;
|
|
|
|
naux = coff_sym->NumberOfAuxSymbols;
|
2002-09-11 02:49:48 +02:00
|
|
|
|
2009-12-09 20:55:07 +01:00
|
|
|
switch (coff_sym->StorageClass)
|
2002-09-11 02:49:48 +02:00
|
|
|
{
|
2009-12-09 20:55:07 +01:00
|
|
|
case IMAGE_SYM_CLASS_FILE:
|
|
|
|
printf("file %s\n", (const char *) (coff_sym + 1));
|
|
|
|
break;
|
|
|
|
case IMAGE_SYM_CLASS_STATIC:
|
|
|
|
case IMAGE_SYM_CLASS_EXTERNAL:
|
|
|
|
case IMAGE_SYM_CLASS_LABEL:
|
|
|
|
if (coff_sym->SectionNumber > 0)
|
|
|
|
{
|
|
|
|
DWORD base = sectHead[coff_sym->SectionNumber - 1].VirtualAddress;
|
|
|
|
nampnt = get_coff_name( coff_sym, coff_strtab );
|
|
|
|
|
|
|
|
printf("%05d | %02d:%08x [%08x] | %s | %s\n",
|
|
|
|
i, coff_sym->SectionNumber - 1, coff_sym->Value,
|
|
|
|
base + coff_sym->Value,
|
|
|
|
storage_class(coff_sym->StorageClass), nampnt);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printf("%05d | %s\n", i, storage_class(coff_sym->StorageClass));
|
2002-09-11 02:49:48 +02:00
|
|
|
}
|
2009-12-09 20:55:07 +01:00
|
|
|
/*
|
|
|
|
* For now, skip past the aux entries.
|
|
|
|
*/
|
|
|
|
i += naux;
|
|
|
|
}
|
|
|
|
}
|
2002-09-11 02:49:48 +02:00
|
|
|
|
2011-05-01 09:46:27 +02:00
|
|
|
void dump_coff(unsigned long coffbase, unsigned long len, const IMAGE_SECTION_HEADER* sectHead)
|
2009-12-09 20:55:07 +01:00
|
|
|
{
|
|
|
|
const IMAGE_COFF_SYMBOLS_HEADER *coff = PRD(coffbase, len);
|
|
|
|
const IMAGE_SYMBOL *coff_symbols =
|
|
|
|
(const IMAGE_SYMBOL *) ((const char *)coff + coff->LvaToFirstSymbol);
|
2002-09-11 02:49:48 +02:00
|
|
|
|
2009-12-09 20:55:07 +01:00
|
|
|
dump_coff_symbol_table(coff_symbols, coff->NumberOfSymbols, sectHead);
|
2002-09-11 02:49:48 +02:00
|
|
|
}
|
|
|
|
|
2001-09-07 18:04:38 +02:00
|
|
|
void dump_codeview(unsigned long base, unsigned long len)
|
|
|
|
{
|
|
|
|
dump_codeview_headers(base, len);
|
|
|
|
}
|
2002-09-11 02:49:48 +02:00
|
|
|
|
|
|
|
void dump_frame_pointer_omission(unsigned long base, unsigned long len)
|
|
|
|
{
|
2007-01-05 21:42:12 +01:00
|
|
|
const FPO_DATA* fpo;
|
|
|
|
const FPO_DATA* last;
|
|
|
|
const char* x;
|
|
|
|
/* FPO is used to describe nonstandard stack frames */
|
|
|
|
printf("Range #loc #pmt Prlg #reg Info\n"
|
|
|
|
"-----------------+----+----+----+----+------------\n");
|
|
|
|
|
2008-11-02 00:20:49 +01:00
|
|
|
fpo = PRD(base, len);
|
2007-01-05 21:42:12 +01:00
|
|
|
if (!fpo) {printf("Couldn't get FPO blob\n"); return;}
|
|
|
|
last = (const FPO_DATA*)((const char*)fpo + len);
|
|
|
|
|
|
|
|
while (fpo < last && fpo->ulOffStart)
|
|
|
|
{
|
|
|
|
switch (fpo->cbFrame)
|
|
|
|
{
|
|
|
|
case FRAME_FPO: x = "FRAME_FPO"; break;
|
|
|
|
case FRAME_NONFPO: x = "FRAME_NONFPO"; break;
|
|
|
|
case FRAME_TRAP: x = "FRAME_TRAP"; break;
|
|
|
|
case FRAME_TSS: x = "case FRAME_TSS"; break;
|
|
|
|
default: x = NULL; break;
|
|
|
|
}
|
|
|
|
printf("%08x-%08x %4u %4u %4u %4u %s%s%s\n",
|
|
|
|
fpo->ulOffStart, fpo->ulOffStart + fpo->cbProcSize,
|
|
|
|
fpo->cdwLocals, fpo->cdwParams, fpo->cbProlog, fpo->cbRegs,
|
|
|
|
x, fpo->fHasSEH ? " SEH" : "", fpo->fUseBP ? " UseBP" : "");
|
|
|
|
fpo++;
|
|
|
|
}
|
2002-09-11 02:49:48 +02:00
|
|
|
}
|
2006-08-27 08:50:46 +02:00
|
|
|
|
|
|
|
struct stab_nlist
|
|
|
|
{
|
|
|
|
union
|
|
|
|
{
|
|
|
|
char* n_name;
|
|
|
|
struct stab_nlist* n_next;
|
|
|
|
long n_strx;
|
|
|
|
} n_un;
|
|
|
|
unsigned char n_type;
|
|
|
|
char n_other;
|
|
|
|
short n_desc;
|
|
|
|
unsigned long n_value;
|
|
|
|
};
|
|
|
|
|
2006-11-28 11:38:22 +01:00
|
|
|
static const char * const stabs_defs[] = {
|
2006-08-27 08:50:46 +02:00
|
|
|
NULL,NULL,NULL,NULL, /* 00 */
|
|
|
|
NULL,NULL,NULL,NULL, /* 08 */
|
|
|
|
NULL,NULL,NULL,NULL, /* 10 */
|
|
|
|
NULL,NULL,NULL,NULL, /* 18 */
|
|
|
|
"GSYM","FNAME","FUN","STSYM", /* 20 */
|
|
|
|
"LCSYM","MAIN","ROSYM","PC", /* 28 */
|
|
|
|
NULL,"NSYMS","NOMAP",NULL, /* 30 */
|
|
|
|
"OBJ",NULL,"OPT",NULL, /* 38 */
|
|
|
|
"RSYM","M2C","SLINE","DSLINE", /* 40 */
|
|
|
|
"BSLINE","DEFD","FLINE",NULL, /* 48 */
|
|
|
|
"EHDECL",NULL,"CATCH",NULL, /* 50 */
|
|
|
|
NULL,NULL,NULL,NULL, /* 58 */
|
|
|
|
"SSYM","ENDM","SO",NULL, /* 60 */
|
|
|
|
NULL,NULL,NULL,NULL, /* 68 */
|
|
|
|
NULL,NULL,NULL,NULL, /* 70 */
|
|
|
|
NULL,NULL,NULL,NULL, /* 78 */
|
|
|
|
"LSYM","BINCL","SOL",NULL, /* 80 */
|
|
|
|
NULL,NULL,NULL,NULL, /* 88 */
|
|
|
|
NULL,NULL,NULL,NULL, /* 90 */
|
|
|
|
NULL,NULL,NULL,NULL, /* 98 */
|
|
|
|
"PSYM","EINCL","ENTRY",NULL, /* a0 */
|
|
|
|
NULL,NULL,NULL,NULL, /* a8 */
|
|
|
|
NULL,NULL,NULL,NULL, /* b0 */
|
|
|
|
NULL,NULL,NULL,NULL, /* b8 */
|
|
|
|
"LBRAC","EXCL","SCOPE",NULL, /* c0 */
|
|
|
|
NULL,NULL,NULL,NULL, /* c8 */
|
|
|
|
NULL,NULL,NULL,NULL, /* d0 */
|
|
|
|
NULL,NULL,NULL,NULL, /* d8 */
|
|
|
|
"RBRAC","BCOMM","ECOMM",NULL, /* e0 */
|
|
|
|
"ECOML","WITH",NULL,NULL, /* e8 */
|
|
|
|
"NBTEXT","NBDATA","NBBSS","NBSTS", /* f0 */
|
|
|
|
"NBLCS",NULL,NULL,NULL /* f8 */
|
|
|
|
};
|
|
|
|
|
|
|
|
void dump_stabs(const void* pv_stabs, unsigned szstabs, const char* stabstr, unsigned szstr)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int nstab;
|
|
|
|
const char* ptr;
|
|
|
|
char* stabbuff;
|
|
|
|
unsigned int stabbufflen;
|
|
|
|
const struct stab_nlist* stab_ptr = pv_stabs;
|
|
|
|
const char* strs_end;
|
|
|
|
char n_buffer[16];
|
|
|
|
|
|
|
|
nstab = szstabs / sizeof(struct stab_nlist);
|
|
|
|
strs_end = stabstr + szstr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate a buffer into which we can build stab strings for cases
|
|
|
|
* where the stab is continued over multiple lines.
|
|
|
|
*/
|
|
|
|
stabbufflen = 65536;
|
2021-09-29 11:14:01 +02:00
|
|
|
stabbuff = xmalloc(stabbufflen);
|
2006-08-27 08:50:46 +02:00
|
|
|
|
|
|
|
stabbuff[0] = '\0';
|
|
|
|
|
|
|
|
printf("#Sym n_type n_othr n_desc n_value n_strx String\n");
|
|
|
|
|
|
|
|
for (i = 0; i < nstab; i++, stab_ptr++)
|
|
|
|
{
|
|
|
|
ptr = stabstr + stab_ptr->n_un.n_strx;
|
|
|
|
if ((ptr > strs_end) || (ptr + strlen(ptr) > strs_end))
|
|
|
|
{
|
|
|
|
ptr = "[[*** bad string ***]]";
|
|
|
|
}
|
|
|
|
else if (ptr[strlen(ptr) - 1] == '\\')
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Indicates continuation. Append this to the buffer, and go onto the
|
|
|
|
* next record. Repeat the process until we find a stab without the
|
|
|
|
* '/' character, as this indicates we have the whole thing.
|
|
|
|
*/
|
|
|
|
unsigned len = strlen(ptr);
|
|
|
|
if (strlen(stabbuff) + len > stabbufflen)
|
|
|
|
{
|
|
|
|
stabbufflen += 65536;
|
2021-09-29 11:14:01 +02:00
|
|
|
stabbuff = xrealloc(stabbuff, stabbufflen);
|
2006-08-27 08:50:46 +02:00
|
|
|
}
|
2018-08-15 07:08:04 +02:00
|
|
|
strcat(stabbuff, ptr);
|
2006-08-27 08:50:46 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (stabbuff[0] != '\0')
|
|
|
|
{
|
|
|
|
strcat(stabbuff, ptr);
|
|
|
|
ptr = stabbuff;
|
|
|
|
}
|
|
|
|
if ((stab_ptr->n_type & 1) || !stabs_defs[stab_ptr->n_type / 2])
|
|
|
|
sprintf(n_buffer, "<0x%02x>", stab_ptr->n_type);
|
|
|
|
else
|
|
|
|
sprintf(n_buffer, "%-6s", stabs_defs[stab_ptr->n_type / 2]);
|
|
|
|
printf("%4d %s %-8x % 6d %-8lx %-6lx %s\n",
|
|
|
|
i, n_buffer, stab_ptr->n_other, stab_ptr->n_desc, stab_ptr->n_value,
|
|
|
|
stab_ptr->n_un.n_strx, ptr);
|
|
|
|
}
|
|
|
|
free(stabbuff);
|
|
|
|
}
|