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
68 typedef enum _PARAM_TYPES
76 } PARAM_TYPES
, *PPARAM_TYPES
;
89 #define MAX_PARAMETERS 64
90 typedef struct _EXPORT
96 DWORD dwCallingConvention
;
101 PARAM_TYPES aeParameters
[MAX_PARAMETERS
];
104 typedef struct _EXPORT_DATA
106 ULONG cNumberOfExports
;
108 } EXPORT_DATA
, *PEXPORT_DATA
;
114 _In_
const char* pszFormat
,
123 dwLastError
= GetLastError();
125 va_start(argptr
, pszFormat
);
126 cchBuffer
= vsprintf_s(szBuffer
, sizeof(szBuffer
), pszFormat
, argptr
);
129 /* Strip trailing newlines */
130 _Analysis_assume_(cchBuffer
< sizeof(szBuffer
));
131 while ((cchBuffer
>= 1) &&
132 ((szBuffer
[cchBuffer
- 1] == '\r') ||
133 (szBuffer
[cchBuffer
- 1] == '\n')))
135 szBuffer
[cchBuffer
- 1] = '\0';
139 /* Check if we have an error */
140 if (dwLastError
!= ERROR_SUCCESS
)
142 /* Append error code */
143 cchBuffer
+= sprintf_s(szBuffer
+ cchBuffer
,
144 sizeof(szBuffer
) - cchBuffer
,
145 " [error %lu: ", dwLastError
);
147 /* Format the last error code */
148 cchBuffer
+= FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM
,
152 szBuffer
+ cchBuffer
,
153 (DWORD
)(sizeof(szBuffer
) - cchBuffer
),
156 /* Strip trailing newlines */
157 _Analysis_assume_(cchBuffer
< sizeof(szBuffer
));
158 while ((cchBuffer
>= 1) &&
159 ((szBuffer
[cchBuffer
- 1] == '\r') ||
160 (szBuffer
[cchBuffer
- 1] == '\n')))
162 szBuffer
[cchBuffer
- 1] = '\0';
166 fprintf(stderr
, "%s]\n", szBuffer
);
170 fprintf(stderr
, "%s\n", szBuffer
);
178 static const char *pszMsSymbolServer
= "srv**symbols*http://msdl.microsoft.com/download/symbols";
181 /* Save current process ;-) */
182 ghProcess
= GetCurrentProcess();
184 /* Initialize dbghelp */
185 if (!SymInitialize(ghProcess
, 0, FALSE
))
187 error("SymInitialize() failed.");
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
);
197 /* Test if we can reach the MS symbol server */
198 if (!SymSrvIsStore(ghProcess
, pszMsSymbolServer
))
200 error("Failed to connect to symbol server.");
204 /* Set MS symbol server as symbol search path */
205 SymSetSearchPath(ghProcess
, pszMsSymbolServer
);
211 LoadModuleWithSymbolsFullPath(
212 _In_ PSTR pszFullModuleFileName
)
215 DWORD64 dwModuleBase
;
218 hmod
= LoadLibraryExA(pszFullModuleFileName
,
220 LOAD_IGNORE_CODE_AUTHZ_LEVEL
|
221 DONT_RESOLVE_DLL_REFERENCES
|
222 LOAD_WITH_ALTERED_SEARCH_PATH
);
228 /* Load symbols for this module */
229 dwModuleBase
= SymLoadModule64(ghProcess
,
231 pszFullModuleFileName
,
235 if (dwModuleBase
== 0)
237 /* ERROR_SUCCESS means, we have symbols already */
238 if (GetLastError() != ERROR_SUCCESS
)
245 printf("Successfully loaded symbols for '%s'\n",
246 pszFullModuleFileName
);
253 LoadModuleWithSymbols(
254 _In_ PSTR pszModuleName
)
256 CHAR szFullFileName
[MAX_PATH
];
259 /* Check if the file name has a path */
260 if (strchr(pszModuleName
, '\\') != NULL
)
263 hmod
= LoadModuleWithSymbolsFullPath(pszModuleName
);
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
);
281 strcpy_s(szFullFileName
, sizeof(szFullFileName
), "%systemroot%\\system32\\");
282 strcat_s(szFullFileName
, sizeof(szFullFileName
), pszModuleName
);
283 hmod
= LoadModuleWithSymbolsFullPath(szFullFileName
);
291 strcpy_s(szFullFileName
, sizeof(szFullFileName
), "%systemroot%\\SysWOW64\\");
292 strcat_s(szFullFileName
, sizeof(szFullFileName
), pszModuleName
);
293 hmod
= LoadModuleWithSymbolsFullPath(szFullFileName
);
306 _Out_ PEXPORT_DATA
* ppExportData
)
309 PIMAGE_EXPORT_DIRECTORY pExportDir
;
310 ULONG i
, cjExportSize
, cFunctions
, cjTableSize
;
311 PEXPORT_DATA pExportData
;
312 PULONG pulAddressTable
, pulNameTable
;
313 PUSHORT pusOrdinalTable
;
315 pjImageBase
= (PBYTE
)hmod
;
317 /* Get the export directory */
318 pExportDir
= ImageDirectoryEntryToData(pjImageBase
,
320 IMAGE_DIRECTORY_ENTRY_EXPORT
,
322 if (pExportDir
== NULL
)
324 fprintf(stderr
, "Failed to get export directory\n");
328 cFunctions
= pExportDir
->NumberOfFunctions
;
329 cjTableSize
= FIELD_OFFSET(EXPORT_DATA
, aExports
[cFunctions
]);
331 pExportData
= malloc(cjTableSize
);
332 if (pExportData
== NULL
)
334 error("Failed to allocate %u bytes of memory for export table\n", cjTableSize
);
335 return E_OUTOFMEMORY
;
338 RtlZeroMemory(pExportData
, cjTableSize
);
340 pulAddressTable
= (PULONG
)(pjImageBase
+ pExportDir
->AddressOfFunctions
);
342 pExportData
->cNumberOfExports
= cFunctions
;
344 /* Loop through the function table */
345 for (i
= 0; i
< cFunctions
; i
++)
347 PVOID pvFunction
= (pjImageBase
+ pulAddressTable
[i
]);
349 /* Check if this is a forwarder */
350 if ((ULONG_PTR
)((PUCHAR
)pvFunction
- (PUCHAR
)pExportDir
) < cjExportSize
)
352 pExportData
->aExports
[i
].pszForwarder
= _strdup(pvFunction
);
356 pExportData
->aExports
[i
].ulRva
= pulAddressTable
[i
];
360 pulNameTable
= (PULONG
)(pjImageBase
+ pExportDir
->AddressOfNames
);
361 pusOrdinalTable
= (PUSHORT
)(pjImageBase
+ pExportDir
->AddressOfNameOrdinals
);
363 /* Loop through the name table */
364 for (i
= 0; i
< pExportDir
->NumberOfNames
; i
++)
366 ULONG iIndex
= pusOrdinalTable
[i
];
367 PSTR pszName
= (PSTR
)(pjImageBase
+ pulNameTable
[i
]);
369 pExportData
->aExports
[iIndex
].pszName
= _strdup(pszName
);
372 *ppExportData
= pExportData
;
378 EnumParametersCallback(
379 _In_ PSYMBOL_INFO pSymInfo
,
380 _In_ ULONG SymbolSize
,
381 _In_ PVOID UserContext
)
383 PEXPORT pExport
= (PEXPORT
)UserContext
;
384 enum SymTagEnum eSymTag
;
385 enum BasicType eBaseType
;
389 /* If it's not a parameter, skip it */
390 if (!(pSymInfo
->Flags
& SYMFLAG_PARAMETER
))
395 /* Count this parameter */
396 pExport
->cParameters
++;
398 /* Get the type for the parameter */
399 if (SymGetTypeInfo(ghProcess
,
410 /* Try to get the size */
411 if (SymGetTypeInfo(ghProcess
,
419 /* That is probably not possible */
426 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_DOUBLE
;
431 /* Default to 'long' type */
432 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_LONG
;
437 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_LONG
;
440 case SymTagPointerType
:
442 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_PTR
;
444 /* Try to get the underlying type */
445 if (SymGetTypeInfo(ghProcess
,
451 /* Try to get the base type */
452 if (SymGetTypeInfo(ghProcess
,
458 /* Check for string types */
459 if (eBaseType
== btChar
)
462 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_STR
;
464 else if (eBaseType
== btWChar
)
467 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_WSTR
;
474 printf("Unhandled eSymTag: %u\n", eSymTag
);
480 printf("Could not get type info. Fallig back to ptr\n");
481 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_PTR
;
488 GetFunctionFromForwarder(
489 _In_ PCSTR pszForwarder
)
491 CHAR szDllName
[MAX_SYM_NAME
];
496 /* Copy the forwarder name */
497 strcpy_s(szDllName
, sizeof(szDllName
), pszForwarder
);
500 pchDot
= strchr(szDllName
, '.');
503 error("Invalid name for forwarder '%s'!", pszForwarder
);
507 /* Terminate DLL name */
511 hmod
= LoadModuleWithSymbols(szDllName
);
514 error("Failed to load module for forwarder '%s'!", pszForwarder
);
518 /* Get the function name and check for ordinal */
519 pszName
= pchDot
+ 1;
520 if (pszName
[0] == '#')
522 ULONG iOrdinal
= strtoul(pszName
+ 1, NULL
, 10);
523 if ((iOrdinal
== 0) || (iOrdinal
> 0xFFFF))
525 error("Got invalid ordinal %u for ''", iOrdinal
, pszForwarder
);
529 pszName
= (PSTR
)(ULONG_PTR
)iOrdinal
;
532 /* Get the function address */
533 ullFunction
= (ULONG_PTR
)GetProcAddress(hmod
, pszName
);
534 if (ullFunction
== 0)
536 error("Failed to resolve '%s' in '%s'.", pchDot
+ 1, szDllName
);
546 _Inout_ PEXPORT_DATA pExportData
)
548 DWORD64 dwModuleBase
;
550 IMAGEHLP_STACK_FRAME StackFrame
;
551 SYMBOL_INFO_PACKAGE sym
;
553 dwModuleBase
= (DWORD_PTR
)hmod
;
555 /* Loop through all exports */
556 for (i
= 0; i
< pExportData
->cNumberOfExports
; i
++)
558 PEXPORT pExport
= &pExportData
->aExports
[i
];
559 ULONG64 ullFunction
= dwModuleBase
+ pExportData
->aExports
[i
].ulRva
;
560 ULONG64 ullDisplacement
;
562 /* Check if this is a forwarder */
563 if (pExport
->pszForwarder
!= NULL
)
565 /* Load the module and get the function address */
566 ullFunction
= GetFunctionFromForwarder(pExport
->pszForwarder
);
567 if (ullFunction
== 0)
569 printf("Failed to get function for forwarder '%s'. Skipping.\n", pExport
->pszForwarder
);
574 RtlZeroMemory(&sym
, sizeof(sym
));
575 sym
.si
.SizeOfStruct
= sizeof(SYMBOL_INFO
);
576 sym
.si
.MaxNameLen
= MAX_SYM_NAME
;
578 /* Try to find the symbol */
579 if (!SymFromAddr(ghProcess
, ullFunction
, &ullDisplacement
, &sym
.si
))
581 error("Error: SymFromAddr() failed.");
585 /* Get the symbol name */
586 pExport
->pszSymbol
= _strdup(sym
.si
.Name
);
588 /* Check if it is a function */
589 if (sym
.si
.Tag
== SymTagFunction
)
591 /* Get the calling convention */
592 if (!SymGetTypeInfo(ghProcess
,
595 TI_GET_CALLING_CONVENTION
,
596 &pExport
->dwCallingConvention
))
598 /* Fall back to __stdcall */
599 pExport
->dwCallingConvention
= CV_CALL_NEAR_STD
;
602 /* Set the context to the function address */
603 RtlZeroMemory(&StackFrame
, sizeof(StackFrame
));
604 StackFrame
.InstructionOffset
= ullFunction
;
605 if (!SymSetContext(ghProcess
, &StackFrame
, NULL
))
607 error("SymSetContext failed for i = %u.", i
);
611 /* Enumerate all symbols for this function */
612 if (!SymEnumSymbols(ghProcess
,
613 0, // use SymSetContext
615 EnumParametersCallback
,
618 error("SymEnumSymbols failed for i = %u.", i
);
622 else if (sym
.si
.Tag
== SymTagPublicSymbol
)
624 pExport
->dwCallingConvention
= CV_CALL_NEAR_STD
;
626 else if (sym
.si
.Tag
== SymTagData
)
628 pExport
->fData
= TRUE
;
636 GetCallingConvention(
637 _In_ PEXPORT pExport
)
645 switch (pExport
->dwCallingConvention
)
649 case CV_CALL_NEAR_FAST
:
651 case CV_CALL_NEAR_STD
:
653 case CV_CALL_NEAR_SYS
:
655 case CV_CALL_THISCALL
:
666 _In_ PCSTR pszSpecFile
,
667 _In_ PEXPORT_DATA pExportData
)
673 /* Create the spec file */
674 if (fopen_s(&file
, pszSpecFile
, "w") != 0)
676 error("Failed to open spec file: '%s'\n", pszSpecFile
);
680 /* Loop all exports */
681 for (i
= 0; i
< pExportData
->cNumberOfExports
; i
++)
683 pExport
= &pExportData
->aExports
[i
];
685 fprintf(file
, "%lu %s ", i
+ 1, GetCallingConvention(pExport
));
686 //if (pExport->fNoName)
687 if (pExport
->pszName
== NULL
)
689 fprintf(file
, "-noname ");
692 if (pExport
->pszName
!= NULL
)
694 fprintf(file
, "%s", pExport
->pszName
);
696 else if (pExport
->pszSymbol
!= NULL
)
698 fprintf(file
, "%s", pExport
->pszSymbol
);
702 fprintf(file
, "NamelessExport_%lu", i
);
708 for (p
= 0; p
< pExport
->cParameters
; p
++)
710 fprintf(file
, "%s", gapszTypeStrings
[pExport
->aeParameters
[p
]]);
711 if ((p
+ 1) < pExport
->cParameters
)
719 if (pExport
->pszForwarder
!= NULL
)
721 fprintf(file
, " %s", pExport
->pszForwarder
);
732 int main(int argc
, char* argv
[])
735 CHAR szSpecFile
[MAX_PATH
];
737 PEXPORT_DATA pExportData
;
740 /* Check parameters */
741 if ((argc
< 2) || !strcmp(argv
[1], "/?"))
743 printf("syntax: createspec <image file> [<spec file>]\n");
747 /* Check if we have a spec file name */
750 pszSpecFile
= argv
[2];
754 /* Create spec file name from image file name */
755 PSTR pszStart
= strrchr(argv
[1], '\\');
759 strcpy_s(szSpecFile
, sizeof(szSpecFile
), pszStart
);
760 strcat_s(szSpecFile
, sizeof(szSpecFile
), ".spec");
761 pszSpecFile
= szSpecFile
;
764 /* Initialize dbghelp.dll */
767 error("Failed to init dbghelp!\n"
768 "Make sure you have dbghelp.dll and symsrv.dll in the same folder.\n");
772 /* Load the file including symbols */
773 printf("Loading symbols for '%s', please wait...\n", argv
[1]);
774 hmod
= LoadModuleWithSymbols(argv
[1]);
777 error("Failed to load module '%s'!", argv
[1]);
781 /* Get the exports */
782 hr
= GetExportsFromFile(hmod
, &pExportData
);
785 error("Failed to get exports: %lx\n", hr
);
789 /* Get additional info from symbols */
790 hr
= ParseImageSymbols(hmod
, pExportData
);
793 error("Failed to get symbol information: hr=%lx\n", hr
);
796 /* Write the spec file */
797 hr
= CreateSpecFile(pszSpecFile
, pExportData
);
799 printf("Spec file '%s' was successfully written.\n", szSpecFile
);