diff --git a/rostests/apitests/CMakeLists.txt b/rostests/apitests/CMakeLists.txt index ff582efdd9e..e43ca4f87d3 100644 --- a/rostests/apitests/CMakeLists.txt +++ b/rostests/apitests/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory(atl) add_subdirectory(browseui) add_subdirectory(com) add_subdirectory(crt) +add_subdirectory(dbghelp) add_subdirectory(dciman32) add_subdirectory(dnsapi) add_subdirectory(gdi32) diff --git a/rostests/apitests/dbghelp/CMakeLists.txt b/rostests/apitests/dbghelp/CMakeLists.txt new file mode 100644 index 00000000000..fdc68332709 --- /dev/null +++ b/rostests/apitests/dbghelp/CMakeLists.txt @@ -0,0 +1,20 @@ + +add_definitions(-D__ROS_LONG64__ -DWINETEST_USE_DBGSTR_LONGLONG) + +include_directories( + ${REACTOS_SOURCE_DIR}/sdk/include/reactos/libs/zlib + ${REACTOS_SOURCE_DIR}/sdk/tools/rsym +) + +list(APPEND SOURCE + pdb.c + rsym.c + data.c + testlist.c) + +add_executable(dbghelp_apitest ${SOURCE} resource.rc) +set_module_type(dbghelp_apitest win32cui) +target_link_libraries(dbghelp_apitest zlib) +add_delay_importlibs(dbghelp_apitest dbghelp) +add_importlibs(dbghelp_apitest msvcrt kernel32 ntdll) +add_rostests_file(TARGET dbghelp_apitest) diff --git a/rostests/apitests/dbghelp/data.c b/rostests/apitests/dbghelp/data.c new file mode 100644 index 00000000000..13c823df490 --- /dev/null +++ b/rostests/apitests/dbghelp/data.c @@ -0,0 +1,277 @@ +/* + * PROJECT: ReactOS api tests + * LICENSE: GPLv2+ - See COPYING in the top level directory + * PURPOSE: Support functions for dbghelp api test + * PROGRAMMER: Mark Jansen + */ + +#include +#include +#include + +#include "wine/test.h" + +extern IMAGE_DOS_HEADER __ImageBase; + +static char szTempPath[MAX_PATH]; + +static const char* tmpdir() +{ + if (szTempPath[0] == '\0') + { + GetTempPathA(MAX_PATH, szTempPath); + lstrcatA(szTempPath, "dbghelp_tst"); + } + return szTempPath; +} + +static int extract_one(const char* filename, const char* resid) +{ + HMODULE mod = (HMODULE)&__ImageBase; + HGLOBAL glob; + PVOID data, decompressed; + uLongf size, dstsize; + DWORD gccSize, dwErr; + HANDLE file; + int ret; + HRSRC rsrc = FindResourceA(mod, resid, MAKEINTRESOURCEA(RT_RCDATA)); + ok(rsrc != 0, "Failed finding '%s' res\n", resid); + if (!rsrc) + return 0; + + size = SizeofResource(mod, rsrc); + glob = LoadResource(mod, rsrc); + ok(glob != NULL, "Failed loading '%s' res\n", resid); + if (!glob) + return 0; + + data = LockResource(glob); + + dstsize = 1024 * 256; + decompressed = malloc(dstsize); + + if (uncompress(decompressed, &dstsize, data, size) != Z_OK) + { + ok(0, "uncompress failed for %s\n", resid); + free(decompressed); + return 0; + } + + + file = CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + gccSize = size; + ret = WriteFile(file, decompressed, dstsize, &gccSize, NULL); + dwErr = GetLastError(); + CloseHandle(file); + free(decompressed); + ok(ret, "WriteFile failed (%d)\n", dwErr); + return ret && dstsize == gccSize; +} + + +int extract_msvc_exe(char szFile[MAX_PATH]) +{ + const char* dir = tmpdir(); + BOOL ret = CreateDirectoryA(dir, NULL); + ok(ret, "CreateDirectoryA failed(%d)\n", GetLastError()); + + sprintf(szFile, "%s\\uffs.pdb", dir); + if (!extract_one(szFile, "msvc_uffs.pdb")) + return 0; + + sprintf(szFile, "%s\\uffs.dll", dir); + if (!extract_one(szFile, "msvc_uffs.dll")) + return 0; + + return 1; +} + +void cleanup_msvc_exe() +{ + char szFile[MAX_PATH]; + BOOL ret; + const char* dir = tmpdir(); + + sprintf(szFile, "%s\\uffs.pdb", dir); + ret = DeleteFileA(szFile); + ok(ret, "DeleteFileA failed(%d)\n", GetLastError()); + + sprintf(szFile, "%s\\uffs.dll", dir); + ret = DeleteFileA(szFile); + ok(ret, "DeleteFileA failed(%d)\n", GetLastError()); + ret = RemoveDirectoryA(dir); + ok(ret, "RemoveDirectoryA failed(%d)\n", GetLastError()); +} + +int extract_gcc_exe(char szFile[MAX_PATH]) +{ + const char* dir = tmpdir(); + BOOL ret = CreateDirectoryA(dir, NULL); + ok(ret, "CreateDirectoryA failed(%d)\n", GetLastError()); + + sprintf(szFile, "%s\\uffs.dll", dir); + if (!extract_one(szFile, "gcc_uffs.dll")) + return 0; + + return 1; +} + +void cleanup_gcc_exe() +{ + char szFile[MAX_PATH]; + BOOL ret; + const char* dir = tmpdir(); + + sprintf(szFile, "%s\\uffs.dll", dir); + ret = DeleteFileA(szFile); + ok(ret, "DeleteFileA failed(%d)\n", GetLastError()); + ret = RemoveDirectoryA(dir); + ok(ret, "RemoveDirectoryA failed(%d)\n", GetLastError()); +} + + +#if 0 +static int compress_one(const char* src, const char* dest) +{ + DWORD size, size2, res; + FILE* file = fopen(src, "rb"); + fseek(file, 0, SEEK_END); + size = ftell(file); + fseek(file, 0, SEEK_SET); + + Bytef* buffer, *buffer2; + DWORD dwErr = GetLastError(); + + buffer = malloc(size); + res = fread(buffer, 1, size, file); + + fclose(file); + + if (res != size) + { + printf("Could not read file: 0x%x\n", dwErr); + free(buffer); + CloseHandle(file); + return 0; + } + size2 = size *2; + buffer2 = malloc(size2); + res = compress(buffer2, &size2, buffer, size); + + free(buffer); + + if (Z_OK != res) + { + free(buffer2); + return 0; + } + + file = fopen(dest, "wb"); + res = fwrite(buffer2, 1, size2, file); + fclose(file); + + free(buffer2); + + return size2 == res; +} + +void create_compressed_files() +{ + SetCurrentDirectoryA("R:/src/trunk/reactos/modules/rostests/apitests/dbghelp"); + if (!compress_one("testdata/msvc_uffs.dll", "testdata/msvc_uffs.dll.compr")) + printf("msvc_uffs.dll failed\n"); + if (!compress_one("testdata/msvc_uffs.pdb", "testdata/msvc_uffs.pdb.compr")) + printf("msvc_uffs.pdb failed\n"); + if (!compress_one("testdata/gcc_uffs.dll", "testdata/gcc_uffs.dll.compr")) + printf("gcc_uffs.dll failed\n"); +} +#endif + +#if 0 +typedef struct _SYMBOLFILE_HEADER { + ULONG SymbolsOffset; + ULONG SymbolsLength; + ULONG StringsOffset; + ULONG StringsLength; +} SYMBOLFILE_HEADER, *PSYMBOLFILE_HEADER; + +typedef struct _ROSSYM_ENTRY { + ULONG Address; + ULONG FunctionOffset; + ULONG FileOffset; + ULONG SourceLine; +} ROSSYM_ENTRY, *PROSSYM_ENTRY; + + +static int is_metadata(const char* name) +{ + size_t len = name ? strlen(name) : 0; + return len > 3 && name[0] == '_' && name[1] != '_' && name[len-1] == '_' && name[len-2] == '_'; +}; + +static void dump_rsym_internal(void* data) +{ + PSYMBOLFILE_HEADER RosSymHeader = (PSYMBOLFILE_HEADER)data; + PROSSYM_ENTRY Entries = (PROSSYM_ENTRY)((char *)data + RosSymHeader->SymbolsOffset); + size_t symbols = RosSymHeader->SymbolsLength / sizeof(ROSSYM_ENTRY); + size_t i; + char *Strings = (char *)data + RosSymHeader->StringsOffset; + + for (i = 0; i < symbols; i++) + { + PROSSYM_ENTRY Entry = Entries + i; + if (!Entry->FileOffset) + { + if (Entry->SourceLine) + printf("ERR: SOURCELINE (%D) ", Entry->SourceLine); + if (is_metadata(Strings + Entry->FunctionOffset)) + printf("metadata: %s: 0x%x\n", Strings + Entry->FunctionOffset, Entry->Address); + else + printf("0x%x: %s\n", Entry->Address, Strings + Entry->FunctionOffset); + } + else + { + printf("0x%x: %s (%s:%u)\n", Entry->Address, + Strings + Entry->FunctionOffset, + Strings + Entry->FileOffset, + Entry->SourceLine); + } + } + +} + +void dump_rsym(const char* filename) +{ + char* data; + long size, res; + PIMAGE_FILE_HEADER PEFileHeader; + PIMAGE_OPTIONAL_HEADER PEOptHeader; + PIMAGE_SECTION_HEADER PESectionHeaders; + WORD i; + + FILE* f = fopen(filename, "rb"); + + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + + data = malloc(size); + res = fread(data, 1, size, f); + fclose(f); + + PEFileHeader = (PIMAGE_FILE_HEADER)((char *)data + ((PIMAGE_DOS_HEADER)data)->e_lfanew + sizeof(ULONG)); + PEOptHeader = (PIMAGE_OPTIONAL_HEADER)(PEFileHeader + 1); + PESectionHeaders = (PIMAGE_SECTION_HEADER)((char *)PEOptHeader + PEFileHeader->SizeOfOptionalHeader); + + for (i = 0; i < PEFileHeader->NumberOfSections; i++) + { + if (!strcmp((char *)PESectionHeaders[i].Name, ".rossym")) + { + dump_rsym_internal(data + PESectionHeaders[i].PointerToRawData); + break; + } + } + free(data); +} + +#endif diff --git a/rostests/apitests/dbghelp/pdb.c b/rostests/apitests/dbghelp/pdb.c new file mode 100644 index 00000000000..64942c1d393 --- /dev/null +++ b/rostests/apitests/dbghelp/pdb.c @@ -0,0 +1,378 @@ +/* + * PROJECT: ReactOS api tests + * LICENSE: GPLv2+ - See COPYING in the top level directory + * PURPOSE: Test for dbghelp PDB functions + * PROGRAMMER: Mark Jansen + */ + +#include +#define WIN32_NO_STATUS +#include +#include +#include // SymTagXXX +#include + +#include "wine/test.h" + +#define ok_ulonglong(expression, result) \ + do { \ + ULONG64 _value = (expression); \ + ULONG64 _result = (result); \ + ok(_value == (result), "Wrong value for '%s', expected: " #result " (%s), got: %s\n", \ + #expression, wine_dbgstr_longlong(_result), wine_dbgstr_longlong(_value)); \ + } while (0) + + +// data.c +void create_compressed_files(); +int extract_msvc_exe(char szFile[MAX_PATH]); +void cleanup_msvc_exe(); + +static HANDLE proc() +{ + return GetCurrentProcess(); +} + +static BOOL init_sym_imp(const char* file, int line) +{ + if (!SymInitialize(proc(), NULL, FALSE)) + { + DWORD err = GetLastError(); + ok_(file, line)(0, "Failed to init: 0x%x\n", err); + return FALSE; + } + return TRUE; +} + +static void deinit_sym() +{ + SymCleanup(proc()); +} + +#define init_sym() init_sym_imp(__FILE__, __LINE__) + +#define INIT_PSYM(buff) do { \ + memset((buff), 0, sizeof((buff))); \ + ((PSYMBOL_INFO)(buff))->SizeOfStruct = sizeof(SYMBOL_INFO); \ + ((PSYMBOL_INFO)(buff))->MaxNameLen = MAX_SYM_NAME; \ +} while (0) + + +/* Maybe our dbghelp.dll is too old? */ +static BOOL can_enumerate(HANDLE hProc, DWORD64 BaseAddress) +{ + IMAGEHLP_MODULE64 ModuleInfo; + BOOL Ret; + + memset(&ModuleInfo, 0, sizeof(ModuleInfo)); + ModuleInfo.SizeOfStruct = sizeof(ModuleInfo); + Ret = SymGetModuleInfo64(hProc, BaseAddress, &ModuleInfo); + + return Ret && ModuleInfo.SymType == SymPdb; +} + + +static void test_SymFromName(HANDLE hProc, const char* szModuleName) +{ + BOOL Ret; + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; + PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; + + DWORD64 BaseAddress; + DWORD dwErr; + + if (!init_sym()) + return; + + SetLastError(ERROR_SUCCESS); + BaseAddress = SymLoadModule64(hProc, NULL, szModuleName, NULL, 0x600000, 0); + dwErr = GetLastError(); + + ok_ulonglong(BaseAddress, 0x600000); + ok_hex(dwErr, ERROR_SUCCESS); + + if (!can_enumerate(hProc, BaseAddress)) + { + skip("dbghelp.dll too old or cannot enumerate symbols!\n"); + } + else + { + INIT_PSYM(buffer); + Ret = SymFromName(hProc, "DllMain", pSymbol); + ok_int(Ret, TRUE); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, 0); + ok_ulonglong(pSymbol->Address, BaseAddress + 0x1010); + ok_hex(pSymbol->Tag, SymTagFunction); + ok_str(pSymbol->Name, "DllMain"); + + INIT_PSYM(buffer); + Ret = SymFromName(hProc, "_DllMain@12", pSymbol); + ok_int(Ret, TRUE); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, 0x400000); // ?? + ok_ulonglong(pSymbol->Address, BaseAddress + 0x1010); + ok_hex(pSymbol->Tag, SymTagPublicSymbol); + ok_str(pSymbol->Name, "_DllMain@12"); + + INIT_PSYM(buffer); + Ret = SymFromName(hProc, "FfsChkdsk", pSymbol); + ok_int(Ret, TRUE); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, 0); + ok_ulonglong(pSymbol->Address, BaseAddress + 0x1040); + ok_hex(pSymbol->Tag, SymTagFunction); + ok_str(pSymbol->Name, "FfsChkdsk"); + + INIT_PSYM(buffer); + Ret = SymFromName(hProc, "_FfsChkdsk@24", pSymbol); + ok_int(Ret, TRUE); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, 0x400000); // ?? + ok_ulonglong(pSymbol->Address, BaseAddress + 0x1040); + ok_hex(pSymbol->Tag, SymTagPublicSymbol); + ok_str(pSymbol->Name, "_FfsChkdsk@24"); + + INIT_PSYM(buffer); + Ret = SymFromName(hProc, "FfsFormat", pSymbol); + ok_int(Ret, TRUE); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, 0); + ok_ulonglong(pSymbol->Address, BaseAddress + 0x1070); + ok_hex(pSymbol->Tag, SymTagFunction); + ok_str(pSymbol->Name, "FfsFormat"); + + INIT_PSYM(buffer); + Ret = SymFromName(hProc, "_FfsFormat@24", pSymbol); + ok_int(Ret, TRUE); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, 0x400000); // ?? + ok_ulonglong(pSymbol->Address, BaseAddress + 0x1070); + ok_hex(pSymbol->Tag, SymTagPublicSymbol); + ok_str(pSymbol->Name, "_FfsFormat@24"); + } + + deinit_sym(); +} + +static void test_SymFromAddr(HANDLE hProc, const char* szModuleName) +{ + BOOL Ret; + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; + PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; + + DWORD64 BaseAddress, Displacement; + DWORD dwErr; + + if (!init_sym()) + return; + + SetLastError(ERROR_SUCCESS); + BaseAddress = SymLoadModule64(hProc, NULL, szModuleName, NULL, 0x600000, 0); + dwErr = GetLastError(); + + ok_ulonglong(BaseAddress, 0x600000); + ok_hex(dwErr, ERROR_SUCCESS); + + /* No address found before load address of module */ + Displacement = 0; + INIT_PSYM(buffer); + Ret = SymFromAddr(hProc, BaseAddress -1, &Displacement, pSymbol); + dwErr = GetLastError(); + ok_int(Ret, FALSE); + ok_hex(dwErr, ERROR_MOD_NOT_FOUND); + + /* Right at the start of the module is recognized as the first symbol found */ + Displacement = 0; + INIT_PSYM(buffer); + Ret = SymFromAddr(hProc, BaseAddress, &Displacement, pSymbol); + ok_int(Ret, TRUE); + ok_ulonglong(Displacement, 0xffffffffffffffff); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, 0); + ok_ulonglong(pSymbol->Address, BaseAddress + 0x1010); + ok_hex(pSymbol->Tag, SymTagFunction); + ok_str(pSymbol->Name, "DllMain"); + + /* The actual first instruction of the function */ + Displacement = 0; + INIT_PSYM(buffer); + Ret = SymFromAddr(hProc, BaseAddress + 0x1010, &Displacement, pSymbol); + ok_int(Ret, TRUE); + ok_ulonglong(Displacement, 0); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, 0); + ok_ulonglong(pSymbol->Address, BaseAddress + 0x1010); + ok_hex(pSymbol->Tag, SymTagFunction); + ok_str(pSymbol->Name, "DllMain"); + + /* The last instruction in the function */ + Displacement = 0; + INIT_PSYM(buffer); + Ret = SymFromAddr(hProc, BaseAddress + 0x102D, &Displacement, pSymbol); + ok_int(Ret, TRUE); + ok_ulonglong(Displacement, 0x1d); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, 0); + ok_ulonglong(pSymbol->Address, BaseAddress + 0x1010); + ok_hex(pSymbol->Tag, SymTagFunction); + ok_str(pSymbol->Name, "DllMain"); + + /* The padding below the function */ + Displacement = 0; + INIT_PSYM(buffer); + Ret = SymFromAddr(hProc, BaseAddress + 0x102E, &Displacement, pSymbol); + ok_int(Ret, TRUE); + ok_ulonglong(Displacement, 0x1e); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, 0); + ok_ulonglong(pSymbol->Address, BaseAddress + 0x1010); + ok_hex(pSymbol->Tag, SymTagFunction); + ok_str(pSymbol->Name, "DllMain"); + + /* One byte before the next function */ + Displacement = 0; + INIT_PSYM(buffer); + Ret = SymFromAddr(hProc, BaseAddress + 0x103f, &Displacement, pSymbol); + ok_int(Ret, TRUE); + ok_ulonglong(Displacement, 0x2f); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, 0); + ok_ulonglong(pSymbol->Address, BaseAddress + 0x1010); + ok_hex(pSymbol->Tag, SymTagFunction); + ok_str(pSymbol->Name, "DllMain"); + + /* First byte of the next function */ + Displacement = 0; + INIT_PSYM(buffer); + Ret = SymFromAddr(hProc, BaseAddress + 0x1040, &Displacement, pSymbol); + ok_int(Ret, TRUE); + ok_ulonglong(Displacement, 0); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, 0); + ok_ulonglong(pSymbol->Address, BaseAddress + 0x1040); + ok_hex(pSymbol->Tag, SymTagFunction); + ok_str(pSymbol->Name, "FfsChkdsk"); + + if (!can_enumerate(hProc, BaseAddress)) + { + skip("dbghelp.dll too old or cannot read this symbol!\n"); + } + else + { + /* .idata */ + Displacement = 0; + INIT_PSYM(buffer); + Ret = SymFromAddr(hProc, BaseAddress + 0x2000, &Displacement, pSymbol); + ok_int(Ret, TRUE); + ok_ulonglong(Displacement, 0); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, 0); + ok_ulonglong(pSymbol->Address, BaseAddress + 0x2000); + ok_hex(pSymbol->Tag, SymTagPublicSymbol); + ok_str(pSymbol->Name, "__imp__DbgPrint"); + } + + deinit_sym(); +} + +typedef struct _test_context +{ + DWORD64 BaseAddress; + SIZE_T Index; +} test_context; + +static struct _test_data { + DWORD64 AddressOffset; + ULONG Size; + ULONG Tag; + const char* Name; +} test_data[] = { + /* TODO: Order is based on magic, should find entries based on name, and mark as 'seen' */ + { 0x1070, 36, SymTagFunction, "FfsFormat" }, + { 0x1010, 32, SymTagFunction, "DllMain" }, + { 0x1040, 36, SymTagFunction, "FfsChkdsk" }, + + { 0x2100, 0, SymTagPublicSymbol, "__IMPORT_DESCRIPTOR_ntdll" }, + { 0x109a, 0, SymTagPublicSymbol, "_DbgPrint" }, + { 0x2004, 0, SymTagPublicSymbol, "\x7fntdll_NULL_THUNK_DATA" }, + { 0x2000, 0, SymTagPublicSymbol, "__imp__DbgPrint" }, + { 0x2114, 0, SymTagPublicSymbol, "__NULL_IMPORT_DESCRIPTOR" }, +}; + +static BOOL CALLBACK EnumSymProc(PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext) +{ + test_context* ctx = UserContext; + + if (ctx->Index < ARRAYSIZE(test_data)) + { + ok_ulonglong(pSymInfo->ModBase, ctx->BaseAddress); + ok_ulonglong(pSymInfo->Address, ctx->BaseAddress + test_data[ctx->Index].AddressOffset); + ok_hex(pSymInfo->Tag, test_data[ctx->Index].Tag); + ok_str(pSymInfo->Name, test_data[ctx->Index].Name); + + ctx->Index++; + } + else + { + ok(0, "Out of bounds (%lu), max is: %i!\n", ctx->Index, ARRAYSIZE(test_data)); + } + + return TRUE; +} + +static void test_SymEnumSymbols(HANDLE hProc, const char* szModuleName) +{ + BOOL Ret; + DWORD dwErr; + + test_context ctx; + + if (!init_sym()) + return; + + ctx.Index = 0; + SetLastError(ERROR_SUCCESS); + ctx.BaseAddress = SymLoadModule64(hProc, NULL, szModuleName, NULL, 0x600000, 0); + dwErr = GetLastError(); + + ok_ulonglong(ctx.BaseAddress, 0x600000); + ok_hex(dwErr, ERROR_SUCCESS); + + if (!can_enumerate(hProc, ctx.BaseAddress)) + { + skip("dbghelp.dll too old or cannot enumerate symbols!\n"); + } + else + { + Ret = SymEnumSymbols(hProc, ctx.BaseAddress, NULL, EnumSymProc, &ctx); + ok_int(Ret, TRUE); + ok_int(ctx.Index, ARRAYSIZE(test_data)); + } + + deinit_sym(); +} + + +START_TEST(pdb) +{ + char szDllName[MAX_PATH]; + //create_compressed_files(); + + DWORD Options = SymGetOptions(); + Options &= ~(SYMOPT_UNDNAME); + //Options |= SYMOPT_DEBUG; + SymSetOptions(Options); + + if (!extract_msvc_exe(szDllName)) + { + ok(0, "Failed extracting files\n"); + return; + } + + test_SymFromName(proc(), szDllName); + test_SymFromAddr(proc(), szDllName); + test_SymEnumSymbols(proc(), szDllName); + + cleanup_msvc_exe(); +} diff --git a/rostests/apitests/dbghelp/resource.rc b/rostests/apitests/dbghelp/resource.rc new file mode 100644 index 00000000000..79920a0e64e --- /dev/null +++ b/rostests/apitests/dbghelp/resource.rc @@ -0,0 +1,5 @@ +#include "windef.h" + +msvc_uffs.dll RCDATA testdata/msvc_uffs.dll.compr +msvc_uffs.pdb RCDATA testdata/msvc_uffs.pdb.compr +gcc_uffs.dll RCDATA testdata/gcc_uffs.dll.compr diff --git a/rostests/apitests/dbghelp/rsym.c b/rostests/apitests/dbghelp/rsym.c new file mode 100644 index 00000000000..b471849e4ad --- /dev/null +++ b/rostests/apitests/dbghelp/rsym.c @@ -0,0 +1,369 @@ +/* + * PROJECT: ReactOS api tests + * LICENSE: GPLv2+ - See COPYING in the top level directory + * PURPOSE: Test for dbghelp rsym functions + * PROGRAMMER: Mark Jansen + * + * These tests are based on the PDB tests. + */ + +#include +#define WIN32_NO_STATUS +#include +#include +#include // SymTagXXX +#include + +#include "wine/test.h" + +#define ok_ulonglong(expression, result) \ + do { \ + ULONG64 _value = (expression); \ + ULONG64 _result = (result); \ + ok(_value == (result), "Wrong value for '%s', expected: " #result " (%s), got: %s\n", \ + #expression, wine_dbgstr_longlong(_result), wine_dbgstr_longlong(_value)); \ + } while (0) + +#define ok_ulonglong_(file, line, expression, result) \ + do { \ + ULONG64 _value = (expression); \ + ULONG64 _result = (result); \ + ok_(file, line)(_value == (result), "Wrong value for '%s', expected: " #result " (%s), got: %s\n", \ + #expression, wine_dbgstr_longlong(_result), wine_dbgstr_longlong(_value)); \ + } while (0) + +#define ok_hex_(file, line, expression, result) \ + do { \ + int _value = (expression); \ + ok_(file, line)(_value == (result), "Wrong value for '%s', expected: " #result " (0x%x), got: 0x%x\n", \ + #expression, (int)(result), _value); \ + } while (0) + +#define ok_str_(file, line, x, y) \ + ok_(file, line)(strcmp(x, y) == 0, "Wrong string. Expected '%s', got '%s'\n", y, x) + + +// data.c +void dump_rsym(const char* filename); +int extract_gcc_exe(char szFile[MAX_PATH]); +void cleanup_gcc_exe(); + +static HANDLE proc() +{ + return GetCurrentProcess(); +} + +static BOOL init_sym_imp(const char* file, int line) +{ + if (!SymInitialize(proc(), NULL, FALSE)) + { + DWORD err = GetLastError(); + ok_(file, line)(0, "Failed to init: 0x%x\n", err); + return FALSE; + } + return TRUE; +} + +static void deinit_sym() +{ + SymCleanup(proc()); +} + +static BOOL supports_rsym(HANDLE hProc, DWORD64 BaseAddress) +{ + IMAGEHLP_MODULE64 ModuleInfo; + BOOL Ret; + + memset(&ModuleInfo, 0, sizeof(ModuleInfo)); + ModuleInfo.SizeOfStruct = sizeof(ModuleInfo); + Ret = SymGetModuleInfo64(hProc, BaseAddress, &ModuleInfo); + + return Ret && + ModuleInfo.SymType == SymDia && + ModuleInfo.CVSig == ('R' | ('S' << 8) | ('Y' << 16) | ('M' << 24)); +} + +#define init_sym() init_sym_imp(__FILE__, __LINE__) + +#define INIT_PSYM(buff) do { \ + memset((buff), 0, sizeof((buff))); \ + ((PSYMBOL_INFO)(buff))->SizeOfStruct = sizeof(SYMBOL_INFO); \ + ((PSYMBOL_INFO)(buff))->MaxNameLen = MAX_SYM_NAME; \ +} while (0) + + +static void test_SymFromName(HANDLE hProc, const char* szModuleName) +{ + BOOL Ret; + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; + PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; + + DWORD64 BaseAddress; + DWORD dwErr; + + if (!init_sym()) + return; + + SetLastError(ERROR_SUCCESS); + BaseAddress = SymLoadModule64(hProc, NULL, szModuleName, NULL, 0x600000, 0); + dwErr = GetLastError(); + + if (supports_rsym(hProc, BaseAddress)) + { + ok_ulonglong(BaseAddress, 0x600000); + ok_hex(dwErr, ERROR_SUCCESS); + + INIT_PSYM(buffer); + Ret = SymFromName(hProc, "DllMain", pSymbol); + ok_int(Ret, TRUE); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, SYMFLAG_FUNCTION); + ok_ulonglong(pSymbol->Address, BaseAddress + 0x1000); + ok_hex(pSymbol->Tag, SymTagFunction); + ok_str(pSymbol->Name, "DllMain"); + + INIT_PSYM(buffer); + Ret = SymFromName(hProc, "FfsChkdsk", pSymbol); + ok_int(Ret, TRUE); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, SYMFLAG_FUNCTION); + ok_ulonglong(pSymbol->Address, BaseAddress + 0x103F); + ok_hex(pSymbol->Tag, SymTagFunction); + ok_str(pSymbol->Name, "FfsChkdsk"); + + INIT_PSYM(buffer); + Ret = SymFromName(hProc, "FfsFormat", pSymbol); + ok_int(Ret, TRUE); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, SYMFLAG_FUNCTION); + ok_ulonglong(pSymbol->Address, BaseAddress + 0x100C); + ok_hex(pSymbol->Tag, SymTagFunction); + ok_str(pSymbol->Name, "FfsFormat"); + } + else + { + skip("dbghelp.dll cannot parse rsym\n"); + } + + deinit_sym(); +} + +static void test_SymFromAddr(HANDLE hProc, const char* szModuleName) +{ + BOOL Ret; + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; + PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; + + DWORD64 BaseAddress, Displacement; + DWORD dwErr; + + if (!init_sym()) + return; + + SetLastError(ERROR_SUCCESS); + BaseAddress = SymLoadModule64(hProc, NULL, szModuleName, NULL, 0x600000, 0); + dwErr = GetLastError(); + + if (supports_rsym(hProc, BaseAddress)) + { + ok_ulonglong(BaseAddress, 0x600000); + ok_hex(dwErr, ERROR_SUCCESS); + + /* No address found before load address of module */ + Displacement = 0; + INIT_PSYM(buffer); + Ret = SymFromAddr(hProc, BaseAddress -1, &Displacement, pSymbol); + dwErr = GetLastError(); + ok_int(Ret, FALSE); + ok_hex(dwErr, ERROR_MOD_NOT_FOUND); + + /* Right at the start of the module is recognized as the first symbol found */ + Displacement = 0; + INIT_PSYM(buffer); + Ret = SymFromAddr(hProc, BaseAddress, &Displacement, pSymbol); + /* Our dbghelp.dll does not recognize this yet */ + todo_if(!Ret) + { + ok_int(Ret, TRUE); + ok_ulonglong(Displacement, 0xffffffffffffffff); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, SYMFLAG_FUNCTION); + ok_ulonglong(pSymbol->Address, BaseAddress + 0x1000); + ok_hex(pSymbol->Tag, SymTagFunction); + ok_str(pSymbol->Name, "DllMain"); + } + + /* The actual first instruction of the function */ + Displacement = 0; + INIT_PSYM(buffer); + Ret = SymFromAddr(hProc, BaseAddress + 0x1000, &Displacement, pSymbol); + ok_int(Ret, TRUE); + ok_ulonglong(Displacement, 0); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, SYMFLAG_FUNCTION); + ok_ulonglong(pSymbol->Address, BaseAddress + 0x1000); + ok_hex(pSymbol->Tag, SymTagFunction); + ok_str(pSymbol->Name, "DllMain"); + + /* The last instruction in the function */ + Displacement = 0; + INIT_PSYM(buffer); + Ret = SymFromAddr(hProc, BaseAddress + 0x1009, &Displacement, pSymbol); + ok_int(Ret, TRUE); + ok_ulonglong(Displacement, 0x9); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, SYMFLAG_FUNCTION); + ok_ulonglong(pSymbol->Address, BaseAddress + 0x1000); + ok_hex(pSymbol->Tag, SymTagFunction); + ok_str(pSymbol->Name, "DllMain"); + + /* First byte of the next function */ + Displacement = 0; + INIT_PSYM(buffer); + Ret = SymFromAddr(hProc, BaseAddress + 0x103F, &Displacement, pSymbol); + ok_int(Ret, TRUE); + ok_ulonglong(Displacement, 0); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, SYMFLAG_FUNCTION); + ok_ulonglong(pSymbol->Address, BaseAddress + 0x103F); + ok_hex(pSymbol->Tag, SymTagFunction); + ok_str(pSymbol->Name, "FfsChkdsk"); + + /* .idata */ + Displacement = 0; + INIT_PSYM(buffer); + Ret = SymFromAddr(hProc, BaseAddress + 0x4000, &Displacement, pSymbol); + ok_int(Ret, TRUE); + ok_ulonglong(Displacement, 0); + ok_ulonglong(pSymbol->ModBase, BaseAddress); + ok_hex(pSymbol->Flags, SYMFLAG_EXPORT); + ok_ulonglong(pSymbol->Address, BaseAddress + 0x4000); + ok_hex(pSymbol->Tag, SymTagPublicSymbol); + ok_str(pSymbol->Name, "_head_dll_ntdll_libntdll_a"); + } + else + { + skip("dbghelp.dll cannot parse rsym\n"); + } + + deinit_sym(); +} + +typedef struct _test_context +{ + DWORD64 BaseAddress; + SIZE_T Index; +} test_context; + +static struct _test_data { + DWORD64 AddressOffset; + ULONG Size; + ULONG Tag; + const char* Name; + int Line; +} test_data[] = { + + /* TODO: Order is based on magic, should find entries based on name, and mark as 'seen' */ + { 0x107c, 0, SymTagPublicSymbol, "__CTOR_LIST__", __LINE__ }, + { 0x2074, 0, SymTagPublicSymbol, "__RUNTIME_PSEUDO_RELOC_LIST_END__", __LINE__ }, + { 0x1000, 12, SymTagPublicSymbol, "EntryPoint", __LINE__ }, + { 0x100c, 51, SymTagFunction, "FfsFormat", __LINE__ }, + { 0x4030, 0, SymTagPublicSymbol, "_imp__DbgPrint", __LINE__ }, + { 0x1084, 0, SymTagPublicSymbol, "__DTOR_LIST__", __LINE__ }, + { 0x103f, 53, SymTagFunction, "FfsChkdsk", __LINE__ }, + { 0x2074, 0, SymTagPublicSymbol, "_rt_psrelocs_end", __LINE__ }, + { 0x103f, 53, SymTagPublicSymbol, "ChkdskEx", __LINE__ }, + { 0x4048, 0, SymTagPublicSymbol, "_dll_ntdll_libntdll_a_iname", __LINE__ }, + + + + { 0x2074, 0, SymTagPublicSymbol, "_rt_psrelocs_start", __LINE__ }, + { 0x1000, 12, SymTagFunction, "DllMain", __LINE__ }, + { 0x100c, 0, SymTagPublicSymbol, "FormatEx", __LINE__ }, + { 0x1074, 0, SymTagPublicSymbol, "DbgPrint", __LINE__ }, + { 0x68900000, 0, SymTagPublicSymbol, "__ImageBase", __LINE__ }, + { 0x68902074, 0, SymTagPublicSymbol, "__RUNTIME_PSEUDO_RELOC_LIST__", __LINE__ }, + { 0x4000, 0, SymTagPublicSymbol, "_head_dll_ntdll_libntdll_a", __LINE__ }, +}; + +BOOL CALLBACK EnumSymProc(PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext) +{ + test_context* ctx = UserContext; + + if (ctx->Index < ARRAYSIZE(test_data)) + { + ok_ulonglong_(__FILE__, test_data[ctx->Index].Line, pSymInfo->ModBase, ctx->BaseAddress); + if (test_data[ctx->Index].AddressOffset > 0x100000) + ok_ulonglong_(__FILE__, test_data[ctx->Index].Line, pSymInfo->Address, test_data[ctx->Index].AddressOffset); + else + ok_ulonglong_(__FILE__, test_data[ctx->Index].Line, pSymInfo->Address, ctx->BaseAddress + test_data[ctx->Index].AddressOffset); + ok_hex_(__FILE__, test_data[ctx->Index].Line, pSymInfo->Tag, test_data[ctx->Index].Tag); + ok_str_(__FILE__, test_data[ctx->Index].Line, pSymInfo->Name, test_data[ctx->Index].Name); + + ctx->Index++; + } + else + { + ok(0, "Out of bounds (%lu), max is: %i!\n", ctx->Index, ARRAYSIZE(test_data)); + } + + return TRUE; +} + +static void test_SymEnumSymbols(HANDLE hProc, const char* szModuleName) +{ + BOOL Ret; + DWORD dwErr; + + test_context ctx; + + if (!init_sym()) + return; + + ctx.Index = 0; + SetLastError(ERROR_SUCCESS); + ctx.BaseAddress = SymLoadModule64(hProc, NULL, szModuleName, NULL, 0x600000, 0); + dwErr = GetLastError(); + + if (supports_rsym(hProc, ctx.BaseAddress)) + { + ok_ulonglong(ctx.BaseAddress, 0x600000); + ok_hex(dwErr, ERROR_SUCCESS); + + Ret = SymEnumSymbols(hProc, ctx.BaseAddress, NULL, EnumSymProc, &ctx); + ok_int(Ret, TRUE); + ok_int(ctx.Index, ARRAYSIZE(test_data)); + } + else + { + skip("dbghelp.dll cannot parse rsym\n"); + } + + deinit_sym(); +} + + + + +START_TEST(rsym) +{ + char szDllName[MAX_PATH]; + //dump_rsym("R:\\src\\trunk\\reactos\\modules\\rostests\\apitests\\dbghelp\\testdata\\gcc_uffs.dll"); + + DWORD Options = SymGetOptions(); + Options &= ~(SYMOPT_UNDNAME); + //Options |= SYMOPT_DEBUG; + SymSetOptions(Options); + + if (!extract_gcc_exe(szDllName)) + { + ok(0, "Failed extracting files\n"); + return; + } + + test_SymFromName(proc(), szDllName); + test_SymFromAddr(proc(), szDllName); + test_SymEnumSymbols(proc(), szDllName); + + cleanup_gcc_exe(); +} diff --git a/rostests/apitests/dbghelp/testdata/gcc_uffs.dll.compr b/rostests/apitests/dbghelp/testdata/gcc_uffs.dll.compr new file mode 100644 index 00000000000..f3e38c93232 Binary files /dev/null and b/rostests/apitests/dbghelp/testdata/gcc_uffs.dll.compr differ diff --git a/rostests/apitests/dbghelp/testdata/msvc_uffs.dll.compr b/rostests/apitests/dbghelp/testdata/msvc_uffs.dll.compr new file mode 100644 index 00000000000..c02738094ef Binary files /dev/null and b/rostests/apitests/dbghelp/testdata/msvc_uffs.dll.compr differ diff --git a/rostests/apitests/dbghelp/testdata/msvc_uffs.pdb.compr b/rostests/apitests/dbghelp/testdata/msvc_uffs.pdb.compr new file mode 100644 index 00000000000..2fbd57031a9 Binary files /dev/null and b/rostests/apitests/dbghelp/testdata/msvc_uffs.pdb.compr differ diff --git a/rostests/apitests/dbghelp/testlist.c b/rostests/apitests/dbghelp/testlist.c new file mode 100644 index 00000000000..d4758e4acc8 --- /dev/null +++ b/rostests/apitests/dbghelp/testlist.c @@ -0,0 +1,13 @@ + +#define STANDALONE +#include + +extern void func_pdb(void); +extern void func_rsym(void); + +const struct test winetest_testlist[] = +{ + { "pdb", func_pdb }, + { "rsym", func_rsym }, + { 0, 0 } +};