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