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(); +}