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