/*++ 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 #include #include #include #include #include #include #include #include #include // // 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