Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

829 lines
17 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. dumpsym.cxx
  5. Abstract:
  6. This is the command line tool to dump symbols from an image.
  7. Author:
  8. David Fields - Feb 23, 2000
  9. Silviu Calinoiu - Feb 28, 2000
  10. Revision History:
  11. --*/
  12. #include <nt.h>
  13. #include <ntrtl.h>
  14. #include <nturtl.h>
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <stdarg.h>
  18. #include <tchar.h>
  19. #include <windows.h>
  20. #include <imagehlp.h>
  21. #include <common.ver>
  22. //
  23. // Section information
  24. //
  25. typedef struct {
  26. CHAR Name [9];
  27. DWORD64 Start;
  28. ULONG Size;
  29. } IMG_SECTION_INFO, * PIMG_SECTION_INFO;
  30. #define MAX_NUMBER_OF_SECTIONS 1024
  31. IMG_SECTION_INFO Section [MAX_NUMBER_OF_SECTIONS];
  32. ULONG SectionWriteIndex = 0;
  33. typedef struct {
  34. HANDLE File;
  35. HANDLE Section;
  36. LPBYTE ImageBase;
  37. PIMAGE_DOS_HEADER DosHeader;
  38. PIMAGE_FILE_HEADER FileHeader;
  39. PIMAGE_OPTIONAL_HEADER OptionalHeader;
  40. PIMAGE_SECTION_HEADER SectionHeader;
  41. DWORD FileSignature;
  42. PIMAGE_DATA_DIRECTORY ImportDirectory;
  43. PIMAGE_SECTION_HEADER ImportSection;
  44. PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
  45. DWORD_PTR AddressCorrection;
  46. } IMAGE_BROWSE_INFO, *PIMAGE_BROWSE_INFO;
  47. BOOL
  48. ImgInitializeBrowseInfo (
  49. LPCTSTR FilePath,
  50. PIMAGE_BROWSE_INFO Info);
  51. BOOL
  52. ImgDeleteBrowseInfo (
  53. PIMAGE_BROWSE_INFO Info);
  54. PCHAR
  55. ImgSearchSectionForAddress (
  56. DWORD64 Address
  57. );
  58. BOOL
  59. ShouldExcludeSymbol (
  60. LPSTR Name
  61. );
  62. BOOL
  63. OpenExcludeFile (
  64. LPSTR FilePath
  65. );
  66. //
  67. // Symbol information
  68. //
  69. typedef struct {
  70. LPSTR Name;
  71. DWORD64 Address;
  72. ULONG Size;
  73. BOOL Exclude;
  74. } SYMBOL, *PSYMBOL;
  75. PSYMBOL Symbols;
  76. DWORD SymbolCount;
  77. DWORD TotalNumberOfSymbols;
  78. VOID
  79. DumpSymbols(
  80. char *,
  81. BOOL All,
  82. BOOL SortBySize);
  83. VOID
  84. PrintUsage(
  85. );
  86. VOID
  87. Error (
  88. char * Fmt,
  89. ...
  90. );
  91. int __cdecl
  92. SymbolCompareBySize(
  93. const void * Arg1,
  94. const void * Arg2
  95. );
  96. int __cdecl
  97. SymbolCompareByAddress(
  98. const void * Arg1,
  99. const void * Arg2
  100. );
  101. BOOL
  102. CALLBACK
  103. SymbolEnumerationCallback(
  104. LPSTR SymbolName,
  105. DWORD64 SymbolAddress,
  106. ULONG SymbolSize,
  107. PVOID UserContext
  108. );
  109. /////////////////////////////////////////////////////////////////////
  110. /////////////////////////////////////////////////////////////////////
  111. /////////////////////////////////////////////////////////////////////
  112. VOID
  113. Help (
  114. )
  115. {
  116. printf("dumpsym BINARY-PATH [OPTIONS] \n");
  117. printf("%s \n", VER_LEGALCOPYRIGHT_STR);
  118. printf(" \n");
  119. printf("OPTIONS: \n");
  120. printf("/notpaged Print all symbols that are not pageable \n");
  121. printf("/all Print all symbols (default) \n");
  122. printf("/address Sort by address in increasing order \n");
  123. printf("/size Sort by size in decreasing order (default) \n");
  124. printf("/exclude PATH File with symbols that should be ignored \n");
  125. printf("/symbols PATH Symbols path. If not specified symbols must be \n");
  126. printf(" in the directory containing the binary. \n");
  127. printf(" \n");
  128. printf("Ex. dumpsym c:\\binaries.x86fre\\ntoskrnl.exe \n");
  129. printf(" /symbols c:\\binaries.x86fre\\Symbols.pri\\retail \n");
  130. printf(" \n");
  131. printf("This tool can be used to determine what symbols are not paged \n");
  132. printf("and then manually analyze if any of the functions or variables \n");
  133. printf("can be moved into a PAGEXXXX section (become pageable). When \n");
  134. printf("analyzing this data please take into account that the size for \n");
  135. printf("some symbols includes padding/alignment zones and therefore \n");
  136. printf("appears to be bigger than it really is. \n");
  137. printf(" \n");
  138. printf("Ex. dumpsym \\\\robsvbl1\\latest\\ntfs.sys \n");
  139. printf(" /symbols \\\\robsvbl1\\latest\\Symbols.pri\\retail \n");
  140. printf(" /notpaged /size \n");
  141. printf(" \n");
  142. printf(" \n");
  143. exit(-1);
  144. }
  145. VOID
  146. Error (
  147. char * Fmt,
  148. ...
  149. )
  150. {
  151. va_list Prms;
  152. va_start (Prms, Fmt);
  153. fprintf (stderr, "Dumpsym error: ");
  154. vfprintf (stderr, Fmt, Prms);
  155. fprintf (stderr, "\n");
  156. fflush (stderr);
  157. exit (1);
  158. }
  159. PCHAR *
  160. SearchOption (
  161. PCHAR * Args,
  162. PCHAR Option
  163. )
  164. {
  165. for ( ; Args && *Args; Args++) {
  166. if (_stricmp (*Args, Option) == 0) {
  167. return Args;
  168. }
  169. }
  170. return NULL;
  171. }
  172. //
  173. // main
  174. //
  175. VOID __cdecl
  176. main (
  177. int argc,
  178. char *argv[]
  179. )
  180. {
  181. IMAGE_BROWSE_INFO Info;
  182. PCHAR ExeName;
  183. PCHAR LongName;
  184. BOOL OptionAll;
  185. BOOL OptionSortBySize;
  186. PCHAR * OptionString;
  187. if (argc == 1 || SearchOption (argv, "/?")) {
  188. Help ();
  189. }
  190. SymInitialize(GetCurrentProcess(), NULL, FALSE);
  191. SymSetOptions(SYMOPT_UNDNAME);
  192. //
  193. // /exclude EXCLUDE-FILE-PATH
  194. //
  195. if ((OptionString = SearchOption (argv, "/exclude"))) {
  196. OpenExcludeFile (*(OptionString + 1));
  197. }
  198. //
  199. // dumpsym PATH-TO-BINARY
  200. //
  201. if ((OptionString = SearchOption (argv, argv[0]))) {
  202. ImgInitializeBrowseInfo (*(OptionString + 1), &Info);
  203. LongName = *(OptionString + 1);
  204. }
  205. else {
  206. Help ();
  207. }
  208. //
  209. // /symbols SYMBOL-PATH
  210. //
  211. if ((OptionString = SearchOption (argv, "/symbols"))) {
  212. SetCurrentDirectory (*(OptionString + 1));
  213. }
  214. //
  215. // Dump options.
  216. //
  217. OptionAll = TRUE;
  218. OptionSortBySize = TRUE;
  219. if (SearchOption (argv, "/notpaged")) {
  220. OptionAll = FALSE;
  221. }
  222. if (SearchOption (argv, "/all")) {
  223. OptionAll = TRUE;
  224. }
  225. if (SearchOption (argv, "/address")) {
  226. OptionSortBySize = FALSE;
  227. }
  228. if (SearchOption (argv, "/size")) {
  229. OptionSortBySize = TRUE;
  230. }
  231. //
  232. // Dump stuff.
  233. //
  234. DumpSymbols (LongName, OptionAll, OptionSortBySize);
  235. }
  236. LPSTR
  237. CopyString (
  238. LPSTR Source
  239. )
  240. {
  241. LPSTR Target;
  242. Target = (LPSTR) malloc (strlen(Source) + 1);
  243. if (Target) {
  244. strcpy (Target, Source);
  245. }
  246. return Target;
  247. }
  248. BOOL
  249. CALLBACK
  250. SymbolEnumerationCallback(
  251. LPSTR SymbolName,
  252. DWORD64 SymbolAddress,
  253. ULONG SymbolSize,
  254. PVOID UserContext
  255. )
  256. {
  257. if (PtrToUlong(UserContext) == 1) {
  258. if (SymbolName == NULL) {
  259. Error ("Ooops");
  260. }
  261. if (SymbolCount >= TotalNumberOfSymbols) {
  262. Error ("enumerated more symbols on second pass");
  263. }
  264. Symbols[SymbolCount].Name = CopyString (SymbolName);
  265. Symbols[SymbolCount].Address = SymbolAddress;
  266. Symbols[SymbolCount].Size = SymbolSize;
  267. if (Symbols[SymbolCount].Name == NULL) {
  268. Symbols[SymbolCount].Name = "*error*";
  269. }
  270. }
  271. SymbolCount += 1;
  272. return TRUE;
  273. }
  274. int __cdecl
  275. SymbolCompareBySize(
  276. const void * Arg1,
  277. const void * Arg2
  278. )
  279. {
  280. PSYMBOL Sym1 = (PSYMBOL) Arg1;
  281. PSYMBOL Sym2 = (PSYMBOL) Arg2;
  282. // decreasing order by size
  283. return (Sym2->Size - Sym1->Size);
  284. }
  285. int __cdecl
  286. SymbolCompareByAddress(
  287. const void * Arg1,
  288. const void * Arg2
  289. )
  290. {
  291. PSYMBOL Sym1 = (PSYMBOL) Arg1;
  292. PSYMBOL Sym2 = (PSYMBOL) Arg2;
  293. INT64 Delta;
  294. // increasing order by address
  295. Delta = (INT64)(Sym1->Address - Sym2->Address);
  296. if (Delta > 0) {
  297. return 1;
  298. }
  299. else if (Delta == 0) {
  300. return 0;
  301. }
  302. else {
  303. return -1;
  304. }
  305. }
  306. VOID
  307. DumpSymbols(
  308. LPTSTR ImageName,
  309. BOOL All,
  310. BOOL SortBySize)
  311. {
  312. DWORD64 BaseOfDll;
  313. PCHAR SectionName;
  314. DWORD I, J;
  315. BOOL FoundOne;
  316. //
  317. // Load symbols
  318. //
  319. BaseOfDll = SymLoadModule64(
  320. GetCurrentProcess (),
  321. NULL,
  322. ImageName,
  323. NULL,
  324. 0,
  325. 0);
  326. if (BaseOfDll == 0) {
  327. Error ("cannot load symbols for %s \n", ImageName);
  328. }
  329. //
  330. // Number the symbols
  331. //
  332. SymbolCount = 0;
  333. SymEnumerateSymbols64(
  334. GetCurrentProcess(),
  335. BaseOfDll,
  336. SymbolEnumerationCallback,
  337. 0); // Count them
  338. TotalNumberOfSymbols = SymbolCount;
  339. printf("Detected %u symbols in %s \n\n", TotalNumberOfSymbols, ImageName);
  340. //
  341. // Read all symbols.
  342. //
  343. SymbolCount = 0;
  344. Symbols = malloc(TotalNumberOfSymbols * sizeof(SYMBOL));
  345. if (Symbols == NULL) {
  346. Error ("out of memory (failed to allocate %u bytes)", TotalNumberOfSymbols * sizeof(SYMBOL));
  347. }
  348. SymEnumerateSymbols64(
  349. GetCurrentProcess(),
  350. BaseOfDll,
  351. SymbolEnumerationCallback,
  352. (PVOID)1);
  353. //
  354. // Sort symbols
  355. //
  356. qsort(
  357. Symbols,
  358. TotalNumberOfSymbols,
  359. sizeof(SYMBOL),
  360. (SortBySize ? SymbolCompareBySize : SymbolCompareByAddress));
  361. //
  362. // Figure out symbols that should not be printed.
  363. //
  364. for (J = 0; J < TotalNumberOfSymbols; J++) {
  365. if (ShouldExcludeSymbol (Symbols[J].Name)) {
  366. Symbols[J].Exclude = TRUE;
  367. }
  368. else {
  369. Symbols[J].Exclude = FALSE;
  370. }
  371. }
  372. //
  373. // Print symbols
  374. //
  375. printf("%-8s %-16s %-8s %s \n", "Section", "Address", "Size", "Symbol");
  376. printf("-------------------------------------------------------------\n");
  377. for (I = 0; I < SectionWriteIndex; I++) {
  378. for (J = 0, FoundOne = FALSE; J < TotalNumberOfSymbols; J++) {
  379. if (Symbols[J].Exclude) {
  380. continue;
  381. }
  382. SectionName = ImgSearchSectionForAddress (
  383. Symbols[J].Address - BaseOfDll);
  384. if (strcmp (SectionName, Section[I].Name) == 0) {
  385. if (All || strstr (SectionName,"PAGE") == NULL) {
  386. if (Symbols[J].Name == NULL) {
  387. printf(".\n");
  388. continue;
  389. }
  390. printf("%-8s %016I64X %08X %s \n",
  391. SectionName,
  392. Symbols[J].Address - BaseOfDll,
  393. Symbols[J].Size,
  394. Symbols[J].Name);
  395. FoundOne = TRUE;
  396. }
  397. }
  398. }
  399. if (FoundOne) {
  400. printf("\n");
  401. }
  402. }
  403. //
  404. // Unload symbols
  405. //
  406. if (SymUnloadModule64(GetCurrentProcess(), BaseOfDll) == FALSE) {
  407. Error ("cannot unload symbols");
  408. }
  409. }
  410. /////////////////////////////////////////////////////////////////////
  411. /////////////////////////////////////// Section manipulation routines
  412. /////////////////////////////////////////////////////////////////////
  413. //
  414. // Function:
  415. //
  416. // ImgInitializeBrowseInfo
  417. //
  418. // Description:
  419. //
  420. // This functions fills oout the `Info' structure with
  421. // various pointers to PE data from the mapped image file.
  422. //
  423. // Note. Even if the function returned false the destructor
  424. // `ImgDeleteBrowseInfo' should be called because it does some
  425. // cleanup.
  426. //
  427. // Return:
  428. //
  429. // True if all the PE data pointers have been obtained.
  430. //
  431. BOOL
  432. ImgInitializeBrowseInfo (
  433. LPCTSTR FilePath,
  434. PIMAGE_BROWSE_INFO Info)
  435. {
  436. DWORD Index, I;
  437. if (Info == NULL) {
  438. return FALSE;
  439. }
  440. ZeroMemory (Info, sizeof *Info);
  441. Info->File = CreateFile (
  442. FilePath,
  443. GENERIC_READ,
  444. FILE_SHARE_READ | FILE_SHARE_WRITE,
  445. NULL,
  446. OPEN_EXISTING,
  447. 0,
  448. NULL);
  449. if (Info->File == INVALID_HANDLE_VALUE) {
  450. Error ("create file %s (error %u)", FilePath, GetLastError());
  451. return FALSE;
  452. }
  453. Info->Section = CreateFileMapping (
  454. Info->File,
  455. NULL,
  456. PAGE_READONLY,
  457. 0,
  458. 0,
  459. NULL);
  460. if (Info->Section == NULL) {
  461. return FALSE;
  462. }
  463. Info->ImageBase = (LPBYTE) MapViewOfFile (
  464. Info->Section,
  465. FILE_MAP_READ,
  466. 0,
  467. 0,
  468. 0);
  469. if (Info->ImageBase == NULL) {
  470. return FALSE;
  471. }
  472. //
  473. // Check the signature
  474. //
  475. Info->DosHeader = (PIMAGE_DOS_HEADER)Info->ImageBase;
  476. if (Info->DosHeader->e_magic != 'ZM') {
  477. return FALSE;
  478. }
  479. Info->FileHeader = (PIMAGE_FILE_HEADER)
  480. (Info->ImageBase + Info->DosHeader->e_lfanew + sizeof(DWORD));
  481. Info->FileSignature = *((DWORD *)Info->FileHeader - 1);
  482. if (Info->FileSignature != IMAGE_NT_SIGNATURE) {
  483. return FALSE;
  484. }
  485. Info->OptionalHeader = (PIMAGE_OPTIONAL_HEADER)(Info->FileHeader + 1);
  486. Info->ImportDirectory = & (Info->OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]);
  487. Info->SectionHeader = (PIMAGE_SECTION_HEADER)(Info->OptionalHeader + 1);
  488. Info->ImportSection = NULL;
  489. //
  490. // Find the section containing the import table
  491. //
  492. printf("Sections in %s \n\n", FilePath);
  493. for (Index = 0; Index < Info->FileHeader->NumberOfSections; Index++) {
  494. //
  495. // SilviuC: I wonder if there is a way to get a 64 bit value for VirtualAddress.
  496. // Apparently it is stored as a ULONG in PE format.
  497. //
  498. Section[SectionWriteIndex].Start = (DWORD64)((Info->SectionHeader + Index)->VirtualAddress);
  499. Section[SectionWriteIndex].Size = (Info->SectionHeader + Index)->SizeOfRawData;
  500. for (I = 0; I < 8; I++) {
  501. Section[SectionWriteIndex].Name[I] = ((Info->SectionHeader + Index)->Name)[I];
  502. }
  503. Section[SectionWriteIndex].Name[I] = 0;
  504. printf("%-8s %08X %08X \n",
  505. Section[SectionWriteIndex].Name,
  506. Section[SectionWriteIndex].Start,
  507. Section[SectionWriteIndex].Size);
  508. SectionWriteIndex += 1;
  509. }
  510. printf("\n");
  511. //
  512. // Find the address of import data in the section body.
  513. //
  514. #if 0
  515. Info->AddressCorrection = (DWORD_PTR)Info->ImageBase
  516. + Info->ImportSection->PointerToRawData
  517. - Info->ImportSection->VirtualAddress;
  518. Info->ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(Info->AddressCorrection
  519. + Info->ImportDirectory->VirtualAddress);
  520. #endif
  521. //
  522. // Finish
  523. //
  524. return TRUE;
  525. }
  526. //
  527. // Function:
  528. //
  529. // ImgDeleteBrowseInfo
  530. //
  531. // Description:
  532. //
  533. // This function cleans up the `Info' structure, unmaps views,
  534. // closes handles, etc.
  535. //
  536. BOOL
  537. ImgDeleteBrowseInfo (
  538. PIMAGE_BROWSE_INFO Info)
  539. {
  540. if (Info == NULL)
  541. return FALSE;
  542. UnmapViewOfFile (Info->ImageBase);
  543. CloseHandle (Info->Section);
  544. CloseHandle (Info->File);
  545. ZeroMemory (Info, sizeof *Info);
  546. return TRUE;
  547. }
  548. PCHAR
  549. ImgSearchSectionForAddress (
  550. DWORD64 Address
  551. )
  552. {
  553. DWORD I;
  554. for (I = 0; I < SectionWriteIndex; I++) {
  555. if (Section[I].Start <= Address && Address < Section[I].Start + Section[I].Size) {
  556. return Section[I].Name;
  557. }
  558. }
  559. return "unknown";
  560. }
  561. //
  562. // Exclude file logic
  563. //
  564. PCHAR *ExcludeStrings;
  565. DWORD NumberOfExcludeStrings;
  566. BOOL
  567. ShouldExcludeSymbol (
  568. LPSTR Name
  569. )
  570. {
  571. DWORD I;
  572. if (ExcludeStrings == NULL) {
  573. return FALSE;
  574. }
  575. for (I = 0; I <NumberOfExcludeStrings; I += 1) {
  576. if (_stricmp (Name, ExcludeStrings[I]) == 0) {
  577. return TRUE;
  578. }
  579. }
  580. return FALSE;
  581. }
  582. BOOL
  583. OpenExcludeFile (
  584. LPSTR FilePath
  585. )
  586. {
  587. FILE * File;
  588. CHAR String[1024];
  589. DWORD StringCount = 0;
  590. File = fopen (FilePath, "r");
  591. if (File == NULL) {
  592. Error ("cannot open exclude file %s", FilePath);
  593. }
  594. while (fgets (String, 1024, File)) {
  595. StringCount += 1;
  596. }
  597. fclose (File);
  598. ExcludeStrings = (PCHAR *)malloc (StringCount * sizeof(PVOID));
  599. if (ExcludeStrings == NULL) {
  600. Error ("cannot allocate exclude strings buffer");
  601. }
  602. NumberOfExcludeStrings = StringCount;
  603. printf("Excluding %u symbols from %s \n",
  604. NumberOfExcludeStrings,
  605. FilePath);
  606. File = fopen (FilePath, "r");
  607. if (!File) {
  608. Error ("cannot open file");
  609. }
  610. StringCount = 0;
  611. while (fgets (String, 1024, File)) {
  612. PCHAR Start, Current;
  613. Current = String;
  614. while (*Current == ' ' || *Current == '\t') {
  615. Current += 1;
  616. }
  617. Start = Current;
  618. while (*Current && *Current != ' ' && *Current != '\t' && *Current != '\n') {
  619. Current += 1;
  620. }
  621. *Current = '\0';
  622. if (StringCount < NumberOfExcludeStrings) {
  623. ExcludeStrings[StringCount] = CopyString (Start);
  624. // printf("Exclude %s \n", ExcludeStrings[StringCount]);
  625. }
  626. StringCount += 1;
  627. }
  628. fclose (File);
  629. return TRUE;
  630. }