1 /* 2 - Info: 3 - http://stackoverflow.com/questions/32251638/dbghelp-get-full-symbol-signature-function-name-parameters-types 4 - http://www.debuginfo.com/articles/dbghelptypeinfo.html 5 - TODO: 6 - Dump usage 7 - Test for dbghelp + symsrv and warn if not working 8 - Resolve forwarders 9 10 */ 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <windows.h> 14 15 #ifdef __REACTOS__ 16 #include <dbghelp.h> 17 #include <cvconst.h> 18 19 // dirty hacks! 20 #define sprintf_s(dst, size, format, ...) sprintf(dst, format, __VA_ARGS__) 21 #define vsprintf_s(dst, size, format, ap) vsprintf(dst, format, ap) 22 #define fopen_s(pfile, name, mode) ((*pfile = fopen(name, mode)), (*pfile != 0) ? 0 : -1) 23 #define strcpy_s(dst, size, src) strncpy(dst, src, size) 24 #define strcat_s(dst, size, src) strncat(dst, src, size) 25 26 #else 27 #ifdef _MSC_VER 28 #pragma warning(disable:4091) 29 #endif 30 #define _NO_CVCONST_H 31 #include <dbghelp.h> 32 33 // This is from cvconst.h, but win sdk lacks this file 34 enum BasicType { 35 btNoType = 0, 36 btVoid = 1, 37 btChar = 2, 38 btWChar = 3, 39 btInt = 6, 40 btUInt = 7, 41 btFloat = 8, 42 btBCD = 9, 43 btBool = 10, 44 btLong = 13, 45 btULong = 14, 46 btCurrency = 25, 47 btDate = 26, 48 btVariant = 27, 49 btComplex = 28, 50 btBit = 29, 51 btBSTR = 30, 52 btHresult = 31 53 }; 54 55 typedef enum CV_call_e { 56 CV_CALL_NEAR_C = 0x00, 57 CV_CALL_NEAR_FAST = 0x04, 58 CV_CALL_NEAR_STD = 0x07, 59 CV_CALL_NEAR_SYS = 0x09, 60 CV_CALL_THISCALL = 0x0b, 61 CV_CALL_CLRCALL = 0x16 62 } CV_call_e; 63 64 #endif // __REACTOS__ 65 66 67 typedef enum _PARAM_TYPES 68 { 69 TYPE_NONE, 70 TYPE_LONG, 71 TYPE_DOUBLE, 72 TYPE_PTR, 73 TYPE_STR, 74 TYPE_WSTR 75 } PARAM_TYPES, *PPARAM_TYPES; 76 77 const char* 78 gapszTypeStrings[] = 79 { 80 "???", 81 "long", 82 "double", 83 "ptr", 84 "str", 85 "wstr" 86 }; 87 88 #define MAX_PARAMETERS 64 89 typedef struct _EXPORT 90 { 91 PSTR pszName; 92 PSTR pszSymbol; 93 PSTR pszForwarder; 94 ULONG ulRva; 95 DWORD dwCallingConvention; 96 ULONG fForwarder : 1; 97 ULONG fNoName : 1; 98 ULONG fData : 1; 99 ULONG cParameters; 100 PARAM_TYPES aeParameters[MAX_PARAMETERS]; 101 } EXPORT, *PEXPORT; 102 103 typedef struct _EXPORT_DATA 104 { 105 ULONG cNumberOfExports; 106 EXPORT aExports[1]; 107 } EXPORT_DATA, *PEXPORT_DATA; 108 109 HANDLE ghProcess; 110 111 void 112 error( 113 _In_ const char* pszFormat, 114 ...) 115 { 116 CHAR szBuffer[512]; 117 SIZE_T cchBuffer; 118 DWORD dwLastError; 119 va_list argptr; 120 121 /* Get last error */ 122 dwLastError = GetLastError(); 123 124 va_start(argptr, pszFormat); 125 cchBuffer = vsprintf_s(szBuffer, sizeof(szBuffer), pszFormat, argptr); 126 va_end(argptr); 127 128 /* Strip trailing newlines */ 129 _Analysis_assume_(cchBuffer < sizeof(szBuffer)); 130 while ((cchBuffer >= 1) && 131 ((szBuffer[cchBuffer - 1] == '\r') || 132 (szBuffer[cchBuffer - 1] == '\n'))) 133 { 134 szBuffer[cchBuffer - 1] = '\0'; 135 cchBuffer--; 136 } 137 138 /* Check if we have an error */ 139 if (dwLastError != ERROR_SUCCESS) 140 { 141 /* Append error code */ 142 cchBuffer += sprintf_s(szBuffer + cchBuffer, 143 sizeof(szBuffer) - cchBuffer, 144 " [error %lu: ", dwLastError); 145 146 /* Format the last error code */ 147 cchBuffer += FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 148 NULL, 149 dwLastError, 150 0, 151 szBuffer + cchBuffer, 152 (DWORD)(sizeof(szBuffer) - cchBuffer), 153 NULL); 154 155 /* Strip trailing newlines */ 156 _Analysis_assume_(cchBuffer < sizeof(szBuffer)); 157 while ((cchBuffer >= 1) && 158 ((szBuffer[cchBuffer - 1] == '\r') || 159 (szBuffer[cchBuffer - 1] == '\n'))) 160 { 161 szBuffer[cchBuffer - 1] = '\0'; 162 cchBuffer--; 163 } 164 165 fprintf(stderr, "%s]\n", szBuffer); 166 } 167 else 168 { 169 fprintf(stderr, "%s\n", szBuffer); 170 } 171 } 172 173 BOOL 174 InitDbgHelp( 175 VOID) 176 { 177 static const char *pszMsSymbolServer = "srv**symbols*http://msdl.microsoft.com/download/symbols"; 178 DWORD Options; 179 180 /* Save current process ;-) */ 181 ghProcess = GetCurrentProcess(); 182 183 /* Initialize dbghelp */ 184 if (!SymInitialize(ghProcess, 0, FALSE)) 185 { 186 error("SymInitialize() failed."); 187 return FALSE; 188 } 189 190 /* Set options */ 191 Options = SymGetOptions(); 192 Options |= SYMOPT_ALLOW_ABSOLUTE_SYMBOLS | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEBUG;// | SYMOPT_NO_PROMPTS; 193 Options &= ~SYMOPT_DEFERRED_LOADS; 194 SymSetOptions(Options); 195 196 /* Test if we can reach the MS symbol server */ 197 if (!SymSrvIsStore(ghProcess, pszMsSymbolServer)) 198 { 199 error("Failed to connect to symbol server."); 200 return FALSE; 201 } 202 203 /* Set MS symbol server as symbol search path */ 204 SymSetSearchPath(ghProcess, pszMsSymbolServer); 205 206 return TRUE; 207 } 208 209 HMODULE 210 LoadModuleWithSymbolsFullPath( 211 _In_ PSTR pszFullModuleFileName) 212 { 213 HMODULE hmod; 214 DWORD64 dwModuleBase; 215 216 /* Load the DLL */ 217 hmod = LoadLibraryExA(pszFullModuleFileName, 218 NULL, 219 LOAD_IGNORE_CODE_AUTHZ_LEVEL | 220 DONT_RESOLVE_DLL_REFERENCES | 221 LOAD_WITH_ALTERED_SEARCH_PATH); 222 if (hmod == NULL) 223 { 224 return NULL; 225 } 226 227 /* Load symbols for this module */ 228 dwModuleBase = SymLoadModule64(ghProcess, 229 NULL, 230 pszFullModuleFileName, 231 NULL, 232 (DWORD_PTR)hmod, 233 0); 234 if (dwModuleBase == 0) 235 { 236 /* ERROR_SUCCESS means, we have symbols already */ 237 if (GetLastError() != ERROR_SUCCESS) 238 { 239 return NULL; 240 } 241 } 242 else 243 { 244 printf("Successfully loaded symbols for '%s'\n", 245 pszFullModuleFileName); 246 } 247 248 return hmod; 249 } 250 251 HMODULE 252 LoadModuleWithSymbols( 253 _In_ PSTR pszModuleName) 254 { 255 CHAR szFullFileName[MAX_PATH]; 256 HMODULE hmod; 257 258 /* Check if the file name has a path */ 259 if (strchr(pszModuleName, '\\') != NULL) 260 { 261 /* Try as it is */ 262 hmod = LoadModuleWithSymbolsFullPath(pszModuleName); 263 if (hmod != NULL) 264 { 265 return hmod; 266 } 267 } 268 269 /* Try current directory */ 270 GetCurrentDirectoryA(MAX_PATH, szFullFileName); 271 strcat_s(szFullFileName, sizeof(szFullFileName), "\\"); 272 strcat_s(szFullFileName, sizeof(szFullFileName), pszModuleName); 273 hmod = LoadModuleWithSymbolsFullPath(szFullFileName); 274 if (hmod != NULL) 275 { 276 return hmod; 277 } 278 279 /* Try system32 */ 280 strcpy_s(szFullFileName, sizeof(szFullFileName), "%systemroot%\\system32\\"); 281 strcat_s(szFullFileName, sizeof(szFullFileName), pszModuleName); 282 hmod = LoadModuleWithSymbolsFullPath(szFullFileName); 283 if (hmod != NULL) 284 { 285 return hmod; 286 } 287 288 #ifdef _WIN64 289 /* Try SysWOW64 */ 290 strcpy_s(szFullFileName, sizeof(szFullFileName), "%systemroot%\\SysWOW64\\"); 291 strcat_s(szFullFileName, sizeof(szFullFileName), pszModuleName); 292 hmod = LoadModuleWithSymbolsFullPath(szFullFileName); 293 if (hmod != NULL) 294 { 295 return hmod; 296 } 297 #endif // _WIN64 298 299 return NULL; 300 } 301 302 HRESULT 303 GetExportsFromFile( 304 _In_ HMODULE hmod, 305 _Out_ PEXPORT_DATA* ppExportData) 306 { 307 PBYTE pjImageBase; 308 PIMAGE_EXPORT_DIRECTORY pExportDir; 309 ULONG i, cjExportSize, cFunctions, cjTableSize; 310 PEXPORT_DATA pExportData; 311 PULONG pulAddressTable, pulNameTable; 312 PUSHORT pusOrdinalTable; 313 314 pjImageBase = (PBYTE)hmod; 315 316 /* Get the export directory */ 317 pExportDir = ImageDirectoryEntryToData(pjImageBase, 318 TRUE, 319 IMAGE_DIRECTORY_ENTRY_EXPORT, 320 &cjExportSize); 321 if (pExportDir == NULL) 322 { 323 fprintf(stderr, "Failed to get export directory\n"); 324 return E_FAIL; 325 } 326 327 cFunctions = pExportDir->NumberOfFunctions; 328 cjTableSize = FIELD_OFFSET(EXPORT_DATA, aExports[cFunctions]); 329 330 pExportData = malloc(cjTableSize); 331 if (pExportData == NULL) 332 { 333 error("Failed to allocate %u bytes of memory for export table\n", cjTableSize); 334 return E_OUTOFMEMORY; 335 } 336 337 RtlZeroMemory(pExportData, cjTableSize); 338 339 pulAddressTable = (PULONG)(pjImageBase + pExportDir->AddressOfFunctions); 340 341 pExportData->cNumberOfExports = cFunctions; 342 343 /* Loop through the function table */ 344 for (i = 0; i < cFunctions; i++) 345 { 346 PVOID pvFunction = (pjImageBase + pulAddressTable[i]); 347 348 /* Check if this is a forwarder */ 349 if ((ULONG_PTR)((PUCHAR)pvFunction - (PUCHAR)pExportDir) < cjExportSize) 350 { 351 pExportData->aExports[i].pszForwarder = _strdup(pvFunction); 352 } 353 else 354 { 355 pExportData->aExports[i].ulRva = pulAddressTable[i]; 356 } 357 } 358 359 pulNameTable = (PULONG)(pjImageBase + pExportDir->AddressOfNames); 360 pusOrdinalTable = (PUSHORT)(pjImageBase + pExportDir->AddressOfNameOrdinals); 361 362 /* Loop through the name table */ 363 for (i = 0; i < pExportDir->NumberOfNames; i++) 364 { 365 ULONG iIndex = pusOrdinalTable[i]; 366 PSTR pszName = (PSTR)(pjImageBase + pulNameTable[i]); 367 368 pExportData->aExports[iIndex].pszName = _strdup(pszName); 369 } 370 371 *ppExportData = pExportData; 372 return S_OK; 373 } 374 375 BOOL 376 CALLBACK 377 EnumParametersCallback( 378 _In_ PSYMBOL_INFO pSymInfo, 379 _In_ ULONG SymbolSize, 380 _In_ PVOID UserContext) 381 { 382 PEXPORT pExport = (PEXPORT)UserContext; 383 enum SymTagEnum eSymTag; 384 enum BasicType eBaseType; 385 DWORD dwTypeIndex; 386 ULONG64 ullLength; 387 388 /* If it's not a parameter, skip it */ 389 if (!(pSymInfo->Flags & SYMFLAG_PARAMETER)) 390 { 391 return TRUE; 392 } 393 394 /* Count this parameter */ 395 pExport->cParameters++; 396 397 /* Get the type for the parameter */ 398 if (SymGetTypeInfo(ghProcess, 399 pSymInfo->ModBase, 400 pSymInfo->TypeIndex, 401 TI_GET_SYMTAG, 402 &eSymTag)) 403 { 404 switch (eSymTag) 405 { 406 case SymTagUDT: 407 case SymTagBaseType: 408 409 /* Try to get the size */ 410 if (SymGetTypeInfo(ghProcess, 411 pSymInfo->ModBase, 412 pSymInfo->TypeIndex, 413 TI_GET_LENGTH, 414 &ullLength)) 415 { 416 if (ullLength > 8) 417 { 418 /* That is probably not possible */ 419 __debugbreak(); 420 } 421 422 if (ullLength > 4) 423 { 424 /* 'double' type */ 425 pExport->aeParameters[pExport->cParameters - 1] = TYPE_DOUBLE; 426 break; 427 } 428 } 429 430 /* Default to 'long' type */ 431 pExport->aeParameters[pExport->cParameters - 1] = TYPE_LONG; 432 break; 433 434 case SymTagEnum: 435 /* 'long' type */ 436 pExport->aeParameters[pExport->cParameters - 1] = TYPE_LONG; 437 break; 438 439 case SymTagPointerType: 440 /* 'ptr' type */ 441 pExport->aeParameters[pExport->cParameters - 1] = TYPE_PTR; 442 443 /* Try to get the underlying type */ 444 if (SymGetTypeInfo(ghProcess, 445 pSymInfo->ModBase, 446 pSymInfo->TypeIndex, 447 TI_GET_TYPEID, 448 &dwTypeIndex)) 449 { 450 /* Try to get the base type */ 451 if (SymGetTypeInfo(ghProcess, 452 pSymInfo->ModBase, 453 dwTypeIndex, 454 TI_GET_BASETYPE, 455 &eBaseType)) 456 { 457 /* Check for string types */ 458 if (eBaseType == btChar) 459 { 460 /* 'str' type */ 461 pExport->aeParameters[pExport->cParameters - 1] = TYPE_STR; 462 } 463 else if (eBaseType == btWChar) 464 { 465 /* 'wstr' type */ 466 pExport->aeParameters[pExport->cParameters - 1] = TYPE_WSTR; 467 } 468 } 469 } 470 break; 471 472 default: 473 printf("Unhandled eSymTag: %u\n", eSymTag); 474 return FALSE; 475 } 476 } 477 else 478 { 479 printf("Could not get type info. Fallig back to ptr\n"); 480 pExport->aeParameters[pExport->cParameters - 1] = TYPE_PTR; 481 } 482 483 return TRUE; 484 } 485 486 ULONG64 487 GetFunctionFromForwarder( 488 _In_ PCSTR pszForwarder) 489 { 490 CHAR szDllName[MAX_SYM_NAME]; 491 PCH pchDot, pszName; 492 ULONG64 ullFunction; 493 HMODULE hmod; 494 495 /* Copy the forwarder name */ 496 strcpy_s(szDllName, sizeof(szDllName), pszForwarder); 497 498 /* Find the '.' */ 499 pchDot = strchr(szDllName, '.'); 500 if (pchDot == NULL) 501 { 502 error("Invalid name for forwarder '%s'!", pszForwarder); 503 return 0; 504 } 505 506 /* Terminate DLL name */ 507 *pchDot = '\0'; 508 509 /* Load the DLL */ 510 hmod = LoadModuleWithSymbols(szDllName); 511 if (hmod == NULL) 512 { 513 error("Failed to load module for forwarder '%s'!", pszForwarder); 514 return 0; 515 } 516 517 /* Get the function name and check for ordinal */ 518 pszName = pchDot + 1; 519 if (pszName[0] == '#') 520 { 521 ULONG iOrdinal = strtoul(pszName + 1, NULL, 10); 522 if ((iOrdinal == 0) || (iOrdinal > 0xFFFF)) 523 { 524 error("Got invalid ordinal %u for ''", iOrdinal, pszForwarder); 525 return 0; 526 } 527 528 pszName = (PSTR)(ULONG_PTR)iOrdinal; 529 } 530 531 /* Get the function address */ 532 ullFunction = (ULONG_PTR)GetProcAddress(hmod, pszName); 533 if (ullFunction == 0) 534 { 535 error("Failed to resolve '%s' in '%s'.", pchDot + 1, szDllName); 536 return 0; 537 } 538 539 return ullFunction; 540 } 541 542 HRESULT 543 ParseImageSymbols( 544 _In_ HMODULE hmod, 545 _Inout_ PEXPORT_DATA pExportData) 546 { 547 DWORD64 dwModuleBase; 548 ULONG i; 549 IMAGEHLP_STACK_FRAME StackFrame; 550 SYMBOL_INFO_PACKAGE sym; 551 552 dwModuleBase = (DWORD_PTR)hmod; 553 554 /* Loop through all exports */ 555 for (i = 0; i < pExportData->cNumberOfExports; i++) 556 { 557 PEXPORT pExport = &pExportData->aExports[i]; 558 ULONG64 ullFunction = dwModuleBase + pExportData->aExports[i].ulRva; 559 ULONG64 ullDisplacement; 560 561 /* Check if this is a forwarder */ 562 if (pExport->pszForwarder != NULL) 563 { 564 /* Load the module and get the function address */ 565 ullFunction = GetFunctionFromForwarder(pExport->pszForwarder); 566 if (ullFunction == 0) 567 { 568 printf("Failed to get function for forwarder '%s'. Skipping.\n", pExport->pszForwarder); 569 continue; 570 } 571 } 572 573 RtlZeroMemory(&sym, sizeof(sym)); 574 sym.si.SizeOfStruct = sizeof(SYMBOL_INFO); 575 sym.si.MaxNameLen = MAX_SYM_NAME; 576 577 /* Try to find the symbol */ 578 if (!SymFromAddr(ghProcess, ullFunction, &ullDisplacement, &sym.si)) 579 { 580 error("Error: SymFromAddr() failed."); 581 continue; 582 } 583 584 /* Get the symbol name */ 585 pExport->pszSymbol = _strdup(sym.si.Name); 586 587 /* Check if it is a function */ 588 if (sym.si.Tag == SymTagFunction) 589 { 590 /* Get the calling convention */ 591 if (!SymGetTypeInfo(ghProcess, 592 dwModuleBase, 593 sym.si.TypeIndex, 594 TI_GET_CALLING_CONVENTION, 595 &pExport->dwCallingConvention)) 596 { 597 /* Fall back to __stdcall */ 598 pExport->dwCallingConvention = CV_CALL_NEAR_STD; 599 } 600 601 /* Set the context to the function address */ 602 RtlZeroMemory(&StackFrame, sizeof(StackFrame)); 603 StackFrame.InstructionOffset = ullFunction; 604 if (!SymSetContext(ghProcess, &StackFrame, NULL)) 605 { 606 error("SymSetContext failed for i = %u.", i); 607 continue; 608 } 609 610 /* Enumerate all symbols for this function */ 611 if (!SymEnumSymbols(ghProcess, 612 0, // use SymSetContext 613 NULL, 614 EnumParametersCallback, 615 pExport)) 616 { 617 error("SymEnumSymbols failed for i = %u.", i); 618 continue; 619 } 620 } 621 else if (sym.si.Tag == SymTagPublicSymbol) 622 { 623 pExport->dwCallingConvention = CV_CALL_NEAR_STD; 624 } 625 else if (sym.si.Tag == SymTagData) 626 { 627 pExport->fData = TRUE; 628 } 629 } 630 631 return S_OK; 632 } 633 634 const CHAR* 635 GetCallingConvention( 636 _In_ PEXPORT pExport) 637 { 638 if (pExport->fData) 639 { 640 return "extern"; 641 } 642 643 #ifndef _M_AMD64 644 switch (pExport->dwCallingConvention) 645 { 646 case CV_CALL_NEAR_C: 647 return "cdecl"; 648 case CV_CALL_NEAR_FAST: 649 return "fastcall"; 650 case CV_CALL_NEAR_STD: 651 return "stdcall"; 652 case CV_CALL_NEAR_SYS: 653 return "syscall"; 654 case CV_CALL_THISCALL: 655 return "thiscall"; 656 default: 657 __debugbreak(); 658 } 659 #endif 660 return "stdcall"; 661 } 662 663 HRESULT 664 CreateSpecFile( 665 _In_ PCSTR pszSpecFile, 666 _In_ PEXPORT_DATA pExportData) 667 { 668 FILE *file; 669 ULONG i, p; 670 PEXPORT pExport; 671 672 /* Create the spec file */ 673 if (fopen_s(&file, pszSpecFile, "w") != 0) 674 { 675 error("Failed to open spec file: '%s'\n", pszSpecFile); 676 return E_FAIL; 677 } 678 679 /* Loop all exports */ 680 for (i = 0; i < pExportData->cNumberOfExports; i++) 681 { 682 pExport = &pExportData->aExports[i]; 683 684 fprintf(file, "%lu %s ", i + 1, GetCallingConvention(pExport)); 685 //if (pExport->fNoName) 686 if (pExport->pszName == NULL) 687 { 688 fprintf(file, "-noname "); 689 } 690 691 if (pExport->pszName != NULL) 692 { 693 fprintf(file, "%s", pExport->pszName); 694 } 695 else if (pExport->pszSymbol != NULL) 696 { 697 fprintf(file, "%s", pExport->pszSymbol); 698 } 699 else 700 { 701 fprintf(file, "NamelessExport_%lu", i); 702 } 703 704 if (!pExport->fData) 705 { 706 fprintf(file, "("); 707 for (p = 0; p < pExport->cParameters; p++) 708 { 709 fprintf(file, "%s", gapszTypeStrings[pExport->aeParameters[p]]); 710 if ((p + 1) < pExport->cParameters) 711 { 712 fprintf(file, " "); 713 } 714 } 715 fprintf(file, ")"); 716 } 717 718 if (pExport->pszForwarder != NULL) 719 { 720 fprintf(file, " %s", pExport->pszForwarder); 721 } 722 723 fprintf(file, "\n"); 724 } 725 726 fclose(file); 727 728 return S_OK; 729 } 730 731 int main(int argc, char* argv[]) 732 { 733 HRESULT hr; 734 CHAR szSpecFile[MAX_PATH]; 735 PSTR pszSpecFile; 736 PEXPORT_DATA pExportData; 737 HMODULE hmod; 738 739 /* Check parameters */ 740 if ((argc < 2) || !strcmp(argv[1], "/?")) 741 { 742 printf("syntax: createspec <image file> [<spec file>]\n"); 743 return 0; 744 } 745 746 /* Check if we have a spec file name */ 747 if (argc > 2) 748 { 749 pszSpecFile = argv[2]; 750 } 751 else 752 { 753 /* Create spec file name from image file name */ 754 PSTR pszStart = strrchr(argv[1], '\\'); 755 if (pszStart == 0) 756 pszStart = argv[1]; 757 758 strcpy_s(szSpecFile, sizeof(szSpecFile), pszStart); 759 strcat_s(szSpecFile, sizeof(szSpecFile), ".spec"); 760 pszSpecFile = szSpecFile; 761 } 762 763 /* Initialize dbghelp.dll */ 764 if (!InitDbgHelp()) 765 { 766 error("Failed to init dbghelp!\n" 767 "Make sure you have dbghelp.dll and symsrv.dll in the same folder.\n"); 768 return E_FAIL; 769 } 770 771 /* Load the file including symbols */ 772 printf("Loading symbols for '%s', please wait...\n", argv[1]); 773 hmod = LoadModuleWithSymbols(argv[1]); 774 if (hmod == NULL) 775 { 776 error("Failed to load module '%s'!", argv[1]); 777 return E_FAIL; 778 } 779 780 /* Get the exports */ 781 hr = GetExportsFromFile(hmod, &pExportData); 782 if (!SUCCEEDED(hr)) 783 { 784 error("Failed to get exports: %lx\n", hr); 785 return hr; 786 } 787 788 /* Get additional info from symbols */ 789 hr = ParseImageSymbols(hmod, pExportData); 790 if (!SUCCEEDED(hr)) 791 { 792 error("Failed to get symbol information: hr=%lx\n", hr); 793 } 794 795 /* Write the spec file */ 796 hr = CreateSpecFile(pszSpecFile, pExportData); 797 798 printf("Spec file '%s' was successfully written.\n", szSpecFile); 799 800 return hr; 801 } 802