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
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)
28 #pragma warning(disable:4091)
33 // This is from cvconst.h, but win sdk lacks this file
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
67 typedef enum _PARAM_TYPES
75 } PARAM_TYPES
, *PPARAM_TYPES
;
88 #define MAX_PARAMETERS 64
89 typedef struct _EXPORT
95 DWORD dwCallingConvention
;
100 PARAM_TYPES aeParameters
[MAX_PARAMETERS
];
103 typedef struct _EXPORT_DATA
105 ULONG cNumberOfExports
;
107 } EXPORT_DATA
, *PEXPORT_DATA
;
113 _In_
const char* pszFormat
,
122 dwLastError
= GetLastError();
124 va_start(argptr
, pszFormat
);
125 cchBuffer
= vsprintf_s(szBuffer
, sizeof(szBuffer
), pszFormat
, argptr
);
128 /* Strip trailing newlines */
129 _Analysis_assume_(cchBuffer
< sizeof(szBuffer
));
130 while ((cchBuffer
>= 1) &&
131 ((szBuffer
[cchBuffer
- 1] == '\r') ||
132 (szBuffer
[cchBuffer
- 1] == '\n')))
134 szBuffer
[cchBuffer
- 1] = '\0';
138 /* Check if we have an error */
139 if (dwLastError
!= ERROR_SUCCESS
)
141 /* Append error code */
142 cchBuffer
+= sprintf_s(szBuffer
+ cchBuffer
,
143 sizeof(szBuffer
) - cchBuffer
,
144 " [error %lu: ", dwLastError
);
146 /* Format the last error code */
147 cchBuffer
+= FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM
,
151 szBuffer
+ cchBuffer
,
152 (DWORD
)(sizeof(szBuffer
) - cchBuffer
),
155 /* Strip trailing newlines */
156 _Analysis_assume_(cchBuffer
< sizeof(szBuffer
));
157 while ((cchBuffer
>= 1) &&
158 ((szBuffer
[cchBuffer
- 1] == '\r') ||
159 (szBuffer
[cchBuffer
- 1] == '\n')))
161 szBuffer
[cchBuffer
- 1] = '\0';
165 fprintf(stderr
, "%s]\n", szBuffer
);
169 fprintf(stderr
, "%s\n", szBuffer
);
177 static const char *pszMsSymbolServer
= "srv**symbols*http://msdl.microsoft.com/download/symbols";
180 /* Save current process ;-) */
181 ghProcess
= GetCurrentProcess();
183 /* Initialize dbghelp */
184 if (!SymInitialize(ghProcess
, 0, FALSE
))
186 error("SymInitialize() failed.");
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
);
196 /* Test if we can reach the MS symbol server */
197 if (!SymSrvIsStore(ghProcess
, pszMsSymbolServer
))
199 error("Failed to connect to symbol server.");
203 /* Set MS symbol server as symbol search path */
204 SymSetSearchPath(ghProcess
, pszMsSymbolServer
);
210 LoadModuleWithSymbolsFullPath(
211 _In_ PSTR pszFullModuleFileName
)
214 DWORD64 dwModuleBase
;
217 hmod
= LoadLibraryExA(pszFullModuleFileName
,
219 LOAD_IGNORE_CODE_AUTHZ_LEVEL
|
220 DONT_RESOLVE_DLL_REFERENCES
|
221 LOAD_WITH_ALTERED_SEARCH_PATH
);
227 /* Load symbols for this module */
228 dwModuleBase
= SymLoadModule64(ghProcess
,
230 pszFullModuleFileName
,
234 if (dwModuleBase
== 0)
236 /* ERROR_SUCCESS means, we have symbols already */
237 if (GetLastError() != ERROR_SUCCESS
)
244 printf("Successfully loaded symbols for '%s'\n",
245 pszFullModuleFileName
);
252 LoadModuleWithSymbols(
253 _In_ PSTR pszModuleName
)
255 CHAR szFullFileName
[MAX_PATH
];
258 /* Check if the file name has a path */
259 if (strchr(pszModuleName
, '\\') != NULL
)
262 hmod
= LoadModuleWithSymbolsFullPath(pszModuleName
);
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
);
280 strcpy_s(szFullFileName
, sizeof(szFullFileName
), "%systemroot%\\system32\\");
281 strcat_s(szFullFileName
, sizeof(szFullFileName
), pszModuleName
);
282 hmod
= LoadModuleWithSymbolsFullPath(szFullFileName
);
290 strcpy_s(szFullFileName
, sizeof(szFullFileName
), "%systemroot%\\SysWOW64\\");
291 strcat_s(szFullFileName
, sizeof(szFullFileName
), pszModuleName
);
292 hmod
= LoadModuleWithSymbolsFullPath(szFullFileName
);
305 _Out_ PEXPORT_DATA
* ppExportData
)
308 PIMAGE_EXPORT_DIRECTORY pExportDir
;
309 ULONG i
, cjExportSize
, cFunctions
, cjTableSize
;
310 PEXPORT_DATA pExportData
;
311 PULONG pulAddressTable
, pulNameTable
;
312 PUSHORT pusOrdinalTable
;
314 pjImageBase
= (PBYTE
)hmod
;
316 /* Get the export directory */
317 pExportDir
= ImageDirectoryEntryToData(pjImageBase
,
319 IMAGE_DIRECTORY_ENTRY_EXPORT
,
321 if (pExportDir
== NULL
)
323 fprintf(stderr
, "Failed to get export directory\n");
327 cFunctions
= pExportDir
->NumberOfFunctions
;
328 cjTableSize
= FIELD_OFFSET(EXPORT_DATA
, aExports
[cFunctions
]);
330 pExportData
= malloc(cjTableSize
);
331 if (pExportData
== NULL
)
333 error("Failed to allocate %u bytes of memory for export table\n", cjTableSize
);
334 return E_OUTOFMEMORY
;
337 RtlZeroMemory(pExportData
, cjTableSize
);
339 pulAddressTable
= (PULONG
)(pjImageBase
+ pExportDir
->AddressOfFunctions
);
341 pExportData
->cNumberOfExports
= cFunctions
;
343 /* Loop through the function table */
344 for (i
= 0; i
< cFunctions
; i
++)
346 PVOID pvFunction
= (pjImageBase
+ pulAddressTable
[i
]);
348 /* Check if this is a forwarder */
349 if ((ULONG_PTR
)((PUCHAR
)pvFunction
- (PUCHAR
)pExportDir
) < cjExportSize
)
351 pExportData
->aExports
[i
].pszForwarder
= _strdup(pvFunction
);
355 pExportData
->aExports
[i
].ulRva
= pulAddressTable
[i
];
359 pulNameTable
= (PULONG
)(pjImageBase
+ pExportDir
->AddressOfNames
);
360 pusOrdinalTable
= (PUSHORT
)(pjImageBase
+ pExportDir
->AddressOfNameOrdinals
);
362 /* Loop through the name table */
363 for (i
= 0; i
< pExportDir
->NumberOfNames
; i
++)
365 ULONG iIndex
= pusOrdinalTable
[i
];
366 PSTR pszName
= (PSTR
)(pjImageBase
+ pulNameTable
[i
]);
368 pExportData
->aExports
[iIndex
].pszName
= _strdup(pszName
);
371 *ppExportData
= pExportData
;
377 EnumParametersCallback(
378 _In_ PSYMBOL_INFO pSymInfo
,
379 _In_ ULONG SymbolSize
,
380 _In_ PVOID UserContext
)
382 PEXPORT pExport
= (PEXPORT
)UserContext
;
383 enum SymTagEnum eSymTag
;
384 enum BasicType eBaseType
;
388 /* If it's not a parameter, skip it */
389 if (!(pSymInfo
->Flags
& SYMFLAG_PARAMETER
))
394 /* Count this parameter */
395 pExport
->cParameters
++;
397 /* Get the type for the parameter */
398 if (SymGetTypeInfo(ghProcess
,
409 /* Try to get the size */
410 if (SymGetTypeInfo(ghProcess
,
418 /* That is probably not possible */
425 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_DOUBLE
;
430 /* Default to 'long' type */
431 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_LONG
;
436 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_LONG
;
439 case SymTagPointerType
:
441 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_PTR
;
443 /* Try to get the underlying type */
444 if (SymGetTypeInfo(ghProcess
,
450 /* Try to get the base type */
451 if (SymGetTypeInfo(ghProcess
,
457 /* Check for string types */
458 if (eBaseType
== btChar
)
461 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_STR
;
463 else if (eBaseType
== btWChar
)
466 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_WSTR
;
473 printf("Unhandled eSymTag: %u\n", eSymTag
);
479 printf("Could not get type info. Fallig back to ptr\n");
480 pExport
->aeParameters
[pExport
->cParameters
- 1] = TYPE_PTR
;
487 GetFunctionFromForwarder(
488 _In_ PCSTR pszForwarder
)
490 CHAR szDllName
[MAX_SYM_NAME
];
495 /* Copy the forwarder name */
496 strcpy_s(szDllName
, sizeof(szDllName
), pszForwarder
);
499 pchDot
= strchr(szDllName
, '.');
502 error("Invalid name for forwarder '%s'!", pszForwarder
);
506 /* Terminate DLL name */
510 hmod
= LoadModuleWithSymbols(szDllName
);
513 error("Failed to load module for forwarder '%s'!", pszForwarder
);
517 /* Get the function name and check for ordinal */
518 pszName
= pchDot
+ 1;
519 if (pszName
[0] == '#')
521 ULONG iOrdinal
= strtoul(pszName
+ 1, NULL
, 10);
522 if ((iOrdinal
== 0) || (iOrdinal
> 0xFFFF))
524 error("Got invalid ordinal %u for ''", iOrdinal
, pszForwarder
);
528 pszName
= (PSTR
)(ULONG_PTR
)iOrdinal
;
531 /* Get the function address */
532 ullFunction
= (ULONG_PTR
)GetProcAddress(hmod
, pszName
);
533 if (ullFunction
== 0)
535 error("Failed to resolve '%s' in '%s'.", pchDot
+ 1, szDllName
);
545 _Inout_ PEXPORT_DATA pExportData
)
547 DWORD64 dwModuleBase
;
549 IMAGEHLP_STACK_FRAME StackFrame
;
550 SYMBOL_INFO_PACKAGE sym
;
552 dwModuleBase
= (DWORD_PTR
)hmod
;
554 /* Loop through all exports */
555 for (i
= 0; i
< pExportData
->cNumberOfExports
; i
++)
557 PEXPORT pExport
= &pExportData
->aExports
[i
];
558 ULONG64 ullFunction
= dwModuleBase
+ pExportData
->aExports
[i
].ulRva
;
559 ULONG64 ullDisplacement
;
561 /* Check if this is a forwarder */
562 if (pExport
->pszForwarder
!= NULL
)
564 /* Load the module and get the function address */
565 ullFunction
= GetFunctionFromForwarder(pExport
->pszForwarder
);
566 if (ullFunction
== 0)
568 printf("Failed to get function for forwarder '%s'. Skipping.\n", pExport
->pszForwarder
);
573 RtlZeroMemory(&sym
, sizeof(sym
));
574 sym
.si
.SizeOfStruct
= sizeof(SYMBOL_INFO
);
575 sym
.si
.MaxNameLen
= MAX_SYM_NAME
;
577 /* Try to find the symbol */
578 if (!SymFromAddr(ghProcess
, ullFunction
, &ullDisplacement
, &sym
.si
))
580 error("Error: SymFromAddr() failed.");
584 /* Get the symbol name */
585 pExport
->pszSymbol
= _strdup(sym
.si
.Name
);
587 /* Check if it is a function */
588 if (sym
.si
.Tag
== SymTagFunction
)
590 /* Get the calling convention */
591 if (!SymGetTypeInfo(ghProcess
,
594 TI_GET_CALLING_CONVENTION
,
595 &pExport
->dwCallingConvention
))
597 /* Fall back to __stdcall */
598 pExport
->dwCallingConvention
= CV_CALL_NEAR_STD
;
601 /* Set the context to the function address */
602 RtlZeroMemory(&StackFrame
, sizeof(StackFrame
));
603 StackFrame
.InstructionOffset
= ullFunction
;
604 if (!SymSetContext(ghProcess
, &StackFrame
, NULL
))
606 error("SymSetContext failed for i = %u.", i
);
610 /* Enumerate all symbols for this function */
611 if (!SymEnumSymbols(ghProcess
,
612 0, // use SymSetContext
614 EnumParametersCallback
,
617 error("SymEnumSymbols failed for i = %u.", i
);
621 else if (sym
.si
.Tag
== SymTagPublicSymbol
)
623 pExport
->dwCallingConvention
= CV_CALL_NEAR_STD
;
625 else if (sym
.si
.Tag
== SymTagData
)
627 pExport
->fData
= TRUE
;
635 GetCallingConvention(
636 _In_ PEXPORT pExport
)
644 switch (pExport
->dwCallingConvention
)
648 case CV_CALL_NEAR_FAST
:
650 case CV_CALL_NEAR_STD
:
652 case CV_CALL_NEAR_SYS
:
654 case CV_CALL_THISCALL
:
665 _In_ PCSTR pszSpecFile
,
666 _In_ PEXPORT_DATA pExportData
)
672 /* Create the spec file */
673 if (fopen_s(&file
, pszSpecFile
, "w") != 0)
675 error("Failed to open spec file: '%s'\n", pszSpecFile
);
679 /* Loop all exports */
680 for (i
= 0; i
< pExportData
->cNumberOfExports
; i
++)
682 pExport
= &pExportData
->aExports
[i
];
684 fprintf(file
, "%lu %s ", i
+ 1, GetCallingConvention(pExport
));
685 //if (pExport->fNoName)
686 if (pExport
->pszName
== NULL
)
688 fprintf(file
, "-noname ");
691 if (pExport
->pszName
!= NULL
)
693 fprintf(file
, "%s", pExport
->pszName
);
695 else if (pExport
->pszSymbol
!= NULL
)
697 fprintf(file
, "%s", pExport
->pszSymbol
);
701 fprintf(file
, "NamelessExport_%lu", i
);
707 for (p
= 0; p
< pExport
->cParameters
; p
++)
709 fprintf(file
, "%s", gapszTypeStrings
[pExport
->aeParameters
[p
]]);
710 if ((p
+ 1) < pExport
->cParameters
)
718 if (pExport
->pszForwarder
!= NULL
)
720 fprintf(file
, " %s", pExport
->pszForwarder
);
731 int main(int argc
, char* argv
[])
734 CHAR szSpecFile
[MAX_PATH
];
736 PEXPORT_DATA pExportData
;
739 /* Check parameters */
740 if ((argc
< 2) || !strcmp(argv
[1], "/?"))
742 printf("syntax: createspec <image file> [<spec file>]\n");
746 /* Check if we have a spec file name */
749 pszSpecFile
= argv
[2];
753 /* Create spec file name from image file name */
754 PSTR pszStart
= strrchr(argv
[1], '\\');
758 strcpy_s(szSpecFile
, sizeof(szSpecFile
), pszStart
);
759 strcat_s(szSpecFile
, sizeof(szSpecFile
), ".spec");
760 pszSpecFile
= szSpecFile
;
763 /* Initialize dbghelp.dll */
766 error("Failed to init dbghelp!\n"
767 "Make sure you have dbghelp.dll and symsrv.dll in the same folder.\n");
771 /* Load the file including symbols */
772 printf("Loading symbols for '%s', please wait...\n", argv
[1]);
773 hmod
= LoadModuleWithSymbols(argv
[1]);
776 error("Failed to load module '%s'!", argv
[1]);
780 /* Get the exports */
781 hr
= GetExportsFromFile(hmod
, &pExportData
);
784 error("Failed to get exports: %lx\n", hr
);
788 /* Get additional info from symbols */
789 hr
= ParseImageSymbols(hmod
, pExportData
);
792 error("Failed to get symbol information: hr=%lx\n", hr
);
795 /* Write the spec file */
796 hr
= CreateSpecFile(pszSpecFile
, pExportData
);
798 printf("Spec file '%s' was successfully written.\n", szSpecFile
);