mirror of
https://github.com/reactos/reactos.git
synced 2025-02-22 16:36:33 +00:00
[CREATESPEC]
Small utility to auto-create spec files. Uses MS symbol server to evaluate data not available in the export table. Features: - Parse export table for export names and forwarders - Find function name for nameless exports - Recognize calling convention / data exports - Analyze function parameters svn path=/trunk/; revision=70559
This commit is contained in:
parent
6ed0a4c6b2
commit
2a5c5c0c76
2 changed files with 601 additions and 0 deletions
6
rosapps/applications/devutils/createspec/CMakeLists.txt
Normal file
6
rosapps/applications/devutils/createspec/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
add_executable(createspec createspec.c)
|
||||
set_module_type(createspec win32cui)
|
||||
target_link_libraries(createspec wine)
|
||||
add_importlibs(createspec dbghelp msvcrt kernel32)
|
||||
add_cd_file(TARGET createspec DESTINATION reactos/system32 FOR all)
|
595
rosapps/applications/devutils/createspec/createspec.c
Normal file
595
rosapps/applications/devutils/createspec/createspec.c
Normal file
|
@ -0,0 +1,595 @@
|
|||
/*
|
||||
- Info:
|
||||
- http://stackoverflow.com/questions/32251638/dbghelp-get-full-symbol-signature-function-name-parameters-types
|
||||
- http://www.debuginfo.com/articles/dbghelptypeinfo.html
|
||||
- TODO:
|
||||
- Dump usage
|
||||
- Test for dbghelp + symsrv and warn if not working
|
||||
- Resolve forwarders
|
||||
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4091)
|
||||
#endif
|
||||
#define _NO_CVCONST_H
|
||||
#include <dbghelp.h>
|
||||
|
||||
// doesn't seem to be defined anywhere
|
||||
enum BasicType {
|
||||
btNoType = 0,
|
||||
btVoid = 1,
|
||||
btChar = 2,
|
||||
btWChar = 3,
|
||||
btInt = 6,
|
||||
btUInt = 7,
|
||||
btFloat = 8,
|
||||
btBCD = 9,
|
||||
btBool = 10,
|
||||
btLong = 13,
|
||||
btULong = 14,
|
||||
btCurrency = 25,
|
||||
btDate = 26,
|
||||
btVariant = 27,
|
||||
btComplex = 28,
|
||||
btBit = 29,
|
||||
btBSTR = 30,
|
||||
btHresult = 31
|
||||
};
|
||||
|
||||
typedef enum CV_call_e {
|
||||
CV_CALL_NEAR_C = 0x00,
|
||||
CV_CALL_NEAR_FAST = 0x04,
|
||||
CV_CALL_NEAR_STD = 0x07,
|
||||
CV_CALL_NEAR_SYS = 0x09,
|
||||
CV_CALL_THISCALL = 0x0b,
|
||||
CV_CALL_CLRCALL = 0x16
|
||||
} CV_call_e;
|
||||
|
||||
#define MAX_SYMBOL_NAME 1024
|
||||
typedef struct _SYMINFO_EX
|
||||
{
|
||||
SYMBOL_INFO si;
|
||||
CHAR achName[MAX_SYMBOL_NAME];
|
||||
} SYMINFO_EX;
|
||||
|
||||
typedef enum _PARAM_TYPES
|
||||
{
|
||||
TYPE_NONE,
|
||||
TYPE_LONG,
|
||||
TYPE_DOUBLE,
|
||||
TYPE_PTR,
|
||||
TYPE_STR,
|
||||
TYPE_WSTR
|
||||
} PARAM_TYPES, *PPARAM_TYPES;
|
||||
|
||||
const char*
|
||||
gapszTypeStrings[] =
|
||||
{
|
||||
"???",
|
||||
"long",
|
||||
"double",
|
||||
"ptr",
|
||||
"str",
|
||||
"wstr"
|
||||
};
|
||||
|
||||
#define MAX_PARAMETERS 64
|
||||
typedef struct _EXPORT
|
||||
{
|
||||
PSTR pszName;
|
||||
PSTR pszSymbol;
|
||||
PSTR pszForwarder;
|
||||
ULONG ulRva;
|
||||
DWORD dwCallingConvention;
|
||||
ULONG fForwarder : 1;
|
||||
ULONG fNoName : 1;
|
||||
ULONG fData : 1;
|
||||
ULONG cParameters;
|
||||
PARAM_TYPES aeParameters[MAX_PARAMETERS];
|
||||
} EXPORT, *PEXPORT;
|
||||
|
||||
typedef struct _EXPORT_DATA
|
||||
{
|
||||
ULONG cNumberOfExports;
|
||||
EXPORT aExports[1];
|
||||
} EXPORT_DATA, *PEXPORT_DATA;
|
||||
|
||||
HANDLE ghProcess;
|
||||
CHAR gszModuleFileName[MAX_PATH+1];
|
||||
|
||||
HRESULT
|
||||
OpenFileFromName(
|
||||
_In_ PCSTR pszDllName,
|
||||
_Out_ PHANDLE phFile)
|
||||
{
|
||||
HANDLE hFile;
|
||||
|
||||
/* Try current directory */
|
||||
GetCurrentDirectoryA(MAX_PATH, gszModuleFileName);
|
||||
strcat_s(gszModuleFileName, sizeof(gszModuleFileName), "\\");
|
||||
strcat_s(gszModuleFileName, sizeof(gszModuleFileName), pszDllName);
|
||||
hFile = CreateFileA(gszModuleFileName,
|
||||
FILE_READ_DATA,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
if (hFile != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
*phFile = hFile;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/* Try system32 directory */
|
||||
strcat_s(gszModuleFileName, sizeof(gszModuleFileName), "%systemroot%\\system32\\");
|
||||
strcat_s(gszModuleFileName, sizeof(gszModuleFileName), pszDllName);
|
||||
hFile = CreateFileA(gszModuleFileName,
|
||||
FILE_READ_DATA,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
if (hFile != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
*phFile = hFile;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
HRESULT
|
||||
GetExportsFromFile(
|
||||
_In_ HANDLE hFile,
|
||||
_Out_ PEXPORT_DATA* ppExportData)
|
||||
{
|
||||
HANDLE hMap;
|
||||
PBYTE pjImageBase;
|
||||
PIMAGE_EXPORT_DIRECTORY pExportDir;
|
||||
ULONG i, cjExportSize, cFunctions, cjTableSize;
|
||||
PEXPORT_DATA pExportData;
|
||||
PULONG pulAddressTable, pulNameTable;
|
||||
PUSHORT pusOrdinalTable;
|
||||
|
||||
/* Create an image file mapping */
|
||||
hMap = CreateFileMappingA(hFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL);
|
||||
if (!hMap)
|
||||
{
|
||||
fprintf(stderr, "CreateFileMapping() failed: %ld\n", GetLastError());
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
/* Map the file */
|
||||
pjImageBase = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
|
||||
if(pjImageBase == NULL)
|
||||
{
|
||||
fprintf(stderr, "MapViewOfFile() failed: %ld\n", GetLastError());
|
||||
CloseHandle(hMap);
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
/* Get the export directory */
|
||||
pExportDir = ImageDirectoryEntryToData(pjImageBase,
|
||||
TRUE,
|
||||
IMAGE_DIRECTORY_ENTRY_EXPORT,
|
||||
&cjExportSize);
|
||||
|
||||
cFunctions = pExportDir->NumberOfFunctions;
|
||||
cjTableSize = FIELD_OFFSET(EXPORT_DATA, aExports[cFunctions]);
|
||||
|
||||
pExportData = malloc(cjTableSize);
|
||||
if (pExportData == NULL)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
RtlZeroMemory(pExportData, cjTableSize);
|
||||
|
||||
pulAddressTable = (PULONG)(pjImageBase + pExportDir->AddressOfFunctions);
|
||||
|
||||
pExportData->cNumberOfExports = cFunctions;
|
||||
|
||||
/* Loop through the function table */
|
||||
for (i = 0; i < cFunctions; i++)
|
||||
{
|
||||
PVOID pvFunction = (pjImageBase + pulAddressTable[i]);
|
||||
|
||||
if ((ULONG_PTR)((PUCHAR)pvFunction - (PUCHAR)pExportDir) < cjExportSize)
|
||||
{
|
||||
pExportData->aExports[i].pszForwarder = _strdup(pvFunction);
|
||||
}
|
||||
else
|
||||
{
|
||||
pExportData->aExports[i].ulRva = pulAddressTable[i];
|
||||
}
|
||||
}
|
||||
|
||||
pulNameTable = (PULONG)(pjImageBase + pExportDir->AddressOfNames);
|
||||
pusOrdinalTable = (PUSHORT)(pjImageBase + pExportDir->AddressOfNameOrdinals);
|
||||
|
||||
/* Loop through the name table */
|
||||
for (i = 0; i < pExportDir->NumberOfNames; i++)
|
||||
{
|
||||
ULONG iIndex = pusOrdinalTable[i];
|
||||
PSTR pszName = (PSTR)(pjImageBase + pulNameTable[i]);
|
||||
|
||||
pExportData->aExports[iIndex].pszName = _strdup(pszName);
|
||||
}
|
||||
|
||||
*ppExportData = pExportData;
|
||||
UnmapViewOfFile(pjImageBase);
|
||||
CloseHandle(hMap);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
BOOL
|
||||
CALLBACK
|
||||
EnumParametersCallback(
|
||||
_In_ PSYMBOL_INFO pSymInfo,
|
||||
_In_ ULONG SymbolSize,
|
||||
_In_opt_ PVOID UserContext)
|
||||
{
|
||||
PEXPORT pExport = (PEXPORT)UserContext;
|
||||
enum SymTagEnum eSymTag;
|
||||
enum BasicType eBaseType;
|
||||
DWORD dwTypeIndex;
|
||||
ULONG64 ullLength;
|
||||
|
||||
/* If it's not a parameter, skip it */
|
||||
if (!(pSymInfo->Flags & SYMFLAG_PARAMETER))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Count this parameter */
|
||||
pExport->cParameters++;
|
||||
|
||||
/* Get the type for the parameter */
|
||||
if (SymGetTypeInfo(ghProcess,
|
||||
pSymInfo->ModBase,
|
||||
pSymInfo->TypeIndex,
|
||||
TI_GET_SYMTAG,
|
||||
&eSymTag))
|
||||
{
|
||||
switch (eSymTag)
|
||||
{
|
||||
case SymTagUDT:
|
||||
case SymTagBaseType:
|
||||
|
||||
/* Try to get the size */
|
||||
if (SymGetTypeInfo(ghProcess,
|
||||
pSymInfo->ModBase,
|
||||
pSymInfo->TypeIndex,
|
||||
TI_GET_LENGTH,
|
||||
&ullLength))
|
||||
{
|
||||
if (ullLength > 8)
|
||||
{
|
||||
/* That is probably not possible */
|
||||
__debugbreak();
|
||||
}
|
||||
|
||||
if (ullLength > 4)
|
||||
{
|
||||
/* 'double' type */
|
||||
pExport->aeParameters[pExport->cParameters - 1] = TYPE_DOUBLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* 'long' type */
|
||||
pExport->aeParameters[pExport->cParameters - 1] = TYPE_LONG;
|
||||
break;
|
||||
|
||||
case SymTagEnum:
|
||||
/* 'long' type */
|
||||
pExport->aeParameters[pExport->cParameters - 1] = TYPE_LONG;
|
||||
break;
|
||||
|
||||
case SymTagPointerType:
|
||||
/* 'ptr' type */
|
||||
pExport->aeParameters[pExport->cParameters - 1] = TYPE_PTR;
|
||||
|
||||
/* Try to get the underlying type */
|
||||
if (SymGetTypeInfo(ghProcess,
|
||||
pSymInfo->ModBase,
|
||||
pSymInfo->TypeIndex,
|
||||
TI_GET_TYPEID,
|
||||
&dwTypeIndex))
|
||||
{
|
||||
/* Try to get the base type */
|
||||
if (SymGetTypeInfo(ghProcess,
|
||||
pSymInfo->ModBase,
|
||||
dwTypeIndex,
|
||||
TI_GET_BASETYPE,
|
||||
&eBaseType))
|
||||
{
|
||||
if (eBaseType == btChar)
|
||||
{
|
||||
pExport->aeParameters[pExport->cParameters - 1] = TYPE_STR;
|
||||
}
|
||||
else if (eBaseType == btWChar)
|
||||
{
|
||||
pExport->aeParameters[pExport->cParameters - 1] = TYPE_WSTR;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("Unhandled eSymTag: %u\n", eSymTag);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Could not get type info. Fallig back to ptr\n");
|
||||
pExport->aeParameters[pExport->cParameters - 1] = TYPE_PTR;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
ParseExportSymbols(
|
||||
_In_ HANDLE hFile,
|
||||
_Inout_ PEXPORT_DATA pExportData)
|
||||
{
|
||||
DWORD Options;
|
||||
DWORD64 dwModuleBase;
|
||||
ULONG i;
|
||||
IMAGEHLP_STACK_FRAME StackFrame;
|
||||
SYMINFO_EX sym;
|
||||
|
||||
|
||||
/* Initialize dbghelp */
|
||||
if (!SymInitialize(ghProcess, 0, FALSE))
|
||||
return E_FAIL;
|
||||
|
||||
Options = SymGetOptions();
|
||||
Options |= SYMOPT_ALLOW_ABSOLUTE_SYMBOLS | SYMOPT_DEBUG;// | SYMOPT_NO_PROMPTS;
|
||||
Options &= ~SYMOPT_DEFERRED_LOADS;
|
||||
SymSetOptions(Options);
|
||||
SymSetSearchPath(ghProcess, "srv**symbols*http://msdl.microsoft.com/download/symbols");
|
||||
|
||||
printf("Loading symbols, please wait...\n");
|
||||
dwModuleBase = SymLoadModule64(ghProcess, 0, gszModuleFileName, 0, 0, 0);
|
||||
if (dwModuleBase == 0)
|
||||
{
|
||||
fprintf(stderr, "SymLoadModule64() failed: %ld\n", GetLastError());
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < pExportData->cNumberOfExports; i++)
|
||||
{
|
||||
PEXPORT pExport = &pExportData->aExports[i];
|
||||
ULONG64 ullFunction = dwModuleBase + pExportData->aExports[i].ulRva;
|
||||
ULONG64 ullDisplacement;
|
||||
|
||||
/* Skip forwarder */
|
||||
if (pExport->pszForwarder != NULL)
|
||||
continue;
|
||||
|
||||
RtlZeroMemory(&sym, sizeof(sym));
|
||||
sym.si.SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
sym.si.MaxNameLen = MAX_SYMBOL_NAME;
|
||||
|
||||
/* Try to find the symbol */
|
||||
if (!SymFromAddr(ghProcess, ullFunction, &ullDisplacement, &sym.si))
|
||||
{
|
||||
printf("Error: SymFromAddr() failed. Error code: %u \n", GetLastError());
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Symbol found. Check if it is a function */
|
||||
if (sym.si.Tag == SymTagFunction)
|
||||
{
|
||||
/* If we don't have a name yet, get one */
|
||||
pExport->pszSymbol = _strdup(sym.si.Name);
|
||||
|
||||
/* Get the calling convention */
|
||||
if (!SymGetTypeInfo(ghProcess,
|
||||
dwModuleBase,
|
||||
sym.si.TypeIndex,
|
||||
TI_GET_CALLING_CONVENTION,
|
||||
&pExport->dwCallingConvention))
|
||||
{
|
||||
/* Fall back to __stdcall */
|
||||
pExport->dwCallingConvention = 0x07; // CV_CALL_NEAR_STD
|
||||
}
|
||||
|
||||
/* Set the context to the function address */
|
||||
RtlZeroMemory(&StackFrame, sizeof(StackFrame));
|
||||
StackFrame.InstructionOffset = ullFunction;
|
||||
if (!SymSetContext(ghProcess, &StackFrame, NULL))
|
||||
{
|
||||
DWORD dwLastError = GetLastError();
|
||||
__debugbreak();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Enumerate all symbols for this function */
|
||||
if (!SymEnumSymbols(ghProcess,
|
||||
0, // use SymSetContext
|
||||
NULL,
|
||||
EnumParametersCallback,
|
||||
pExport))
|
||||
{
|
||||
DWORD dwLastError = GetLastError();
|
||||
__debugbreak();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (sym.si.Tag == SymTagData)
|
||||
{
|
||||
pExport->fData = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
const CHAR*
|
||||
GetCallingConvention(
|
||||
_In_ PEXPORT pExport)
|
||||
{
|
||||
if (pExport->fData)
|
||||
{
|
||||
return "extern";
|
||||
}
|
||||
|
||||
#ifndef _M_AMD64
|
||||
switch (pExport->dwCallingConvention)
|
||||
{
|
||||
case CV_CALL_NEAR_C:
|
||||
return "cdecl";
|
||||
case CV_CALL_NEAR_FAST:
|
||||
return "fastcall";
|
||||
case CV_CALL_NEAR_STD:
|
||||
return "stdcall";
|
||||
case CV_CALL_NEAR_SYS:
|
||||
return "syscall";
|
||||
case CV_CALL_THISCALL:
|
||||
return "thiscall";
|
||||
case CV_CALL_CLRCALL:
|
||||
return "clrcall";
|
||||
default:
|
||||
__debugbreak();
|
||||
}
|
||||
#endif
|
||||
return "stdcall";
|
||||
}
|
||||
|
||||
HRESULT
|
||||
CreateSpecFile(
|
||||
_In_ PCSTR pszSpecFile,
|
||||
_In_ PEXPORT_DATA pExportData)
|
||||
{
|
||||
FILE *file;
|
||||
ULONG i, p;
|
||||
PEXPORT pExport;
|
||||
|
||||
/* Create the spec file */
|
||||
if (fopen_s(&file, pszSpecFile, "w") != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to open spec file: '%s'\n", pszSpecFile);
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
/* Loop all exports */
|
||||
for (i = 0; i < pExportData->cNumberOfExports; i++)
|
||||
{
|
||||
pExport = &pExportData->aExports[i];
|
||||
|
||||
fprintf(file, "%u %s ", i + 1, GetCallingConvention(pExport));
|
||||
//if (pExport->fNoName)
|
||||
if (pExport->pszName == NULL)
|
||||
{
|
||||
fprintf(file, "-noname ");
|
||||
}
|
||||
|
||||
if (pExport->pszName != NULL)
|
||||
{
|
||||
fprintf(file, "%s", pExport->pszName);
|
||||
}
|
||||
else if (pExport->pszSymbol != NULL)
|
||||
{
|
||||
fprintf(file, "%s", pExport->pszSymbol);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(file, "NamelessExport_%u", i);
|
||||
}
|
||||
|
||||
if (!pExport->fData)
|
||||
{
|
||||
fprintf(file, "(");
|
||||
for (p = 0; p < pExport->cParameters; p++)
|
||||
{
|
||||
fprintf(file, "%s", gapszTypeStrings[pExport->aeParameters[p]]);
|
||||
if ((p + 1) < pExport->cParameters)
|
||||
{
|
||||
fprintf(file, " ");
|
||||
}
|
||||
}
|
||||
fprintf(file, ")");
|
||||
}
|
||||
|
||||
if (pExport->pszForwarder != NULL)
|
||||
{
|
||||
fprintf(file, " %s", pExport->pszForwarder);
|
||||
}
|
||||
|
||||
fprintf(file, "\n");
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
HRESULT hr;
|
||||
CHAR szSpecFile[MAX_PATH];
|
||||
PSTR pszSpecFile;
|
||||
HANDLE hFile;
|
||||
PEXPORT_DATA pExportData;
|
||||
|
||||
// check params
|
||||
// help
|
||||
|
||||
ghProcess = GetCurrentProcess();
|
||||
|
||||
/* Open the file */
|
||||
hr = OpenFileFromName(argv[1], &hFile);
|
||||
if (!SUCCEEDED(hr))
|
||||
{
|
||||
fprintf(stderr, "Failed to open file: %lx\n", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* Get the exports */
|
||||
hr = GetExportsFromFile(hFile, &pExportData);
|
||||
if (!SUCCEEDED(hr))
|
||||
{
|
||||
fprintf(stderr, "Failed to get exports: %lx\n", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* Get additional info from symbols */
|
||||
hr = ParseExportSymbols(hFile, pExportData);
|
||||
if (!SUCCEEDED(hr))
|
||||
{
|
||||
fprintf(stderr, "Failed to get symbol information: %lx\n", hr);
|
||||
}
|
||||
|
||||
if (argc > 2)
|
||||
{
|
||||
pszSpecFile = argv[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
PSTR pszStart = strrchr(argv[1], '\\');
|
||||
if (pszStart == 0)
|
||||
pszStart = argv[1];
|
||||
|
||||
strcpy_s(szSpecFile, sizeof(szSpecFile), pszStart);
|
||||
strcat_s(szSpecFile, sizeof(szSpecFile), ".spec");
|
||||
pszSpecFile = szSpecFile;
|
||||
}
|
||||
|
||||
hr = CreateSpecFile(pszSpecFile, pExportData);
|
||||
|
||||
CloseHandle(hFile);
|
||||
|
||||
printf("Spec file '%s' was successfully written.\n", szSpecFile);
|
||||
|
||||
return hr;
|
||||
}
|
Loading…
Reference in a new issue