[DBGHELP_APITEST] Add tests for pdb and rsym parsing. CORE-12773
authormjansen <mjansen@97493ccd-5924-5043-b1f5-66cb403b36ce>
Fri, 17 Feb 2017 16:15:49 +0000 (16:15 +0000)
committermjansen <mjansen@97493ccd-5924-5043-b1f5-66cb403b36ce>
Fri, 17 Feb 2017 16:15:49 +0000 (16:15 +0000)
git-svn-id: file:///srv/svn/reactos/trunk@73816 97493ccd-5924-5043-b1f5-66cb403b36ce

rostests/apitests/CMakeLists.txt
rostests/apitests/dbghelp/CMakeLists.txt [new file with mode: 0644]
rostests/apitests/dbghelp/data.c [new file with mode: 0644]
rostests/apitests/dbghelp/pdb.c [new file with mode: 0644]
rostests/apitests/dbghelp/resource.rc [new file with mode: 0644]
rostests/apitests/dbghelp/rsym.c [new file with mode: 0644]
rostests/apitests/dbghelp/testdata/gcc_uffs.dll.compr [new file with mode: 0644]
rostests/apitests/dbghelp/testdata/msvc_uffs.dll.compr [new file with mode: 0644]
rostests/apitests/dbghelp/testdata/msvc_uffs.pdb.compr [new file with mode: 0644]
rostests/apitests/dbghelp/testlist.c [new file with mode: 0644]

index ff582ef..e43ca4f 100644 (file)
@@ -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 (file)
index 0000000..fdc6833
--- /dev/null
@@ -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 (file)
index 0000000..13c823d
--- /dev/null
@@ -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 <windows.h>
+#include <stdio.h>
+#include <zlib.h>
+
+#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 (file)
index 0000000..64942c1
--- /dev/null
@@ -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 <ntstatus.h>
+#define WIN32_NO_STATUS
+#include <windows.h>
+#include <dbghelp.h>
+#include <cvconst.h>    // SymTagXXX
+#include <stdio.h>
+
+#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 (file)
index 0000000..79920a0
--- /dev/null
@@ -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 (file)
index 0000000..b471849
--- /dev/null
@@ -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 <ntstatus.h>
+#define WIN32_NO_STATUS
+#include <windows.h>
+#include <dbghelp.h>
+#include <cvconst.h>    // SymTagXXX
+#include <stdio.h>
+
+#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 (file)
index 0000000..f3e38c9
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 (file)
index 0000000..c027380
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 (file)
index 0000000..2fbd570
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 (file)
index 0000000..d4758e4
--- /dev/null
@@ -0,0 +1,13 @@
+
+#define STANDALONE
+#include <wine/test.h>
+
+extern void func_pdb(void);
+extern void func_rsym(void);
+
+const struct test winetest_testlist[] =
+{
+    { "pdb", func_pdb },
+    { "rsym", func_rsym },
+    { 0, 0 }
+};