3 - http://stackoverflow.com/questions/32251638/dbghelp-get-full-symbol-signature-function-name-parameters-types
4 - http://www.debuginfo.com/articles/dbghelptypeinfo.html
7 - Test for dbghelp + symsrv and warn if not working
11 #define MINGW_HAS_SECURE_API
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)
29 #pragma warning(disable:4091)
34 // This is from cvconst.h, but win sdk lacks this file
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
67 #define MAX_SYMBOL_NAME 1024
68 typedef struct _SYMINFO_EX
71 CHAR achName
[MAX_SYMBOL_NAME
];
74 typedef struct _SYMBOL64_EX
76 IMAGEHLP_SYMBOL64 sym64
;
77 CHAR achName
[MAX_SYMBOL_NAME
];
78 } SYMBOL64_EX
, *PSYMBOL64_EX
;
80 typedef enum _PARAM_TYPES
88 } PARAM_TYPES
, *PPARAM_TYPES
;
101 #define MAX_PARAMETERS 64
102 typedef struct _EXPORT
108 DWORD dwCallingConvention
;
109 ULONG fForwarder
: 1;
113 PARAM_TYPES aeParameters
[MAX_PARAMETERS
];
116 typedef struct _EXPORT_DATA
118 ULONG cNumberOfExports
;
120 } EXPORT_DATA
, *PEXPORT_DATA
;
126 _In_
const char* pszFormat
,
135 dwLastError
= GetLastError();
137 va_start(argptr
, pszFormat
);
138 cchBuffer
= vsprintf_s(szBuffer
, sizeof(szBuffer
), pszFormat
, argptr
);
141 /* Strip trailing newlines */
142 _Analysis_assume_(cchBuffer
< sizeof(szBuffer
));
143 while ((cchBuffer
>= 1) &&
144 ((szBuffer
[cchBuffer
- 1] == '\r') ||
145 (szBuffer
[cchBuffer
- 1] == '\n')))
147 szBuffer
[cchBuffer
- 1] = '\0';
151 /* Check if we have an error */
152 if (dwLastError
!= ERROR_SUCCESS
)
154 /* Append error code */
155 cchBuffer
+= sprintf_s(szBuffer
+ cchBuffer
,
156 sizeof(szBuffer
) - cchBuffer
,
157 " [error %lu: ", dwLastError
);
159 /* Format the last error code */
160 cchBuffer
+= FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM
,
164 szBuffer
+ cchBuffer
,
165 (DWORD
)(sizeof(szBuffer
) - cchBuffer
),
168 /* Strip trailing newlines */
169 _Analysis_assume_(cchBuffer
< sizeof(szBuffer
));
170 while ((cchBuffer
>= 1) &&
171 ((szBuffer
[cchBuffer
- 1] == '\r') ||
172 (szBuffer
[cchBuffer
- 1] == '\n')))
174 szBuffer
[cchBuffer
- 1] = '\0';
178 fprintf(stderr
, "%s]\n", szBuffer
);
182 fprintf(stderr
, "%s\n", szBuffer
);
190 static const char *pszMsSymbolServer
= "srv**symbols*http://msdl.microsoft.com/download/symbols";
193 /* Save current process ;-) */
194 ghProcess
= GetCurrentProcess();
196 /* Initialize dbghelp */
197 if (!SymInitialize(ghProcess
, 0, FALSE
))
199 error("SymInitialize() failed.");
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
);
209 /* Test if we can reach the MS symbol server */
210 if (!SymSrvIsStore(ghProcess
, pszMsSymbolServer
))
212 error("Failed to connect to symbol server.");
216 /* Set MS symbol server as symbol search path */
217 SymSetSearchPath(ghProcess
, pszMsSymbolServer
);
223 LoadModuleWithSymbolsFullPath(
224 _In_ PSTR pszFullModuleFileName
)
227 DWORD64 dwModuleBase
;
230 hmod
= LoadLibraryExA(pszFullModuleFileName
,
232 LOAD_IGNORE_CODE_AUTHZ_LEVEL
|
233 DONT_RESOLVE_DLL_REFERENCES
|
234 LOAD_WITH_ALTERED_SEARCH_PATH
);
240 /* Load symbols for this module */
241 dwModuleBase
= SymLoadModule64(ghProcess
,
243 pszFullModuleFileName
,
247 if (dwModuleBase
== 0)
249 /* ERROR_SUCCESS means, we have symbols already */
250 if (GetLastError() != ERROR_SUCCESS
)
257 printf("Successfully loaded symbols for '%s'\n",
258 pszFullModuleFileName
);
265 LoadModuleWithSymbols(
266 _In_ PSTR pszModuleName
)
268 CHAR szFullFileName
[MAX_PATH
];
271 /* Check if the file name has a path */
272 if (strchr(pszModuleName
, '\\') != NULL
)
275 hmod
= LoadModuleWithSymbolsFullPath(pszModuleName
);
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
);
293 strcpy_s(szFullFileName
, sizeof(szFullFileName
), "%systemroot%\\system32");
294 strcat_s(szFullFileName
, sizeof(szFullFileName
), pszModuleName
);
295 hmod
= LoadModuleWithSymbolsFullPath(szFullFileName
);
303 strcpy_s(szFullFileName
, sizeof(szFullFileName
), "%systemroot%\\system32");
304 strcat_s(szFullFileName
, sizeof(szFullFileName
), pszModuleName
);
305 hmod
= LoadModuleWithSymbolsFullPath(szFullFileName
);
318 _Out_ PEXPORT_DATA
* ppExportData
)
321 PIMAGE_EXPORT_DIRECTORY pExportDir
;
322 ULONG i
, cjExportSize
, cFunctions
, cjTableSize
;
323 PEXPORT_DATA pExportData
;
324 PULONG pulAddressTable
, pulNameTable
;
325 PUSHORT pusOrdinalTable
;
327 pjImageBase
= (PBYTE
)hmod
;
329 /* Get the export directory */
330 pExportDir
= ImageDirectoryEntryToData(pjImageBase
,
332 IMAGE_DIRECTORY_ENTRY_EXPORT
,
334 if (pExportDir
== NULL
)
336 fprintf(stderr
, "Failed to get export directory\n");
340 cFunctions
= pExportDir
->NumberOfFunctions
;
341 cjTableSize
= FIELD_OFFSET(EXPORT_DATA
, aExports
[cFunctions
]);
343 pExportData
= malloc(cjTableSize
);
344 if (pExportData
== NULL
)
346 error("Failed to allocate %u bytes of memory for export table\n", cjTableSize
);
347 return E_OUTOFMEMORY
;
350 RtlZeroMemory(pExportData
, cjTableSize
);
352 pulAddressTable
= (PULONG
)(pjImageBase
+ pExportDir
->AddressOfFunctions
);
354 pExportData
->cNumberOfExports
= cFunctions
;
356 /* Loop through the function table */
357 for (i
= 0; i
< cFunctions
; i
++)
359 PVOID pvFunction
= (pjImageBase
+ pulAddressTable
[i
]);
361 /* Check if this is a forwarder */
362 if ((ULONG_PTR
)((PUCHAR
)pvFunction
- (PUCHAR
)pExportDir
) < cjExportSize
)
364 pExportData
->aExports
[i
].pszForwarder
= _strdup(pvFunction
);
368 pExportData
->aExports
[i
].ulRva
= pulAddressTable
[i
];
372 pulNameTable
= (PULONG
)(pjImageBase
+ pExportDir
->AddressOfNames
);
373 pusOrdinalTable
= (PUSHORT
)(pjImageBase
+ pExportDir
->AddressOfNameOrdinals
);
375 /* Loop through the name table */
376 for (i
= 0; i
< pExportDir
->NumberOfNames
; i
++)
378 ULONG iIndex
= pusOrdinalTable
[i
];
379 PSTR pszName
= (PSTR
)(pjImageBase
+ pulNameTable
[i
]);
381 pExportData
->aExports
[iIndex
].pszName
= _strdup(pszName
);
384 *ppExportData
= pExportData
;
390 EnumParametersCallback(
391 _In_ PSYMBOL_INFO pSymInfo
,
392 _In_ ULONG SymbolSize
,
393 _In_ PVOID UserContext
)
395 PEXPORT pExport
= (PEXPORT
)UserContext
;
396 enum SymTagEnum eSymTag
;
397 enum BasicType eBaseType
;
401 /* If it's not a parameter, skip it */
402 if (!(pSymInfo
->Flags
& SYMFLAG_PARAMETER
))
407 /* Count this parameter */
408 pExport
->cParameters
++;
410 /* Get the type for the parameter */
411 if (SymGetTypeInfo(ghProcess
,
422 /* Try to get the size */
423 if (SymGetTypeInfo(ghProcess
,
431 /* That is probably not possible */
438 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_DOUBLE
;
443 /* Default to 'long' type */
444 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_LONG
;
449 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_LONG
;
452 case SymTagPointerType
:
454 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_PTR
;
456 /* Try to get the underlying type */
457 if (SymGetTypeInfo(ghProcess
,
463 /* Try to get the base type */
464 if (SymGetTypeInfo(ghProcess
,
470 /* Check for string types */
471 if (eBaseType
== btChar
)
474 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_STR
;
476 else if (eBaseType
== btWChar
)
479 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_WSTR
;
486 printf("Unhandled eSymTag: %u\n", eSymTag
);
492 printf("Could not get type info. Fallig back to ptr\n");
493 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_PTR
;
500 GetFunctionFromForwarder(
501 _In_ PCSTR pszForwarder
)
503 CHAR szDllName
[MAX_SYMBOL_NAME
];
508 /* Copy the forwarder name */
509 strcpy_s(szDllName
, sizeof(szDllName
), pszForwarder
);
512 pchDot
= strchr(szDllName
, '.');
515 error("Invalid name for forwarder '%s'!", pszForwarder
);
519 /* Terminate DLL name */
523 hmod
= LoadModuleWithSymbols(szDllName
);
526 error("Failed to load module for forwarder '%s'!", pszForwarder
);
530 /* Get the function name and check for ordinal */
531 pszName
= pchDot
+ 1;
532 if (pszName
[0] == '#')
534 ULONG iOrdinal
= strtoul(pszName
+ 1, NULL
, 10);
535 if ((iOrdinal
== 0) || (iOrdinal
> 0xFFFF))
537 error("Got invalid ordinal %u for ''", iOrdinal
, pszForwarder
);
541 pszName
= (PSTR
)(ULONG_PTR
)iOrdinal
;
544 /* Get the function address */
545 ullFunction
= (ULONG_PTR
)GetProcAddress(hmod
, pszName
);
546 if (ullFunction
== 0)
548 error("Failed to resolve '%s' in '%s'.", pchDot
+ 1, szDllName
);
558 _Inout_ PEXPORT_DATA pExportData
)
560 DWORD64 dwModuleBase
;
562 IMAGEHLP_STACK_FRAME StackFrame
;
565 dwModuleBase
= (DWORD_PTR
)hmod
;
567 /* Loop through all exports */
568 for (i
= 0; i
< pExportData
->cNumberOfExports
; i
++)
570 PEXPORT pExport
= &pExportData
->aExports
[i
];
571 ULONG64 ullFunction
= dwModuleBase
+ pExportData
->aExports
[i
].ulRva
;
572 ULONG64 ullDisplacement
;
574 /* Check if this is a forwarder */
575 if (pExport
->pszForwarder
!= NULL
)
577 /* Load the module and get the function address */
578 ullFunction
= GetFunctionFromForwarder(pExport
->pszForwarder
);
579 if (ullFunction
== 0)
581 printf("Failed to get function for forwarder '%s'. Skipping.\n", pExport
->pszForwarder
);
586 RtlZeroMemory(&sym
, sizeof(sym
));
587 sym
.si
.SizeOfStruct
= sizeof(SYMBOL_INFO
);
588 sym
.si
.MaxNameLen
= MAX_SYMBOL_NAME
;
590 /* Try to find the symbol */
591 if (!SymFromAddr(ghProcess
, ullFunction
, &ullDisplacement
, &sym
.si
))
593 error("Error: SymFromAddr() failed.");
597 /* Get the symbol name */
598 pExport
->pszSymbol
= _strdup(sym
.si
.Name
);
600 /* Check if it is a function */
601 if (sym
.si
.Tag
== SymTagFunction
)
603 /* Get the calling convention */
604 if (!SymGetTypeInfo(ghProcess
,
607 TI_GET_CALLING_CONVENTION
,
608 &pExport
->dwCallingConvention
))
610 /* Fall back to __stdcall */
611 pExport
->dwCallingConvention
= CV_CALL_NEAR_STD
;
614 /* Set the context to the function address */
615 RtlZeroMemory(&StackFrame
, sizeof(StackFrame
));
616 StackFrame
.InstructionOffset
= ullFunction
;
617 if (!SymSetContext(ghProcess
, &StackFrame
, NULL
))
619 error("SymSetContext failed for i = %u.", i
);
623 /* Enumerate all symbols for this function */
624 if (!SymEnumSymbols(ghProcess
,
625 0, // use SymSetContext
627 EnumParametersCallback
,
630 error("SymEnumSymbols failed for i = %u.", i
);
634 else if (sym
.si
.Tag
== SymTagPublicSymbol
)
636 pExport
->dwCallingConvention
= CV_CALL_NEAR_STD
;
638 else if (sym
.si
.Tag
== SymTagData
)
640 pExport
->fData
= TRUE
;
648 GetCallingConvention(
649 _In_ PEXPORT pExport
)
657 switch (pExport
->dwCallingConvention
)
661 case CV_CALL_NEAR_FAST
:
663 case CV_CALL_NEAR_STD
:
665 case CV_CALL_NEAR_SYS
:
667 case CV_CALL_THISCALL
:
678 _In_ PCSTR pszSpecFile
,
679 _In_ PEXPORT_DATA pExportData
)
685 /* Create the spec file */
686 if (fopen_s(&file
, pszSpecFile
, "w") != 0)
688 error("Failed to open spec file: '%s'\n", pszSpecFile
);
692 /* Loop all exports */
693 for (i
= 0; i
< pExportData
->cNumberOfExports
; i
++)
695 pExport
= &pExportData
->aExports
[i
];
697 fprintf(file
, "%lu %s ", i
+ 1, GetCallingConvention(pExport
));
698 //if (pExport->fNoName)
699 if (pExport
->pszName
== NULL
)
701 fprintf(file
, "-noname ");
704 if (pExport
->pszName
!= NULL
)
706 fprintf(file
, "%s", pExport
->pszName
);
708 else if (pExport
->pszSymbol
!= NULL
)
710 fprintf(file
, "%s", pExport
->pszSymbol
);
714 fprintf(file
, "NamelessExport_%lu", i
);
720 for (p
= 0; p
< pExport
->cParameters
; p
++)
722 fprintf(file
, "%s", gapszTypeStrings
[pExport
->aeParameters
[p
]]);
723 if ((p
+ 1) < pExport
->cParameters
)
731 if (pExport
->pszForwarder
!= NULL
)
733 fprintf(file
, " %s", pExport
->pszForwarder
);
744 int main(int argc
, char* argv
[])
747 CHAR szSpecFile
[MAX_PATH
];
749 PEXPORT_DATA pExportData
;
752 /* Check parameters */
753 if ((argc
< 2) || !strcmp(argv
[1], "/?"))
755 printf("syntax: createspec <image file> [<spec file>]\n");
759 /* Check if we have a spec file name */
762 pszSpecFile
= argv
[2];
766 /* Create spec file name from image file name */
767 PSTR pszStart
= strrchr(argv
[1], '\\');
771 strcpy_s(szSpecFile
, sizeof(szSpecFile
), pszStart
);
772 strcat_s(szSpecFile
, sizeof(szSpecFile
), ".spec");
773 pszSpecFile
= szSpecFile
;
776 /* Initialize dbghelp.dll */
779 error("Failed to init dbghelp!\n"
780 "Make sure you have dbghelp.dll and symsrv.dll in the same folder.\n");
784 /* Load the file including symbols */
785 printf("Loading symbols for '%s', please wait...\n", argv
[1]);
786 hmod
= LoadModuleWithSymbols(argv
[1]);
789 error("Failed to load module '%s'!", argv
[1]);
793 /* Get the exports */
794 hr
= GetExportsFromFile(hmod
, &pExportData
);
797 error("Failed to get exports: %lx\n", hr
);
801 /* Get additional info from symbols */
802 hr
= ParseImageSymbols(hmod
, pExportData
);
805 error("Failed to get symbol information: hr=%lx\n", hr
);
808 /* Write the spec file */
809 hr
= CreateSpecFile(pszSpecFile
, pExportData
);
811 printf("Spec file '%s' was successfully written.\n", szSpecFile
);