|
|
/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
dumpsym.cxx
Abstract:
This is the command line tool to dump symbols from an image.
Author:
David Fields - Feb 23, 2000 Silviu Calinoiu - Feb 28, 2000
Revision History:
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <tchar.h>
#include <windows.h>
#include <imagehlp.h>
#include <common.ver>
//
// Section information
//
typedef struct {
CHAR Name [9]; DWORD64 Start; ULONG Size;
} IMG_SECTION_INFO, * PIMG_SECTION_INFO;
#define MAX_NUMBER_OF_SECTIONS 1024
IMG_SECTION_INFO Section [MAX_NUMBER_OF_SECTIONS]; ULONG SectionWriteIndex = 0;
typedef struct {
HANDLE File; HANDLE Section; LPBYTE ImageBase;
PIMAGE_DOS_HEADER DosHeader; PIMAGE_FILE_HEADER FileHeader; PIMAGE_OPTIONAL_HEADER OptionalHeader; PIMAGE_SECTION_HEADER SectionHeader;
DWORD FileSignature;
PIMAGE_DATA_DIRECTORY ImportDirectory; PIMAGE_SECTION_HEADER ImportSection; PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor; DWORD_PTR AddressCorrection;
} IMAGE_BROWSE_INFO, *PIMAGE_BROWSE_INFO;
BOOL ImgInitializeBrowseInfo (
LPCTSTR FilePath, PIMAGE_BROWSE_INFO Info);
BOOL ImgDeleteBrowseInfo (
PIMAGE_BROWSE_INFO Info);
PCHAR ImgSearchSectionForAddress ( DWORD64 Address );
BOOL ShouldExcludeSymbol ( LPSTR Name );
BOOL OpenExcludeFile ( LPSTR FilePath );
//
// Symbol information
//
typedef struct {
LPSTR Name; DWORD64 Address; ULONG Size; BOOL Exclude;
} SYMBOL, *PSYMBOL;
PSYMBOL Symbols; DWORD SymbolCount; DWORD TotalNumberOfSymbols;
VOID DumpSymbols( char *, BOOL All, BOOL SortBySize);
VOID PrintUsage( );
VOID Error ( char * Fmt, ... );
int __cdecl SymbolCompareBySize( const void * Arg1, const void * Arg2 );
int __cdecl SymbolCompareByAddress( const void * Arg1, const void * Arg2 );
BOOL CALLBACK SymbolEnumerationCallback( LPSTR SymbolName, DWORD64 SymbolAddress, ULONG SymbolSize, PVOID UserContext );
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
VOID Help ( ) { printf("dumpsym BINARY-PATH [OPTIONS] \n"); printf("%s \n", VER_LEGALCOPYRIGHT_STR); printf(" \n"); printf("OPTIONS: \n"); printf("/notpaged Print all symbols that are not pageable \n"); printf("/all Print all symbols (default) \n"); printf("/address Sort by address in increasing order \n"); printf("/size Sort by size in decreasing order (default) \n"); printf("/exclude PATH File with symbols that should be ignored \n"); printf("/symbols PATH Symbols path. If not specified symbols must be \n"); printf(" in the directory containing the binary. \n"); printf(" \n"); printf("Ex. dumpsym c:\\binaries.x86fre\\ntoskrnl.exe \n"); printf(" /symbols c:\\binaries.x86fre\\Symbols.pri\\retail \n"); printf(" \n"); printf("This tool can be used to determine what symbols are not paged \n"); printf("and then manually analyze if any of the functions or variables \n"); printf("can be moved into a PAGEXXXX section (become pageable). When \n"); printf("analyzing this data please take into account that the size for \n"); printf("some symbols includes padding/alignment zones and therefore \n"); printf("appears to be bigger than it really is. \n"); printf(" \n"); printf("Ex. dumpsym \\\\robsvbl1\\latest\\ntfs.sys \n"); printf(" /symbols \\\\robsvbl1\\latest\\Symbols.pri\\retail \n"); printf(" /notpaged /size \n"); printf(" \n"); printf(" \n"); exit(-1); }
VOID Error ( char * Fmt, ... ) { va_list Prms;
va_start (Prms, Fmt); fprintf (stderr, "Dumpsym error: "); vfprintf (stderr, Fmt, Prms); fprintf (stderr, "\n"); fflush (stderr); exit (1); }
PCHAR * SearchOption ( PCHAR * Args, PCHAR Option ) { for ( ; Args && *Args; Args++) { if (_stricmp (*Args, Option) == 0) { return Args; } }
return NULL; }
//
// main
//
VOID __cdecl main ( int argc, char *argv[] ) { IMAGE_BROWSE_INFO Info; PCHAR ExeName; PCHAR LongName; BOOL OptionAll; BOOL OptionSortBySize; PCHAR * OptionString;
if (argc == 1 || SearchOption (argv, "/?")) { Help (); }
SymInitialize(GetCurrentProcess(), NULL, FALSE); SymSetOptions(SYMOPT_UNDNAME);
//
// /exclude EXCLUDE-FILE-PATH
//
if ((OptionString = SearchOption (argv, "/exclude"))) { OpenExcludeFile (*(OptionString + 1)); } //
// dumpsym PATH-TO-BINARY
//
if ((OptionString = SearchOption (argv, argv[0]))) { ImgInitializeBrowseInfo (*(OptionString + 1), &Info); LongName = *(OptionString + 1); } else { Help (); } //
// /symbols SYMBOL-PATH
//
if ((OptionString = SearchOption (argv, "/symbols"))) { SetCurrentDirectory (*(OptionString + 1)); } //
// Dump options.
//
OptionAll = TRUE; OptionSortBySize = TRUE;
if (SearchOption (argv, "/notpaged")) { OptionAll = FALSE; } if (SearchOption (argv, "/all")) { OptionAll = TRUE; } if (SearchOption (argv, "/address")) { OptionSortBySize = FALSE; } if (SearchOption (argv, "/size")) { OptionSortBySize = TRUE; } //
// Dump stuff.
//
DumpSymbols (LongName, OptionAll, OptionSortBySize); }
LPSTR CopyString ( LPSTR Source ) { LPSTR Target;
Target = (LPSTR) malloc (strlen(Source) + 1);
if (Target) { strcpy (Target, Source); }
return Target; }
BOOL CALLBACK SymbolEnumerationCallback( LPSTR SymbolName, DWORD64 SymbolAddress, ULONG SymbolSize, PVOID UserContext ) { if (PtrToUlong(UserContext) == 1) { if (SymbolName == NULL) { Error ("Ooops"); }
if (SymbolCount >= TotalNumberOfSymbols) { Error ("enumerated more symbols on second pass"); }
Symbols[SymbolCount].Name = CopyString (SymbolName); Symbols[SymbolCount].Address = SymbolAddress; Symbols[SymbolCount].Size = SymbolSize;
if (Symbols[SymbolCount].Name == NULL) { Symbols[SymbolCount].Name = "*error*"; } }
SymbolCount += 1; return TRUE; }
int __cdecl SymbolCompareBySize( const void * Arg1, const void * Arg2 ) { PSYMBOL Sym1 = (PSYMBOL) Arg1; PSYMBOL Sym2 = (PSYMBOL) Arg2;
// decreasing order by size
return (Sym2->Size - Sym1->Size); }
int __cdecl SymbolCompareByAddress( const void * Arg1, const void * Arg2 ) { PSYMBOL Sym1 = (PSYMBOL) Arg1; PSYMBOL Sym2 = (PSYMBOL) Arg2; INT64 Delta;
// increasing order by address
Delta = (INT64)(Sym1->Address - Sym2->Address);
if (Delta > 0) { return 1; } else if (Delta == 0) { return 0; } else { return -1; } }
VOID DumpSymbols( LPTSTR ImageName, BOOL All, BOOL SortBySize) { DWORD64 BaseOfDll; PCHAR SectionName; DWORD I, J; BOOL FoundOne;
//
// Load symbols
//
BaseOfDll = SymLoadModule64( GetCurrentProcess (), NULL, ImageName, NULL, 0, 0);
if (BaseOfDll == 0) { Error ("cannot load symbols for %s \n", ImageName); }
//
// Number the symbols
//
SymbolCount = 0;
SymEnumerateSymbols64( GetCurrentProcess(), BaseOfDll, SymbolEnumerationCallback, 0); // Count them
TotalNumberOfSymbols = SymbolCount; printf("Detected %u symbols in %s \n\n", TotalNumberOfSymbols, ImageName);
//
// Read all symbols.
//
SymbolCount = 0; Symbols = malloc(TotalNumberOfSymbols * sizeof(SYMBOL));
if (Symbols == NULL) { Error ("out of memory (failed to allocate %u bytes)", TotalNumberOfSymbols * sizeof(SYMBOL)); }
SymEnumerateSymbols64( GetCurrentProcess(), BaseOfDll, SymbolEnumerationCallback, (PVOID)1);
//
// Sort symbols
//
qsort( Symbols, TotalNumberOfSymbols, sizeof(SYMBOL), (SortBySize ? SymbolCompareBySize : SymbolCompareByAddress));
//
// Figure out symbols that should not be printed.
//
for (J = 0; J < TotalNumberOfSymbols; J++) {
if (ShouldExcludeSymbol (Symbols[J].Name)) { Symbols[J].Exclude = TRUE; } else { Symbols[J].Exclude = FALSE; } }
//
// Print symbols
//
printf("%-8s %-16s %-8s %s \n", "Section", "Address", "Size", "Symbol"); printf("-------------------------------------------------------------\n");
for (I = 0; I < SectionWriteIndex; I++) {
for (J = 0, FoundOne = FALSE; J < TotalNumberOfSymbols; J++) {
if (Symbols[J].Exclude) { continue; }
SectionName = ImgSearchSectionForAddress ( Symbols[J].Address - BaseOfDll);
if (strcmp (SectionName, Section[I].Name) == 0) { if (All || strstr (SectionName,"PAGE") == NULL) {
if (Symbols[J].Name == NULL) { printf(".\n"); continue; } printf("%-8s %016I64X %08X %s \n", SectionName, Symbols[J].Address - BaseOfDll, Symbols[J].Size, Symbols[J].Name);
FoundOne = TRUE; } } } if (FoundOne) { printf("\n"); } }
//
// Unload symbols
//
if (SymUnloadModule64(GetCurrentProcess(), BaseOfDll) == FALSE) { Error ("cannot unload symbols"); } }
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////// Section manipulation routines
/////////////////////////////////////////////////////////////////////
//
// Function:
//
// ImgInitializeBrowseInfo
//
// Description:
//
// This functions fills oout the `Info' structure with
// various pointers to PE data from the mapped image file.
//
// Note. Even if the function returned false the destructor
// `ImgDeleteBrowseInfo' should be called because it does some
// cleanup.
//
// Return:
//
// True if all the PE data pointers have been obtained.
//
BOOL ImgInitializeBrowseInfo (
LPCTSTR FilePath, PIMAGE_BROWSE_INFO Info) { DWORD Index, I;
if (Info == NULL) { return FALSE; }
ZeroMemory (Info, sizeof *Info);
Info->File = CreateFile (
FilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (Info->File == INVALID_HANDLE_VALUE) {
Error ("create file %s (error %u)", FilePath, GetLastError()); return FALSE; }
Info->Section = CreateFileMapping (
Info->File, NULL, PAGE_READONLY, 0, 0, NULL);
if (Info->Section == NULL) {
return FALSE; }
Info->ImageBase = (LPBYTE) MapViewOfFile (
Info->Section, FILE_MAP_READ, 0, 0, 0);
if (Info->ImageBase == NULL) {
return FALSE; }
//
// Check the signature
//
Info->DosHeader = (PIMAGE_DOS_HEADER)Info->ImageBase;
if (Info->DosHeader->e_magic != 'ZM') {
return FALSE; }
Info->FileHeader = (PIMAGE_FILE_HEADER) (Info->ImageBase + Info->DosHeader->e_lfanew + sizeof(DWORD));
Info->FileSignature = *((DWORD *)Info->FileHeader - 1);
if (Info->FileSignature != IMAGE_NT_SIGNATURE) {
return FALSE; }
Info->OptionalHeader = (PIMAGE_OPTIONAL_HEADER)(Info->FileHeader + 1); Info->ImportDirectory = & (Info->OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]); Info->SectionHeader = (PIMAGE_SECTION_HEADER)(Info->OptionalHeader + 1); Info->ImportSection = NULL;
//
// Find the section containing the import table
//
printf("Sections in %s \n\n", FilePath);
for (Index = 0; Index < Info->FileHeader->NumberOfSections; Index++) {
//
// SilviuC: I wonder if there is a way to get a 64 bit value for VirtualAddress.
// Apparently it is stored as a ULONG in PE format.
//
Section[SectionWriteIndex].Start = (DWORD64)((Info->SectionHeader + Index)->VirtualAddress); Section[SectionWriteIndex].Size = (Info->SectionHeader + Index)->SizeOfRawData;
for (I = 0; I < 8; I++) { Section[SectionWriteIndex].Name[I] = ((Info->SectionHeader + Index)->Name)[I]; }
Section[SectionWriteIndex].Name[I] = 0;
printf("%-8s %08X %08X \n", Section[SectionWriteIndex].Name, Section[SectionWriteIndex].Start, Section[SectionWriteIndex].Size); SectionWriteIndex += 1; } printf("\n");
//
// Find the address of import data in the section body.
//
#if 0
Info->AddressCorrection = (DWORD_PTR)Info->ImageBase + Info->ImportSection->PointerToRawData - Info->ImportSection->VirtualAddress;
Info->ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(Info->AddressCorrection + Info->ImportDirectory->VirtualAddress); #endif
//
// Finish
//
return TRUE; }
//
// Function:
//
// ImgDeleteBrowseInfo
//
// Description:
//
// This function cleans up the `Info' structure, unmaps views,
// closes handles, etc.
//
BOOL ImgDeleteBrowseInfo (
PIMAGE_BROWSE_INFO Info) { if (Info == NULL) return FALSE;
UnmapViewOfFile (Info->ImageBase); CloseHandle (Info->Section); CloseHandle (Info->File);
ZeroMemory (Info, sizeof *Info);
return TRUE; }
PCHAR ImgSearchSectionForAddress ( DWORD64 Address ) { DWORD I;
for (I = 0; I < SectionWriteIndex; I++) { if (Section[I].Start <= Address && Address < Section[I].Start + Section[I].Size) { return Section[I].Name; } }
return "unknown"; }
//
// Exclude file logic
//
PCHAR *ExcludeStrings; DWORD NumberOfExcludeStrings;
BOOL ShouldExcludeSymbol ( LPSTR Name ) { DWORD I;
if (ExcludeStrings == NULL) { return FALSE; } for (I = 0; I <NumberOfExcludeStrings; I += 1) {
if (_stricmp (Name, ExcludeStrings[I]) == 0) { return TRUE; } }
return FALSE; }
BOOL OpenExcludeFile ( LPSTR FilePath ) { FILE * File; CHAR String[1024]; DWORD StringCount = 0;
File = fopen (FilePath, "r");
if (File == NULL) { Error ("cannot open exclude file %s", FilePath); }
while (fgets (String, 1024, File)) { StringCount += 1; }
fclose (File);
ExcludeStrings = (PCHAR *)malloc (StringCount * sizeof(PVOID));
if (ExcludeStrings == NULL) { Error ("cannot allocate exclude strings buffer"); }
NumberOfExcludeStrings = StringCount;
printf("Excluding %u symbols from %s \n", NumberOfExcludeStrings, FilePath);
File = fopen (FilePath, "r"); if (!File) { Error ("cannot open file"); }
StringCount = 0;
while (fgets (String, 1024, File)) { PCHAR Start, Current;
Current = String;
while (*Current == ' ' || *Current == '\t') { Current += 1; }
Start = Current;
while (*Current && *Current != ' ' && *Current != '\t' && *Current != '\n') { Current += 1; }
*Current = '\0';
if (StringCount < NumberOfExcludeStrings) { ExcludeStrings[StringCount] = CopyString (Start);
// printf("Exclude %s \n", ExcludeStrings[StringCount]);
}
StringCount += 1; }
fclose (File);
return TRUE; }
|