Implements SymSrvGetFileIndexesInfo() for PE images. (.dbg and .pdb are still to be done).
From: Eric Pouech eric.pouech@gmail.com
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- include/dbghelp.h | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-)
diff --git a/include/dbghelp.h b/include/dbghelp.h index 84fa7e10595..15e9f9d2d77 100644 --- a/include/dbghelp.h +++ b/include/dbghelp.h @@ -310,17 +310,19 @@ typedef struct _SOURCEFILEW PWSTR FileName; } SOURCEFILEW, *PSOURCEFILEW;
-#define CBA_DEFERRED_SYMBOL_LOAD_START 0x00000001 -#define CBA_DEFERRED_SYMBOL_LOAD_COMPLETE 0x00000002 -#define CBA_DEFERRED_SYMBOL_LOAD_FAILURE 0x00000003 -#define CBA_SYMBOLS_UNLOADED 0x00000004 -#define CBA_DUPLICATE_SYMBOL 0x00000005 -#define CBA_READ_MEMORY 0x00000006 -#define CBA_DEFERRED_SYMBOL_LOAD_CANCEL 0x00000007 -#define CBA_SET_OPTIONS 0x00000008 -#define CBA_EVENT 0x00000010 -#define CBA_DEFERRED_SYMBOL_LOAD_PARTIAL 0x00000020 -#define CBA_DEBUG_INFO 0x10000000 +#define CBA_DEFERRED_SYMBOL_LOAD_START 0x00000001 +#define CBA_DEFERRED_SYMBOL_LOAD_COMPLETE 0x00000002 +#define CBA_DEFERRED_SYMBOL_LOAD_FAILURE 0x00000003 +#define CBA_SYMBOLS_UNLOADED 0x00000004 +#define CBA_DUPLICATE_SYMBOL 0x00000005 +#define CBA_READ_MEMORY 0x00000006 +#define CBA_DEFERRED_SYMBOL_LOAD_CANCEL 0x00000007 +#define CBA_SET_OPTIONS 0x00000008 +#define CBA_EVENT 0x00000010 +#define CBA_DEFERRED_SYMBOL_LOAD_PARTIAL 0x00000020 +#define CBA_DEBUG_INFO 0x10000000 +#define CBA_SRCSRV_INFO 0x20000000 +#define CBA_SRCSRV_EVENT 0x40000000
typedef struct _IMAGEHLP_CBA_READ_MEMORY { @@ -1115,9 +1117,13 @@ BOOL WINAPI SymSetExtendedOption(IMAGEHLP_EXTENDED_OPTIONS option, BOOL value);
BOOL WINAPI SymSetParentWindow(HWND);
-/************************* - * Version, global stuff * - *************************/ +/*************************** + * Symbol servers & stores * + ***************************/ +BOOL WINAPI SymSrvGetFileIndexes(PCSTR, GUID *, PDWORD, PDWORD, DWORD); +BOOL WINAPI SymSrvGetFileIndexesW(PCWSTR, GUID *, PDWORD, PDWORD, DWORD); +BOOL WINAPI SymSrvGetFileIndexInfo(PCSTR, PSYMSRV_INDEX_INFO, DWORD); +BOOL WINAPI SymSrvGetFileIndexInfoW(PCWSTR, PSYMSRV_INDEX_INFOW, DWORD);
typedef BOOL (WINAPI* PSYMBOLSERVERPROC)(PCSTR, PCSTR, PVOID, DWORD, DWORD, PSTR); typedef BOOL (WINAPI* PSYMBOLSERVERPROCA)(PCSTR, PCSTR, PVOID, DWORD, DWORD, PSTR); @@ -1150,6 +1156,9 @@ typedef BOOL (WINAPI* PSYMBOLSERVERPINGPROCW)(PCWSTR); #define SSRVACTION_TRACE 1 #define SSRVACTION_QUERYCANCEL 2 #define SSRVACTION_EVENT 3 +#define SSRVACTION_EVENTW 4 +#define SSRVACTION_SIZE 5 +#define SSRVACTION_HTTPSTATUS 6
/* 32-bit functions */
From: Eric Pouech eric.pouech@gmail.com
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/dbghelp/dbghelp.spec | 6 +-- dlls/dbghelp/path.c | 78 +++++++++++++++++++++++++++++++++++++++ dlls/dbghelp/symbol.c | 11 ------ 3 files changed, 81 insertions(+), 14 deletions(-)
diff --git a/dlls/dbghelp/dbghelp.spec b/dlls/dbghelp/dbghelp.spec index fc2218f636b..9ef5922386e 100644 --- a/dlls/dbghelp/dbghelp.spec +++ b/dlls/dbghelp/dbghelp.spec @@ -176,11 +176,11 @@ @ stub SymSrvDeltaName @ stub SymSrvDeltaNameW @ stdcall SymSrvGetFileIndexInfo(str ptr long) -@ stub SymSrvGetFileIndexInfoW +@ stdcall SymSrvGetFileIndexInfoW(wstr ptr long) @ stub SymSrvGetFileIndexString @ stub SymSrvGetFileIndexStringW -@ stub SymSrvGetFileIndexes -@ stub SymSrvGetFileIndexesW +@ stdcall SymSrvGetFileIndexes(str ptr ptr ptr long) +@ stdcall SymSrvGetFileIndexesW(wstr ptr ptr ptr long) @ stub SymSrvGetSupplement @ stub SymSrvGetSupplementW @ stub SymSrvIsStore diff --git a/dlls/dbghelp/path.c b/dlls/dbghelp/path.c index fe1da8849eb..7c356e3a6ea 100644 --- a/dlls/dbghelp/path.c +++ b/dlls/dbghelp/path.c @@ -839,3 +839,81 @@ BOOL search_unix_path(const WCHAR *name, const WCHAR *path, BOOL (*match)(void*, heap_free(buf); return ret; } + +/****************************************************************** + * SymSrvGetFileIndexInfo (DBGHELP.@) + * + */ +BOOL WINAPI SymSrvGetFileIndexInfo(const char *file, SYMSRV_INDEX_INFO* info, DWORD flags) +{ + SYMSRV_INDEX_INFOW infoW; + WCHAR fileW[MAX_PATH]; + BOOL ret; + + TRACE("(%s, %p, 0x%08lx)\n", debugstr_a(file), info, flags); + + if (info->sizeofstruct < sizeof(*info)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + MultiByteToWideChar(CP_ACP, 0, file, -1, fileW, ARRAY_SIZE(fileW)); + infoW.sizeofstruct = sizeof(infoW); + ret = SymSrvGetFileIndexInfoW(fileW, &infoW, flags); + if (ret) + { + WideCharToMultiByte(CP_ACP, 0, infoW.file, -1, info->file, ARRAY_SIZE(info->file), NULL, NULL); + info->stripped = infoW.stripped; + info->timestamp = infoW.timestamp; + info->size = infoW.size; + WideCharToMultiByte(CP_ACP, 0, infoW.dbgfile, -1, info->dbgfile, ARRAY_SIZE(info->dbgfile), NULL, NULL); + WideCharToMultiByte(CP_ACP, 0, infoW.pdbfile, -1, info->pdbfile, ARRAY_SIZE(info->pdbfile), NULL, NULL); + info->guid = infoW.guid; + info->sig = infoW.sig; + info->age = infoW.age; + } + return ret; +} + +/****************************************************************** + * SymSrvGetFileIndexInfoW (DBGHELP.@) + * + */ +BOOL WINAPI SymSrvGetFileIndexInfoW(const WCHAR *file, SYMSRV_INDEX_INFOW* info, DWORD flags) +{ + FIXME("(%s, %p, 0x%08lx): stub!\n", debugstr_w(file), info, flags); + + if (info->sizeofstruct < sizeof(*info)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +/****************************************************************** + * SymSrvGetFileIndexes (DBGHELP.@) + * + */ +BOOL WINAPI SymSrvGetFileIndexes(PCSTR file, GUID* guid, PDWORD pdw1, PDWORD pdw2, DWORD flags) +{ + WCHAR fileW[MAX_PATH]; + + TRACE("(%s, %p, %p, %p, 0x%08lx)\n", debugstr_a(file), guid, pdw1, pdw2, flags); + + MultiByteToWideChar(CP_ACP, 0, file, -1, fileW, ARRAY_SIZE(fileW)); + return SymSrvGetFileIndexesW(fileW, guid, pdw1, pdw2, flags); +} + +/****************************************************************** + * SymSrvGetFileIndexesW (DBGHELP.@) + * + */ +BOOL WINAPI SymSrvGetFileIndexesW(PCWSTR file, GUID* guid, PDWORD pdw1, PDWORD pdw2, DWORD flags) +{ + FIXME("(%s, %p, %p, %p, 0x%08lx): stub!\n", debugstr_w(file), guid, pdw1, pdw2, flags); + + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 5579d79bf48..ad3c049e218 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -2851,14 +2851,3 @@ BOOL WINAPI SymQueryInlineTrace(HANDLE hProcess, DWORD64 StartAddress, DWORD Sta } return TRUE; } - -/****************************************************************** - * SymSrvGetFileIndexInfo (DBGHELP.@) - * - */ -BOOL WINAPI SymSrvGetFileIndexInfo(const char *file, SYMSRV_INDEX_INFO* info, DWORD flags) -{ - FIXME("(%s, %p, 0x%08lx): stub!\n", debugstr_a(file), info, flags); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; -}
From: Eric Pouech epouech@codeweavers.com
Tests on PE image (header part, no debug directory). Add infrastructure to generate PE image (derived from kernel32/tests/loader.c).
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/dbghelp/tests/Makefile.in | 3 +- dlls/dbghelp/tests/path.c | 366 +++++++++++++++++++++++++++++++++ 2 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 dlls/dbghelp/tests/path.c
diff --git a/dlls/dbghelp/tests/Makefile.in b/dlls/dbghelp/tests/Makefile.in index 40ec97eca8c..171c0840179 100644 --- a/dlls/dbghelp/tests/Makefile.in +++ b/dlls/dbghelp/tests/Makefile.in @@ -2,4 +2,5 @@ TESTDLL = dbghelp.dll IMPORTS = dbghelp user32
C_SRCS = \ - dbghelp.c + dbghelp.c \ + path.c diff --git a/dlls/dbghelp/tests/path.c b/dlls/dbghelp/tests/path.c new file mode 100644 index 00000000000..7d95e1e6641 --- /dev/null +++ b/dlls/dbghelp/tests/path.c @@ -0,0 +1,366 @@ +/* + * Copyright 2023 Eric Pouech for Codeweavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <assert.h> +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windows.h" +#include "dbghelp.h" +#include "wine/test.h" +#include "winternl.h" +#include "winnt.h" + +static const IMAGE_DOS_HEADER dos_header = +{ + .e_magic = IMAGE_DOS_SIGNATURE, + .e_lfanew = sizeof(dos_header), +}; + +static const GUID null_guid; + +#define ALIGN(v, a) (((v - 1) / (a) + 1) * (a)) +#define NUM_OF(x, a) (((x) + (a) - 1) / (a)) + +#define check_write_file(a, b, c) _check_write_file(__LINE__, (a), (b), (c)) +static void _check_write_file(unsigned line, HANDLE file, const void* ptr, size_t len) +{ + DWORD written; + BOOL ret; + + ret = WriteFile(file, ptr, len, &written, NULL); + ok_(__FILE__, line)(ret, "WriteFile error %ld\n", GetLastError()); + ok_(__FILE__, line)(written == len, "Unexpected written len %lu (%Iu)\n", written, len); +} + +static void init_headers64(IMAGE_NT_HEADERS64* header, DWORD timestamp, DWORD size_of_image, DWORD charac) +{ + unsigned int j; + + header->Signature = IMAGE_NT_SIGNATURE; + header->FileHeader.Machine = IMAGE_FILE_MACHINE_AMD64; + header->FileHeader.NumberOfSections = 2; + header->FileHeader.TimeDateStamp = timestamp; + header->FileHeader.PointerToSymbolTable = 0; + header->FileHeader.NumberOfSymbols = 0; + header->FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER64); + header->FileHeader.Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL | IMAGE_FILE_LARGE_ADDRESS_AWARE | charac; + + header->OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC; + header->OptionalHeader.MajorLinkerVersion = 14; + header->OptionalHeader.MinorLinkerVersion = 35; + header->OptionalHeader.SizeOfCode = 0x200; + header->OptionalHeader.SizeOfInitializedData = 0x200; + header->OptionalHeader.SizeOfUninitializedData = 0; + header->OptionalHeader.AddressOfEntryPoint = 0x1000; + header->OptionalHeader.BaseOfCode = 0x1000; + header->OptionalHeader.ImageBase = 0x180000000; + header->OptionalHeader.SectionAlignment = 0x1000; + header->OptionalHeader.FileAlignment = 0x200; + header->OptionalHeader.MajorOperatingSystemVersion = 6; + header->OptionalHeader.MinorOperatingSystemVersion = 0; + header->OptionalHeader.MajorImageVersion = 0; + header->OptionalHeader.MinorImageVersion = 0; + header->OptionalHeader.MajorSubsystemVersion = 6; + header->OptionalHeader.MinorSubsystemVersion = 0; + header->OptionalHeader.Win32VersionValue = 0; + header->OptionalHeader.SizeOfImage = size_of_image; + header->OptionalHeader.SizeOfHeaders = ALIGN(sizeof(dos_header) + sizeof(IMAGE_NT_HEADERS64), header->OptionalHeader.FileAlignment); + header->OptionalHeader.CheckSum = 0; + header->OptionalHeader.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI; + header->OptionalHeader.DllCharacteristics = 0x160; + header->OptionalHeader.SizeOfStackReserve = 0x100000; + header->OptionalHeader.SizeOfStackCommit = 0x1000; + header->OptionalHeader.SizeOfHeapReserve = 0x100000; + header->OptionalHeader.SizeOfHeapCommit = 0x1000; + header->OptionalHeader.LoaderFlags = 0; + header->OptionalHeader.NumberOfRvaAndSizes = IMAGE_NUMBEROF_DIRECTORY_ENTRIES; + for (j = 0; j < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; j++) + header->OptionalHeader.DataDirectory[j].VirtualAddress = header->OptionalHeader.DataDirectory[j].Size = 0; +} + +static void init_headers32(IMAGE_NT_HEADERS32* header, DWORD timestamp, DWORD size_of_image, DWORD charac) +{ + unsigned int j; + + header->Signature = IMAGE_NT_SIGNATURE; + header->FileHeader.Machine = IMAGE_FILE_MACHINE_I386; + header->FileHeader.NumberOfSections = 2; + header->FileHeader.TimeDateStamp = timestamp; + header->FileHeader.PointerToSymbolTable = 0; + header->FileHeader.NumberOfSymbols = 0; + header->FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER32); + header->FileHeader.Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL | IMAGE_FILE_LARGE_ADDRESS_AWARE | charac; + + header->OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR32_MAGIC; + header->OptionalHeader.MajorLinkerVersion = 14; + header->OptionalHeader.MinorLinkerVersion = 35; + header->OptionalHeader.SizeOfCode = 0x200; + header->OptionalHeader.SizeOfInitializedData = 0x200; + header->OptionalHeader.SizeOfUninitializedData = 0; + header->OptionalHeader.AddressOfEntryPoint = 0x1000; + header->OptionalHeader.BaseOfCode = 0x1000; + header->OptionalHeader.BaseOfData = 0x2000; + header->OptionalHeader.ImageBase = 0x18000000; + header->OptionalHeader.SectionAlignment = 0x1000; + header->OptionalHeader.FileAlignment = 0x200; + header->OptionalHeader.MajorOperatingSystemVersion = 6; + header->OptionalHeader.MinorOperatingSystemVersion = 0; + header->OptionalHeader.MajorImageVersion = 0; + header->OptionalHeader.MinorImageVersion = 0; + header->OptionalHeader.MajorSubsystemVersion = 6; + header->OptionalHeader.MinorSubsystemVersion = 0; + header->OptionalHeader.Win32VersionValue = 0; + header->OptionalHeader.SizeOfImage = size_of_image; + header->OptionalHeader.SizeOfHeaders = ALIGN(sizeof(dos_header) + sizeof(IMAGE_NT_HEADERS32), header->OptionalHeader.FileAlignment); + header->OptionalHeader.CheckSum = 0; + header->OptionalHeader.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI; + header->OptionalHeader.DllCharacteristics = 0x160; + header->OptionalHeader.SizeOfStackReserve = 0x100000; + header->OptionalHeader.SizeOfStackCommit = 0x1000; + header->OptionalHeader.SizeOfHeapReserve = 0x100000; + header->OptionalHeader.SizeOfHeapCommit = 0x1000; + header->OptionalHeader.LoaderFlags = 0; + header->OptionalHeader.NumberOfRvaAndSizes = IMAGE_NUMBEROF_DIRECTORY_ENTRIES; + for (j = 0; j < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; j++) + header->OptionalHeader.DataDirectory[j].VirtualAddress = header->OptionalHeader.DataDirectory[j].Size = 0; +} + +/* reminder: the hdr*.FileHeader have the same layout between hdr32 and hdr64... + * only the optional part differs (except magic field). + */ +union nt_header +{ + IMAGE_NT_HEADERS64 nt_header64; + IMAGE_NT_HEADERS32 nt_header32; +}; +static inline IMAGE_FILE_HEADER* file_header(union nt_header* hdr) {return &hdr->nt_header32.FileHeader;} +static inline IMAGE_DATA_DIRECTORY* header_data_dir(union nt_header* hdr, unsigned d) +{ + switch (hdr->nt_header64.OptionalHeader.Magic) + { + case IMAGE_NT_OPTIONAL_HDR32_MAGIC: + return &hdr->nt_header32.OptionalHeader.DataDirectory[d]; + case IMAGE_NT_OPTIONAL_HDR64_MAGIC: + return &hdr->nt_header64.OptionalHeader.DataDirectory[d]; + default: + return NULL; + } +} + +/* a blob is IMAGE_DEBUG_DIRECTORY followed by the directory content. */ +struct debug_directory_blob +{ + IMAGE_DEBUG_DIRECTORY debug_directory; + char content[]; +}; + +static BOOL create_test_dll(union nt_header* hdr, unsigned size_hdr, + struct debug_directory_blob** blobs, unsigned num_blobs, const WCHAR* dll_name) +{ + IMAGE_SECTION_HEADER section; + IMAGE_DATA_DIRECTORY* data_dir; + HANDLE hfile; + char filler[0x200]; + DWORD where; + int i; + + hfile = CreateFileW(dll_name, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0); + ok(hfile != INVALID_HANDLE_VALUE, "Failed to create %ls err %lu\n", dll_name, GetLastError()); + if (hfile == INVALID_HANDLE_VALUE) return FALSE; + + file_header(hdr)->NumberOfSections = 1; + + strcpy((char*)section.Name, ".rdata"); + section.Misc.VirtualSize = 0x200; + section.VirtualAddress = 0x2000; + section.SizeOfRawData = 0x200; + section.PointerToRawData = 0x400; + section.PointerToRelocations = 0; + section.PointerToLinenumbers = 0; + section.NumberOfRelocations = 0; + section.NumberOfLinenumbers = 0; + section.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; + + data_dir = header_data_dir(hdr, IMAGE_FILE_DEBUG_DIRECTORY); + ok(data_dir != NULL, "Unexpected case\n"); + if (!data_dir) return FALSE; + + if (blobs && num_blobs) + { + where = section.PointerToRawData; + data_dir->Size = num_blobs * sizeof(IMAGE_DEBUG_DIRECTORY); + data_dir->VirtualAddress = section.VirtualAddress; + where += num_blobs * sizeof(IMAGE_DEBUG_DIRECTORY); + for (i = 0; i < num_blobs; i++) + { + blobs[i]->debug_directory.PointerToRawData = where; + where += blobs[i]->debug_directory.SizeOfData; + } + assert(section.SizeOfRawData >= where - section.PointerToRawData); + } + + check_write_file(hfile, &dos_header, sizeof(dos_header)); + + SetFilePointer(hfile, dos_header.e_lfanew, NULL, FILE_BEGIN); + check_write_file(hfile, hdr, size_hdr); + + check_write_file(hfile, §ion, sizeof(section)); + + memset(filler, 0, sizeof(filler)); + + SetFilePointer(hfile, 0x200, NULL, FILE_BEGIN); + check_write_file(hfile, filler, 0x200); + + SetFilePointer(hfile, 0x400, NULL, FILE_BEGIN); + if (blobs && num_blobs) + { + for (i = 0; i < num_blobs; i++) + check_write_file(hfile, &blobs[i]->debug_directory, sizeof(IMAGE_DEBUG_DIRECTORY)); + for (i = 0; i < num_blobs; i++) + check_write_file(hfile, blobs[i]->content, blobs[i]->debug_directory.SizeOfData); + } + check_write_file(hfile, filler, + section.PointerToRawData + section.Misc.VirtualSize - SetFilePointer(hfile, 0, NULL, FILE_CURRENT)); + + CloseHandle(hfile); + + return TRUE; +} + +static struct debug_directory_blob* make_empty_blob(void) +{ + struct debug_directory_blob* blob; + + blob = malloc(offsetof(struct debug_directory_blob, content[0])); + + blob->debug_directory.AddressOfRawData = 0; + blob->debug_directory.Characteristics = 0; + blob->debug_directory.MajorVersion = 0; + blob->debug_directory.MinorVersion = 0; + blob->debug_directory.PointerToRawData = 0; + blob->debug_directory.SizeOfData = 0; + blob->debug_directory.TimeDateStamp = 0; + blob->debug_directory.Type = IMAGE_DEBUG_TYPE_UNKNOWN; + + return blob; +} + +static void test_srvgetindexes(void) +{ + struct debug_directory_blob* blob_refs[1]; + struct debug_directory_blob* blob_used[4]; + SYMSRV_INDEX_INFOW ssii; + union nt_header hdr; + int i, j, bitness; + BOOL ret; + WCHAR filename[32]; + + static struct + { + /* input parameters */ + WORD charac; + short blobs[ARRAY_SIZE(blob_used)]; /* one of index in blob_refs, -1 to end */ + /* output parameters */ + DWORD age; + const GUID* guid; + DWORD sig; + WCHAR pdb_name[16]; + WCHAR dbg_name[16]; + BOOL in_error; + } + indexes[] = + { + /* error cases */ + {0, {-1, -1, -1}, .in_error = TRUE}, + {IMAGE_FILE_DEBUG_STRIPPED, { 0, -1, -1}, .in_error = TRUE}, + {IMAGE_FILE_DEBUG_STRIPPED, {-1, -1, -1}, .in_error = TRUE}, /* not 100% logical ! */ + /* success */ + {0, { 0, -1, -1}, 0, &null_guid, 0, L""}, + }; + + /* testing PE header with debug directory content */ + blob_refs[0] = make_empty_blob(); + + for (bitness = 32; bitness <= 64; bitness += 32) + { + for (i = 0; i < ARRAY_SIZE(indexes); i++) + { + winetest_push_context("%u-bit #%02u", bitness, i); + + /* create dll */ + for (j = 0; j < ARRAY_SIZE(indexes[i].blobs); j++) + { + if (indexes[i].blobs[j] == -1 || indexes[i].blobs[j] >= ARRAY_SIZE(blob_refs)) break; + blob_used[j] = blob_refs[indexes[i].blobs[j]]; + } + swprintf(filename, ARRAY_SIZE(filename), L".\winetest%02u.dll", i); + if (bitness == 32) + { + init_headers32(&hdr.nt_header32, 0x67890000 + i, 0x0030cafe, indexes[i].charac); + create_test_dll(&hdr, sizeof(hdr.nt_header32), blob_used, j, filename); + } + else + { + init_headers64(&hdr.nt_header64, 0x67890000 + i, 0x0030cafe, indexes[i].charac); + create_test_dll(&hdr, sizeof(hdr.nt_header64), blob_used, j, filename); + } + + memset(&ssii, 0xa5, sizeof(ssii)); + ssii.sizeofstruct = sizeof(ssii); + ret = SymSrvGetFileIndexInfoW(filename, &ssii, 0); + if (indexes[i].in_error) + { + ok(!ret, "SymSrvGetFileIndexInfo should have failed\n"); + todo_wine + ok(GetLastError() == ERROR_BAD_EXE_FORMAT, "Mismatch in GetLastError: %lu\n", GetLastError()); + } + else todo_wine + { + ok(ret, "SymSrvGetFileIndexInfo failed: %lu\n", GetLastError()); + + ok(ssii.age == indexes[i].age, "Mismatch in age: %lx\n", ssii.age); + ok(IsEqualGUID(&ssii.guid, indexes[i].guid), + "Mismatch in guid: guid=%s\n", wine_dbgstr_guid(&ssii.guid)); + + ok(ssii.sig == indexes[i].sig, "Mismatch in sig: %lx\n", ssii.sig); + ok(ssii.size == 0x0030cafe, "Mismatch in size: %lx <> %x\n", ssii.size, 0x0030cafe); + ok(ssii.stripped != 0xa5a5a5a5 && + (!ssii.stripped) == ((indexes[i].charac & IMAGE_FILE_DEBUG_STRIPPED) == 0), + "Mismatch in stripped: %x\n", ssii.stripped); + ok(ssii.timestamp == 0x67890000 + i, "Mismatch in timestamp: %lx\n", ssii.timestamp); + ok(!wcscmp(ssii.file, filename + 2), "Mismatch in file: '%ls'\n", ssii.file); + ok(!wcscmp(ssii.dbgfile, indexes[i].dbg_name), "Mismatch in dbgfile: '%ls'\n", ssii.dbgfile); + ok(!wcscmp(ssii.pdbfile, indexes[i].pdb_name), "Mismatch in pdbfile: '%ls'\n", ssii.pdbfile); + } + ret = DeleteFileW(filename); + ok(ret, "Couldn't delete test DLL file\n"); + + winetest_pop_context(); + } + } + for (i = 0; i < ARRAY_SIZE(blob_refs); i++) free(blob_refs[i]); +} + +START_TEST(path) +{ + /* cleanup env variables that affect dbghelp's behavior */ + SetEnvironmentVariableW(L"_NT_SYMBOL_PATH", NULL); + SetEnvironmentVariableW(L"_NT_ALT_SYMBOL_PATH", NULL); + + test_srvgetindexes(); +}
From: Eric Pouech epouech@codeweavers.com
Tests related to debug directories. Extending the PE image generator with debug directories.
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/dbghelp/tests/path.c | 124 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 120 insertions(+), 4 deletions(-)
diff --git a/dlls/dbghelp/tests/path.c b/dlls/dbghelp/tests/path.c index 7d95e1e6641..1e76886a70e 100644 --- a/dlls/dbghelp/tests/path.c +++ b/dlls/dbghelp/tests/path.c @@ -32,6 +32,8 @@ static const IMAGE_DOS_HEADER dos_header = };
static const GUID null_guid; +static const GUID guid1 = {0x1234abcd, 0x3456, 0x5678, {0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0x0a, 0x0b}}; +static const GUID guid2 = {0x1234abcd, 0x3456, 0x5678, {0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0x0a, 0x0c}};
#define ALIGN(v, a) (((v - 1) / (a) + 1) * (a)) #define NUM_OF(x, a) (((x) + (a) - 1) / (a)) @@ -260,9 +262,94 @@ static struct debug_directory_blob* make_empty_blob(void) return blob; }
+static struct debug_directory_blob* make_pdb_jg_blob(DWORD dd_timestamp, DWORD jg_timestamp, DWORD age, const char* name) +{ + struct debug_directory_blob* blob; + DWORD size; + + size = ALIGN(16 + strlen(name) + 1, 4); + blob = malloc(offsetof(struct debug_directory_blob, content[size])); + + blob->debug_directory.AddressOfRawData = 0; + blob->debug_directory.Characteristics = 0; + blob->debug_directory.MajorVersion = 0; + blob->debug_directory.MinorVersion = 0; + blob->debug_directory.PointerToRawData = 0; + blob->debug_directory.SizeOfData = size; + blob->debug_directory.TimeDateStamp = dd_timestamp; + blob->debug_directory.Type = IMAGE_DEBUG_TYPE_CODEVIEW; + + blob->content[0] = 'N'; /* signature */ + blob->content[1] = 'B'; + blob->content[2] = '1'; + blob->content[3] = '0'; + *(DWORD*)(blob->content + 4) = 0; /* file pos */ + *(DWORD*)(blob->content + 8) = jg_timestamp; /* timestamp */ + *(DWORD*)(blob->content + 12) = age; /* age */ + strcpy(blob->content + 16, name); /* name */ + + return blob; +} + +static struct debug_directory_blob* make_pdb_ds_blob(DWORD dd_timestamp, const GUID* guid, DWORD age, const char* name) +{ + struct debug_directory_blob* blob; + DWORD size; + + size = ALIGN(4 + sizeof(GUID) + 4 + strlen(name) + 1, 4); + blob = malloc(offsetof(struct debug_directory_blob, content[size])); + + blob->debug_directory.AddressOfRawData = 0; + blob->debug_directory.Characteristics = 0; + blob->debug_directory.MajorVersion = 0; + blob->debug_directory.MinorVersion = 0; + blob->debug_directory.PointerToRawData = 0; + blob->debug_directory.SizeOfData = size; + blob->debug_directory.TimeDateStamp = dd_timestamp; + blob->debug_directory.Type = IMAGE_DEBUG_TYPE_CODEVIEW; + + blob->content[0] = 'R'; /* signature */ + blob->content[1] = 'S'; + blob->content[2] = 'D'; + blob->content[3] = 'S'; + memcpy(blob->content + 4, guid, sizeof(*guid)); /* guid */ + *(DWORD*)(blob->content + 4 + sizeof(GUID)) = age; /* age */ + strcpy(blob->content + 4 + sizeof(GUID) + 4, name); /* name */ + + return blob; +} + +static struct debug_directory_blob* make_dbg_blob(DWORD dd_timestamp, const char* name) +{ + struct debug_directory_blob* blob; + DWORD size; + + size = ALIGN(sizeof(IMAGE_DEBUG_MISC) + strlen(name), 4); + blob = malloc(offsetof(struct debug_directory_blob, content[size])); + + blob->debug_directory.AddressOfRawData = 0; + blob->debug_directory.Characteristics = 0; + blob->debug_directory.MajorVersion = 0; + blob->debug_directory.MinorVersion = 0; + blob->debug_directory.PointerToRawData = 0; + blob->debug_directory.SizeOfData = size; + blob->debug_directory.TimeDateStamp = dd_timestamp; + blob->debug_directory.Type = IMAGE_DEBUG_TYPE_MISC; + + *(DWORD*)blob->content = IMAGE_DEBUG_MISC_EXENAME; /* DataType */ + *(DWORD*)(blob->content + 4) = size; /* Length */ + blob->content[8] = 0; /* Unicode */ + blob->content[9] = 0; /* Reserved */ + blob->content[10] = 0; /* Reserved */ + blob->content[11] = 0; /* Reserved */ + strcpy(blob->content + 12, name); /* Data */ + + return blob; +} + static void test_srvgetindexes(void) { - struct debug_directory_blob* blob_refs[1]; + struct debug_directory_blob* blob_refs[7]; struct debug_directory_blob* blob_used[4]; SYMSRV_INDEX_INFOW ssii; union nt_header hdr; @@ -286,15 +373,44 @@ static void test_srvgetindexes(void) indexes[] = { /* error cases */ - {0, {-1, -1, -1}, .in_error = TRUE}, +/* 0 */{0, {-1, -1, -1}, .in_error = TRUE}, {IMAGE_FILE_DEBUG_STRIPPED, { 0, -1, -1}, .in_error = TRUE}, + {IMAGE_FILE_DEBUG_STRIPPED, { 1, -1, -1}, .in_error = TRUE}, + {IMAGE_FILE_DEBUG_STRIPPED, { 2, -1, -1}, .in_error = TRUE}, {IMAGE_FILE_DEBUG_STRIPPED, {-1, -1, -1}, .in_error = TRUE}, /* not 100% logical ! */ /* success */ - {0, { 0, -1, -1}, 0, &null_guid, 0, L""}, - }; +/* 5 */{0, { 0, -1, -1}, 0, &null_guid, 0 }, + {0, { 1, -1, -1}, 123, &null_guid, 0xaaaabbbb, .pdb_name = L"pdbjg.pdb"}, + {0, { 2, -1, -1}, 124, &guid1, 0, .pdb_name = L"pdbds.pdb"}, + {IMAGE_FILE_DEBUG_STRIPPED, { 3, -1, -1}, 0, &null_guid, 0, .dbg_name = L".\ascii.dbg"}, + {0, { 3, -1, -1}, 0, &null_guid, 0, }, + /* PDB (JS & DS) records are cumulated (age from JS & guid from DS) */ +/* 10 */{0, { 1, 2, -1}, 124, &guid1, 0xaaaabbbb, .pdb_name = L"pdbds.pdb"}, + {0, { 2, 1, -1}, 123, &guid1, 0xaaaabbbb, .pdb_name = L"pdbjg.pdb"}, + /* cumulative records of same type */ + {0, { 1, 4, -1}, 125, &null_guid, 0xaaaacccc, .pdb_name = L"pdbjg2.pdb"}, + {0, { 2, 5, -1}, 126, &guid2, 0, .pdb_name = L"pdbds2.pdb"}, + {0, { 3, 6, -1}, 0, &null_guid, 0, }, +/* 15 */{IMAGE_FILE_DEBUG_STRIPPED, { 3, 6, -1}, 0, &null_guid, 0, .dbg_name = L".\ascii.dbg"}, + /* Mixing MISC with PDB (JG or DS) records */ + {IMAGE_FILE_DEBUG_STRIPPED, { 3, 1, -1}, 123, &null_guid, 0xaaaabbbb, .pdb_name = L"pdbjg.pdb", .dbg_name = L".\ascii.dbg"}, + {0, { 3, 1, -1}, 123, &null_guid, 0xaaaabbbb, .pdb_name = L"pdbjg.pdb"}, + {IMAGE_FILE_DEBUG_STRIPPED, { 3, 2, -1}, 124, &guid1, 0, .pdb_name = L"pdbds.pdb", .dbg_name = L".\ascii.dbg"}, + {0, { 3, 2, -1}, 124, &guid1, 0, .pdb_name = L"pdbds.pdb"}, +/* 20 */{IMAGE_FILE_DEBUG_STRIPPED, { 1, 3, -1}, 123, &null_guid, 0xaaaabbbb, .pdb_name = L"pdbjg.pdb", .dbg_name = L".\ascii.dbg"}, + {0, { 1, 3, -1}, 123, &null_guid, 0xaaaabbbb, .pdb_name = L"pdbjg.pdb"}, + {IMAGE_FILE_DEBUG_STRIPPED, { 2, 3, -1}, 124, &guid1, 0, .pdb_name = L"pdbds.pdb", .dbg_name = L".\ascii.dbg"}, + {0, { 2, 3, -1}, 124, &guid1, 0, .pdb_name = L"pdbds.pdb"}, + };
/* testing PE header with debug directory content */ blob_refs[0] = make_empty_blob(); + blob_refs[1] = make_pdb_jg_blob(0x67899876, 0xaaaabbbb, 123, "pdbjg.pdb"); + blob_refs[2] = make_pdb_ds_blob(0x67899877, &guid1, 124, "pdbds.pdb"); + blob_refs[3] = make_dbg_blob (0x67899878, ".\ascii.dbg"); /* doesn't seem to be returned without path */ + blob_refs[4] = make_pdb_jg_blob(0x67899879, 0xaaaacccc, 125, "pdbjg2.pdb"); + blob_refs[5] = make_pdb_ds_blob(0x67899880, &guid2, 126, "pdbds2.pdb"); + blob_refs[6] = make_dbg_blob (0x67899801, ".\ascii.dbg"); /* doesn't seem to be returned without path */
for (bitness = 32; bitness <= 64; bitness += 32) {
From: Eric Pouech eric.pouech@gmail.com
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/dbghelp/dbghelp_private.h | 5 +++- dlls/dbghelp/msc.c | 44 ++++++++++++++++++++++++++++++++++ dlls/dbghelp/path.c | 33 ++++++++++++++++++++++--- dlls/dbghelp/pe_module.c | 27 +++++++++++++++++++++ dlls/dbghelp/tests/path.c | 3 +-- 5 files changed, 106 insertions(+), 6 deletions(-)
diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index 7ba10447771..f91e2784b15 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -759,10 +759,12 @@ extern WCHAR* get_wine_loader_name(struct process *pcs) __WINE_DEALLOC(Hea
/* msc.c */ extern BOOL pe_load_debug_directory(const struct process* pcs, - struct module* module, + struct module* module, const BYTE* mapping, const IMAGE_SECTION_HEADER* sectp, DWORD nsect, const IMAGE_DEBUG_DIRECTORY* dbg, int nDbg) DECLSPEC_HIDDEN; +extern DWORD msc_get_file_indexinfo(void* image, const IMAGE_DEBUG_DIRECTORY* dbgdir, DWORD size, + SYMSRV_INDEX_INFOW* info) DECLSPEC_HIDDEN; extern BOOL pdb_fetch_file_info(const struct pdb_lookup* pdb_lookup, unsigned* matched) DECLSPEC_HIDDEN; struct pdb_cmd_pair { const char* name; @@ -793,6 +795,7 @@ extern struct module* extern BOOL pe_load_debug_info(const struct process* pcs, struct module* module) DECLSPEC_HIDDEN; extern const char* pe_map_directory(struct module* module, int dirno, DWORD* size) DECLSPEC_HIDDEN; +extern DWORD pe_get_file_indexinfo(void* image, DWORD size, SYMSRV_INDEX_INFOW* info) DECLSPEC_HIDDEN;
/* source.c */ extern unsigned source_new(struct module* module, const char* basedir, const char* source) DECLSPEC_HIDDEN; diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index c6dca22883a..f6b681cb4ae 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -4465,3 +4465,47 @@ typedef struct _FPO_DATA __ENDTRY return ret; } + +DWORD msc_get_file_indexinfo(void* image, const IMAGE_DEBUG_DIRECTORY* debug_dir, DWORD num_dir, SYMSRV_INDEX_INFOW* info) +{ + DWORD i; + unsigned num_misc_records = 0; + + info->age = 0; + memset(&info->guid, 0, sizeof(info->guid)); + info->sig = 0; + info->dbgfile[0] = L'\0'; + info->pdbfile[0] = L'\0'; + + for (i = 0; i < num_dir; i++) + { + if (debug_dir[i].Type == IMAGE_DEBUG_TYPE_CODEVIEW) + { + const CODEVIEW_PDB_DATA* data = (const CODEVIEW_PDB_DATA*)((char*)image + debug_dir[i].PointerToRawData); + const OMFSignatureRSDS* rsds_data = (const OMFSignatureRSDS*)data; + if (!memcmp(data->Signature, "NB10", 4)) + { + info->age = data->age; + info->sig = data->timestamp; + MultiByteToWideChar(CP_ACP, 0, data->name, -1, info->pdbfile, ARRAY_SIZE(info->pdbfile)); + } + if (!memcmp(rsds_data->Signature, "RSDS", 4)) + { + info->age = rsds_data->age; + info->guid = rsds_data->guid; + MultiByteToWideChar(CP_ACP, 0, rsds_data->name, -1, info->pdbfile, ARRAY_SIZE(info->pdbfile)); + } + } + else if (debug_dir[i].Type == IMAGE_DEBUG_TYPE_MISC && info->stripped) + { + const IMAGE_DEBUG_MISC* misc = (const IMAGE_DEBUG_MISC*) + ((const char*)image + debug_dir[i].PointerToRawData); + if (misc->Unicode) + wcscpy(info->dbgfile, (WCHAR*)misc->Data); + else + MultiByteToWideChar(CP_ACP, 0, (const char*)misc->Data, -1, info->dbgfile, ARRAY_SIZE(info->dbgfile)); + num_misc_records++; + } + } + return info->stripped && !num_misc_records ? ERROR_BAD_EXE_FORMAT : ERROR_SUCCESS; +} diff --git a/dlls/dbghelp/path.c b/dlls/dbghelp/path.c index 7c356e3a6ea..5c53e3f857d 100644 --- a/dlls/dbghelp/path.c +++ b/dlls/dbghelp/path.c @@ -881,15 +881,42 @@ BOOL WINAPI SymSrvGetFileIndexInfo(const char *file, SYMSRV_INDEX_INFO* info, DW */ BOOL WINAPI SymSrvGetFileIndexInfoW(const WCHAR *file, SYMSRV_INDEX_INFOW* info, DWORD flags) { - FIXME("(%s, %p, 0x%08lx): stub!\n", debugstr_w(file), info, flags); + HANDLE hFile, hMap = NULL; + void* image = NULL; + DWORD fsize, ret; + + TRACE("(%s, %p, 0x%08lx)\n", debugstr_w(file), info, flags);
if (info->sizeofstruct < sizeof(*info)) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + + if ((hFile = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE && + ((hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) != NULL) && + ((image = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)) != NULL)) + { + /* must handle PE, or .dbg or .pdb files. So each helper will return: + * - ERROR_SUCCESS: if the file format is recognized and index info filled, + * - ERROR_BAD_FORMAT: if the file doesn't match the expected format, + * - any other error: if the file has expected format, but internal errors + */ + fsize = GetFileSize(hFile, NULL); + /* try PE module first */ + ret = pe_get_file_indexinfo(image, fsize, info); + /* handle (ret == ERROR_BAD_FORMAT) with .dbg and .pdb format */ + } + else ret = ERROR_FILE_NOT_FOUND; + + if (image) UnmapViewOfFile(image); + if (hMap) CloseHandle(hMap); + if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); + + if (ret == ERROR_SUCCESS) wcscpy(info->file, file_name(file)); /* overflow? */ + SetLastError(ret); + return ret == ERROR_SUCCESS; }
/****************************************************************** diff --git a/dlls/dbghelp/pe_module.c b/dlls/dbghelp/pe_module.c index 7aeb1cf8901..3364498e0c7 100644 --- a/dlls/dbghelp/pe_module.c +++ b/dlls/dbghelp/pe_module.c @@ -962,3 +962,30 @@ PVOID WINAPI ImageDirectoryEntryToData( PVOID base, BOOLEAN image, USHORT dir, P { return ImageDirectoryEntryToDataEx( base, image, dir, size, NULL ); } + +DWORD pe_get_file_indexinfo(void* image, DWORD size, SYMSRV_INDEX_INFOW* info) +{ + const IMAGE_NT_HEADERS* nthdr; + const IMAGE_DEBUG_DIRECTORY* dbg; + ULONG dirsize; + + if (!(nthdr = RtlImageNtHeader(image))) return ERROR_BAD_FORMAT; + + dbg = RtlImageDirectoryEntryToData(image, FALSE, IMAGE_DIRECTORY_ENTRY_DEBUG, &dirsize); + if (!dbg || dirsize < sizeof(dbg)) return ERROR_BAD_EXE_FORMAT; + + /* fill in information from NT header */ + info->timestamp = nthdr->FileHeader.TimeDateStamp; + info->stripped = (nthdr->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED) != 0; + if (nthdr->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) + { + const IMAGE_NT_HEADERS64* nthdr64 = (const IMAGE_NT_HEADERS64*)nthdr; + info->size = nthdr64->OptionalHeader.SizeOfImage; + } + else if (nthdr->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + const IMAGE_NT_HEADERS32* nthdr32 = (const IMAGE_NT_HEADERS32*)nthdr; + info->size = nthdr32->OptionalHeader.SizeOfImage; + } + return msc_get_file_indexinfo(image, dbg, dirsize / sizeof(*dbg), info); +} diff --git a/dlls/dbghelp/tests/path.c b/dlls/dbghelp/tests/path.c index 1e76886a70e..7ba8ef52e0b 100644 --- a/dlls/dbghelp/tests/path.c +++ b/dlls/dbghelp/tests/path.c @@ -442,10 +442,9 @@ static void test_srvgetindexes(void) if (indexes[i].in_error) { ok(!ret, "SymSrvGetFileIndexInfo should have failed\n"); - todo_wine ok(GetLastError() == ERROR_BAD_EXE_FORMAT, "Mismatch in GetLastError: %lu\n", GetLastError()); } - else todo_wine + else { ok(ret, "SymSrvGetFileIndexInfo failed: %lu\n", GetLastError());