From dadd3c055fb74c6f209bf91eecf6686e59ab863f Mon Sep 17 00:00:00 2001 From: freeqaz <00free@gmail.com> Date: Thu, 12 Feb 2026 03:54:33 +0000 Subject: [PATCH 1/6] Add X360 linker support with fake mspdb PDB vtable stubs Implement fake PDB COM vtable objects so the MSVC X360 link.exe (16.00.11886.00) can run under wibo without wine. The linker requires a working PDB interface to write PE output; returning failure causes LNK1207 and output deletion. Key changes: - dll/mspdb.cpp: Runtime x86 __thiscall stub generator with 8 fake COM object types (PDB, DBI, Mod, TPI, GSI, Dbg, NameMap, Stream). Stubs are NULL-safe and handle linker's internal calling conventions. - Memory mapping: inode tracking, MAP_FIXED fallback, MAP_SHARED for ALL_ACCESS, heap registration, flushAllFileViews for crash safety. - File I/O: SetEndOfFile, GetFileType, SetFilePointerEx, additional CreateFile flags, FlushFileBuffers improvements. - Crash handler: SIGSEGV/SIGTRAP handler flushes memory-mapped files. - Module loading: builtin mspdb80.dll resolution. Output is byte-identical to wine (minus timestamps and PDB GUID). --- CMakeLists.txt | 2 + dll/kernel32/fileapi.cpp | 79 +++- dll/kernel32/fileapi.h | 2 + dll/kernel32/internal.h | 1 + dll/kernel32/memoryapi.cpp | 84 ++++ dll/kernel32/memoryapi.h | 3 + dll/kernel32/winbase.cpp | 76 ++++ dll/kernel32/winbase.h | 7 + dll/mspdb.cpp | 886 +++++++++++++++++++++++++++++++++++++ dll/mspdb.h | 33 ++ dll/rpcrt4.h | 2 - src/errors.h | 1 + src/files.cpp | 124 ++++++ src/files.h | 11 + src/main.cpp | 45 ++ src/modules.cpp | 7 +- 16 files changed, 1353 insertions(+), 10 deletions(-) create mode 100644 dll/mspdb.cpp create mode 100644 dll/mspdb.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a4a410a..219370a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,6 +189,7 @@ add_executable(wibo dll/kernel32/winnt.cpp dll/kernel32/wow64apiset.cpp dll/lmgr.cpp + dll/mspdb.cpp dll/mscoree.cpp dll/ntdll.cpp dll/ole32.cpp @@ -351,6 +352,7 @@ wibo_codegen_module(NAME version HEADERS dll/version.h) wibo_codegen_module(NAME rpcrt4 HEADERS dll/rpcrt4.h) wibo_codegen_module(NAME vcruntime HEADERS dll/vcruntime.h) wibo_codegen_module(NAME lmgr HEADERS dll/lmgr.h) +wibo_codegen_module(NAME mspdb HEADERS dll/mspdb.h) wibo_codegen_module(NAME ole32 HEADERS dll/ole32.h) wibo_codegen_module(NAME user32 HEADERS dll/user32.h) wibo_codegen_module(NAME ntdll HEADERS dll/ntdll.h) diff --git a/dll/kernel32/fileapi.cpp b/dll/kernel32/fileapi.cpp index a650a70..d514686 100644 --- a/dll/kernel32/fileapi.cpp +++ b/dll/kernel32/fileapi.cpp @@ -928,7 +928,22 @@ HANDLE WINAPI CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShar return INVALID_HANDLE_VALUE; } - // TODO: verify share mode against existing opens + // Compute access category for share violation checks + uint32_t accessCategory = 0; + if (dwDesiredAccess & (GENERIC_READ | FILE_READ_DATA | FILE_EXECUTE)) + accessCategory |= FILE_SHARE_READ; + if (dwDesiredAccess & (GENERIC_WRITE | FILE_WRITE_DATA | FILE_APPEND_DATA)) + accessCategory |= FILE_SHARE_WRITE; + if (dwDesiredAccess & DELETE) + accessCategory |= FILE_SHARE_DELETE; + + std::filesystem::path checkPath = files::canonicalPath(hostPath); + DEBUG_LOG(" share check: path=%s accessCat=%u shareMode=%u\n", checkPath.c_str(), accessCategory, dwShareMode); + if (files::checkShareViolation(checkPath, accessCategory, dwShareMode)) { + setLastError(ERROR_SHARING_VIOLATION); + DEBUG_LOG("-> ERROR_SHARING_VIOLATION\n"); + return INVALID_HANDLE_VALUE; + } bool allowCreate = false; bool truncateExisting = false; @@ -1036,9 +1051,9 @@ HANDLE WINAPI CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShar if (dwCreationDisposition == CREATE_NEW) { openFlags |= O_EXCL; } - if (truncateExisting && !isDirectory) { - openFlags |= O_TRUNC; - } + // Don't use O_TRUNC directly — we need to check for active memory mappings first + // (Windows prevents truncation of files with active mapped sections) + bool deferredTruncate = truncateExisting && !isDirectory; if (isDirectory) { openFlags |= O_RDONLY | O_DIRECTORY; @@ -1065,6 +1080,19 @@ HANDLE WINAPI CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShar return INVALID_HANDLE_VALUE; } + // Apply deferred truncation — skip if the file has active memory mappings (Windows behavior) + if (deferredTruncate) { + if (files::isFileMapped(fd)) { + DEBUG_LOG(" skipping truncation: file has active memory mapping\n"); + } else { + if (ftruncate(fd, 0) != 0) { + setLastErrorFromErrno(); + close(fd); + return INVALID_HANDLE_VALUE; + } + } + } + struct stat st{}; if (fstat(fd, &st) == 0 && S_ISDIR(st.st_mode)) { isDirectory = true; @@ -1084,7 +1112,12 @@ HANDLE WINAPI CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShar } fsObject->canonicalPath = std::move(canonicalPath); fsObject->shareAccess = shareMask; + fsObject->accessCategory = accessCategory; fsObject->deletePending = deleteOnClose; + if (accessCategory != 0 && !fsObject->canonicalPath.empty()) { + DEBUG_LOG(" share register: path=%s accessCat=%u share=%u\n", fsObject->canonicalPath.c_str(), accessCategory, shareMask); + files::registerOpenFile(fsObject->canonicalPath, accessCategory, shareMask); + } uint32_t handleFlags = 0; if (lpSecurityAttributes && lpSecurityAttributes->bInheritHandle) { @@ -1604,12 +1637,13 @@ DWORD WINAPI GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength, LPWSTR lp } std::string narrow = wideStringToString(lpFileName); + DEBUG_LOG("GetFullPathNameW input: %s\n", narrow.c_str()); FullPathInfo info; if (!computeFullPath(narrow, info)) { return 0; } - DEBUG_LOG(" -> %s\n", info.path.c_str()); + DEBUG_LOG("GetFullPathNameW result: %s\n", info.path.c_str()); auto widePath = stringToWideString(info.path.c_str()); const size_t wideLen = widePath.size(); @@ -1836,6 +1870,41 @@ HANDLE WINAPI FindFirstFileExA(LPCSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelI return findFirstFileCommon(std::string(lpFileName), findData); } +HANDLE WINAPI FindFirstFileExW(LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData, + FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("FindFirstFileExW(%p, %d, %p, %d, %p, 0x%x)\n", lpFileName, fInfoLevelId, + lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags); + if (!lpFindFileData) { + setLastError(ERROR_INVALID_PARAMETER); + return INVALID_HANDLE_VALUE; + } + if (!lpFileName) { + setLastError(ERROR_PATH_NOT_FOUND); + return INVALID_HANDLE_VALUE; + } + if (fInfoLevelId != FindExInfoStandard) { + setLastError(ERROR_INVALID_PARAMETER); + return INVALID_HANDLE_VALUE; + } + if (fSearchOp != FindExSearchNameMatch) { + setLastError(ERROR_INVALID_PARAMETER); + return INVALID_HANDLE_VALUE; + } + if (lpSearchFilter) { + setLastError(ERROR_INVALID_PARAMETER); + return INVALID_HANDLE_VALUE; + } + if (dwAdditionalFlags != 0) { + setLastError(ERROR_INVALID_PARAMETER); + return INVALID_HANDLE_VALUE; + } + + std::string narrowName = wideStringToString(lpFileName); + auto *findData = static_cast(lpFindFileData); + return findFirstFileCommon(narrowName, findData); +} + BOOL WINAPI FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData) { HOST_CONTEXT_GUARD(); DEBUG_LOG("FindNextFileA(%p, %p)\n", hFindFile, lpFindFileData); diff --git a/dll/kernel32/fileapi.h b/dll/kernel32/fileapi.h index a21afc1..a3cd1b7 100644 --- a/dll/kernel32/fileapi.h +++ b/dll/kernel32/fileapi.h @@ -66,6 +66,8 @@ HANDLE WINAPI FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileDat HANDLE WINAPI FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData); HANDLE WINAPI FindFirstFileExA(LPCSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags); +HANDLE WINAPI FindFirstFileExW(LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData, + FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags); BOOL WINAPI FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData); BOOL WINAPI FindNextFileW(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData); BOOL WINAPI FindClose(HANDLE hFindFile); diff --git a/dll/kernel32/internal.h b/dll/kernel32/internal.h index 947ec9d..e0fb22a 100644 --- a/dll/kernel32/internal.h +++ b/dll/kernel32/internal.h @@ -16,6 +16,7 @@ struct FsObject : ObjectBase { int fd = -1; std::filesystem::path canonicalPath; uint32_t shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE; + uint32_t accessCategory = 0; // FILE_SHARE_READ/WRITE/DELETE bits indicating what this handle does bool deletePending = false; bool closeOnDestroy = true; diff --git a/dll/kernel32/memoryapi.cpp b/dll/kernel32/memoryapi.cpp index 48a7907..d227949 100644 --- a/dll/kernel32/memoryapi.cpp +++ b/dll/kernel32/memoryapi.cpp @@ -3,6 +3,7 @@ #include "common.h" #include "context.h" #include "errors.h" +#include "files.h" #include "handles.h" #include "heap.h" #include "internal.h" @@ -17,6 +18,7 @@ #include #include #include +#include #include #include @@ -56,6 +58,9 @@ struct ViewInfo { DWORD allocationProtect = PAGE_NOACCESS; DWORD type = MEM_PRIVATE; bool managed = false; + bool trackedInode = false; + dev_t trackedDev = 0; + ino_t trackedIno = 0; }; std::map g_viewInfo; @@ -222,6 +227,15 @@ HANDLE WINAPI CreateFileMappingA(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappi return NO_HANDLE; } size = static_cast(fileSize); + } else { + // Windows extends the file to the mapping size if it's smaller. + off_t fileSize = lseek(dupFd, 0, SEEK_END); + if (fileSize >= 0 && static_cast(fileSize) < size) { + if (ftruncate(dupFd, static_cast(size)) != 0) { + setLastErrorFromErrno(); + return NO_HANDLE; + } + } } mapping->maxSize = size; } @@ -276,6 +290,10 @@ static LPVOID mapViewOfFileInternal(Pin mapping, DWORD dwDesiredA bool wantAllAccess = (dwDesiredAccess & FILE_MAP_ALL_ACCESS) == FILE_MAP_ALL_ACCESS; if (wantAllAccess) { wantWrite = true; + // FILE_MAP_ALL_ACCESS includes FILE_MAP_COPY as part of its bitmask, + // but on Windows WRITE takes precedence over COPY. Clear wantCopy so + // we use MAP_SHARED (write-through) instead of MAP_PRIVATE (COW). + wantCopy = false; } int prot = PROT_READ; if (mapping->protect == PAGE_READWRITE) { @@ -349,6 +367,17 @@ static LPVOID mapViewOfFileInternal(Pin mapping, DWORD dwDesiredA errno = 0; void *mapBase = mmap(requestedBase, mapLength, prot, mapFlags, mmapFd, alignedOffset); +#ifdef MAP_FIXED_NOREPLACE + // On Windows, MapViewOfFileEx can map over freed VirtualAlloc regions. + // VirtualFree(MEM_RELEASE) leaves PROT_NONE pages, so MAP_FIXED_NOREPLACE + // fails with EEXIST. Fall back to MAP_FIXED to match Windows behavior. + if (mapBase == MAP_FAILED && baseAddress && errno == EEXIST) { + DEBUG_LOG("mapViewOfFileInternal: MAP_FIXED_NOREPLACE failed, retrying with MAP_FIXED\n"); + mapFlags = (mapFlags & ~MAP_FIXED_NOREPLACE) | MAP_FIXED; + errno = 0; + mapBase = mmap(requestedBase, mapLength, prot, mapFlags, mmapFd, alignedOffset); + } +#endif if (mapBase == MAP_FAILED) { int err = errno; if (baseAddress && (err == ENOMEM || err == EEXIST || err == EINVAL || err == EPERM)) { @@ -386,8 +415,27 @@ static LPVOID mapViewOfFileInternal(Pin mapping, DWORD dwDesiredA view.allocationProtect = protect; view.type = MEM_MAPPED; view.managed = reservedMapping; + // Track inode for file-backed views so we can prevent truncation (Windows behavior) + DEBUG_LOG("mapViewOfFileInternal: mmapFd=%d anonymous=%d\n", mmapFd, view.owner ? view.owner->anonymous : -1); + if (mmapFd != -1) { + struct stat st {}; + int rc = fstat(mmapFd, &st); + DEBUG_LOG("mapViewOfFileInternal: fstat(%d) = %d dev=%lu ino=%lu\n", + mmapFd, rc, rc == 0 ? (unsigned long)st.st_dev : 0, rc == 0 ? (unsigned long)st.st_ino : 0); + if (rc == 0) { + view.trackedInode = true; + view.trackedDev = st.st_dev; + view.trackedIno = st.st_ino; + files::trackMappedFile(st.st_dev, st.st_ino); + } + } if (reservedMapping) { wibo::heap::registerViewRange(mapBase, mapLength, protect, view.protect); + } else if (baseAddress) { + // Caller-specified base: register with the heap manager so VirtualAlloc + // won't allocate overlapping regions (matching Windows address space behavior). + view.managed = true; + wibo::heap::registerViewRange(mapBase, mapLength, protect, view.protect); } { std::lock_guard guard(g_viewInfoMutex); @@ -438,6 +486,9 @@ BOOL WINAPI UnmapViewOfFile(LPCVOID lpBaseAddress) { void *base = reinterpret_cast(it->second.allocationBase); size_t length = it->second.allocationLength; bool managed = it->second.managed; + bool trackedInode = it->second.trackedInode; + dev_t trackedDev = it->second.trackedDev; + ino_t trackedIno = it->second.trackedIno; g_viewInfo.erase(it); lk.unlock(); if (length != 0) { @@ -446,6 +497,9 @@ BOOL WINAPI UnmapViewOfFile(LPCVOID lpBaseAddress) { if (managed) { wibo::heap::releaseViewRange(base); } + if (trackedInode) { + files::untrackMappedFile(trackedDev, trackedIno); + } return TRUE; } @@ -628,4 +682,34 @@ BOOL WINAPI SetProcessWorkingSetSize(HANDLE hProcess, SIZE_T dwMinimumWorkingSet return TRUE; } +void flushAllFileViews() { + std::lock_guard guard(g_viewInfoMutex); + for (const auto &entry : g_viewInfo) { + const ViewInfo &view = entry.second; + if (view.allocationLength == 0) { + continue; + } + void *base = reinterpret_cast(view.allocationBase); + size_t length = view.allocationLength; + // Check first bytes of the view for debugging + const unsigned char *p = static_cast(base); + bool allZero = true; + size_t checkLen = length < 256 ? length : 256; + for (size_t i = 0; i < checkLen; i++) { + if (p[i] != 0) { + allZero = false; + break; + } + } + DEBUG_LOG("flushAllFileViews: syncing view at %p length %zu first256=%s first4=%02x%02x%02x%02x\n", + base, length, allZero ? "ALL_ZERO" : "HAS_DATA", + checkLen > 0 ? p[0] : 0, checkLen > 1 ? p[1] : 0, + checkLen > 2 ? p[2] : 0, checkLen > 3 ? p[3] : 0); + int rc = msync(base, length, MS_SYNC); + if (rc != 0) { + DEBUG_LOG("flushAllFileViews: msync failed errno=%d\n", errno); + } + } +} + } // namespace kernel32 diff --git a/dll/kernel32/memoryapi.h b/dll/kernel32/memoryapi.h index 6faf134..d234a36 100644 --- a/dll/kernel32/memoryapi.h +++ b/dll/kernel32/memoryapi.h @@ -24,4 +24,7 @@ BOOL WINAPI GetProcessWorkingSetSize(HANDLE hProcess, PSIZE_T lpMinimumWorkingSe PSIZE_T lpMaximumWorkingSetSize); BOOL WINAPI SetProcessWorkingSetSize(HANDLE hProcess, SIZE_T dwMinimumWorkingSetSize, SIZE_T dwMaximumWorkingSetSize); +// Flush all active MAP_SHARED views to disk (call before _exit to ensure data persists) +void flushAllFileViews(); + } // namespace kernel32 diff --git a/dll/kernel32/winbase.cpp b/dll/kernel32/winbase.cpp index 91db64c..7b4730a 100644 --- a/dll/kernel32/winbase.cpp +++ b/dll/kernel32/winbase.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1101,6 +1102,7 @@ DWORD WINAPI GetCurrentDirectoryW(DWORD nBufferLength, LPWSTR lpBuffer) { if (!tryGetCurrentDirectoryPath(path)) { return 0; } + DEBUG_LOG("GetCurrentDirectoryW result: %s\n", path.c_str()); auto widePath = stringToWideString(path.c_str()); const DWORD required = static_cast(widePath.size()); if (nBufferLength == 0) { @@ -1308,4 +1310,78 @@ BOOL WINAPI GetDiskFreeSpaceExW(LPCWSTR lpDirectoryName, PULARGE_INTEGER lpFreeB lpTotalNumberOfBytes, lpTotalNumberOfFreeBytes); } +int WINAPI lstrcmpA(LPCSTR lpString1, LPCSTR lpString2) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("lstrcmpA(%s, %s)\n", lpString1 ? lpString1 : "(null)", lpString2 ? lpString2 : "(null)"); + if (!lpString1 || !lpString2) { + setLastError(ERROR_INVALID_PARAMETER); + return 0; + } + return std::strcmp(lpString1, lpString2); +} + +int WINAPI lstrcmpW(LPCWSTR lpString1, LPCWSTR lpString2) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("lstrcmpW\n"); + if (!lpString1 || !lpString2) { + setLastError(ERROR_INVALID_PARAMETER); + return 0; + } + const uint16_t *s1 = reinterpret_cast(lpString1); + const uint16_t *s2 = reinterpret_cast(lpString2); + while (*s1 && *s2) { + if (*s1 != *s2) + return (*s1 > *s2) ? 1 : -1; + ++s1; + ++s2; + } + if (*s1 == *s2) return 0; + return (*s1 > *s2) ? 1 : -1; +} + +int WINAPI lstrcmpiA(LPCSTR lpString1, LPCSTR lpString2) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("lstrcmpiA(%s, %s)\n", lpString1 ? lpString1 : "(null)", lpString2 ? lpString2 : "(null)"); + if (!lpString1 || !lpString2) { + setLastError(ERROR_INVALID_PARAMETER); + return 0; + } + return strcasecmp(lpString1, lpString2); +} + +int WINAPI lstrcmpiW(LPCWSTR lpString1, LPCWSTR lpString2) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("lstrcmpiW\n"); + if (!lpString1 || !lpString2) { + setLastError(ERROR_INVALID_PARAMETER); + return 0; + } + const uint16_t *s1 = reinterpret_cast(lpString1); + const uint16_t *s2 = reinterpret_cast(lpString2); + while (*s1 && *s2) { + uint16_t c1 = wcharToLower(*s1); + uint16_t c2 = wcharToLower(*s2); + if (c1 != c2) + return (c1 > c2) ? 1 : -1; + ++s1; + ++s2; + } + uint16_t c1 = wcharToLower(*s1); + uint16_t c2 = wcharToLower(*s2); + if (c1 == c2) return 0; + return (c1 > c2) ? 1 : -1; +} + +int WINAPI lstrlenA(LPCSTR lpString) { + HOST_CONTEXT_GUARD(); + if (!lpString) return 0; + return static_cast(std::strlen(lpString)); +} + +int WINAPI lstrlenW(LPCWSTR lpString) { + HOST_CONTEXT_GUARD(); + if (!lpString) return 0; + return static_cast(wstrlen(reinterpret_cast(lpString))); +} + } // namespace kernel32 diff --git a/dll/kernel32/winbase.h b/dll/kernel32/winbase.h index 0803910..8375b1b 100644 --- a/dll/kernel32/winbase.h +++ b/dll/kernel32/winbase.h @@ -132,4 +132,11 @@ BOOL WINAPI GetDiskFreeSpaceExA(LPCSTR lpDirectoryName, PULARGE_INTEGER lpFreeBy BOOL WINAPI GetDiskFreeSpaceExW(LPCWSTR lpDirectoryName, PULARGE_INTEGER lpFreeBytesAvailableToCaller, PULARGE_INTEGER lpTotalNumberOfBytes, PULARGE_INTEGER lpTotalNumberOfFreeBytes); +int WINAPI lstrcmpA(LPCSTR lpString1, LPCSTR lpString2); +int WINAPI lstrcmpW(LPCWSTR lpString1, LPCWSTR lpString2); +int WINAPI lstrcmpiA(LPCSTR lpString1, LPCSTR lpString2); +int WINAPI lstrcmpiW(LPCWSTR lpString1, LPCWSTR lpString2); +int WINAPI lstrlenA(LPCSTR lpString); +int WINAPI lstrlenW(LPCWSTR lpString); + } // namespace kernel32 diff --git a/dll/mspdb.cpp b/dll/mspdb.cpp new file mode 100644 index 0000000..7598af3 --- /dev/null +++ b/dll/mspdb.cpp @@ -0,0 +1,886 @@ +#include "mspdb.h" + +#include "common.h" +#include "context.h" +#include "kernel32/memoryapi.h" +#include "modules.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +// ============================================================================ +// Fake PDB vtable infrastructure +// +// The X360 linker requires a working PDB/XDB interface to complete PE writing. +// We provide fake COM-style objects with vtables full of x86 __thiscall stubs +// that accept all calls and return success/no-op values. The stubs are tiny +// snippets of x86 machine code generated at runtime into mmap'd executable +// memory. +// +// __thiscall convention: this in ECX, args on stack, callee pops args. +// Vtable dispatch: mov eax,[ecx]; call [eax+N*4] +// ============================================================================ + +// 32-bit COM-style object: first dword is vtable pointer +struct FakeVtObj { + uint32_t vptr; +}; + +// Fake objects (must be at 32-bit addresses - wibo is loaded at 0x70000000) +static FakeVtObj g_obj_pdb; +static FakeVtObj g_obj_dbi; +static FakeVtObj g_obj_mod; +static FakeVtObj g_obj_tpi; +static FakeVtObj g_obj_gsi; +static FakeVtObj g_obj_dbg; +static FakeVtObj g_obj_namemap; +static FakeVtObj g_obj_stream; + +// Vtables: arrays of 32-bit x86 function pointers +static uint32_t g_vt_pdb[64]; +static uint32_t g_vt_dbi[64]; +static uint32_t g_vt_mod[64]; +static uint32_t g_vt_tpi[32]; +static uint32_t g_vt_gsi[16]; +static uint32_t g_vt_dbg[16]; +static uint32_t g_vt_namemap[20]; +static uint32_t g_vt_stream[12]; + +// --- x86 machine code generation --- + +static uint8_t *g_code_page; +static size_t g_code_pos; +static constexpr size_t CODE_PAGE_SIZE = 16384; + +static uint8_t *codeAlloc(size_t n) { + assert(g_code_page && g_code_pos + n <= CODE_PAGE_SIZE); + uint8_t *p = g_code_page + g_code_pos; + g_code_pos += n; + return p; +} + +static uint32_t addr32(void *p) { + return (uint32_t)(uintptr_t)p; +} + +// Generate: mov eax, ; ret +// For __thiscall methods that return a constant value. +static uint32_t genRet(uint32_t retval, int nargs) { + uint16_t pop = nargs * 4; + size_t sz = 5 + (pop ? 3 : 1); + uint8_t *s = codeAlloc(sz); + uint8_t *p = s; + *p++ = 0xB8; + memcpy(p, &retval, 4); + p += 4; // mov eax, retval + if (pop) { + *p++ = 0xC2; + memcpy(p, &pop, 2); + } // ret pop + else { + *p++ = 0xC3; + } // ret + return addr32(s); +} + +// Generate: ret (void return) +static uint32_t genVoid(int nargs) { + uint16_t pop = nargs * 4; + size_t sz = pop ? 3 : 1; + uint8_t *s = codeAlloc(sz); + uint8_t *p = s; + if (pop) { + *p++ = 0xC2; + memcpy(p, &pop, 2); + } else { + *p++ = 0xC3; + } + return addr32(s); +} + +// Generate: mov eax,[esp+off]; test eax,eax; jz skip; mov dword [eax],val; skip: mov eax,1; ret pop +// For __thiscall methods that write a 32-bit value to an output pointer param. +// outArg: 0-based index of the output param among stack args (not counting this) +// NULL-safe: skips the write if the output pointer is NULL. +static uint32_t genOut(int outArg, uint32_t val, int nargs) { + uint8_t off = (uint8_t)((outArg + 1) * 4); // +1 for return address + uint16_t pop = nargs * 4; + uint8_t *s = codeAlloc(28); + uint8_t *p = s; + // mov eax, [esp+off] + *p++ = 0x8B; + *p++ = 0x44; + *p++ = 0x24; + *p++ = off; + // test eax, eax + *p++ = 0x85; + *p++ = 0xC0; + // jz +6 (skip the mov dword ptr [eax], val) + *p++ = 0x74; + *p++ = 0x06; + // mov dword ptr [eax], val + *p++ = 0xC7; + *p++ = 0x00; + memcpy(p, &val, 4); + p += 4; + // mov eax, 1 + *p++ = 0xB8; + *p++ = 0x01; + *p++ = 0x00; + *p++ = 0x00; + *p++ = 0x00; + // ret pop + if (pop) { + *p++ = 0xC2; + memcpy(p, &pop, 2); + } else { + *p++ = 0xC3; + } + return addr32(s); +} + +// Generate: mov eax,[esp+off]; mov byte [eax],0; xor eax,eax; ret pop +// For QueryLastError-style methods: write empty string to buffer, return 0. +static uint32_t genClearBuf(int bufArg, int nargs) { + uint8_t off = (uint8_t)((bufArg + 1) * 4); + uint16_t pop = nargs * 4; + uint8_t *s = codeAlloc(16); + uint8_t *p = s; + // mov eax, [esp+off] + *p++ = 0x8B; + *p++ = 0x44; + *p++ = 0x24; + *p++ = off; + // mov byte ptr [eax], 0 + *p++ = 0xC6; + *p++ = 0x00; + *p++ = 0x00; + // xor eax, eax + *p++ = 0x31; + *p++ = 0xC0; + // ret pop + if (pop) { + *p++ = 0xC2; + memcpy(p, &pop, 2); + } else { + *p++ = 0xC3; + } + return addr32(s); +} + +// Generate: int3 (trap for unimplemented methods, with index in AL for debugging) +static uint32_t genTrap(uint8_t idx) { + uint8_t *s = codeAlloc(4); + s[0] = 0xB0; + s[1] = idx; // mov al, idx + s[2] = 0xCC; // int3 + s[3] = 0xC3; // ret (fallthrough safety) + return addr32(s); +} + +// PDB version constants (VS2010 era - matches X360 linker) +static constexpr uint32_t PDB_INTV = 20091201; +static constexpr uint32_t PDB_IMPV = 20091201; + +static bool g_vtables_ready = false; + +static void initVtables() { + if (g_vtables_ready) + return; + g_vtables_ready = true; + + // Allocate executable page for x86 stubs (MAP_32BIT = first 2GB) + g_code_page = (uint8_t *)mmap(nullptr, CODE_PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0); + if (g_code_page == MAP_FAILED) { + // Fallback without MAP_32BIT + g_code_page = (uint8_t *)mmap(nullptr, CODE_PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + } + assert(g_code_page != MAP_FAILED); + assert((uintptr_t)g_code_page < 0xFFFFFFFFULL); // must be 32-bit addressable + g_code_pos = 0; + + DEBUG_LOG("mspdb: code page at %p, objects: PDB=%p DBI=%p Mod=%p TPI=%p GSI=%p\n", g_code_page, &g_obj_pdb, + &g_obj_dbi, &g_obj_mod, &g_obj_tpi, &g_obj_gsi); + + // Shorthand addresses for fake sub-objects + uint32_t pPDB = addr32(&g_obj_pdb); + uint32_t pDBI = addr32(&g_obj_dbi); + uint32_t pMod = addr32(&g_obj_mod); + uint32_t pTPI = addr32(&g_obj_tpi); + uint32_t pGSI = addr32(&g_obj_gsi); + uint32_t pDbg = addr32(&g_obj_dbg); + uint32_t pNM = addr32(&g_obj_namemap); + uint32_t pStr = addr32(&g_obj_stream); + + // --- PDB vtable (from microsoft-pdb PDB interface) --- + // Fill with traps first + for (int i = 0; i < 64; i++) + g_vt_pdb[i] = genTrap((uint8_t)i); + // 0: QueryInterfaceVersion() -> INTV + g_vt_pdb[0] = genRet(PDB_INTV, 0); + // 1: QueryImplementationVersion() -> IMPV + g_vt_pdb[1] = genRet(PDB_IMPV, 0); + // 2: QueryLastError(char szError[]) -> EC + g_vt_pdb[2] = genClearBuf(0, 1); + // 3: QueryPDBName(char szPDB[]) -> char* (return the buffer with empty string) + g_vt_pdb[3] = genClearBuf(0, 1); // sets buf[0]=0, returns 0 (NULL) but that's OK + // 4: QuerySignature() -> SIG + g_vt_pdb[4] = genRet(0, 0); + // 5: QueryAge() -> AGE + g_vt_pdb[5] = genRet(1, 0); + // 6: CreateDBI(szTarget, DBI** ppdbi) -> BOOL [2 args, out=arg1] + g_vt_pdb[6] = genOut(1, pDBI, 2); + // 7: OpenDBI(szTarget, szMode, DBI** ppdbi) -> BOOL [3 args, out=arg2] + g_vt_pdb[7] = genOut(2, pDBI, 3); + // 8: OpenTpi(szMode, TPI** pptpi) -> BOOL [2 args, out=arg1] + g_vt_pdb[8] = genOut(1, pTPI, 2); + // 9: OpenIpi(szMode, TPI** pptpi) -> BOOL [2 args, out=arg1] + g_vt_pdb[9] = genOut(1, pTPI, 2); + // 10: Commit() -> BOOL + g_vt_pdb[10] = genRet(1, 0); + // 11: Close() -> BOOL + g_vt_pdb[11] = genRet(1, 0); + // 12: OpenStream(szStream, Stream** ppstream) -> BOOL [2 args, out=arg1] + g_vt_pdb[12] = genOut(1, pStr, 2); + // 13: GetEnumStreamNameMap(Enum**) -> BOOL + g_vt_pdb[13] = genRet(0, 1); // return FALSE - no enumerator + // 14: GetRawBytes(pfn) -> BOOL + g_vt_pdb[14] = genRet(0, 1); + // 15: QueryPdbImplementationVersion() -> IMPV + g_vt_pdb[15] = genRet(PDB_IMPV, 0); + // 16: OpenDBIEx(szTarget, szMode, DBI**, pfn) -> BOOL [4 args, out=arg2] + g_vt_pdb[16] = genOut(2, pDBI, 4); + // 17: CopyTo(szDst, filter, reserved) -> BOOL + g_vt_pdb[17] = genRet(1, 3); + // 18: OpenSrc(Src**) -> BOOL + g_vt_pdb[18] = genRet(0, 1); // return FALSE + // 19: QueryLastErrorExW(wszError, cchMax) -> EC + g_vt_pdb[19] = genClearBuf(0, 2); + // 20: QueryPDBNameExW(wszPDB, cchMax) -> wchar_t* + // NOTE: linker's helper at 0x42EA4B only pushes 1 arg before calling this + g_vt_pdb[20] = genClearBuf(0, 1); + // 21: QuerySignature2(PSIG70) -> BOOL + g_vt_pdb[21] = genRet(1, 1); + // 22: CopyToW(szDst, filter, reserved) -> BOOL + g_vt_pdb[22] = genRet(1, 3); + // 23: fIsSZPDB() -> BOOL + g_vt_pdb[23] = genRet(1, 0); + // 24: OpenStreamW(szStream, Stream**) -> BOOL [2 args, out=arg1] + g_vt_pdb[24] = genOut(1, pStr, 2); + // 25: CopyToW2(szDst, filter, pfn, pvCtx) -> BOOL + g_vt_pdb[25] = genRet(1, 4); + // 26: OpenStreamEx(szStream, szMode, Stream**) -> BOOL [3 args, out=arg2] + g_vt_pdb[26] = genOut(2, pStr, 3); + // 27: RegisterPDBMapping(from, to) -> BOOL + g_vt_pdb[27] = genRet(1, 2); + // 28: EnablePrefetching() -> BOOL + g_vt_pdb[28] = genRet(1, 0); + // 29: FLazy() -> BOOL + g_vt_pdb[29] = genRet(0, 0); + // 30: FMinimal() -> BOOL + g_vt_pdb[30] = genRet(0, 0); + // 31: ResetGUID(pb, cb) -> BOOL + g_vt_pdb[31] = genRet(1, 2); + + // --- DBI vtable --- + for (int i = 0; i < 64; i++) + g_vt_dbi[i] = genTrap((uint8_t)i); + // 0: QueryImplementationVersion() -> IMPV + g_vt_dbi[0] = genRet(PDB_IMPV, 0); + // 1: QueryInterfaceVersion() -> INTV + g_vt_dbi[1] = genRet(PDB_INTV, 0); + // 2: OpenMod(szModule, szFile, Mod**) [3 args, out=arg2] + g_vt_dbi[2] = genOut(2, pMod, 3); + // 3: DeleteMod(szModule) -> BOOL + g_vt_dbi[3] = genRet(1, 1); + // 4: QueryNextMod(pmod, ppmodNext) -> BOOL [2 args, out=arg1 -> NULL] + g_vt_dbi[4] = genOut(1, 0, 2); // *ppmodNext = NULL (end of list) + // 5: OpenGlobals(GSI**) [1 arg, out=arg0] + g_vt_dbi[5] = genOut(0, pGSI, 1); + // 6: OpenPublics(GSI**) [1 arg, out=arg0] + g_vt_dbi[6] = genOut(0, pGSI, 1); + // 7: AddSec(isect, flags, off, cb) -> BOOL + g_vt_dbi[7] = genRet(1, 4); + // 8: QueryModFromAddr(6 args) -> BOOL + g_vt_dbi[8] = genRet(0, 6); + // 9: QuerySecMap(pb, pcb) -> BOOL [2 args, out=arg1 -> 0] + g_vt_dbi[9] = genOut(1, 0, 2); + // 10: QueryFileInfo(pb, pcb) -> BOOL [2 args, out=arg1 -> 0] + g_vt_dbi[10] = genOut(1, 0, 2); + // 11: DumpMods() -> void + g_vt_dbi[11] = genVoid(0); + // 12: DumpSecContribs() -> void + g_vt_dbi[12] = genVoid(0); + // 13: DumpSecMap() -> void + g_vt_dbi[13] = genVoid(0); + // 14: Close() -> BOOL + g_vt_dbi[14] = genRet(1, 0); + // 15: AddThunkMap(7 args) -> BOOL + g_vt_dbi[15] = genRet(1, 7); + // 16: AddPublic(szPublic, isect, off) -> BOOL + g_vt_dbi[16] = genRet(1, 3); + // 17: getEnumContrib(Enum**) -> BOOL + g_vt_dbi[17] = genRet(0, 1); + // 18: QueryTypeServer(itsm, TPI**) -> BOOL + g_vt_dbi[18] = genRet(0, 2); + // 19: QueryItsmForTi(ti, pitsm) -> BOOL + g_vt_dbi[19] = genRet(0, 2); + // 20: QueryNextItsm(itsm, inext) -> BOOL + g_vt_dbi[20] = genRet(0, 2); + // 21: QueryLazyTypes() -> BOOL + g_vt_dbi[21] = genRet(0, 0); + // 22: SetLazyTypes(fLazy) -> BOOL + g_vt_dbi[22] = genRet(1, 1); + // 23: FindTypeServers(pec, szError) -> BOOL + g_vt_dbi[23] = genRet(1, 2); + // 24: DumpTypeServers() -> void + g_vt_dbi[24] = genVoid(0); + // 25: OpenDbg(dbgtype, Dbg**) [2 args, out=arg1] + g_vt_dbi[25] = genOut(1, pDbg, 2); + // 26: QueryDbgTypes(pdbgtype, pcDbgtype) -> BOOL [2 args, out=arg1 -> 0] + g_vt_dbi[26] = genOut(1, 0, 2); + // 27: QueryAddrForSec(6 args) -> BOOL + g_vt_dbi[27] = genRet(0, 6); + // 28: QueryAddrForSecEx(7 args) -> BOOL + g_vt_dbi[28] = genRet(0, 7); + // 29: QuerySupportsEC() -> BOOL + g_vt_dbi[29] = genRet(0, 0); + // 30: QueryPdb(PDB**) [1 arg, out=arg0] + g_vt_dbi[30] = genOut(0, pPDB, 1); + // 31: AddLinkInfo(pli) -> BOOL + g_vt_dbi[31] = genRet(1, 1); + // 32: QueryLinkInfo(pli, pcb) -> BOOL [2 args, out=arg1 -> 0] + g_vt_dbi[32] = genOut(1, 0, 2); + // 33: QueryAge() const -> AGE + g_vt_dbi[33] = genRet(1, 0); + // 34: QueryHeader() const -> void* + g_vt_dbi[34] = genRet(0, 0); + // 35: FlushTypeServers() -> void + g_vt_dbi[35] = genVoid(0); + // 36: QueryTypeServerByPdb(szPdb, pitsm) -> BOOL + g_vt_dbi[36] = genRet(0, 2); + // 37: OpenModW(szModule, szFile, Mod**) [3 args, out=arg2] + g_vt_dbi[37] = genOut(2, pMod, 3); + // 38: DeleteModW(szModule) -> BOOL + g_vt_dbi[38] = genRet(1, 1); + // 39: AddPublicW(szPublic, isect, off, cvpsf) -> BOOL + g_vt_dbi[39] = genRet(1, 4); + // 40: QueryTypeServerByPdbW(szPdb, pitsm) -> BOOL + g_vt_dbi[40] = genRet(0, 2); + // 41: AddLinkInfoW(pli) -> BOOL + g_vt_dbi[41] = genRet(1, 1); + // 42: AddPublic2(szPublic, isect, off, cvpsf) -> BOOL + g_vt_dbi[42] = genRet(1, 4); + // 43: QueryMachineType() const -> USHORT + g_vt_dbi[43] = genRet(0, 0); + // 44: SetMachineType(wMachine) -> void + g_vt_dbi[44] = genVoid(1); + // 45: RemoveDataForRva(rva, cb) -> void + g_vt_dbi[45] = genVoid(2); + // 46: FStripped() -> BOOL + g_vt_dbi[46] = genRet(0, 0); + // 47: QueryModFromAddr2(7 args) -> BOOL + g_vt_dbi[47] = genRet(0, 7); + // 48: QueryNoOfMods(pcMods) -> BOOL [1 arg, out=arg0 -> 0] + g_vt_dbi[48] = genOut(0, 0, 1); + // 49: QueryMods(ppmodNext, cMods) -> BOOL + g_vt_dbi[49] = genRet(1, 2); + // 50: QueryImodFromAddr(7 args) -> BOOL + g_vt_dbi[50] = genRet(0, 7); + // 51: OpenModFromImod(imod, Mod**) [2 args] + g_vt_dbi[51] = genRet(0, 2); + // 52: QueryHeader2(cb, pb, pcbOut) -> BOOL [3 args, out=arg2 -> 0] + g_vt_dbi[52] = genOut(2, 0, 3); + // 53: FAddSourceMappingItem(3 args) -> BOOL + g_vt_dbi[53] = genRet(1, 3); + // 54: FSetPfnNotePdbUsed(pvCtx, pfn) -> BOOL + g_vt_dbi[54] = genRet(1, 2); + // 55: FCTypes() -> BOOL + g_vt_dbi[55] = genRet(0, 0); + // 56: QueryFileInfo2(pb, pcb) -> BOOL [2 args, out=arg1 -> 0] + g_vt_dbi[56] = genOut(1, 0, 2); + // 57: FSetPfnQueryCallback(pvCtx, pfn) -> BOOL + g_vt_dbi[57] = genRet(1, 2); + // 58: FSetPfnNoteTypeMismatch(pvCtx, pfn) -> BOOL + g_vt_dbi[58] = genRet(1, 2); + // 59: FSetPfnTmdTypeFilter(pvCtx, pfn) -> BOOL + g_vt_dbi[59] = genRet(1, 2); + // 60: RemovePublic(szPublic) -> BOOL + g_vt_dbi[60] = genRet(1, 1); + // 61: getEnumContrib2(Enum**) -> BOOL + g_vt_dbi[61] = genRet(0, 1); + // 62: QueryModFromAddrEx(8 args) -> BOOL + g_vt_dbi[62] = genRet(0, 8); + // 63: QueryImodFromAddrEx(8 args) -> BOOL + g_vt_dbi[63] = genRet(0, 8); + + // --- Mod vtable --- + for (int i = 0; i < 64; i++) + g_vt_mod[i] = genTrap((uint8_t)i); + // 0: QueryInterfaceVersion() -> INTV + g_vt_mod[0] = genRet(PDB_INTV, 0); + // 1: QueryImplementationVersion() -> IMPV + g_vt_mod[1] = genRet(PDB_IMPV, 0); + // 2: AddTypes(pbTypes, cb) -> BOOL + g_vt_mod[2] = genRet(1, 2); + // 3: AddSymbols(pbSym, cb) -> BOOL + g_vt_mod[3] = genRet(1, 2); + // 4: AddPublic(szPublic, isect, off) -> BOOL + g_vt_mod[4] = genRet(1, 3); + // 5: AddLines(szSrc, isect, offCon, cbCon, doff, lineStart, pbCoff, cbCoff) -> BOOL + g_vt_mod[5] = genRet(1, 8); + // 6: AddSecContrib(isect, off, cb, dwCharacteristics) -> BOOL + g_vt_mod[6] = genRet(1, 4); + // 7: QueryCBName(pcb) -> BOOL [1 arg, out=arg0 -> 0] + g_vt_mod[7] = genOut(0, 0, 1); + // 8: QueryName(szName, pcb) -> BOOL + g_vt_mod[8] = genClearBuf(0, 2); + // 9: QuerySymbols(pbSym, pcb) -> BOOL [2 args, out=arg1 -> 0] + g_vt_mod[9] = genOut(1, 0, 2); + // 10: QueryLines(pbLines, pcb) -> BOOL [2 args, out=arg1 -> 0] + g_vt_mod[10] = genOut(1, 0, 2); + // 11: SetPvClient(pvClient) -> BOOL + g_vt_mod[11] = genRet(1, 1); + // 12: GetPvClient(ppvClient) -> BOOL + g_vt_mod[12] = genOut(0, 0, 1); + // 13: QueryFirstCodeSecContrib(4 args) -> BOOL + g_vt_mod[13] = genRet(0, 4); + // 14: QueryImod(pimod) -> BOOL [1 arg, out=arg0 -> 0] + g_vt_mod[14] = genOut(0, 0, 1); + // 15: QueryDBI(ppdbi) -> BOOL [1 arg, out=arg0] + g_vt_mod[15] = genOut(0, pDBI, 1); + // 16: Close() -> BOOL + g_vt_mod[16] = genRet(1, 0); + // 17: QueryCBFile(pcb) -> BOOL [1 arg, out=arg0 -> 0] + g_vt_mod[17] = genOut(0, 0, 1); + // 18: QueryFile(szFile, pcb) -> BOOL + g_vt_mod[18] = genClearBuf(0, 2); + // 19: QueryTpi(pptpi) -> BOOL [1 arg, out=arg0] + g_vt_mod[19] = genOut(0, pTPI, 1); + // 20: AddSecContribEx(isect, off, cb, dwChar, dwDataCrc, dwRelocCrc) -> BOOL + g_vt_mod[20] = genRet(1, 6); + // 21: QueryItsm(pitsm) -> BOOL [1 arg, out=arg0 -> 0] + g_vt_mod[21] = genOut(0, 0, 1); + // 22: QuerySrcFile(szFile, pcb) -> BOOL + g_vt_mod[22] = genClearBuf(0, 2); + // 23: QuerySupportsEC() -> BOOL + g_vt_mod[23] = genRet(0, 0); + // 24: QueryPdbFile(szFile, pcb) -> BOOL + g_vt_mod[24] = genClearBuf(0, 2); + // 25: ReplaceLines(pbLines, cb) -> BOOL + g_vt_mod[25] = genRet(1, 2); + // 26: GetEnumLines(EnumLines**) -> bool + g_vt_mod[26] = genRet(0, 1); + // 27: QueryLineFlags(pdwFlags) -> bool + g_vt_mod[27] = genRet(0, 1); + // 28: QueryFileNameInfo(5 args) -> bool + g_vt_mod[28] = genRet(0, 5); + // 29: AddPublicW(szPublic, isect, off, cvpsf) -> BOOL + g_vt_mod[29] = genRet(1, 4); + // 30: AddLinesW(szSrc, isect, offCon, cbCon, doff, lineStart, pbCoff, cbCoff) -> BOOL + g_vt_mod[30] = genRet(1, 8); + // 31: QueryNameW(szName, pcb) -> BOOL + g_vt_mod[31] = genClearBuf(0, 2); + // 32: QueryFileW(szFile, pcb) -> BOOL + g_vt_mod[32] = genClearBuf(0, 2); + // 33: QuerySrcFileW(szFile, pcb) -> BOOL + g_vt_mod[33] = genClearBuf(0, 2); + // 34: QueryPdbFileW(szFile, pcb) -> BOOL + g_vt_mod[34] = genClearBuf(0, 2); + // 35: AddPublic2(szPublic, isect, off, cvpsf) -> BOOL + g_vt_mod[35] = genRet(1, 4); + // 36: InsertLines(pbLines, cb) -> BOOL + g_vt_mod[36] = genRet(1, 2); + // 37: QueryLines2(cbLines, pbLines, pcbLines) -> BOOL [3 args, out=arg2 -> 0] + g_vt_mod[37] = genOut(2, 0, 3); + // 38-49: Various Query/Add methods -> return 0 or 1 + for (int i = 38; i <= 49; i++) + g_vt_mod[i] = genRet(0, 3); // safe default: return FALSE, pop 3 args + + // --- TPI vtable --- + for (int i = 0; i < 32; i++) + g_vt_tpi[i] = genTrap((uint8_t)i); + // 0: QueryInterfaceVersion() -> INTV + g_vt_tpi[0] = genRet(PDB_INTV, 0); + // 1: QueryImplementationVersion() -> IMPV + g_vt_tpi[1] = genRet(PDB_IMPV, 0); + // 2: QueryTi16ForCVRecord(pb, pti) -> BOOL + g_vt_tpi[2] = genRet(0, 2); + // 3: QueryCVRecordForTi16(ti, pb, pcb) -> BOOL + g_vt_tpi[3] = genRet(0, 3); + // 4: QueryPbCVRecordForTi16(ti, ppb) -> BOOL + g_vt_tpi[4] = genRet(0, 2); + // 5: QueryTi16Min() -> TI16 + g_vt_tpi[5] = genRet(0, 0); + // 6: QueryTi16Mac() -> TI16 + g_vt_tpi[6] = genRet(0, 0); + // 7: QueryCb() -> long + g_vt_tpi[7] = genRet(0, 0); + // 8: Close() -> BOOL + g_vt_tpi[8] = genRet(1, 0); + // 9: Commit() -> BOOL + g_vt_tpi[9] = genRet(1, 0); + // 10: QueryTi16ForUDT(sz, fCase, pti) -> BOOL + g_vt_tpi[10] = genRet(0, 3); + // 11: SupportQueryTiForUDT() -> BOOL + g_vt_tpi[11] = genRet(0, 0); + // 12: fIs16bitTypePool() -> BOOL + g_vt_tpi[12] = genRet(0, 0); + // 13: QueryTiForUDT(sz, fCase, pti) -> BOOL + g_vt_tpi[13] = genRet(0, 3); + // 14: QueryTiForCVRecord(pb, pti) -> BOOL + g_vt_tpi[14] = genRet(0, 2); + // 15: QueryCVRecordForTi(ti, pb, pcb) -> BOOL + g_vt_tpi[15] = genRet(0, 3); + // 16: QueryPbCVRecordForTi(ti, ppb) -> BOOL + g_vt_tpi[16] = genRet(0, 2); + // 17: QueryTiMin() -> TI + g_vt_tpi[17] = genRet(0x1000, 0); // standard min TI + // 18: QueryTiMac() -> TI + g_vt_tpi[18] = genRet(0x1000, 0); // same = empty type pool + // 19: AreTypesEqual(ti1, ti2) -> BOOL + g_vt_tpi[19] = genRet(0, 2); + // 20: IsTypeServed(ti) -> BOOL + g_vt_tpi[20] = genRet(0, 1); + // 21: QueryTiForUDTW(wcs, fCase, pti) -> BOOL + g_vt_tpi[21] = genRet(0, 3); + // 22: QueryModSrcLineForUDTDefn(4 args) -> BOOL + g_vt_tpi[22] = genRet(0, 4); + + // --- GSI vtable --- + for (int i = 0; i < 16; i++) + g_vt_gsi[i] = genTrap((uint8_t)i); + // 0: QueryInterfaceVersion() -> INTV + g_vt_gsi[0] = genRet(PDB_INTV, 0); + // 1: QueryImplementationVersion() -> IMPV + g_vt_gsi[1] = genRet(PDB_IMPV, 0); + // 2: NextSym(pbSym) -> BYTE* (return NULL = end) + g_vt_gsi[2] = genRet(0, 1); + // 3: HashSym(szName, pbSym) -> BYTE* + g_vt_gsi[3] = genRet(0, 2); + // 4: NearestSym(isect, off, pdisp) -> BYTE* + g_vt_gsi[4] = genRet(0, 3); + // 5: Close() -> BOOL + g_vt_gsi[5] = genRet(1, 0); + // 6: getEnumThunk(isect, off, ppenum) -> BOOL + g_vt_gsi[6] = genRet(0, 3); + // 7: OffForSym(pbSym) -> ulong + g_vt_gsi[7] = genRet(0, 1); + // 8: SymForOff(off) -> BYTE* + g_vt_gsi[8] = genRet(0, 1); + // 9: HashSymW(wcsName, pbSym) -> BYTE* + g_vt_gsi[9] = genRet(0, 2); + // 10: getEnumByAddr(ppEnum) -> BOOL + g_vt_gsi[10] = genRet(0, 1); + + // --- Dbg vtable --- + for (int i = 0; i < 16; i++) + g_vt_dbg[i] = genTrap((uint8_t)i); + // 0: Close() -> BOOL + g_vt_dbg[0] = genRet(1, 0); + // 1: QuerySize() -> long + g_vt_dbg[1] = genRet(0, 0); + // 2: Reset() -> void + g_vt_dbg[2] = genVoid(0); + // 3: Skip(celt) -> BOOL + g_vt_dbg[3] = genRet(1, 1); + // 4: QueryNext(celt, rgelt) -> BOOL + g_vt_dbg[4] = genRet(0, 2); // return FALSE = no more + // 5: Find(pelt) -> BOOL + g_vt_dbg[5] = genRet(0, 1); + // 6: Clear() -> BOOL + g_vt_dbg[6] = genRet(1, 0); + // 7: Append(celt, rgelt) -> BOOL + g_vt_dbg[7] = genRet(1, 2); + // 8: ReplaceNext(celt, rgelt) -> BOOL + g_vt_dbg[8] = genRet(1, 2); + // 9: Clone(ppDbg) -> BOOL + g_vt_dbg[9] = genRet(0, 1); + // 10: QueryElementSize() -> long + g_vt_dbg[10] = genRet(0, 0); + + // --- NameMap vtable --- + for (int i = 0; i < 20; i++) + g_vt_namemap[i] = genTrap((uint8_t)i); + // 0: close() -> BOOL + g_vt_namemap[0] = genRet(1, 0); + // 1: reinitialize() -> BOOL + g_vt_namemap[1] = genRet(1, 0); + // 2: getNi(sz, pni) -> BOOL [2 args, out=arg1 -> 0] + g_vt_namemap[2] = genOut(1, 0, 2); + // 3: getName(ni, psz) -> BOOL + g_vt_namemap[3] = genRet(0, 2); + // 4: getEnumNameMap(ppenum) -> BOOL + g_vt_namemap[4] = genRet(0, 1); + // 5: contains(sz, pni) -> BOOL + g_vt_namemap[5] = genRet(0, 2); + // 6: commit() -> BOOL + g_vt_namemap[6] = genRet(1, 0); + // 7: isValidNi(ni) -> BOOL + g_vt_namemap[7] = genRet(0, 1); + // 8: getNiW(sz, pni) -> BOOL [2 args, out=arg1 -> 0] + g_vt_namemap[8] = genOut(1, 0, 2); + // 9: getNameW(ni, szName, pcch) -> BOOL + g_vt_namemap[9] = genRet(0, 3); + // 10: containsW(sz, pni) -> BOOL + g_vt_namemap[10] = genRet(0, 2); + // 11: containsUTF8(sz, pni) -> BOOL + g_vt_namemap[11] = genRet(0, 2); + // 12: getNiUTF8(sz, pni) -> BOOL [2 args, out=arg1 -> 0] + g_vt_namemap[12] = genOut(1, 0, 2); + // 13: getNameA(ni, psz) -> BOOL + g_vt_namemap[13] = genRet(0, 2); + // 14: getNameW2(ni, pwsz) -> BOOL + g_vt_namemap[14] = genRet(0, 2); + + // --- Stream vtable --- + for (int i = 0; i < 12; i++) + g_vt_stream[i] = genTrap((uint8_t)i); + // 0: QueryCb() -> long + g_vt_stream[0] = genRet(0, 0); + // 1: Read(off, pvBuf, pcbBuf) -> BOOL + g_vt_stream[1] = genRet(1, 3); + // 2: Write(off, pvBuf, cbBuf) -> BOOL + g_vt_stream[2] = genRet(1, 3); + // 3: Replace(pvBuf, cbBuf) -> BOOL + g_vt_stream[3] = genRet(1, 2); + // 4: Append(pvBuf, cbBuf) -> BOOL + g_vt_stream[4] = genRet(1, 2); + // 5: Delete() -> BOOL + g_vt_stream[5] = genRet(1, 0); + // 6: Release() -> BOOL + g_vt_stream[6] = genRet(1, 0); + // 7: Read2(off, pvBuf, cbBuf) -> BOOL + g_vt_stream[7] = genRet(1, 3); + // 8: Truncate(cb) -> BOOL + g_vt_stream[8] = genRet(1, 1); + + // --- Wire up vtable pointers --- + g_obj_pdb.vptr = addr32(g_vt_pdb); + g_obj_dbi.vptr = addr32(g_vt_dbi); + g_obj_mod.vptr = addr32(g_vt_mod); + g_obj_tpi.vptr = addr32(g_vt_tpi); + g_obj_gsi.vptr = addr32(g_vt_gsi); + g_obj_dbg.vptr = addr32(g_vt_dbg); + g_obj_namemap.vptr = addr32(g_vt_namemap); + g_obj_stream.vptr = addr32(g_vt_stream); + + DEBUG_LOG("mspdb: fake vtables initialized, code used %zu/%zu bytes\n", g_code_pos, CODE_PAGE_SIZE); +} + +// Legacy sentinel values for C-style export stubs +static int g_fakeStream_legacy = 0; +static int g_fakeNameMap_legacy = 0; + +namespace mspdb { + +int CDECL PDB_Open2W(LPCWSTR wszPDB, LPCSTR szMode, LONG *pec, LPWSTR wszError, UINT cchErrMax, void **ppPDB) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("mspdb::PDB_Open2W(mode=%s)\n", szMode ? szMode : "(null)"); + initVtables(); + if (pec) + *(uint32_t *)pec = 0; // EC_OK + if (ppPDB) + *(uint32_t *)ppPDB = addr32(&g_obj_pdb); + return 1; // TRUE - PDB open succeeded +} + +int CDECL PDB_Open3W(LPCWSTR wszPDB, LPCSTR szMode, DWORD dwSig, void *pcsig70, DWORD dwAge, LONG *pec, + LPWSTR wszError, UINT cchErrMax, void **ppPDB) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("mspdb::PDB_Open3W(mode=%s)\n", szMode ? szMode : "(null)"); + initVtables(); + if (pec) + *(uint32_t *)pec = 0; // EC_OK + if (ppPDB) + *(uint32_t *)ppPDB = addr32(&g_obj_pdb); + return 1; // TRUE - PDB open succeeded +} + +int CDECL PDB_OpenValidate5(LPCWSTR wszPDB, LPCWSTR wszSearchPath, void *pvClient, void *pfnQueryCallback, + void *pfnNoteCallback, DWORD dwUnused, LONG *pec, LPWSTR wszError, UINT cchErrMax, + void **ppPDB) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("mspdb::PDB_OpenValidate5()\n"); + initVtables(); + if (pec) + *(uint32_t *)pec = 0; // EC_OK + if (ppPDB) + *(uint32_t *)ppPDB = addr32(&g_obj_pdb); + return 1; // TRUE +} + +int CDECL PDBExportValidateInterface(DWORD intv) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("mspdb::PDBExportValidateInterface(intv=%u)\n", intv); + return 1; // TRUE - interface is valid +} + +DWORD CDECL SigForPbCb(void *pb, DWORD cb) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("mspdb::SigForPbCb(pb=%p, cb=%u)\n", pb, cb); + return 0; +} + +LPCSTR CDECL SzCanonFilename(LPCSTR szFilename) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("mspdb::SzCanonFilename(%s)\n", szFilename ? szFilename : "(null)"); + return szFilename; +} + +// PDB management functions (C-style exports for supplementary linker module) + +int CDECL PDBOpen2W_C(LPCWSTR wszPDB, LPCSTR szMode, LONG *pec, LPWSTR wszError, UINT cchErrMax, void **ppPDB) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("mspdb::PDBOpen2W_C(mode=%s)\n", szMode ? szMode : "(null)"); + initVtables(); + if (pec) + *(uint32_t *)pec = 0; // EC_OK + if (ppPDB) + *(uint32_t *)ppPDB = addr32(&g_obj_pdb); + return 1; // TRUE +} + +int CDECL PDBOpenStreamEx(void *pPDB, LPCSTR szStream, DWORD dwFlags, void **ppStream) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("mspdb::PDBOpenStreamEx(stream=%s)\n", szStream ? szStream : "(null)"); + if (ppStream) + *ppStream = &g_fakeStream_legacy; + return 1; // TRUE +} + +int CDECL PDBCommit(void *pPDB) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("mspdb::PDBCommit()\n"); + return 1; // TRUE +} + +int CDECL PDBClose(void *pPDB) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("mspdb::PDBClose()\n"); + return 1; // TRUE +} + +// NameMap + +int CDECL NameMap_open(void *pPDB, int fWrite, void **ppNameMap) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("mspdb::NameMap_open(fWrite=%d)\n", fWrite); + initVtables(); + if (ppNameMap) + *(uint32_t *)ppNameMap = addr32(&g_obj_namemap); + return 1; // TRUE +} + +// Stream functions + +int CDECL StreamAppend(void *pStream, void *pvData, LONG cbData) { + HOST_CONTEXT_GUARD(); + VERBOSE_LOG("mspdb::StreamAppend(cb=%d)\n", cbData); + return 1; // TRUE +} + +LONG CDECL StreamQueryCb(void *pStream) { + HOST_CONTEXT_GUARD(); + VERBOSE_LOG("mspdb::StreamQueryCb()\n"); + return 0; // stream is empty +} + +int CDECL StreamRead(void *pStream, LONG off, void *pvData, LONG *pcbData) { + HOST_CONTEXT_GUARD(); + VERBOSE_LOG("mspdb::StreamRead(off=%d)\n", off); + if (pcbData) + *pcbData = 0; + return 1; // TRUE +} + +int CDECL StreamRelease(void *pStream) { + HOST_CONTEXT_GUARD(); + VERBOSE_LOG("mspdb::StreamRelease()\n"); + return 1; // TRUE +} + +int CDECL StreamReplace(void *pStream, void *pvData, LONG cbData) { + HOST_CONTEXT_GUARD(); + VERBOSE_LOG("mspdb::StreamReplace(cb=%d)\n", cbData); + return 1; // TRUE +} + +int CDECL StreamTruncate(void *pStream, LONG cbData) { + HOST_CONTEXT_GUARD(); + VERBOSE_LOG("mspdb::StreamTruncate(cb=%d)\n", cbData); + return 1; // TRUE +} + +int CDECL StreamWrite(void *pStream, LONG off, void *pvData, LONG cbData) { + HOST_CONTEXT_GUARD(); + VERBOSE_LOG("mspdb::StreamWrite(off=%d, cb=%d)\n", off, cbData); + return 1; // TRUE +} + +} // namespace mspdb + +#include "mspdb_trampolines.h" + +static void *resolveByName(const char *name) { + // Decorated C++ names (imported by link.exe main module) + if (strcmp(name, "?Open2W@PDB@@SAHPBGPBDPAJPAGIPAPAU1@@Z") == 0) + return (void *)thunk_mspdb_PDB_Open2W; + if (strcmp(name, "?Open3W@PDB@@SAHPBGPBDKPBU_GUID@@KPAJPAGIPAPAU1@@Z") == 0) + return (void *)thunk_mspdb_PDB_Open3W; + if (strcmp(name, "?OpenValidate5@PDB@@SAHPBG0PAXP6AP6AHXZ1W4POVC@@@ZPAJPAGIPAPAU1@@Z") == 0) + return (void *)thunk_mspdb_PDB_OpenValidate5; + if (strcmp(name, "PDBExportValidateInterface") == 0) + return (void *)thunk_mspdb_PDBExportValidateInterface; + if (strcmp(name, "SigForPbCb") == 0) + return (void *)thunk_mspdb_SigForPbCb; + if (strcmp(name, "SzCanonFilename") == 0) + return (void *)thunk_mspdb_SzCanonFilename; + + // C-style exports (imported by linker supplementary module) + if (strcmp(name, "PDBOpen2W") == 0) + return (void *)thunk_mspdb_PDBOpen2W_C; + if (strcmp(name, "PDBOpenStreamEx") == 0) + return (void *)thunk_mspdb_PDBOpenStreamEx; + if (strcmp(name, "PDBCommit") == 0) + return (void *)thunk_mspdb_PDBCommit; + if (strcmp(name, "PDBClose") == 0) + return (void *)thunk_mspdb_PDBClose; + if (strcmp(name, "?open@NameMap@@SAHPAUPDB@@HPAPAU1@@Z") == 0) + return (void *)thunk_mspdb_NameMap_open; + if (strcmp(name, "StreamAppend") == 0) + return (void *)thunk_mspdb_StreamAppend; + if (strcmp(name, "StreamQueryCb") == 0) + return (void *)thunk_mspdb_StreamQueryCb; + if (strcmp(name, "StreamRead") == 0) + return (void *)thunk_mspdb_StreamRead; + if (strcmp(name, "StreamRelease") == 0) + return (void *)thunk_mspdb_StreamRelease; + if (strcmp(name, "StreamReplace") == 0) + return (void *)thunk_mspdb_StreamReplace; + if (strcmp(name, "StreamTruncate") == 0) + return (void *)thunk_mspdb_StreamTruncate; + if (strcmp(name, "StreamWrite") == 0) + return (void *)thunk_mspdb_StreamWrite; + + return nullptr; +} + +extern const wibo::ModuleStub lib_mspdb = { + (const char *[]){ + "mspdbxx", + "mspdb100", + "mspdb80", + nullptr, + }, + resolveByName, + nullptr, +}; diff --git a/dll/mspdb.h b/dll/mspdb.h new file mode 100644 index 0000000..90d9fcd --- /dev/null +++ b/dll/mspdb.h @@ -0,0 +1,33 @@ +#pragma once + +#include "types.h" + +namespace mspdb { + +// PDB open/create functions (imported by link.exe main module) +int CDECL PDB_Open2W(LPCWSTR wszPDB, LPCSTR szMode, LONG *pec, LPWSTR wszError, UINT cchErrMax, void **ppPDB); +int CDECL PDB_Open3W(LPCWSTR wszPDB, LPCSTR szMode, DWORD dwSig, void *pcsig70, DWORD dwAge, LONG *pec, LPWSTR wszError, UINT cchErrMax, void **ppPDB); +int CDECL PDB_OpenValidate5(LPCWSTR wszPDB, LPCWSTR wszSearchPath, void *pvClient, void *pfnQueryCallback, void *pfnNoteCallback, DWORD dwUnused, LONG *pec, LPWSTR wszError, UINT cchErrMax, void **ppPDB); +int CDECL PDBExportValidateInterface(DWORD intv); +DWORD CDECL SigForPbCb(void *pb, DWORD cb); +LPCSTR CDECL SzCanonFilename(LPCSTR szFilename); + +// PDB management functions (imported by linker supplementary module) +int CDECL PDBOpen2W_C(LPCWSTR wszPDB, LPCSTR szMode, LONG *pec, LPWSTR wszError, UINT cchErrMax, void **ppPDB); +int CDECL PDBOpenStreamEx(void *pPDB, LPCSTR szStream, DWORD dwFlags, void **ppStream); +int CDECL PDBCommit(void *pPDB); +int CDECL PDBClose(void *pPDB); + +// NameMap +int CDECL NameMap_open(void *pPDB, int fWrite, void **ppNameMap); + +// Stream functions +int CDECL StreamAppend(void *pStream, void *pvData, LONG cbData); +LONG CDECL StreamQueryCb(void *pStream); +int CDECL StreamRead(void *pStream, LONG off, void *pvData, LONG *pcbData); +int CDECL StreamRelease(void *pStream); +int CDECL StreamReplace(void *pStream, void *pvData, LONG cbData); +int CDECL StreamTruncate(void *pStream, LONG cbData); +int CDECL StreamWrite(void *pStream, LONG off, void *pvData, LONG cbData); + +} // namespace mspdb diff --git a/dll/rpcrt4.h b/dll/rpcrt4.h index 56e6dba..2a1db46 100644 --- a/dll/rpcrt4.h +++ b/dll/rpcrt4.h @@ -33,9 +33,7 @@ RPC_STATUS WINAPI RpcBindingSetAuthInfoExW(RPC_BINDING_HANDLE binding, RPC_WSTR RPC_SECURITY_QOS *securityQos); RPC_STATUS WINAPI RpcBindingFree(GUEST_PTR *binding); RPC_STATUS WINAPI RpcStringFreeW(GUEST_PTR *string); -#ifndef __x86_64__ // TODO CLIENT_CALL_RETURN CDECL_NO_CONV NdrClientCall2(PMIDL_STUB_DESC stubDescriptor, PFORMAT_STRING format, ...); -#endif VOID WINAPI NdrServerCall2(PRPC_MESSAGE message); } // namespace rpcrt4 diff --git a/src/errors.h b/src/errors.h index 422db2d..170ae3f 100644 --- a/src/errors.h +++ b/src/errors.h @@ -42,6 +42,7 @@ #define ERROR_BAD_EXE_FORMAT 193 #define ERROR_DLL_INIT_FAILED 1114 #define ERROR_INVALID_FLAGS 1004 +#define ERROR_SHARING_VIOLATION 32 #define ERROR_ALREADY_EXISTS 183 #define ERROR_NOT_OWNER 288 #define ERROR_TIMEOUT 1460 diff --git a/src/files.cpp b/src/files.cpp index fd87621..e4ea48b 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -13,11 +13,17 @@ #include #include #include +#include +#include #include #include +#include #include kernel32::FsObject::~FsObject() { + if (!canonicalPath.empty() && accessCategory != 0) { + files::unregisterOpenFile(canonicalPath, accessCategory, shareAccess); + } int fd = std::exchange(this->fd, -1); if (fd >= 0 && closeOnDestroy) { close(fd); @@ -31,6 +37,12 @@ kernel32::FsObject::~FsObject() { namespace files { +static std::vector> gPathAliases; + +void addPathAlias(const std::string &hostPrefix, const std::string &windowsPrefix) { + gPathAliases.push_back({hostPrefix, windowsPrefix}); +} + static std::vector splitList(const std::string &value, char delimiter) { std::vector entries; size_t start = 0; @@ -89,6 +101,16 @@ std::filesystem::path pathFromWindows(const char *inStr) { str.erase(0, 4); } + // Check path aliases (reverse mapping: windows prefix -> host prefix) + for (const auto &alias : gPathAliases) { + std::string winPrefix = alias.second; + std::replace(winPrefix.begin(), winPrefix.end(), '\\', '/'); + if (strncasecmp(str.c_str(), winPrefix.c_str(), winPrefix.size()) == 0) { + str = alias.first + str.substr(winPrefix.size()); + return std::filesystem::path(str).lexically_normal(); + } + } + // Remove the drive letter if (str.rfind("z:/", 0) == 0 || str.rfind("Z:/", 0) == 0 || str.rfind("c:/", 0) == 0 || str.rfind("C:/", 0) == 0) { str.erase(0, 2); @@ -134,6 +156,15 @@ std::filesystem::path pathFromWindows(const char *inStr) { std::string pathToWindows(const std::filesystem::path &path) { std::string str = path.lexically_normal(); + // Check path aliases before default Z: mapping + for (const auto &alias : gPathAliases) { + if (str.rfind(alias.first, 0) == 0) { + str = alias.second + str.substr(alias.first.size()); + std::replace(str.begin(), str.end(), '/', '\\'); + return str; + } + } + if (path.is_absolute()) { str.insert(0, "Z:"); } @@ -421,4 +452,97 @@ std::string windowsPathListToHost(const std::string &value) { } return result; } +static std::mutex gShareMutex; +static std::unordered_map>> gShareMap; + +bool checkShareViolation(const std::filesystem::path &path, uint32_t accessCategory, uint32_t shareMode) { + if (accessCategory == 0) { + return false; + } + std::string key = path.string(); + std::lock_guard lk(gShareMutex); + auto it = gShareMap.find(key); + if (it == gShareMap.end()) { + return false; + } + for (const auto &[existAccess, existShare] : it->second) { + // Existing handle does something the new opener doesn't allow + if ((existAccess & ~shareMode) != 0) { + return true; + } + // New opener wants to do something the existing handle doesn't allow + if ((accessCategory & ~existShare) != 0) { + return true; + } + } + return false; +} + +void registerOpenFile(const std::filesystem::path &path, uint32_t accessCategory, uint32_t shareMode) { + if (accessCategory == 0) { + return; + } + std::string key = path.string(); + std::lock_guard lk(gShareMutex); + gShareMap[key].emplace_back(accessCategory, shareMode); +} + +void unregisterOpenFile(const std::filesystem::path &path, uint32_t accessCategory, uint32_t shareMode) { + std::string key = path.string(); + std::lock_guard lk(gShareMutex); + auto it = gShareMap.find(key); + if (it == gShareMap.end()) { + return; + } + auto &vec = it->second; + for (auto jt = vec.begin(); jt != vec.end(); ++jt) { + if (jt->first == accessCategory && jt->second == shareMode) { + vec.erase(jt); + break; + } + } + if (vec.empty()) { + gShareMap.erase(it); + } +} + +static std::mutex gMappedFileMutex; +static std::map, int> gMappedFileCount; + +void trackMappedFile(dev_t dev, ino_t ino) { + std::lock_guard lk(gMappedFileMutex); + int count = ++gMappedFileCount[{dev, ino}]; + DEBUG_LOG("trackMappedFile: dev=%lu ino=%lu count=%d\n", + (unsigned long)dev, (unsigned long)ino, count); +} + +void untrackMappedFile(dev_t dev, ino_t ino) { + std::lock_guard lk(gMappedFileMutex); + auto it = gMappedFileCount.find({dev, ino}); + if (it != gMappedFileCount.end()) { + if (--it->second <= 0) { + DEBUG_LOG("untrackMappedFile: dev=%lu ino=%lu (removed)\n", + (unsigned long)dev, (unsigned long)ino); + gMappedFileCount.erase(it); + } else { + DEBUG_LOG("untrackMappedFile: dev=%lu ino=%lu count=%d\n", + (unsigned long)dev, (unsigned long)ino, it->second); + } + } +} + +bool isFileMapped(int fd) { + struct stat st {}; + if (fstat(fd, &st) != 0) { + DEBUG_LOG("isFileMapped: fstat failed for fd=%d\n", fd); + return false; + } + std::lock_guard lk(gMappedFileMutex); + bool mapped = gMappedFileCount.count({st.st_dev, st.st_ino}) > 0; + DEBUG_LOG("isFileMapped: fd=%d dev=%lu ino=%lu -> %s\n", + fd, (unsigned long)st.st_dev, (unsigned long)st.st_ino, + mapped ? "YES (skip truncation)" : "no"); + return mapped; +} + } // namespace files diff --git a/src/files.h b/src/files.h index df1c73d..04098df 100644 --- a/src/files.h +++ b/src/files.h @@ -19,6 +19,7 @@ struct IOResult { }; void init(); +void addPathAlias(const std::string &hostPrefix, const std::string &windowsPrefix); std::filesystem::path pathFromWindows(const char *inStr); std::string pathToWindows(const std::filesystem::path &path); IOResult read(FileObject *file, void *buffer, size_t bytesToRead, const std::optional &offset, @@ -33,6 +34,16 @@ std::filesystem::path canonicalPath(const std::filesystem::path &path); std::string hostPathListToWindows(const std::string &value); std::string windowsPathListToHost(const std::string &value); +// Share violation tracking +bool checkShareViolation(const std::filesystem::path &path, uint32_t accessCategory, uint32_t shareMode); +void registerOpenFile(const std::filesystem::path &path, uint32_t accessCategory, uint32_t shareMode); +void unregisterOpenFile(const std::filesystem::path &path, uint32_t accessCategory, uint32_t shareMode); + +// Memory-mapped file tracking (prevents truncation of mapped files, matching Windows behavior) +void trackMappedFile(dev_t dev, ino_t ino); +void untrackMappedFile(dev_t dev, ino_t ino); +bool isFileMapped(int fd); + } // namespace files inline bool endsWith(const std::string &str, const std::string &suffix) { diff --git a/src/main.cpp b/src/main.cpp index bccce60..6b1fd8b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,12 +12,15 @@ #include "version_info.h" #include +#include #include #include #include #include #include +#include "kernel32/memoryapi.h" + char **wibo::argv; int wibo::argc; std::filesystem::path wibo::guestExecutablePath; @@ -260,6 +263,33 @@ int main(int argc, char **argv) { optionDebug = true; continue; } + if (strncmp(arg, "--path-alias=", 13) == 0) { + std::string mapping = arg + 13; + auto eqPos = mapping.find('='); + if (eqPos == std::string::npos) { + fprintf(stderr, "Error: --path-alias requires format 'host_path=windows_path'\n"); + printHelp(argv[0], true); + return 1; + } + files::addPathAlias(mapping.substr(0, eqPos), mapping.substr(eqPos + 1)); + continue; + } + if (strcmp(arg, "--path-alias") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "Error: '%s' requires a mapping argument.\n", arg); + printHelp(argv[0], true); + return 1; + } + std::string mapping = argv[++i]; + auto eqPos = mapping.find('='); + if (eqPos == std::string::npos) { + fprintf(stderr, "Error: --path-alias requires format 'host_path=windows_path'\n"); + printHelp(argv[0], true); + return 1; + } + files::addPathAlias(mapping.substr(0, eqPos), mapping.substr(eqPos + 1)); + continue; + } if (strncmp(arg, "--chdir=", 8) == 0) { chdirPath = arg + 8; continue; @@ -473,6 +503,21 @@ int main(int argc, char **argv) { // Reset last error kernel32::setLastError(0); + // Install crash handlers so memory-mapped file data gets flushed on SIGSEGV + { + struct sigaction sa = {}; + sa.sa_sigaction = [](int sig, siginfo_t *info, void *) { + kernel32::flushAllFileViews(); + fprintf(stderr, "\nwibo: caught signal %d (%s) at address %p\n", sig, + sig == SIGSEGV ? "SIGSEGV" : sig == SIGTRAP ? "SIGTRAP" : "unknown", + info ? info->si_addr : nullptr); + _exit(128 + sig); + }; + sa.sa_flags = SA_SIGINFO; + sigaction(SIGSEGV, &sa, nullptr); + sigaction(SIGTRAP, &sa, nullptr); + } + // Invoke the damn thing call_EntryProc(entryPoint); DEBUG_LOG("We came back\n"); diff --git a/src/modules.cpp b/src/modules.cpp index 5f15978..ff15b32 100644 --- a/src/modules.cpp +++ b/src/modules.cpp @@ -32,6 +32,7 @@ extern const wibo::ModuleStub lib_bcrypt; extern const wibo::ModuleStub lib_kernel32; extern const wibo::ModuleStub lib_lmgr; extern const wibo::ModuleStub lib_mscoree; +extern const wibo::ModuleStub lib_mspdb; #if WIBO_HAS_MSVCRT extern const wibo::ModuleStub lib_msvcrt; #endif @@ -187,8 +188,8 @@ LockedRegistry registry() { if (!reg.initialized) { reg.initialized = true; const wibo::ModuleStub *builtins[] = { - &lib_advapi32, &lib_bcrypt, &lib_kernel32, &lib_lmgr, &lib_mscoree, &lib_ntdll, - &lib_ole32, &lib_rpcrt4, &lib_user32, &lib_vcruntime, &lib_version, &lib_ws2, + &lib_advapi32, &lib_bcrypt, &lib_kernel32, &lib_lmgr, &lib_mscoree, &lib_mspdb, + &lib_ntdll, &lib_ole32, &lib_rpcrt4, &lib_user32, &lib_vcruntime, &lib_version, &lib_ws2, #if WIBO_HAS_MSVCRT &lib_msvcrt, #endif @@ -550,7 +551,7 @@ void registerBuiltinModule(ModuleRegistry ®, const wibo::ModuleStub *module) reg.builtinAliasLists[module] = {}; auto &aliasList = reg.builtinAliasLists[module]; - const bool pinModule = (module == &lib_lmgr); + const bool pinModule = (module == &lib_lmgr || module == &lib_mspdb); if (pinModule) { reg.pinnedModules.insert(raw); } From 088df234ab2f36abefd33039e3de9440062f53dc Mon Sep 17 00:00:00 2001 From: freeqaz <00free@gmail.com> Date: Thu, 12 Feb 2026 04:10:11 +0000 Subject: [PATCH 2/6] Cleanup mspdb.cpp and add integration test - Replace magic vtable array sizes with named constants (kPdbVtableSlots etc.) - Replace assert() with DEBUG_LOG + abort() in codeAlloc and mmap fallback - Factor out duplicated PDB open logic into openPDB() helper - Simplify resolveByName() by delegating C-style names to mspdbThunkByName() - Fix async-signal-safe crash handler (write() instead of fprintf()) - Remove unused g_fakeNameMap_legacy - Add test_mspdb fixture test covering LoadLibrary, GetProcAddress, and calling PDB/Stream exports end-to-end --- CMakeLists.txt | 1 + dll/mspdb.cpp | 156 ++++++++++++++++++++-------------------------- src/main.cpp | 12 ++-- test/test_mspdb.c | 83 ++++++++++++++++++++++++ 4 files changed, 160 insertions(+), 92 deletions(-) create mode 100644 test/test_mspdb.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 219370a..84fe03c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -480,6 +480,7 @@ if (WIBO_ENABLE_FIXTURE_TESTS) wibo_add_fixture_bin(NAME test_srw_lock SOURCES test/test_srw_lock.c) wibo_add_fixture_bin(NAME test_init_once SOURCES test/test_init_once.c) wibo_add_fixture_bin(NAME test_wait_on_address SOURCES test/test_wait_on_address.c COMPILE_OPTIONS -lsynchronization) + wibo_add_fixture_bin(NAME test_mspdb SOURCES test/test_mspdb.c) # DLLs for fixture tests wibo_add_fixture_dll(NAME external_exports SOURCES test/external_exports.c) diff --git a/dll/mspdb.cpp b/dll/mspdb.cpp index 7598af3..7aff63b 100644 --- a/dll/mspdb.cpp +++ b/dll/mspdb.cpp @@ -5,7 +5,6 @@ #include "kernel32/memoryapi.h" #include "modules.h" -#include #include #include #include @@ -42,15 +41,26 @@ static FakeVtObj g_obj_dbg; static FakeVtObj g_obj_namemap; static FakeVtObj g_obj_stream; +// Vtable sizes: must cover all slots the linker may call. +// Source: microsoft-pdb/langapi/include/pdb.h +static constexpr int kPdbVtableSlots = 64; // PDB interface (32 defined + headroom) +static constexpr int kDbiVtableSlots = 64; // DBI interface (57 defined + headroom) +static constexpr int kModVtableSlots = 64; // Mod interface +static constexpr int kTpiVtableSlots = 32; // TPI interface (23 defined) +static constexpr int kGsiVtableSlots = 16; // GSI interface (11 defined) +static constexpr int kDbgVtableSlots = 16; // Dbg interface +static constexpr int kNameMapVtableSlots = 20; // NameMap interface (15 defined) +static constexpr int kStreamVtableSlots = 12; // Stream interface (9 defined) + // Vtables: arrays of 32-bit x86 function pointers -static uint32_t g_vt_pdb[64]; -static uint32_t g_vt_dbi[64]; -static uint32_t g_vt_mod[64]; -static uint32_t g_vt_tpi[32]; -static uint32_t g_vt_gsi[16]; -static uint32_t g_vt_dbg[16]; -static uint32_t g_vt_namemap[20]; -static uint32_t g_vt_stream[12]; +static uint32_t g_vt_pdb[kPdbVtableSlots]; +static uint32_t g_vt_dbi[kDbiVtableSlots]; +static uint32_t g_vt_mod[kModVtableSlots]; +static uint32_t g_vt_tpi[kTpiVtableSlots]; +static uint32_t g_vt_gsi[kGsiVtableSlots]; +static uint32_t g_vt_dbg[kDbgVtableSlots]; +static uint32_t g_vt_namemap[kNameMapVtableSlots]; +static uint32_t g_vt_stream[kStreamVtableSlots]; // --- x86 machine code generation --- @@ -59,7 +69,10 @@ static size_t g_code_pos; static constexpr size_t CODE_PAGE_SIZE = 16384; static uint8_t *codeAlloc(size_t n) { - assert(g_code_page && g_code_pos + n <= CODE_PAGE_SIZE); + if (!g_code_page || g_code_pos + n > CODE_PAGE_SIZE) { + DEBUG_LOG("mspdb: codeAlloc(%zu) failed: page=%p pos=%zu\n", n, g_code_page, g_code_pos); + abort(); + } uint8_t *p = g_code_page + g_code_pos; g_code_pos += n; return p; @@ -203,8 +216,14 @@ static void initVtables() { g_code_page = (uint8_t *)mmap(nullptr, CODE_PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); } - assert(g_code_page != MAP_FAILED); - assert((uintptr_t)g_code_page < 0xFFFFFFFFULL); // must be 32-bit addressable + if (g_code_page == MAP_FAILED) { + DEBUG_LOG("mspdb: mmap for code page failed\n"); + abort(); + } + if ((uintptr_t)g_code_page >= 0xFFFFFFFFULL) { + DEBUG_LOG("mspdb: code page at %p not 32-bit addressable\n", g_code_page); + abort(); + } g_code_pos = 0; DEBUG_LOG("mspdb: code page at %p, objects: PDB=%p DBI=%p Mod=%p TPI=%p GSI=%p\n", g_code_page, &g_obj_pdb, @@ -220,9 +239,9 @@ static void initVtables() { uint32_t pNM = addr32(&g_obj_namemap); uint32_t pStr = addr32(&g_obj_stream); - // --- PDB vtable (from microsoft-pdb PDB interface) --- + // --- PDB vtable (microsoft-pdb/langapi/include/pdb.h, slots 0-31) --- // Fill with traps first - for (int i = 0; i < 64; i++) + for (int i = 0; i < kPdbVtableSlots; i++) g_vt_pdb[i] = genTrap((uint8_t)i); // 0: QueryInterfaceVersion() -> INTV g_vt_pdb[0] = genRet(PDB_INTV, 0); @@ -290,8 +309,8 @@ static void initVtables() { // 31: ResetGUID(pb, cb) -> BOOL g_vt_pdb[31] = genRet(1, 2); - // --- DBI vtable --- - for (int i = 0; i < 64; i++) + // --- DBI vtable (microsoft-pdb DBI interface, slots 0-63) --- + for (int i = 0; i < kDbiVtableSlots; i++) g_vt_dbi[i] = genTrap((uint8_t)i); // 0: QueryImplementationVersion() -> IMPV g_vt_dbi[0] = genRet(PDB_IMPV, 0); @@ -422,8 +441,8 @@ static void initVtables() { // 63: QueryImodFromAddrEx(8 args) -> BOOL g_vt_dbi[63] = genRet(0, 8); - // --- Mod vtable --- - for (int i = 0; i < 64; i++) + // --- Mod vtable (microsoft-pdb Mod interface, slots 0-49) --- + for (int i = 0; i < kModVtableSlots; i++) g_vt_mod[i] = genTrap((uint8_t)i); // 0: QueryInterfaceVersion() -> INTV g_vt_mod[0] = genRet(PDB_INTV, 0); @@ -505,8 +524,8 @@ static void initVtables() { for (int i = 38; i <= 49; i++) g_vt_mod[i] = genRet(0, 3); // safe default: return FALSE, pop 3 args - // --- TPI vtable --- - for (int i = 0; i < 32; i++) + // --- TPI vtable (microsoft-pdb TPI interface, slots 0-22) --- + for (int i = 0; i < kTpiVtableSlots; i++) g_vt_tpi[i] = genTrap((uint8_t)i); // 0: QueryInterfaceVersion() -> INTV g_vt_tpi[0] = genRet(PDB_INTV, 0); @@ -555,8 +574,8 @@ static void initVtables() { // 22: QueryModSrcLineForUDTDefn(4 args) -> BOOL g_vt_tpi[22] = genRet(0, 4); - // --- GSI vtable --- - for (int i = 0; i < 16; i++) + // --- GSI vtable (microsoft-pdb GSI interface, slots 0-10) --- + for (int i = 0; i < kGsiVtableSlots; i++) g_vt_gsi[i] = genTrap((uint8_t)i); // 0: QueryInterfaceVersion() -> INTV g_vt_gsi[0] = genRet(PDB_INTV, 0); @@ -581,8 +600,8 @@ static void initVtables() { // 10: getEnumByAddr(ppEnum) -> BOOL g_vt_gsi[10] = genRet(0, 1); - // --- Dbg vtable --- - for (int i = 0; i < 16; i++) + // --- Dbg vtable (microsoft-pdb Dbg interface, slots 0-10) --- + for (int i = 0; i < kDbgVtableSlots; i++) g_vt_dbg[i] = genTrap((uint8_t)i); // 0: Close() -> BOOL g_vt_dbg[0] = genRet(1, 0); @@ -607,8 +626,8 @@ static void initVtables() { // 10: QueryElementSize() -> long g_vt_dbg[10] = genRet(0, 0); - // --- NameMap vtable --- - for (int i = 0; i < 20; i++) + // --- NameMap vtable (microsoft-pdb NameMap interface, slots 0-14) --- + for (int i = 0; i < kNameMapVtableSlots; i++) g_vt_namemap[i] = genTrap((uint8_t)i); // 0: close() -> BOOL g_vt_namemap[0] = genRet(1, 0); @@ -641,8 +660,8 @@ static void initVtables() { // 14: getNameW2(ni, pwsz) -> BOOL g_vt_namemap[14] = genRet(0, 2); - // --- Stream vtable --- - for (int i = 0; i < 12; i++) + // --- Stream vtable (microsoft-pdb Stream interface, slots 0-8) --- + for (int i = 0; i < kStreamVtableSlots; i++) g_vt_stream[i] = genTrap((uint8_t)i); // 0: QueryCb() -> long g_vt_stream[0] = genRet(0, 0); @@ -676,33 +695,31 @@ static void initVtables() { DEBUG_LOG("mspdb: fake vtables initialized, code used %zu/%zu bytes\n", g_code_pos, CODE_PAGE_SIZE); } -// Legacy sentinel values for C-style export stubs +// Legacy sentinel value for PDBOpenStreamEx C-style export static int g_fakeStream_legacy = 0; -static int g_fakeNameMap_legacy = 0; -namespace mspdb { - -int CDECL PDB_Open2W(LPCWSTR wszPDB, LPCSTR szMode, LONG *pec, LPWSTR wszError, UINT cchErrMax, void **ppPDB) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("mspdb::PDB_Open2W(mode=%s)\n", szMode ? szMode : "(null)"); +static int openPDB(LONG *pec, void **ppPDB) { initVtables(); if (pec) *(uint32_t *)pec = 0; // EC_OK if (ppPDB) *(uint32_t *)ppPDB = addr32(&g_obj_pdb); - return 1; // TRUE - PDB open succeeded + return 1; // TRUE +} + +namespace mspdb { + +int CDECL PDB_Open2W(LPCWSTR wszPDB, LPCSTR szMode, LONG *pec, LPWSTR wszError, UINT cchErrMax, void **ppPDB) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("mspdb::PDB_Open2W(mode=%s)\n", szMode ? szMode : "(null)"); + return openPDB(pec, ppPDB); } int CDECL PDB_Open3W(LPCWSTR wszPDB, LPCSTR szMode, DWORD dwSig, void *pcsig70, DWORD dwAge, LONG *pec, LPWSTR wszError, UINT cchErrMax, void **ppPDB) { HOST_CONTEXT_GUARD(); DEBUG_LOG("mspdb::PDB_Open3W(mode=%s)\n", szMode ? szMode : "(null)"); - initVtables(); - if (pec) - *(uint32_t *)pec = 0; // EC_OK - if (ppPDB) - *(uint32_t *)ppPDB = addr32(&g_obj_pdb); - return 1; // TRUE - PDB open succeeded + return openPDB(pec, ppPDB); } int CDECL PDB_OpenValidate5(LPCWSTR wszPDB, LPCWSTR wszSearchPath, void *pvClient, void *pfnQueryCallback, @@ -710,12 +727,7 @@ int CDECL PDB_OpenValidate5(LPCWSTR wszPDB, LPCWSTR wszSearchPath, void *pvClien void **ppPDB) { HOST_CONTEXT_GUARD(); DEBUG_LOG("mspdb::PDB_OpenValidate5()\n"); - initVtables(); - if (pec) - *(uint32_t *)pec = 0; // EC_OK - if (ppPDB) - *(uint32_t *)ppPDB = addr32(&g_obj_pdb); - return 1; // TRUE + return openPDB(pec, ppPDB); } int CDECL PDBExportValidateInterface(DWORD intv) { @@ -741,12 +753,7 @@ LPCSTR CDECL SzCanonFilename(LPCSTR szFilename) { int CDECL PDBOpen2W_C(LPCWSTR wszPDB, LPCSTR szMode, LONG *pec, LPWSTR wszError, UINT cchErrMax, void **ppPDB) { HOST_CONTEXT_GUARD(); DEBUG_LOG("mspdb::PDBOpen2W_C(mode=%s)\n", szMode ? szMode : "(null)"); - initVtables(); - if (pec) - *(uint32_t *)pec = 0; // EC_OK - if (ppPDB) - *(uint32_t *)ppPDB = addr32(&g_obj_pdb); - return 1; // TRUE + return openPDB(pec, ppPDB); } int CDECL PDBOpenStreamEx(void *pPDB, LPCSTR szStream, DWORD dwFlags, void **ppStream) { @@ -831,47 +838,22 @@ int CDECL StreamWrite(void *pStream, LONG off, void *pvData, LONG cbData) { #include "mspdb_trampolines.h" static void *resolveByName(const char *name) { - // Decorated C++ names (imported by link.exe main module) + // Decorated C++ names (not covered by mspdbThunkByName) if (strcmp(name, "?Open2W@PDB@@SAHPBGPBDPAJPAGIPAPAU1@@Z") == 0) return (void *)thunk_mspdb_PDB_Open2W; if (strcmp(name, "?Open3W@PDB@@SAHPBGPBDKPBU_GUID@@KPAJPAGIPAPAU1@@Z") == 0) return (void *)thunk_mspdb_PDB_Open3W; if (strcmp(name, "?OpenValidate5@PDB@@SAHPBG0PAXP6AP6AHXZ1W4POVC@@@ZPAJPAGIPAPAU1@@Z") == 0) return (void *)thunk_mspdb_PDB_OpenValidate5; - if (strcmp(name, "PDBExportValidateInterface") == 0) - return (void *)thunk_mspdb_PDBExportValidateInterface; - if (strcmp(name, "SigForPbCb") == 0) - return (void *)thunk_mspdb_SigForPbCb; - if (strcmp(name, "SzCanonFilename") == 0) - return (void *)thunk_mspdb_SzCanonFilename; - - // C-style exports (imported by linker supplementary module) - if (strcmp(name, "PDBOpen2W") == 0) - return (void *)thunk_mspdb_PDBOpen2W_C; - if (strcmp(name, "PDBOpenStreamEx") == 0) - return (void *)thunk_mspdb_PDBOpenStreamEx; - if (strcmp(name, "PDBCommit") == 0) - return (void *)thunk_mspdb_PDBCommit; - if (strcmp(name, "PDBClose") == 0) - return (void *)thunk_mspdb_PDBClose; if (strcmp(name, "?open@NameMap@@SAHPAUPDB@@HPAPAU1@@Z") == 0) return (void *)thunk_mspdb_NameMap_open; - if (strcmp(name, "StreamAppend") == 0) - return (void *)thunk_mspdb_StreamAppend; - if (strcmp(name, "StreamQueryCb") == 0) - return (void *)thunk_mspdb_StreamQueryCb; - if (strcmp(name, "StreamRead") == 0) - return (void *)thunk_mspdb_StreamRead; - if (strcmp(name, "StreamRelease") == 0) - return (void *)thunk_mspdb_StreamRelease; - if (strcmp(name, "StreamReplace") == 0) - return (void *)thunk_mspdb_StreamReplace; - if (strcmp(name, "StreamTruncate") == 0) - return (void *)thunk_mspdb_StreamTruncate; - if (strcmp(name, "StreamWrite") == 0) - return (void *)thunk_mspdb_StreamWrite; - - return nullptr; + + // Export name alias (DLL exports "PDBOpen2W", C function is PDBOpen2W_C) + if (strcmp(name, "PDBOpen2W") == 0) + return (void *)thunk_mspdb_PDBOpen2W_C; + + // C-style names: delegate to auto-generated lookup + return mspdbThunkByName(name); } extern const wibo::ModuleStub lib_mspdb = { diff --git a/src/main.cpp b/src/main.cpp index 6b1fd8b..31819db 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -503,14 +503,16 @@ int main(int argc, char **argv) { // Reset last error kernel32::setLastError(0); - // Install crash handlers so memory-mapped file data gets flushed on SIGSEGV + // Install crash handlers so memory-mapped file data gets flushed on SIGSEGV. + // Uses write() instead of fprintf() for async-signal safety. { struct sigaction sa = {}; - sa.sa_sigaction = [](int sig, siginfo_t *info, void *) { + sa.sa_sigaction = [](int sig, siginfo_t *, void *) { kernel32::flushAllFileViews(); - fprintf(stderr, "\nwibo: caught signal %d (%s) at address %p\n", sig, - sig == SIGSEGV ? "SIGSEGV" : sig == SIGTRAP ? "SIGTRAP" : "unknown", - info ? info->si_addr : nullptr); + const char *msg = sig == SIGSEGV ? "\nwibo: caught SIGSEGV\n" + : sig == SIGTRAP ? "\nwibo: caught SIGTRAP\n" + : "\nwibo: caught signal\n"; + write(STDERR_FILENO, msg, strlen(msg)); _exit(128 + sig); }; sa.sa_flags = SA_SIGINFO; diff --git a/test/test_mspdb.c b/test/test_mspdb.c new file mode 100644 index 0000000..4b30bc8 --- /dev/null +++ b/test/test_mspdb.c @@ -0,0 +1,83 @@ +#include +#include +#include + +#include "test_assert.h" + +/* Function pointer types for mspdb exports */ +typedef int(__cdecl *PDBExportValidateInterface_fn)(DWORD intv); +typedef int(__cdecl *PDBOpen2W_fn)(LPCWSTR wszPDB, LPCSTR szMode, LONG *pec, + LPWSTR wszError, UINT cchErrMax, void **ppPDB); +typedef int(__cdecl *PDBCommit_fn)(void *pPDB); +typedef int(__cdecl *PDBClose_fn)(void *pPDB); +typedef LONG(__cdecl *StreamQueryCb_fn)(void *pStream); +typedef int(__cdecl *StreamAppend_fn)(void *pStream, void *pvData, LONG cbData); +typedef int(__cdecl *StreamRelease_fn)(void *pStream); + +int main(void) { + /* 1. LoadLibraryA resolves to the wibo builtin module */ + HMODULE mod = LoadLibraryA("mspdb80.dll"); + TEST_CHECK_MSG(mod != NULL, "LoadLibraryA(mspdb80.dll) failed: %lu", + (unsigned long)GetLastError()); + + /* 2. GetProcAddress finds the C-style exports */ + FARPROC raw_validate = GetProcAddress(mod, "PDBExportValidateInterface"); + FARPROC raw_open = GetProcAddress(mod, "PDBOpen2W"); + FARPROC raw_commit = GetProcAddress(mod, "PDBCommit"); + FARPROC raw_close = GetProcAddress(mod, "PDBClose"); + FARPROC raw_querycb = GetProcAddress(mod, "StreamQueryCb"); + FARPROC raw_append = GetProcAddress(mod, "StreamAppend"); + FARPROC raw_release = GetProcAddress(mod, "StreamRelease"); + + TEST_CHECK_MSG(raw_validate != NULL, "GetProcAddress(PDBExportValidateInterface) failed"); + TEST_CHECK_MSG(raw_open != NULL, "GetProcAddress(PDBOpen2W) failed"); + TEST_CHECK_MSG(raw_commit != NULL, "GetProcAddress(PDBCommit) failed"); + TEST_CHECK_MSG(raw_close != NULL, "GetProcAddress(PDBClose) failed"); + TEST_CHECK_MSG(raw_querycb != NULL, "GetProcAddress(StreamQueryCb) failed"); + TEST_CHECK_MSG(raw_append != NULL, "GetProcAddress(StreamAppend) failed"); + TEST_CHECK_MSG(raw_release != NULL, "GetProcAddress(StreamRelease) failed"); + + PDBExportValidateInterface_fn validate_fn = + (PDBExportValidateInterface_fn)(uintptr_t)raw_validate; + PDBOpen2W_fn open_fn = (PDBOpen2W_fn)(uintptr_t)raw_open; + PDBCommit_fn commit_fn = (PDBCommit_fn)(uintptr_t)raw_commit; + PDBClose_fn close_fn = (PDBClose_fn)(uintptr_t)raw_close; + StreamQueryCb_fn querycb_fn = (StreamQueryCb_fn)(uintptr_t)raw_querycb; + StreamAppend_fn append_fn = (StreamAppend_fn)(uintptr_t)raw_append; + StreamRelease_fn release_fn = (StreamRelease_fn)(uintptr_t)raw_release; + + /* 3. PDBExportValidateInterface(20091201) returns 1 */ + int valid = validate_fn(20091201); + TEST_CHECK_EQ(1, valid); + + /* 4. PDBOpen2W returns 1, sets ec=0, ppPDB!=NULL */ + LONG ec = -1; + void *ppPDB = NULL; + int opened = open_fn(L"test.pdb", "w", &ec, NULL, 0, &ppPDB); + TEST_CHECK_EQ(1, opened); + TEST_CHECK_EQ(0, ec); + TEST_CHECK_MSG(ppPDB != NULL, "PDBOpen2W did not return a PDB handle"); + + /* 5. PDBCommit returns 1 */ + int committed = commit_fn(ppPDB); + TEST_CHECK_EQ(1, committed); + + /* 6. PDBClose returns 1 */ + int closed = close_fn(ppPDB); + TEST_CHECK_EQ(1, closed); + + /* 7. Stream functions */ + void *fakeStream = (void *)(uintptr_t)0x12345678; + LONG cb = querycb_fn(fakeStream); + TEST_CHECK_EQ(0, cb); + + char data[] = "hello"; + int appended = append_fn(fakeStream, data, sizeof(data)); + TEST_CHECK_EQ(1, appended); + + int released = release_fn(fakeStream); + TEST_CHECK_EQ(1, released); + + printf("mspdb: passed\n"); + return EXIT_SUCCESS; +} From 5ebe895b9fb169bec019913792a2ff5d488791ec Mon Sep 17 00:00:00 2001 From: freeqaz <00free@gmail.com> Date: Thu, 12 Feb 2026 05:10:24 +0000 Subject: [PATCH 3/6] Fix mspdb 64-bit compatibility: allocate fake objects from MAP_32BIT page In a 64-bit wibo build, static globals live in host BSS which can be at addresses >4GB. The addr32() helper silently truncates these to garbage 32-bit guest pointers, breaking all vtable dispatch. Fix: allocate all fake PDB objects, vtable arrays, and the legacy stream sentinel from the MAP_32BIT code page (already RWX, guaranteed <4GB) instead of using static globals. Uses ~1.2KB extra from the 16KB page. Also adds vtable dispatch coverage to test_mspdb: calls QueryInterfaceVersion and OpenDBI through the PDB vtable, then dispatches through the returned DBI object's vtable. This exercises the exact code path that breaks when objects aren't 32-bit addressable. --- dll/mspdb.cpp | 109 ++++++++++++++++++++++++++++------------------ test/test_mspdb.c | 51 ++++++++++++++++++++-- 2 files changed, 115 insertions(+), 45 deletions(-) diff --git a/dll/mspdb.cpp b/dll/mspdb.cpp index 7aff63b..231ade6 100644 --- a/dll/mspdb.cpp +++ b/dll/mspdb.cpp @@ -31,15 +31,15 @@ struct FakeVtObj { uint32_t vptr; }; -// Fake objects (must be at 32-bit addresses - wibo is loaded at 0x70000000) -static FakeVtObj g_obj_pdb; -static FakeVtObj g_obj_dbi; -static FakeVtObj g_obj_mod; -static FakeVtObj g_obj_tpi; -static FakeVtObj g_obj_gsi; -static FakeVtObj g_obj_dbg; -static FakeVtObj g_obj_namemap; -static FakeVtObj g_obj_stream; +// Fake objects - allocated from MAP_32BIT code page (must be <4GB for guest pointers) +static FakeVtObj *g_obj_pdb; +static FakeVtObj *g_obj_dbi; +static FakeVtObj *g_obj_mod; +static FakeVtObj *g_obj_tpi; +static FakeVtObj *g_obj_gsi; +static FakeVtObj *g_obj_dbg; +static FakeVtObj *g_obj_namemap; +static FakeVtObj *g_obj_stream; // Vtable sizes: must cover all slots the linker may call. // Source: microsoft-pdb/langapi/include/pdb.h @@ -52,15 +52,18 @@ static constexpr int kDbgVtableSlots = 16; // Dbg interface static constexpr int kNameMapVtableSlots = 20; // NameMap interface (15 defined) static constexpr int kStreamVtableSlots = 12; // Stream interface (9 defined) -// Vtables: arrays of 32-bit x86 function pointers -static uint32_t g_vt_pdb[kPdbVtableSlots]; -static uint32_t g_vt_dbi[kDbiVtableSlots]; -static uint32_t g_vt_mod[kModVtableSlots]; -static uint32_t g_vt_tpi[kTpiVtableSlots]; -static uint32_t g_vt_gsi[kGsiVtableSlots]; -static uint32_t g_vt_dbg[kDbgVtableSlots]; -static uint32_t g_vt_namemap[kNameMapVtableSlots]; -static uint32_t g_vt_stream[kStreamVtableSlots]; +// Vtables: arrays of 32-bit x86 function pointers - allocated from MAP_32BIT code page +static uint32_t *g_vt_pdb; +static uint32_t *g_vt_dbi; +static uint32_t *g_vt_mod; +static uint32_t *g_vt_tpi; +static uint32_t *g_vt_gsi; +static uint32_t *g_vt_dbg; +static uint32_t *g_vt_namemap; +static uint32_t *g_vt_stream; + +// Legacy sentinel value for PDBOpenStreamEx C-style export - allocated from MAP_32BIT code page +static uint32_t *g_fakeStream_legacy; // --- x86 machine code generation --- @@ -226,18 +229,42 @@ static void initVtables() { } g_code_pos = 0; - DEBUG_LOG("mspdb: code page at %p, objects: PDB=%p DBI=%p Mod=%p TPI=%p GSI=%p\n", g_code_page, &g_obj_pdb, - &g_obj_dbi, &g_obj_mod, &g_obj_tpi, &g_obj_gsi); + // Allocate fake objects from MAP_32BIT page (must be <4GB for guest pointers) + g_obj_pdb = (FakeVtObj *)codeAlloc(sizeof(FakeVtObj)); + g_obj_dbi = (FakeVtObj *)codeAlloc(sizeof(FakeVtObj)); + g_obj_mod = (FakeVtObj *)codeAlloc(sizeof(FakeVtObj)); + g_obj_tpi = (FakeVtObj *)codeAlloc(sizeof(FakeVtObj)); + g_obj_gsi = (FakeVtObj *)codeAlloc(sizeof(FakeVtObj)); + g_obj_dbg = (FakeVtObj *)codeAlloc(sizeof(FakeVtObj)); + g_obj_namemap = (FakeVtObj *)codeAlloc(sizeof(FakeVtObj)); + g_obj_stream = (FakeVtObj *)codeAlloc(sizeof(FakeVtObj)); + + // Allocate vtable arrays from MAP_32BIT page + g_vt_pdb = (uint32_t *)codeAlloc(kPdbVtableSlots * sizeof(uint32_t)); + g_vt_dbi = (uint32_t *)codeAlloc(kDbiVtableSlots * sizeof(uint32_t)); + g_vt_mod = (uint32_t *)codeAlloc(kModVtableSlots * sizeof(uint32_t)); + g_vt_tpi = (uint32_t *)codeAlloc(kTpiVtableSlots * sizeof(uint32_t)); + g_vt_gsi = (uint32_t *)codeAlloc(kGsiVtableSlots * sizeof(uint32_t)); + g_vt_dbg = (uint32_t *)codeAlloc(kDbgVtableSlots * sizeof(uint32_t)); + g_vt_namemap = (uint32_t *)codeAlloc(kNameMapVtableSlots * sizeof(uint32_t)); + g_vt_stream = (uint32_t *)codeAlloc(kStreamVtableSlots * sizeof(uint32_t)); + + // Allocate legacy stream sentinel + g_fakeStream_legacy = (uint32_t *)codeAlloc(sizeof(uint32_t)); + *g_fakeStream_legacy = 0; + + DEBUG_LOG("mspdb: code page at %p, objects: PDB=%p DBI=%p Mod=%p TPI=%p GSI=%p\n", g_code_page, g_obj_pdb, + g_obj_dbi, g_obj_mod, g_obj_tpi, g_obj_gsi); // Shorthand addresses for fake sub-objects - uint32_t pPDB = addr32(&g_obj_pdb); - uint32_t pDBI = addr32(&g_obj_dbi); - uint32_t pMod = addr32(&g_obj_mod); - uint32_t pTPI = addr32(&g_obj_tpi); - uint32_t pGSI = addr32(&g_obj_gsi); - uint32_t pDbg = addr32(&g_obj_dbg); - uint32_t pNM = addr32(&g_obj_namemap); - uint32_t pStr = addr32(&g_obj_stream); + uint32_t pPDB = addr32(g_obj_pdb); + uint32_t pDBI = addr32(g_obj_dbi); + uint32_t pMod = addr32(g_obj_mod); + uint32_t pTPI = addr32(g_obj_tpi); + uint32_t pGSI = addr32(g_obj_gsi); + uint32_t pDbg = addr32(g_obj_dbg); + uint32_t pNM = addr32(g_obj_namemap); + uint32_t pStr = addr32(g_obj_stream); // --- PDB vtable (microsoft-pdb/langapi/include/pdb.h, slots 0-31) --- // Fill with traps first @@ -683,27 +710,24 @@ static void initVtables() { g_vt_stream[8] = genRet(1, 1); // --- Wire up vtable pointers --- - g_obj_pdb.vptr = addr32(g_vt_pdb); - g_obj_dbi.vptr = addr32(g_vt_dbi); - g_obj_mod.vptr = addr32(g_vt_mod); - g_obj_tpi.vptr = addr32(g_vt_tpi); - g_obj_gsi.vptr = addr32(g_vt_gsi); - g_obj_dbg.vptr = addr32(g_vt_dbg); - g_obj_namemap.vptr = addr32(g_vt_namemap); - g_obj_stream.vptr = addr32(g_vt_stream); + g_obj_pdb->vptr = addr32(g_vt_pdb); + g_obj_dbi->vptr = addr32(g_vt_dbi); + g_obj_mod->vptr = addr32(g_vt_mod); + g_obj_tpi->vptr = addr32(g_vt_tpi); + g_obj_gsi->vptr = addr32(g_vt_gsi); + g_obj_dbg->vptr = addr32(g_vt_dbg); + g_obj_namemap->vptr = addr32(g_vt_namemap); + g_obj_stream->vptr = addr32(g_vt_stream); DEBUG_LOG("mspdb: fake vtables initialized, code used %zu/%zu bytes\n", g_code_pos, CODE_PAGE_SIZE); } -// Legacy sentinel value for PDBOpenStreamEx C-style export -static int g_fakeStream_legacy = 0; - static int openPDB(LONG *pec, void **ppPDB) { initVtables(); if (pec) *(uint32_t *)pec = 0; // EC_OK if (ppPDB) - *(uint32_t *)ppPDB = addr32(&g_obj_pdb); + *(uint32_t *)ppPDB = addr32(g_obj_pdb); return 1; // TRUE } @@ -759,8 +783,9 @@ int CDECL PDBOpen2W_C(LPCWSTR wszPDB, LPCSTR szMode, LONG *pec, LPWSTR wszError, int CDECL PDBOpenStreamEx(void *pPDB, LPCSTR szStream, DWORD dwFlags, void **ppStream) { HOST_CONTEXT_GUARD(); DEBUG_LOG("mspdb::PDBOpenStreamEx(stream=%s)\n", szStream ? szStream : "(null)"); + initVtables(); if (ppStream) - *ppStream = &g_fakeStream_legacy; + *(uint32_t *)ppStream = addr32(g_fakeStream_legacy); return 1; // TRUE } @@ -783,7 +808,7 @@ int CDECL NameMap_open(void *pPDB, int fWrite, void **ppNameMap) { DEBUG_LOG("mspdb::NameMap_open(fWrite=%d)\n", fWrite); initVtables(); if (ppNameMap) - *(uint32_t *)ppNameMap = addr32(&g_obj_namemap); + *(uint32_t *)ppNameMap = addr32(g_obj_namemap); return 1; // TRUE } diff --git a/test/test_mspdb.c b/test/test_mspdb.c index 4b30bc8..5768b51 100644 --- a/test/test_mspdb.c +++ b/test/test_mspdb.c @@ -58,15 +58,60 @@ int main(void) { TEST_CHECK_EQ(0, ec); TEST_CHECK_MSG(ppPDB != NULL, "PDBOpen2W did not return a PDB handle"); - /* 5. PDBCommit returns 1 */ + /* 5. Vtable dispatch: the PDB handle is a COM-style object whose first + * dword is a vtable pointer. This tests the full chain that breaks on + * 64-bit if objects aren't in 32-bit addressable memory: + * object ptr -> vptr -> stub code -> return value */ + { + /* __thiscall: this in ECX, no stack args for slot 0 */ + typedef int (__attribute__((thiscall)) *QueryInterfaceVersion_fn)(void *thisPtr); + typedef int (__attribute__((thiscall)) *QueryAge_fn)(void *thisPtr); + /* __thiscall: this in ECX, 2 stack args for OpenDBI (slot 7) */ + typedef int (__attribute__((thiscall)) *OpenDBI_fn)(void *thisPtr, + const char *szTarget, const char *szMode, void **ppdbi); + + /* Read vtable pointer from the PDB object */ + DWORD *vtable = *(DWORD **)ppPDB; + TEST_CHECK_MSG(vtable != NULL, "PDB object vtable pointer is NULL"); + + /* Slot 0: QueryInterfaceVersion() -> 20091201 */ + QueryInterfaceVersion_fn queryIV = + (QueryInterfaceVersion_fn)(uintptr_t)vtable[0]; + int intv = queryIV(ppPDB); + TEST_CHECK_EQ(20091201, intv); + + /* Slot 5: QueryAge() -> 1 */ + QueryAge_fn queryAge = (QueryAge_fn)(uintptr_t)vtable[5]; + int age = queryAge(ppPDB); + TEST_CHECK_EQ(1, age); + + /* Slot 7: OpenDBI(szTarget, szMode, DBI**) -> 1, writes DBI ptr */ + void *pDBI = NULL; + OpenDBI_fn openDBI = (OpenDBI_fn)(uintptr_t)vtable[7]; + int dbiOk = openDBI(ppPDB, "target", "r", &pDBI); + TEST_CHECK_EQ(1, dbiOk); + TEST_CHECK_MSG(pDBI != NULL, "OpenDBI did not return a DBI handle"); + + /* DBI object should also have a valid vtable - test dispatch */ + DWORD *dbiVtable = *(DWORD **)pDBI; + TEST_CHECK_MSG(dbiVtable != NULL, "DBI object vtable pointer is NULL"); + + /* DBI slot 0: QueryImplementationVersion() -> 20091201 */ + QueryInterfaceVersion_fn dbiQueryIV = + (QueryInterfaceVersion_fn)(uintptr_t)dbiVtable[0]; + int dbiImpv = dbiQueryIV(pDBI); + TEST_CHECK_EQ(20091201, dbiImpv); + } + + /* 6. PDBCommit returns 1 */ int committed = commit_fn(ppPDB); TEST_CHECK_EQ(1, committed); - /* 6. PDBClose returns 1 */ + /* 7. PDBClose returns 1 */ int closed = close_fn(ppPDB); TEST_CHECK_EQ(1, closed); - /* 7. Stream functions */ + /* 8. Stream functions */ void *fakeStream = (void *)(uintptr_t)0x12345678; LONG cb = querycb_fn(fakeStream); TEST_CHECK_EQ(0, cb); From 5c572b4fa55d31e4ec48bc7fc82b7a4cea9157fe Mon Sep 17 00:00:00 2001 From: freeqaz <00free@gmail.com> Date: Thu, 12 Feb 2026 18:27:33 +0000 Subject: [PATCH 4/6] Address PR #110 review: replace mspdb codegen with cross-compiled DLL Replace 900 lines of runtime x86 machine code generation with a real 32-bit PE DLL cross-compiled by MinGW. The DLL implements PDB COM interfaces as C++ classes with compiler-generated vtables, eliminating MAP_32BIT code pages and manual stub assembly. Review comment fixes: - Simplify MapViewOfFileEx to use MAP_FIXED directly - Remove LLM-style comments (memoryapi, internal.h, memoryapi.h) - Add DEBUG_LOG to FindFirstFileExW validation branches - Remove unnecessary debug logs from GetFullPathNameW - Restore #ifdef __x86_64__ guard on NdrClientCall2 - Remove unused --path-alias feature - Remove speculative share violation tracking --- CMakeLists.txt | 23 +- dll/kernel32/fileapi.cpp | 29 +- dll/kernel32/internal.h | 1 - dll/kernel32/memoryapi.cpp | 17 - dll/kernel32/memoryapi.h | 1 - dll/mspdb.cpp | 893 ------------------------------------- dll/mspdb.h | 33 -- dll/mspdb/mspdb.def | 20 + dll/mspdb/mspdb_dll.cpp | 616 +++++++++++++++++++++++++ dll/mspdb_embed.cpp | 15 + dll/rpcrt4.cpp | 4 + dll/rpcrt4.h | 2 + src/files.cpp | 83 ---- src/files.h | 6 - src/main.cpp | 27 -- src/modules.cpp | 7 +- 16 files changed, 688 insertions(+), 1089 deletions(-) delete mode 100644 dll/mspdb.cpp delete mode 100644 dll/mspdb.h create mode 100644 dll/mspdb/mspdb.def create mode 100644 dll/mspdb/mspdb_dll.cpp create mode 100644 dll/mspdb_embed.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 84fe03c..fb998d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,7 +189,6 @@ add_executable(wibo dll/kernel32/winnt.cpp dll/kernel32/wow64apiset.cpp dll/lmgr.cpp - dll/mspdb.cpp dll/mscoree.cpp dll/ntdll.cpp dll/ole32.cpp @@ -221,6 +220,27 @@ else() embed_binary(dll/msvcrt.cpp "${MSVCRT_DLL}") endif() +find_program(WIBO_MINGW_CXX i686-w64-mingw32-g++) +if (WIBO_MINGW_CXX) + set(MSPDB_DLL ${CMAKE_CURRENT_BINARY_DIR}/vendor/mspdb80.dll) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/vendor) + add_custom_command(OUTPUT ${MSPDB_DLL} + COMMAND ${WIBO_MINGW_CXX} -shared -O2 -Wall -static + -fno-exceptions -fno-rtti + -o ${MSPDB_DLL} + ${CMAKE_CURRENT_SOURCE_DIR}/dll/mspdb/mspdb_dll.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dll/mspdb/mspdb.def + DEPENDS dll/mspdb/mspdb_dll.cpp dll/mspdb/mspdb.def) + add_custom_target(mspdb_dll DEPENDS ${MSPDB_DLL}) + add_dependencies(wibo mspdb_dll) + target_compile_definitions(wibo PRIVATE WIBO_HAS_MSPDB=1) + target_sources(wibo PRIVATE dll/mspdb_embed.cpp) + embed_binary(dll/mspdb_embed.cpp "${MSPDB_DLL}") +else() + message(WARNING "i686-w64-mingw32-g++ not found; mspdb DLL will not be built") + target_compile_definitions(wibo PRIVATE WIBO_HAS_MSPDB=0) +endif() + if (CMAKE_SYSTEM_NAME STREQUAL "Linux") target_sources(wibo PRIVATE src/async_io_epoll.cpp @@ -352,7 +372,6 @@ wibo_codegen_module(NAME version HEADERS dll/version.h) wibo_codegen_module(NAME rpcrt4 HEADERS dll/rpcrt4.h) wibo_codegen_module(NAME vcruntime HEADERS dll/vcruntime.h) wibo_codegen_module(NAME lmgr HEADERS dll/lmgr.h) -wibo_codegen_module(NAME mspdb HEADERS dll/mspdb.h) wibo_codegen_module(NAME ole32 HEADERS dll/ole32.h) wibo_codegen_module(NAME user32 HEADERS dll/user32.h) wibo_codegen_module(NAME ntdll HEADERS dll/ntdll.h) diff --git a/dll/kernel32/fileapi.cpp b/dll/kernel32/fileapi.cpp index d514686..5504d57 100644 --- a/dll/kernel32/fileapi.cpp +++ b/dll/kernel32/fileapi.cpp @@ -928,23 +928,6 @@ HANDLE WINAPI CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShar return INVALID_HANDLE_VALUE; } - // Compute access category for share violation checks - uint32_t accessCategory = 0; - if (dwDesiredAccess & (GENERIC_READ | FILE_READ_DATA | FILE_EXECUTE)) - accessCategory |= FILE_SHARE_READ; - if (dwDesiredAccess & (GENERIC_WRITE | FILE_WRITE_DATA | FILE_APPEND_DATA)) - accessCategory |= FILE_SHARE_WRITE; - if (dwDesiredAccess & DELETE) - accessCategory |= FILE_SHARE_DELETE; - - std::filesystem::path checkPath = files::canonicalPath(hostPath); - DEBUG_LOG(" share check: path=%s accessCat=%u shareMode=%u\n", checkPath.c_str(), accessCategory, dwShareMode); - if (files::checkShareViolation(checkPath, accessCategory, dwShareMode)) { - setLastError(ERROR_SHARING_VIOLATION); - DEBUG_LOG("-> ERROR_SHARING_VIOLATION\n"); - return INVALID_HANDLE_VALUE; - } - bool allowCreate = false; bool truncateExisting = false; bool existedBefore = pathExists; @@ -1112,12 +1095,7 @@ HANDLE WINAPI CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShar } fsObject->canonicalPath = std::move(canonicalPath); fsObject->shareAccess = shareMask; - fsObject->accessCategory = accessCategory; fsObject->deletePending = deleteOnClose; - if (accessCategory != 0 && !fsObject->canonicalPath.empty()) { - DEBUG_LOG(" share register: path=%s accessCat=%u share=%u\n", fsObject->canonicalPath.c_str(), accessCategory, shareMask); - files::registerOpenFile(fsObject->canonicalPath, accessCategory, shareMask); - } uint32_t handleFlags = 0; if (lpSecurityAttributes && lpSecurityAttributes->bInheritHandle) { @@ -1637,14 +1615,11 @@ DWORD WINAPI GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength, LPWSTR lp } std::string narrow = wideStringToString(lpFileName); - DEBUG_LOG("GetFullPathNameW input: %s\n", narrow.c_str()); FullPathInfo info; if (!computeFullPath(narrow, info)) { return 0; } - DEBUG_LOG("GetFullPathNameW result: %s\n", info.path.c_str()); - auto widePath = stringToWideString(info.path.c_str()); const size_t wideLen = widePath.size(); const auto required = static_cast(wideLen); @@ -1884,18 +1859,22 @@ HANDLE WINAPI FindFirstFileExW(LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevel return INVALID_HANDLE_VALUE; } if (fInfoLevelId != FindExInfoStandard) { + DEBUG_LOG(" -> ERROR_INVALID_PARAMETER (fInfoLevelId=%d)\n", fInfoLevelId); setLastError(ERROR_INVALID_PARAMETER); return INVALID_HANDLE_VALUE; } if (fSearchOp != FindExSearchNameMatch) { + DEBUG_LOG(" -> ERROR_INVALID_PARAMETER (fSearchOp=%d)\n", fSearchOp); setLastError(ERROR_INVALID_PARAMETER); return INVALID_HANDLE_VALUE; } if (lpSearchFilter) { + DEBUG_LOG(" -> ERROR_INVALID_PARAMETER (lpSearchFilter=%p)\n", lpSearchFilter); setLastError(ERROR_INVALID_PARAMETER); return INVALID_HANDLE_VALUE; } if (dwAdditionalFlags != 0) { + DEBUG_LOG(" -> ERROR_INVALID_PARAMETER (dwAdditionalFlags=0x%x)\n", dwAdditionalFlags); setLastError(ERROR_INVALID_PARAMETER); return INVALID_HANDLE_VALUE; } diff --git a/dll/kernel32/internal.h b/dll/kernel32/internal.h index e0fb22a..947ec9d 100644 --- a/dll/kernel32/internal.h +++ b/dll/kernel32/internal.h @@ -16,7 +16,6 @@ struct FsObject : ObjectBase { int fd = -1; std::filesystem::path canonicalPath; uint32_t shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE; - uint32_t accessCategory = 0; // FILE_SHARE_READ/WRITE/DELETE bits indicating what this handle does bool deletePending = false; bool closeOnDestroy = true; diff --git a/dll/kernel32/memoryapi.cpp b/dll/kernel32/memoryapi.cpp index d227949..f73f959 100644 --- a/dll/kernel32/memoryapi.cpp +++ b/dll/kernel32/memoryapi.cpp @@ -348,11 +348,7 @@ static LPVOID mapViewOfFileInternal(Pin mapping, DWORD dwDesiredA return nullptr; } requestedBase = reinterpret_cast(mapBaseAddr); -#ifdef MAP_FIXED_NOREPLACE - mapFlags |= MAP_FIXED_NOREPLACE; -#else mapFlags |= MAP_FIXED; -#endif } else { void *candidate = nullptr; wibo::heap::VmStatus reserveStatus = wibo::heap::reserveViewRange(mapLength, 0, 0, &candidate); @@ -367,17 +363,6 @@ static LPVOID mapViewOfFileInternal(Pin mapping, DWORD dwDesiredA errno = 0; void *mapBase = mmap(requestedBase, mapLength, prot, mapFlags, mmapFd, alignedOffset); -#ifdef MAP_FIXED_NOREPLACE - // On Windows, MapViewOfFileEx can map over freed VirtualAlloc regions. - // VirtualFree(MEM_RELEASE) leaves PROT_NONE pages, so MAP_FIXED_NOREPLACE - // fails with EEXIST. Fall back to MAP_FIXED to match Windows behavior. - if (mapBase == MAP_FAILED && baseAddress && errno == EEXIST) { - DEBUG_LOG("mapViewOfFileInternal: MAP_FIXED_NOREPLACE failed, retrying with MAP_FIXED\n"); - mapFlags = (mapFlags & ~MAP_FIXED_NOREPLACE) | MAP_FIXED; - errno = 0; - mapBase = mmap(requestedBase, mapLength, prot, mapFlags, mmapFd, alignedOffset); - } -#endif if (mapBase == MAP_FAILED) { int err = errno; if (baseAddress && (err == ENOMEM || err == EEXIST || err == EINVAL || err == EPERM)) { @@ -432,8 +417,6 @@ static LPVOID mapViewOfFileInternal(Pin mapping, DWORD dwDesiredA if (reservedMapping) { wibo::heap::registerViewRange(mapBase, mapLength, protect, view.protect); } else if (baseAddress) { - // Caller-specified base: register with the heap manager so VirtualAlloc - // won't allocate overlapping regions (matching Windows address space behavior). view.managed = true; wibo::heap::registerViewRange(mapBase, mapLength, protect, view.protect); } diff --git a/dll/kernel32/memoryapi.h b/dll/kernel32/memoryapi.h index d234a36..029b4ab 100644 --- a/dll/kernel32/memoryapi.h +++ b/dll/kernel32/memoryapi.h @@ -24,7 +24,6 @@ BOOL WINAPI GetProcessWorkingSetSize(HANDLE hProcess, PSIZE_T lpMinimumWorkingSe PSIZE_T lpMaximumWorkingSetSize); BOOL WINAPI SetProcessWorkingSetSize(HANDLE hProcess, SIZE_T dwMinimumWorkingSetSize, SIZE_T dwMaximumWorkingSetSize); -// Flush all active MAP_SHARED views to disk (call before _exit to ensure data persists) void flushAllFileViews(); } // namespace kernel32 diff --git a/dll/mspdb.cpp b/dll/mspdb.cpp deleted file mode 100644 index 231ade6..0000000 --- a/dll/mspdb.cpp +++ /dev/null @@ -1,893 +0,0 @@ -#include "mspdb.h" - -#include "common.h" -#include "context.h" -#include "kernel32/memoryapi.h" -#include "modules.h" - -#include -#include -#include -#include -#include -#include -#include - -// ============================================================================ -// Fake PDB vtable infrastructure -// -// The X360 linker requires a working PDB/XDB interface to complete PE writing. -// We provide fake COM-style objects with vtables full of x86 __thiscall stubs -// that accept all calls and return success/no-op values. The stubs are tiny -// snippets of x86 machine code generated at runtime into mmap'd executable -// memory. -// -// __thiscall convention: this in ECX, args on stack, callee pops args. -// Vtable dispatch: mov eax,[ecx]; call [eax+N*4] -// ============================================================================ - -// 32-bit COM-style object: first dword is vtable pointer -struct FakeVtObj { - uint32_t vptr; -}; - -// Fake objects - allocated from MAP_32BIT code page (must be <4GB for guest pointers) -static FakeVtObj *g_obj_pdb; -static FakeVtObj *g_obj_dbi; -static FakeVtObj *g_obj_mod; -static FakeVtObj *g_obj_tpi; -static FakeVtObj *g_obj_gsi; -static FakeVtObj *g_obj_dbg; -static FakeVtObj *g_obj_namemap; -static FakeVtObj *g_obj_stream; - -// Vtable sizes: must cover all slots the linker may call. -// Source: microsoft-pdb/langapi/include/pdb.h -static constexpr int kPdbVtableSlots = 64; // PDB interface (32 defined + headroom) -static constexpr int kDbiVtableSlots = 64; // DBI interface (57 defined + headroom) -static constexpr int kModVtableSlots = 64; // Mod interface -static constexpr int kTpiVtableSlots = 32; // TPI interface (23 defined) -static constexpr int kGsiVtableSlots = 16; // GSI interface (11 defined) -static constexpr int kDbgVtableSlots = 16; // Dbg interface -static constexpr int kNameMapVtableSlots = 20; // NameMap interface (15 defined) -static constexpr int kStreamVtableSlots = 12; // Stream interface (9 defined) - -// Vtables: arrays of 32-bit x86 function pointers - allocated from MAP_32BIT code page -static uint32_t *g_vt_pdb; -static uint32_t *g_vt_dbi; -static uint32_t *g_vt_mod; -static uint32_t *g_vt_tpi; -static uint32_t *g_vt_gsi; -static uint32_t *g_vt_dbg; -static uint32_t *g_vt_namemap; -static uint32_t *g_vt_stream; - -// Legacy sentinel value for PDBOpenStreamEx C-style export - allocated from MAP_32BIT code page -static uint32_t *g_fakeStream_legacy; - -// --- x86 machine code generation --- - -static uint8_t *g_code_page; -static size_t g_code_pos; -static constexpr size_t CODE_PAGE_SIZE = 16384; - -static uint8_t *codeAlloc(size_t n) { - if (!g_code_page || g_code_pos + n > CODE_PAGE_SIZE) { - DEBUG_LOG("mspdb: codeAlloc(%zu) failed: page=%p pos=%zu\n", n, g_code_page, g_code_pos); - abort(); - } - uint8_t *p = g_code_page + g_code_pos; - g_code_pos += n; - return p; -} - -static uint32_t addr32(void *p) { - return (uint32_t)(uintptr_t)p; -} - -// Generate: mov eax, ; ret -// For __thiscall methods that return a constant value. -static uint32_t genRet(uint32_t retval, int nargs) { - uint16_t pop = nargs * 4; - size_t sz = 5 + (pop ? 3 : 1); - uint8_t *s = codeAlloc(sz); - uint8_t *p = s; - *p++ = 0xB8; - memcpy(p, &retval, 4); - p += 4; // mov eax, retval - if (pop) { - *p++ = 0xC2; - memcpy(p, &pop, 2); - } // ret pop - else { - *p++ = 0xC3; - } // ret - return addr32(s); -} - -// Generate: ret (void return) -static uint32_t genVoid(int nargs) { - uint16_t pop = nargs * 4; - size_t sz = pop ? 3 : 1; - uint8_t *s = codeAlloc(sz); - uint8_t *p = s; - if (pop) { - *p++ = 0xC2; - memcpy(p, &pop, 2); - } else { - *p++ = 0xC3; - } - return addr32(s); -} - -// Generate: mov eax,[esp+off]; test eax,eax; jz skip; mov dword [eax],val; skip: mov eax,1; ret pop -// For __thiscall methods that write a 32-bit value to an output pointer param. -// outArg: 0-based index of the output param among stack args (not counting this) -// NULL-safe: skips the write if the output pointer is NULL. -static uint32_t genOut(int outArg, uint32_t val, int nargs) { - uint8_t off = (uint8_t)((outArg + 1) * 4); // +1 for return address - uint16_t pop = nargs * 4; - uint8_t *s = codeAlloc(28); - uint8_t *p = s; - // mov eax, [esp+off] - *p++ = 0x8B; - *p++ = 0x44; - *p++ = 0x24; - *p++ = off; - // test eax, eax - *p++ = 0x85; - *p++ = 0xC0; - // jz +6 (skip the mov dword ptr [eax], val) - *p++ = 0x74; - *p++ = 0x06; - // mov dword ptr [eax], val - *p++ = 0xC7; - *p++ = 0x00; - memcpy(p, &val, 4); - p += 4; - // mov eax, 1 - *p++ = 0xB8; - *p++ = 0x01; - *p++ = 0x00; - *p++ = 0x00; - *p++ = 0x00; - // ret pop - if (pop) { - *p++ = 0xC2; - memcpy(p, &pop, 2); - } else { - *p++ = 0xC3; - } - return addr32(s); -} - -// Generate: mov eax,[esp+off]; mov byte [eax],0; xor eax,eax; ret pop -// For QueryLastError-style methods: write empty string to buffer, return 0. -static uint32_t genClearBuf(int bufArg, int nargs) { - uint8_t off = (uint8_t)((bufArg + 1) * 4); - uint16_t pop = nargs * 4; - uint8_t *s = codeAlloc(16); - uint8_t *p = s; - // mov eax, [esp+off] - *p++ = 0x8B; - *p++ = 0x44; - *p++ = 0x24; - *p++ = off; - // mov byte ptr [eax], 0 - *p++ = 0xC6; - *p++ = 0x00; - *p++ = 0x00; - // xor eax, eax - *p++ = 0x31; - *p++ = 0xC0; - // ret pop - if (pop) { - *p++ = 0xC2; - memcpy(p, &pop, 2); - } else { - *p++ = 0xC3; - } - return addr32(s); -} - -// Generate: int3 (trap for unimplemented methods, with index in AL for debugging) -static uint32_t genTrap(uint8_t idx) { - uint8_t *s = codeAlloc(4); - s[0] = 0xB0; - s[1] = idx; // mov al, idx - s[2] = 0xCC; // int3 - s[3] = 0xC3; // ret (fallthrough safety) - return addr32(s); -} - -// PDB version constants (VS2010 era - matches X360 linker) -static constexpr uint32_t PDB_INTV = 20091201; -static constexpr uint32_t PDB_IMPV = 20091201; - -static bool g_vtables_ready = false; - -static void initVtables() { - if (g_vtables_ready) - return; - g_vtables_ready = true; - - // Allocate executable page for x86 stubs (MAP_32BIT = first 2GB) - g_code_page = (uint8_t *)mmap(nullptr, CODE_PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0); - if (g_code_page == MAP_FAILED) { - // Fallback without MAP_32BIT - g_code_page = (uint8_t *)mmap(nullptr, CODE_PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - } - if (g_code_page == MAP_FAILED) { - DEBUG_LOG("mspdb: mmap for code page failed\n"); - abort(); - } - if ((uintptr_t)g_code_page >= 0xFFFFFFFFULL) { - DEBUG_LOG("mspdb: code page at %p not 32-bit addressable\n", g_code_page); - abort(); - } - g_code_pos = 0; - - // Allocate fake objects from MAP_32BIT page (must be <4GB for guest pointers) - g_obj_pdb = (FakeVtObj *)codeAlloc(sizeof(FakeVtObj)); - g_obj_dbi = (FakeVtObj *)codeAlloc(sizeof(FakeVtObj)); - g_obj_mod = (FakeVtObj *)codeAlloc(sizeof(FakeVtObj)); - g_obj_tpi = (FakeVtObj *)codeAlloc(sizeof(FakeVtObj)); - g_obj_gsi = (FakeVtObj *)codeAlloc(sizeof(FakeVtObj)); - g_obj_dbg = (FakeVtObj *)codeAlloc(sizeof(FakeVtObj)); - g_obj_namemap = (FakeVtObj *)codeAlloc(sizeof(FakeVtObj)); - g_obj_stream = (FakeVtObj *)codeAlloc(sizeof(FakeVtObj)); - - // Allocate vtable arrays from MAP_32BIT page - g_vt_pdb = (uint32_t *)codeAlloc(kPdbVtableSlots * sizeof(uint32_t)); - g_vt_dbi = (uint32_t *)codeAlloc(kDbiVtableSlots * sizeof(uint32_t)); - g_vt_mod = (uint32_t *)codeAlloc(kModVtableSlots * sizeof(uint32_t)); - g_vt_tpi = (uint32_t *)codeAlloc(kTpiVtableSlots * sizeof(uint32_t)); - g_vt_gsi = (uint32_t *)codeAlloc(kGsiVtableSlots * sizeof(uint32_t)); - g_vt_dbg = (uint32_t *)codeAlloc(kDbgVtableSlots * sizeof(uint32_t)); - g_vt_namemap = (uint32_t *)codeAlloc(kNameMapVtableSlots * sizeof(uint32_t)); - g_vt_stream = (uint32_t *)codeAlloc(kStreamVtableSlots * sizeof(uint32_t)); - - // Allocate legacy stream sentinel - g_fakeStream_legacy = (uint32_t *)codeAlloc(sizeof(uint32_t)); - *g_fakeStream_legacy = 0; - - DEBUG_LOG("mspdb: code page at %p, objects: PDB=%p DBI=%p Mod=%p TPI=%p GSI=%p\n", g_code_page, g_obj_pdb, - g_obj_dbi, g_obj_mod, g_obj_tpi, g_obj_gsi); - - // Shorthand addresses for fake sub-objects - uint32_t pPDB = addr32(g_obj_pdb); - uint32_t pDBI = addr32(g_obj_dbi); - uint32_t pMod = addr32(g_obj_mod); - uint32_t pTPI = addr32(g_obj_tpi); - uint32_t pGSI = addr32(g_obj_gsi); - uint32_t pDbg = addr32(g_obj_dbg); - uint32_t pNM = addr32(g_obj_namemap); - uint32_t pStr = addr32(g_obj_stream); - - // --- PDB vtable (microsoft-pdb/langapi/include/pdb.h, slots 0-31) --- - // Fill with traps first - for (int i = 0; i < kPdbVtableSlots; i++) - g_vt_pdb[i] = genTrap((uint8_t)i); - // 0: QueryInterfaceVersion() -> INTV - g_vt_pdb[0] = genRet(PDB_INTV, 0); - // 1: QueryImplementationVersion() -> IMPV - g_vt_pdb[1] = genRet(PDB_IMPV, 0); - // 2: QueryLastError(char szError[]) -> EC - g_vt_pdb[2] = genClearBuf(0, 1); - // 3: QueryPDBName(char szPDB[]) -> char* (return the buffer with empty string) - g_vt_pdb[3] = genClearBuf(0, 1); // sets buf[0]=0, returns 0 (NULL) but that's OK - // 4: QuerySignature() -> SIG - g_vt_pdb[4] = genRet(0, 0); - // 5: QueryAge() -> AGE - g_vt_pdb[5] = genRet(1, 0); - // 6: CreateDBI(szTarget, DBI** ppdbi) -> BOOL [2 args, out=arg1] - g_vt_pdb[6] = genOut(1, pDBI, 2); - // 7: OpenDBI(szTarget, szMode, DBI** ppdbi) -> BOOL [3 args, out=arg2] - g_vt_pdb[7] = genOut(2, pDBI, 3); - // 8: OpenTpi(szMode, TPI** pptpi) -> BOOL [2 args, out=arg1] - g_vt_pdb[8] = genOut(1, pTPI, 2); - // 9: OpenIpi(szMode, TPI** pptpi) -> BOOL [2 args, out=arg1] - g_vt_pdb[9] = genOut(1, pTPI, 2); - // 10: Commit() -> BOOL - g_vt_pdb[10] = genRet(1, 0); - // 11: Close() -> BOOL - g_vt_pdb[11] = genRet(1, 0); - // 12: OpenStream(szStream, Stream** ppstream) -> BOOL [2 args, out=arg1] - g_vt_pdb[12] = genOut(1, pStr, 2); - // 13: GetEnumStreamNameMap(Enum**) -> BOOL - g_vt_pdb[13] = genRet(0, 1); // return FALSE - no enumerator - // 14: GetRawBytes(pfn) -> BOOL - g_vt_pdb[14] = genRet(0, 1); - // 15: QueryPdbImplementationVersion() -> IMPV - g_vt_pdb[15] = genRet(PDB_IMPV, 0); - // 16: OpenDBIEx(szTarget, szMode, DBI**, pfn) -> BOOL [4 args, out=arg2] - g_vt_pdb[16] = genOut(2, pDBI, 4); - // 17: CopyTo(szDst, filter, reserved) -> BOOL - g_vt_pdb[17] = genRet(1, 3); - // 18: OpenSrc(Src**) -> BOOL - g_vt_pdb[18] = genRet(0, 1); // return FALSE - // 19: QueryLastErrorExW(wszError, cchMax) -> EC - g_vt_pdb[19] = genClearBuf(0, 2); - // 20: QueryPDBNameExW(wszPDB, cchMax) -> wchar_t* - // NOTE: linker's helper at 0x42EA4B only pushes 1 arg before calling this - g_vt_pdb[20] = genClearBuf(0, 1); - // 21: QuerySignature2(PSIG70) -> BOOL - g_vt_pdb[21] = genRet(1, 1); - // 22: CopyToW(szDst, filter, reserved) -> BOOL - g_vt_pdb[22] = genRet(1, 3); - // 23: fIsSZPDB() -> BOOL - g_vt_pdb[23] = genRet(1, 0); - // 24: OpenStreamW(szStream, Stream**) -> BOOL [2 args, out=arg1] - g_vt_pdb[24] = genOut(1, pStr, 2); - // 25: CopyToW2(szDst, filter, pfn, pvCtx) -> BOOL - g_vt_pdb[25] = genRet(1, 4); - // 26: OpenStreamEx(szStream, szMode, Stream**) -> BOOL [3 args, out=arg2] - g_vt_pdb[26] = genOut(2, pStr, 3); - // 27: RegisterPDBMapping(from, to) -> BOOL - g_vt_pdb[27] = genRet(1, 2); - // 28: EnablePrefetching() -> BOOL - g_vt_pdb[28] = genRet(1, 0); - // 29: FLazy() -> BOOL - g_vt_pdb[29] = genRet(0, 0); - // 30: FMinimal() -> BOOL - g_vt_pdb[30] = genRet(0, 0); - // 31: ResetGUID(pb, cb) -> BOOL - g_vt_pdb[31] = genRet(1, 2); - - // --- DBI vtable (microsoft-pdb DBI interface, slots 0-63) --- - for (int i = 0; i < kDbiVtableSlots; i++) - g_vt_dbi[i] = genTrap((uint8_t)i); - // 0: QueryImplementationVersion() -> IMPV - g_vt_dbi[0] = genRet(PDB_IMPV, 0); - // 1: QueryInterfaceVersion() -> INTV - g_vt_dbi[1] = genRet(PDB_INTV, 0); - // 2: OpenMod(szModule, szFile, Mod**) [3 args, out=arg2] - g_vt_dbi[2] = genOut(2, pMod, 3); - // 3: DeleteMod(szModule) -> BOOL - g_vt_dbi[3] = genRet(1, 1); - // 4: QueryNextMod(pmod, ppmodNext) -> BOOL [2 args, out=arg1 -> NULL] - g_vt_dbi[4] = genOut(1, 0, 2); // *ppmodNext = NULL (end of list) - // 5: OpenGlobals(GSI**) [1 arg, out=arg0] - g_vt_dbi[5] = genOut(0, pGSI, 1); - // 6: OpenPublics(GSI**) [1 arg, out=arg0] - g_vt_dbi[6] = genOut(0, pGSI, 1); - // 7: AddSec(isect, flags, off, cb) -> BOOL - g_vt_dbi[7] = genRet(1, 4); - // 8: QueryModFromAddr(6 args) -> BOOL - g_vt_dbi[8] = genRet(0, 6); - // 9: QuerySecMap(pb, pcb) -> BOOL [2 args, out=arg1 -> 0] - g_vt_dbi[9] = genOut(1, 0, 2); - // 10: QueryFileInfo(pb, pcb) -> BOOL [2 args, out=arg1 -> 0] - g_vt_dbi[10] = genOut(1, 0, 2); - // 11: DumpMods() -> void - g_vt_dbi[11] = genVoid(0); - // 12: DumpSecContribs() -> void - g_vt_dbi[12] = genVoid(0); - // 13: DumpSecMap() -> void - g_vt_dbi[13] = genVoid(0); - // 14: Close() -> BOOL - g_vt_dbi[14] = genRet(1, 0); - // 15: AddThunkMap(7 args) -> BOOL - g_vt_dbi[15] = genRet(1, 7); - // 16: AddPublic(szPublic, isect, off) -> BOOL - g_vt_dbi[16] = genRet(1, 3); - // 17: getEnumContrib(Enum**) -> BOOL - g_vt_dbi[17] = genRet(0, 1); - // 18: QueryTypeServer(itsm, TPI**) -> BOOL - g_vt_dbi[18] = genRet(0, 2); - // 19: QueryItsmForTi(ti, pitsm) -> BOOL - g_vt_dbi[19] = genRet(0, 2); - // 20: QueryNextItsm(itsm, inext) -> BOOL - g_vt_dbi[20] = genRet(0, 2); - // 21: QueryLazyTypes() -> BOOL - g_vt_dbi[21] = genRet(0, 0); - // 22: SetLazyTypes(fLazy) -> BOOL - g_vt_dbi[22] = genRet(1, 1); - // 23: FindTypeServers(pec, szError) -> BOOL - g_vt_dbi[23] = genRet(1, 2); - // 24: DumpTypeServers() -> void - g_vt_dbi[24] = genVoid(0); - // 25: OpenDbg(dbgtype, Dbg**) [2 args, out=arg1] - g_vt_dbi[25] = genOut(1, pDbg, 2); - // 26: QueryDbgTypes(pdbgtype, pcDbgtype) -> BOOL [2 args, out=arg1 -> 0] - g_vt_dbi[26] = genOut(1, 0, 2); - // 27: QueryAddrForSec(6 args) -> BOOL - g_vt_dbi[27] = genRet(0, 6); - // 28: QueryAddrForSecEx(7 args) -> BOOL - g_vt_dbi[28] = genRet(0, 7); - // 29: QuerySupportsEC() -> BOOL - g_vt_dbi[29] = genRet(0, 0); - // 30: QueryPdb(PDB**) [1 arg, out=arg0] - g_vt_dbi[30] = genOut(0, pPDB, 1); - // 31: AddLinkInfo(pli) -> BOOL - g_vt_dbi[31] = genRet(1, 1); - // 32: QueryLinkInfo(pli, pcb) -> BOOL [2 args, out=arg1 -> 0] - g_vt_dbi[32] = genOut(1, 0, 2); - // 33: QueryAge() const -> AGE - g_vt_dbi[33] = genRet(1, 0); - // 34: QueryHeader() const -> void* - g_vt_dbi[34] = genRet(0, 0); - // 35: FlushTypeServers() -> void - g_vt_dbi[35] = genVoid(0); - // 36: QueryTypeServerByPdb(szPdb, pitsm) -> BOOL - g_vt_dbi[36] = genRet(0, 2); - // 37: OpenModW(szModule, szFile, Mod**) [3 args, out=arg2] - g_vt_dbi[37] = genOut(2, pMod, 3); - // 38: DeleteModW(szModule) -> BOOL - g_vt_dbi[38] = genRet(1, 1); - // 39: AddPublicW(szPublic, isect, off, cvpsf) -> BOOL - g_vt_dbi[39] = genRet(1, 4); - // 40: QueryTypeServerByPdbW(szPdb, pitsm) -> BOOL - g_vt_dbi[40] = genRet(0, 2); - // 41: AddLinkInfoW(pli) -> BOOL - g_vt_dbi[41] = genRet(1, 1); - // 42: AddPublic2(szPublic, isect, off, cvpsf) -> BOOL - g_vt_dbi[42] = genRet(1, 4); - // 43: QueryMachineType() const -> USHORT - g_vt_dbi[43] = genRet(0, 0); - // 44: SetMachineType(wMachine) -> void - g_vt_dbi[44] = genVoid(1); - // 45: RemoveDataForRva(rva, cb) -> void - g_vt_dbi[45] = genVoid(2); - // 46: FStripped() -> BOOL - g_vt_dbi[46] = genRet(0, 0); - // 47: QueryModFromAddr2(7 args) -> BOOL - g_vt_dbi[47] = genRet(0, 7); - // 48: QueryNoOfMods(pcMods) -> BOOL [1 arg, out=arg0 -> 0] - g_vt_dbi[48] = genOut(0, 0, 1); - // 49: QueryMods(ppmodNext, cMods) -> BOOL - g_vt_dbi[49] = genRet(1, 2); - // 50: QueryImodFromAddr(7 args) -> BOOL - g_vt_dbi[50] = genRet(0, 7); - // 51: OpenModFromImod(imod, Mod**) [2 args] - g_vt_dbi[51] = genRet(0, 2); - // 52: QueryHeader2(cb, pb, pcbOut) -> BOOL [3 args, out=arg2 -> 0] - g_vt_dbi[52] = genOut(2, 0, 3); - // 53: FAddSourceMappingItem(3 args) -> BOOL - g_vt_dbi[53] = genRet(1, 3); - // 54: FSetPfnNotePdbUsed(pvCtx, pfn) -> BOOL - g_vt_dbi[54] = genRet(1, 2); - // 55: FCTypes() -> BOOL - g_vt_dbi[55] = genRet(0, 0); - // 56: QueryFileInfo2(pb, pcb) -> BOOL [2 args, out=arg1 -> 0] - g_vt_dbi[56] = genOut(1, 0, 2); - // 57: FSetPfnQueryCallback(pvCtx, pfn) -> BOOL - g_vt_dbi[57] = genRet(1, 2); - // 58: FSetPfnNoteTypeMismatch(pvCtx, pfn) -> BOOL - g_vt_dbi[58] = genRet(1, 2); - // 59: FSetPfnTmdTypeFilter(pvCtx, pfn) -> BOOL - g_vt_dbi[59] = genRet(1, 2); - // 60: RemovePublic(szPublic) -> BOOL - g_vt_dbi[60] = genRet(1, 1); - // 61: getEnumContrib2(Enum**) -> BOOL - g_vt_dbi[61] = genRet(0, 1); - // 62: QueryModFromAddrEx(8 args) -> BOOL - g_vt_dbi[62] = genRet(0, 8); - // 63: QueryImodFromAddrEx(8 args) -> BOOL - g_vt_dbi[63] = genRet(0, 8); - - // --- Mod vtable (microsoft-pdb Mod interface, slots 0-49) --- - for (int i = 0; i < kModVtableSlots; i++) - g_vt_mod[i] = genTrap((uint8_t)i); - // 0: QueryInterfaceVersion() -> INTV - g_vt_mod[0] = genRet(PDB_INTV, 0); - // 1: QueryImplementationVersion() -> IMPV - g_vt_mod[1] = genRet(PDB_IMPV, 0); - // 2: AddTypes(pbTypes, cb) -> BOOL - g_vt_mod[2] = genRet(1, 2); - // 3: AddSymbols(pbSym, cb) -> BOOL - g_vt_mod[3] = genRet(1, 2); - // 4: AddPublic(szPublic, isect, off) -> BOOL - g_vt_mod[4] = genRet(1, 3); - // 5: AddLines(szSrc, isect, offCon, cbCon, doff, lineStart, pbCoff, cbCoff) -> BOOL - g_vt_mod[5] = genRet(1, 8); - // 6: AddSecContrib(isect, off, cb, dwCharacteristics) -> BOOL - g_vt_mod[6] = genRet(1, 4); - // 7: QueryCBName(pcb) -> BOOL [1 arg, out=arg0 -> 0] - g_vt_mod[7] = genOut(0, 0, 1); - // 8: QueryName(szName, pcb) -> BOOL - g_vt_mod[8] = genClearBuf(0, 2); - // 9: QuerySymbols(pbSym, pcb) -> BOOL [2 args, out=arg1 -> 0] - g_vt_mod[9] = genOut(1, 0, 2); - // 10: QueryLines(pbLines, pcb) -> BOOL [2 args, out=arg1 -> 0] - g_vt_mod[10] = genOut(1, 0, 2); - // 11: SetPvClient(pvClient) -> BOOL - g_vt_mod[11] = genRet(1, 1); - // 12: GetPvClient(ppvClient) -> BOOL - g_vt_mod[12] = genOut(0, 0, 1); - // 13: QueryFirstCodeSecContrib(4 args) -> BOOL - g_vt_mod[13] = genRet(0, 4); - // 14: QueryImod(pimod) -> BOOL [1 arg, out=arg0 -> 0] - g_vt_mod[14] = genOut(0, 0, 1); - // 15: QueryDBI(ppdbi) -> BOOL [1 arg, out=arg0] - g_vt_mod[15] = genOut(0, pDBI, 1); - // 16: Close() -> BOOL - g_vt_mod[16] = genRet(1, 0); - // 17: QueryCBFile(pcb) -> BOOL [1 arg, out=arg0 -> 0] - g_vt_mod[17] = genOut(0, 0, 1); - // 18: QueryFile(szFile, pcb) -> BOOL - g_vt_mod[18] = genClearBuf(0, 2); - // 19: QueryTpi(pptpi) -> BOOL [1 arg, out=arg0] - g_vt_mod[19] = genOut(0, pTPI, 1); - // 20: AddSecContribEx(isect, off, cb, dwChar, dwDataCrc, dwRelocCrc) -> BOOL - g_vt_mod[20] = genRet(1, 6); - // 21: QueryItsm(pitsm) -> BOOL [1 arg, out=arg0 -> 0] - g_vt_mod[21] = genOut(0, 0, 1); - // 22: QuerySrcFile(szFile, pcb) -> BOOL - g_vt_mod[22] = genClearBuf(0, 2); - // 23: QuerySupportsEC() -> BOOL - g_vt_mod[23] = genRet(0, 0); - // 24: QueryPdbFile(szFile, pcb) -> BOOL - g_vt_mod[24] = genClearBuf(0, 2); - // 25: ReplaceLines(pbLines, cb) -> BOOL - g_vt_mod[25] = genRet(1, 2); - // 26: GetEnumLines(EnumLines**) -> bool - g_vt_mod[26] = genRet(0, 1); - // 27: QueryLineFlags(pdwFlags) -> bool - g_vt_mod[27] = genRet(0, 1); - // 28: QueryFileNameInfo(5 args) -> bool - g_vt_mod[28] = genRet(0, 5); - // 29: AddPublicW(szPublic, isect, off, cvpsf) -> BOOL - g_vt_mod[29] = genRet(1, 4); - // 30: AddLinesW(szSrc, isect, offCon, cbCon, doff, lineStart, pbCoff, cbCoff) -> BOOL - g_vt_mod[30] = genRet(1, 8); - // 31: QueryNameW(szName, pcb) -> BOOL - g_vt_mod[31] = genClearBuf(0, 2); - // 32: QueryFileW(szFile, pcb) -> BOOL - g_vt_mod[32] = genClearBuf(0, 2); - // 33: QuerySrcFileW(szFile, pcb) -> BOOL - g_vt_mod[33] = genClearBuf(0, 2); - // 34: QueryPdbFileW(szFile, pcb) -> BOOL - g_vt_mod[34] = genClearBuf(0, 2); - // 35: AddPublic2(szPublic, isect, off, cvpsf) -> BOOL - g_vt_mod[35] = genRet(1, 4); - // 36: InsertLines(pbLines, cb) -> BOOL - g_vt_mod[36] = genRet(1, 2); - // 37: QueryLines2(cbLines, pbLines, pcbLines) -> BOOL [3 args, out=arg2 -> 0] - g_vt_mod[37] = genOut(2, 0, 3); - // 38-49: Various Query/Add methods -> return 0 or 1 - for (int i = 38; i <= 49; i++) - g_vt_mod[i] = genRet(0, 3); // safe default: return FALSE, pop 3 args - - // --- TPI vtable (microsoft-pdb TPI interface, slots 0-22) --- - for (int i = 0; i < kTpiVtableSlots; i++) - g_vt_tpi[i] = genTrap((uint8_t)i); - // 0: QueryInterfaceVersion() -> INTV - g_vt_tpi[0] = genRet(PDB_INTV, 0); - // 1: QueryImplementationVersion() -> IMPV - g_vt_tpi[1] = genRet(PDB_IMPV, 0); - // 2: QueryTi16ForCVRecord(pb, pti) -> BOOL - g_vt_tpi[2] = genRet(0, 2); - // 3: QueryCVRecordForTi16(ti, pb, pcb) -> BOOL - g_vt_tpi[3] = genRet(0, 3); - // 4: QueryPbCVRecordForTi16(ti, ppb) -> BOOL - g_vt_tpi[4] = genRet(0, 2); - // 5: QueryTi16Min() -> TI16 - g_vt_tpi[5] = genRet(0, 0); - // 6: QueryTi16Mac() -> TI16 - g_vt_tpi[6] = genRet(0, 0); - // 7: QueryCb() -> long - g_vt_tpi[7] = genRet(0, 0); - // 8: Close() -> BOOL - g_vt_tpi[8] = genRet(1, 0); - // 9: Commit() -> BOOL - g_vt_tpi[9] = genRet(1, 0); - // 10: QueryTi16ForUDT(sz, fCase, pti) -> BOOL - g_vt_tpi[10] = genRet(0, 3); - // 11: SupportQueryTiForUDT() -> BOOL - g_vt_tpi[11] = genRet(0, 0); - // 12: fIs16bitTypePool() -> BOOL - g_vt_tpi[12] = genRet(0, 0); - // 13: QueryTiForUDT(sz, fCase, pti) -> BOOL - g_vt_tpi[13] = genRet(0, 3); - // 14: QueryTiForCVRecord(pb, pti) -> BOOL - g_vt_tpi[14] = genRet(0, 2); - // 15: QueryCVRecordForTi(ti, pb, pcb) -> BOOL - g_vt_tpi[15] = genRet(0, 3); - // 16: QueryPbCVRecordForTi(ti, ppb) -> BOOL - g_vt_tpi[16] = genRet(0, 2); - // 17: QueryTiMin() -> TI - g_vt_tpi[17] = genRet(0x1000, 0); // standard min TI - // 18: QueryTiMac() -> TI - g_vt_tpi[18] = genRet(0x1000, 0); // same = empty type pool - // 19: AreTypesEqual(ti1, ti2) -> BOOL - g_vt_tpi[19] = genRet(0, 2); - // 20: IsTypeServed(ti) -> BOOL - g_vt_tpi[20] = genRet(0, 1); - // 21: QueryTiForUDTW(wcs, fCase, pti) -> BOOL - g_vt_tpi[21] = genRet(0, 3); - // 22: QueryModSrcLineForUDTDefn(4 args) -> BOOL - g_vt_tpi[22] = genRet(0, 4); - - // --- GSI vtable (microsoft-pdb GSI interface, slots 0-10) --- - for (int i = 0; i < kGsiVtableSlots; i++) - g_vt_gsi[i] = genTrap((uint8_t)i); - // 0: QueryInterfaceVersion() -> INTV - g_vt_gsi[0] = genRet(PDB_INTV, 0); - // 1: QueryImplementationVersion() -> IMPV - g_vt_gsi[1] = genRet(PDB_IMPV, 0); - // 2: NextSym(pbSym) -> BYTE* (return NULL = end) - g_vt_gsi[2] = genRet(0, 1); - // 3: HashSym(szName, pbSym) -> BYTE* - g_vt_gsi[3] = genRet(0, 2); - // 4: NearestSym(isect, off, pdisp) -> BYTE* - g_vt_gsi[4] = genRet(0, 3); - // 5: Close() -> BOOL - g_vt_gsi[5] = genRet(1, 0); - // 6: getEnumThunk(isect, off, ppenum) -> BOOL - g_vt_gsi[6] = genRet(0, 3); - // 7: OffForSym(pbSym) -> ulong - g_vt_gsi[7] = genRet(0, 1); - // 8: SymForOff(off) -> BYTE* - g_vt_gsi[8] = genRet(0, 1); - // 9: HashSymW(wcsName, pbSym) -> BYTE* - g_vt_gsi[9] = genRet(0, 2); - // 10: getEnumByAddr(ppEnum) -> BOOL - g_vt_gsi[10] = genRet(0, 1); - - // --- Dbg vtable (microsoft-pdb Dbg interface, slots 0-10) --- - for (int i = 0; i < kDbgVtableSlots; i++) - g_vt_dbg[i] = genTrap((uint8_t)i); - // 0: Close() -> BOOL - g_vt_dbg[0] = genRet(1, 0); - // 1: QuerySize() -> long - g_vt_dbg[1] = genRet(0, 0); - // 2: Reset() -> void - g_vt_dbg[2] = genVoid(0); - // 3: Skip(celt) -> BOOL - g_vt_dbg[3] = genRet(1, 1); - // 4: QueryNext(celt, rgelt) -> BOOL - g_vt_dbg[4] = genRet(0, 2); // return FALSE = no more - // 5: Find(pelt) -> BOOL - g_vt_dbg[5] = genRet(0, 1); - // 6: Clear() -> BOOL - g_vt_dbg[6] = genRet(1, 0); - // 7: Append(celt, rgelt) -> BOOL - g_vt_dbg[7] = genRet(1, 2); - // 8: ReplaceNext(celt, rgelt) -> BOOL - g_vt_dbg[8] = genRet(1, 2); - // 9: Clone(ppDbg) -> BOOL - g_vt_dbg[9] = genRet(0, 1); - // 10: QueryElementSize() -> long - g_vt_dbg[10] = genRet(0, 0); - - // --- NameMap vtable (microsoft-pdb NameMap interface, slots 0-14) --- - for (int i = 0; i < kNameMapVtableSlots; i++) - g_vt_namemap[i] = genTrap((uint8_t)i); - // 0: close() -> BOOL - g_vt_namemap[0] = genRet(1, 0); - // 1: reinitialize() -> BOOL - g_vt_namemap[1] = genRet(1, 0); - // 2: getNi(sz, pni) -> BOOL [2 args, out=arg1 -> 0] - g_vt_namemap[2] = genOut(1, 0, 2); - // 3: getName(ni, psz) -> BOOL - g_vt_namemap[3] = genRet(0, 2); - // 4: getEnumNameMap(ppenum) -> BOOL - g_vt_namemap[4] = genRet(0, 1); - // 5: contains(sz, pni) -> BOOL - g_vt_namemap[5] = genRet(0, 2); - // 6: commit() -> BOOL - g_vt_namemap[6] = genRet(1, 0); - // 7: isValidNi(ni) -> BOOL - g_vt_namemap[7] = genRet(0, 1); - // 8: getNiW(sz, pni) -> BOOL [2 args, out=arg1 -> 0] - g_vt_namemap[8] = genOut(1, 0, 2); - // 9: getNameW(ni, szName, pcch) -> BOOL - g_vt_namemap[9] = genRet(0, 3); - // 10: containsW(sz, pni) -> BOOL - g_vt_namemap[10] = genRet(0, 2); - // 11: containsUTF8(sz, pni) -> BOOL - g_vt_namemap[11] = genRet(0, 2); - // 12: getNiUTF8(sz, pni) -> BOOL [2 args, out=arg1 -> 0] - g_vt_namemap[12] = genOut(1, 0, 2); - // 13: getNameA(ni, psz) -> BOOL - g_vt_namemap[13] = genRet(0, 2); - // 14: getNameW2(ni, pwsz) -> BOOL - g_vt_namemap[14] = genRet(0, 2); - - // --- Stream vtable (microsoft-pdb Stream interface, slots 0-8) --- - for (int i = 0; i < kStreamVtableSlots; i++) - g_vt_stream[i] = genTrap((uint8_t)i); - // 0: QueryCb() -> long - g_vt_stream[0] = genRet(0, 0); - // 1: Read(off, pvBuf, pcbBuf) -> BOOL - g_vt_stream[1] = genRet(1, 3); - // 2: Write(off, pvBuf, cbBuf) -> BOOL - g_vt_stream[2] = genRet(1, 3); - // 3: Replace(pvBuf, cbBuf) -> BOOL - g_vt_stream[3] = genRet(1, 2); - // 4: Append(pvBuf, cbBuf) -> BOOL - g_vt_stream[4] = genRet(1, 2); - // 5: Delete() -> BOOL - g_vt_stream[5] = genRet(1, 0); - // 6: Release() -> BOOL - g_vt_stream[6] = genRet(1, 0); - // 7: Read2(off, pvBuf, cbBuf) -> BOOL - g_vt_stream[7] = genRet(1, 3); - // 8: Truncate(cb) -> BOOL - g_vt_stream[8] = genRet(1, 1); - - // --- Wire up vtable pointers --- - g_obj_pdb->vptr = addr32(g_vt_pdb); - g_obj_dbi->vptr = addr32(g_vt_dbi); - g_obj_mod->vptr = addr32(g_vt_mod); - g_obj_tpi->vptr = addr32(g_vt_tpi); - g_obj_gsi->vptr = addr32(g_vt_gsi); - g_obj_dbg->vptr = addr32(g_vt_dbg); - g_obj_namemap->vptr = addr32(g_vt_namemap); - g_obj_stream->vptr = addr32(g_vt_stream); - - DEBUG_LOG("mspdb: fake vtables initialized, code used %zu/%zu bytes\n", g_code_pos, CODE_PAGE_SIZE); -} - -static int openPDB(LONG *pec, void **ppPDB) { - initVtables(); - if (pec) - *(uint32_t *)pec = 0; // EC_OK - if (ppPDB) - *(uint32_t *)ppPDB = addr32(g_obj_pdb); - return 1; // TRUE -} - -namespace mspdb { - -int CDECL PDB_Open2W(LPCWSTR wszPDB, LPCSTR szMode, LONG *pec, LPWSTR wszError, UINT cchErrMax, void **ppPDB) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("mspdb::PDB_Open2W(mode=%s)\n", szMode ? szMode : "(null)"); - return openPDB(pec, ppPDB); -} - -int CDECL PDB_Open3W(LPCWSTR wszPDB, LPCSTR szMode, DWORD dwSig, void *pcsig70, DWORD dwAge, LONG *pec, - LPWSTR wszError, UINT cchErrMax, void **ppPDB) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("mspdb::PDB_Open3W(mode=%s)\n", szMode ? szMode : "(null)"); - return openPDB(pec, ppPDB); -} - -int CDECL PDB_OpenValidate5(LPCWSTR wszPDB, LPCWSTR wszSearchPath, void *pvClient, void *pfnQueryCallback, - void *pfnNoteCallback, DWORD dwUnused, LONG *pec, LPWSTR wszError, UINT cchErrMax, - void **ppPDB) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("mspdb::PDB_OpenValidate5()\n"); - return openPDB(pec, ppPDB); -} - -int CDECL PDBExportValidateInterface(DWORD intv) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("mspdb::PDBExportValidateInterface(intv=%u)\n", intv); - return 1; // TRUE - interface is valid -} - -DWORD CDECL SigForPbCb(void *pb, DWORD cb) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("mspdb::SigForPbCb(pb=%p, cb=%u)\n", pb, cb); - return 0; -} - -LPCSTR CDECL SzCanonFilename(LPCSTR szFilename) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("mspdb::SzCanonFilename(%s)\n", szFilename ? szFilename : "(null)"); - return szFilename; -} - -// PDB management functions (C-style exports for supplementary linker module) - -int CDECL PDBOpen2W_C(LPCWSTR wszPDB, LPCSTR szMode, LONG *pec, LPWSTR wszError, UINT cchErrMax, void **ppPDB) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("mspdb::PDBOpen2W_C(mode=%s)\n", szMode ? szMode : "(null)"); - return openPDB(pec, ppPDB); -} - -int CDECL PDBOpenStreamEx(void *pPDB, LPCSTR szStream, DWORD dwFlags, void **ppStream) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("mspdb::PDBOpenStreamEx(stream=%s)\n", szStream ? szStream : "(null)"); - initVtables(); - if (ppStream) - *(uint32_t *)ppStream = addr32(g_fakeStream_legacy); - return 1; // TRUE -} - -int CDECL PDBCommit(void *pPDB) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("mspdb::PDBCommit()\n"); - return 1; // TRUE -} - -int CDECL PDBClose(void *pPDB) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("mspdb::PDBClose()\n"); - return 1; // TRUE -} - -// NameMap - -int CDECL NameMap_open(void *pPDB, int fWrite, void **ppNameMap) { - HOST_CONTEXT_GUARD(); - DEBUG_LOG("mspdb::NameMap_open(fWrite=%d)\n", fWrite); - initVtables(); - if (ppNameMap) - *(uint32_t *)ppNameMap = addr32(g_obj_namemap); - return 1; // TRUE -} - -// Stream functions - -int CDECL StreamAppend(void *pStream, void *pvData, LONG cbData) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("mspdb::StreamAppend(cb=%d)\n", cbData); - return 1; // TRUE -} - -LONG CDECL StreamQueryCb(void *pStream) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("mspdb::StreamQueryCb()\n"); - return 0; // stream is empty -} - -int CDECL StreamRead(void *pStream, LONG off, void *pvData, LONG *pcbData) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("mspdb::StreamRead(off=%d)\n", off); - if (pcbData) - *pcbData = 0; - return 1; // TRUE -} - -int CDECL StreamRelease(void *pStream) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("mspdb::StreamRelease()\n"); - return 1; // TRUE -} - -int CDECL StreamReplace(void *pStream, void *pvData, LONG cbData) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("mspdb::StreamReplace(cb=%d)\n", cbData); - return 1; // TRUE -} - -int CDECL StreamTruncate(void *pStream, LONG cbData) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("mspdb::StreamTruncate(cb=%d)\n", cbData); - return 1; // TRUE -} - -int CDECL StreamWrite(void *pStream, LONG off, void *pvData, LONG cbData) { - HOST_CONTEXT_GUARD(); - VERBOSE_LOG("mspdb::StreamWrite(off=%d, cb=%d)\n", off, cbData); - return 1; // TRUE -} - -} // namespace mspdb - -#include "mspdb_trampolines.h" - -static void *resolveByName(const char *name) { - // Decorated C++ names (not covered by mspdbThunkByName) - if (strcmp(name, "?Open2W@PDB@@SAHPBGPBDPAJPAGIPAPAU1@@Z") == 0) - return (void *)thunk_mspdb_PDB_Open2W; - if (strcmp(name, "?Open3W@PDB@@SAHPBGPBDKPBU_GUID@@KPAJPAGIPAPAU1@@Z") == 0) - return (void *)thunk_mspdb_PDB_Open3W; - if (strcmp(name, "?OpenValidate5@PDB@@SAHPBG0PAXP6AP6AHXZ1W4POVC@@@ZPAJPAGIPAPAU1@@Z") == 0) - return (void *)thunk_mspdb_PDB_OpenValidate5; - if (strcmp(name, "?open@NameMap@@SAHPAUPDB@@HPAPAU1@@Z") == 0) - return (void *)thunk_mspdb_NameMap_open; - - // Export name alias (DLL exports "PDBOpen2W", C function is PDBOpen2W_C) - if (strcmp(name, "PDBOpen2W") == 0) - return (void *)thunk_mspdb_PDBOpen2W_C; - - // C-style names: delegate to auto-generated lookup - return mspdbThunkByName(name); -} - -extern const wibo::ModuleStub lib_mspdb = { - (const char *[]){ - "mspdbxx", - "mspdb100", - "mspdb80", - nullptr, - }, - resolveByName, - nullptr, -}; diff --git a/dll/mspdb.h b/dll/mspdb.h deleted file mode 100644 index 90d9fcd..0000000 --- a/dll/mspdb.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "types.h" - -namespace mspdb { - -// PDB open/create functions (imported by link.exe main module) -int CDECL PDB_Open2W(LPCWSTR wszPDB, LPCSTR szMode, LONG *pec, LPWSTR wszError, UINT cchErrMax, void **ppPDB); -int CDECL PDB_Open3W(LPCWSTR wszPDB, LPCSTR szMode, DWORD dwSig, void *pcsig70, DWORD dwAge, LONG *pec, LPWSTR wszError, UINT cchErrMax, void **ppPDB); -int CDECL PDB_OpenValidate5(LPCWSTR wszPDB, LPCWSTR wszSearchPath, void *pvClient, void *pfnQueryCallback, void *pfnNoteCallback, DWORD dwUnused, LONG *pec, LPWSTR wszError, UINT cchErrMax, void **ppPDB); -int CDECL PDBExportValidateInterface(DWORD intv); -DWORD CDECL SigForPbCb(void *pb, DWORD cb); -LPCSTR CDECL SzCanonFilename(LPCSTR szFilename); - -// PDB management functions (imported by linker supplementary module) -int CDECL PDBOpen2W_C(LPCWSTR wszPDB, LPCSTR szMode, LONG *pec, LPWSTR wszError, UINT cchErrMax, void **ppPDB); -int CDECL PDBOpenStreamEx(void *pPDB, LPCSTR szStream, DWORD dwFlags, void **ppStream); -int CDECL PDBCommit(void *pPDB); -int CDECL PDBClose(void *pPDB); - -// NameMap -int CDECL NameMap_open(void *pPDB, int fWrite, void **ppNameMap); - -// Stream functions -int CDECL StreamAppend(void *pStream, void *pvData, LONG cbData); -LONG CDECL StreamQueryCb(void *pStream); -int CDECL StreamRead(void *pStream, LONG off, void *pvData, LONG *pcbData); -int CDECL StreamRelease(void *pStream); -int CDECL StreamReplace(void *pStream, void *pvData, LONG cbData); -int CDECL StreamTruncate(void *pStream, LONG cbData); -int CDECL StreamWrite(void *pStream, LONG off, void *pvData, LONG cbData); - -} // namespace mspdb diff --git a/dll/mspdb/mspdb.def b/dll/mspdb/mspdb.def new file mode 100644 index 0000000..4952eb2 --- /dev/null +++ b/dll/mspdb/mspdb.def @@ -0,0 +1,20 @@ +LIBRARY mspdb80 +EXPORTS + ?Open2W@PDB@@SAHPBGPBDPAJPAGIPAPAU1@@Z = PDB_Open2W + ?Open3W@PDB@@SAHPBGPBDKPBU_GUID@@KPAJPAGIPAPAU1@@Z = PDB_Open3W + ?OpenValidate5@PDB@@SAHPBG0PAXP6AP6AHXZ1W4POVC@@@ZPAJPAGIPAPAU1@@Z = PDB_OpenValidate5 + ?open@NameMap@@SAHPAUPDB@@HPAPAU1@@Z = NameMap_open + PDBOpen2W = PDB_Open2W_C + PDBExportValidateInterface + PDBCommit + PDBClose + PDBOpenStreamEx + SigForPbCb + SzCanonFilename + StreamAppend + StreamQueryCb + StreamRead + StreamRelease + StreamReplace + StreamTruncate + StreamWrite diff --git a/dll/mspdb/mspdb_dll.cpp b/dll/mspdb/mspdb_dll.cpp new file mode 100644 index 0000000..a5587f4 --- /dev/null +++ b/dll/mspdb/mspdb_dll.cpp @@ -0,0 +1,616 @@ +// Cross-compiled 32-bit PE DLL providing fake PDB COM interfaces. +// Built by i686-w64-mingw32-g++ -shared, loaded by wibo as an embedded DLL. +// +// The X360 linker requires a working mspdb DLL with COM-style vtable objects. +// Each class has virtual methods matching the interface slots the linker calls. +// No virtual destructors (MSVC: 1 slot, GCC: 2 slots - would shift all indices). + +#include +#include + +#define DLLEXPORT extern "C" __declspec(dllexport) + +// PDB version constants (VS2010 era - matches X360 linker) +static constexpr uint32_t PDB_INTV = 20091201; +static constexpr uint32_t PDB_IMPV = 20091201; + +// Helper: write a uint32_t to an output pointer if non-null +static void writeOut32(void *ptr, uint32_t val) { + if (ptr) *(uint32_t *)ptr = val; +} + +// Forward-declared addresses (defined after all struct definitions) +struct FakePDB; +struct FakeDBI; +struct FakeMod; +struct FakeTPI; +struct FakeGSI; +struct FakeDbg; +struct FakeNameMap; +struct FakeStream; +extern FakePDB g_pdb; +extern FakeDBI g_dbi; +extern FakeMod g_mod; +extern FakeTPI g_tpi; +extern FakeGSI g_gsi; +extern FakeDbg g_dbg; +extern FakeNameMap g_namemap; +extern FakeStream g_stream; + +// --- PDB interface (64 vtable slots) --- +struct FakePDB { + // 0: QueryInterfaceVersion + virtual uint32_t __thiscall QueryInterfaceVersion() { return PDB_INTV; } + // 1: QueryImplementationVersion + virtual uint32_t __thiscall QueryImplementationVersion() { return PDB_IMPV; } + // 2: QueryLastError(char szError[]) + virtual uint32_t __thiscall QueryLastError(char *sz) { if (sz) *sz = 0; return 0; } + // 3: QueryPDBName(char szPDB[]) + virtual char * __thiscall QueryPDBName(char *sz) { if (sz) *sz = 0; return nullptr; } + // 4: QuerySignature + virtual uint32_t __thiscall QuerySignature() { return 0; } + // 5: QueryAge + virtual uint32_t __thiscall QueryAge() { return 1; } + // 6: CreateDBI(szTarget, DBI**) + virtual int __thiscall CreateDBI(const char *, void **pp) { writeOut32(pp, (uint32_t)(uintptr_t)&g_dbi); return 1; } + // 7: OpenDBI(szTarget, szMode, DBI**) + virtual int __thiscall OpenDBI(const char *, const char *, void **pp) { writeOut32(pp, (uint32_t)(uintptr_t)&g_dbi); return 1; } + // 8: OpenTpi(szMode, TPI**) + virtual int __thiscall OpenTpi(const char *, void **pp) { writeOut32(pp, (uint32_t)(uintptr_t)&g_tpi); return 1; } + // 9: OpenIpi(szMode, TPI**) + virtual int __thiscall OpenIpi(const char *, void **pp) { writeOut32(pp, (uint32_t)(uintptr_t)&g_tpi); return 1; } + // 10: Commit + virtual int __thiscall Commit() { return 1; } + // 11: Close + virtual int __thiscall Close() { return 1; } + // 12: OpenStream(szStream, Stream**) + virtual int __thiscall OpenStream(const char *, void **pp) { writeOut32(pp, (uint32_t)(uintptr_t)&g_stream); return 1; } + // 13: GetEnumStreamNameMap + virtual int __thiscall GetEnumStreamNameMap(void *) { return 0; } + // 14: GetRawBytes + virtual int __thiscall GetRawBytes(void *) { return 0; } + // 15: QueryPdbImplementationVersion + virtual uint32_t __thiscall QueryPdbImplementationVersion() { return PDB_IMPV; } + // 16: OpenDBIEx(szTarget, szMode, DBI**, pfn) + virtual int __thiscall OpenDBIEx(const char *, const char *, void **pp, void *) { writeOut32(pp, (uint32_t)(uintptr_t)&g_dbi); return 1; } + // 17: CopyTo + virtual int __thiscall CopyTo(const char *, uint32_t, uint32_t) { return 1; } + // 18: OpenSrc + virtual int __thiscall OpenSrc(void *) { return 0; } + // 19: QueryLastErrorExW(wchar_t*, uint32_t) + virtual uint32_t __thiscall QueryLastErrorExW(wchar_t *sz, uint32_t) { if (sz) *sz = 0; return 0; } + // 20: QueryPDBNameExW(wchar_t*, uint32_t) + virtual wchar_t * __thiscall QueryPDBNameExW(wchar_t *sz) { if (sz) *sz = 0; return nullptr; } + // 21: QuerySignature2 + virtual int __thiscall QuerySignature2(void *) { return 1; } + // 22: CopyToW + virtual int __thiscall CopyToW(const wchar_t *, uint32_t, uint32_t) { return 1; } + // 23: fIsSZPDB + virtual int __thiscall fIsSZPDB() { return 1; } + // 24: OpenStreamW(szStream, Stream**) + virtual int __thiscall OpenStreamW(const wchar_t *, void **pp) { writeOut32(pp, (uint32_t)(uintptr_t)&g_stream); return 1; } + // 25: CopyToW2 + virtual int __thiscall CopyToW2(const wchar_t *, uint32_t, void *, void *) { return 1; } + // 26: OpenStreamEx(szStream, szMode, Stream**) + virtual int __thiscall OpenStreamEx(const char *, const char *, void **pp) { writeOut32(pp, (uint32_t)(uintptr_t)&g_stream); return 1; } + // 27: RegisterPDBMapping + virtual int __thiscall RegisterPDBMapping(const char *, const char *) { return 1; } + // 28: EnablePrefetching + virtual int __thiscall EnablePrefetching() { return 1; } + // 29: FLazy + virtual int __thiscall FLazy() { return 0; } + // 30: FMinimal + virtual int __thiscall FMinimal() { return 0; } + // 31: ResetGUID + virtual int __thiscall ResetGUID(void *, uint32_t) { return 1; } + // Padding to 64 slots + virtual int __thiscall _pad32() { return 0; } + virtual int __thiscall _pad33() { return 0; } + virtual int __thiscall _pad34() { return 0; } + virtual int __thiscall _pad35() { return 0; } + virtual int __thiscall _pad36() { return 0; } + virtual int __thiscall _pad37() { return 0; } + virtual int __thiscall _pad38() { return 0; } + virtual int __thiscall _pad39() { return 0; } + virtual int __thiscall _pad40() { return 0; } + virtual int __thiscall _pad41() { return 0; } + virtual int __thiscall _pad42() { return 0; } + virtual int __thiscall _pad43() { return 0; } + virtual int __thiscall _pad44() { return 0; } + virtual int __thiscall _pad45() { return 0; } + virtual int __thiscall _pad46() { return 0; } + virtual int __thiscall _pad47() { return 0; } + virtual int __thiscall _pad48() { return 0; } + virtual int __thiscall _pad49() { return 0; } + virtual int __thiscall _pad50() { return 0; } + virtual int __thiscall _pad51() { return 0; } + virtual int __thiscall _pad52() { return 0; } + virtual int __thiscall _pad53() { return 0; } + virtual int __thiscall _pad54() { return 0; } + virtual int __thiscall _pad55() { return 0; } + virtual int __thiscall _pad56() { return 0; } + virtual int __thiscall _pad57() { return 0; } + virtual int __thiscall _pad58() { return 0; } + virtual int __thiscall _pad59() { return 0; } + virtual int __thiscall _pad60() { return 0; } + virtual int __thiscall _pad61() { return 0; } + virtual int __thiscall _pad62() { return 0; } + virtual int __thiscall _pad63() { return 0; } +}; + +// --- DBI interface (64 vtable slots) --- +struct FakeDBI { + // 0: QueryImplementationVersion + virtual uint32_t __thiscall QueryImplementationVersion() { return PDB_IMPV; } + // 1: QueryInterfaceVersion + virtual uint32_t __thiscall QueryInterfaceVersion() { return PDB_INTV; } + // 2: OpenMod(szModule, szFile, Mod**) + virtual int __thiscall OpenMod(const char *, const char *, void **pp) { writeOut32(pp, (uint32_t)(uintptr_t)&g_mod); return 1; } + // 3: DeleteMod + virtual int __thiscall DeleteMod(const char *) { return 1; } + // 4: QueryNextMod(pmod, ppmodNext) -> *ppmodNext = NULL + virtual int __thiscall QueryNextMod(void *, void **pp) { writeOut32(pp, 0); return 1; } + // 5: OpenGlobals(GSI**) + virtual int __thiscall OpenGlobals(void **pp) { writeOut32(pp, (uint32_t)(uintptr_t)&g_gsi); return 1; } + // 6: OpenPublics(GSI**) + virtual int __thiscall OpenPublics(void **pp) { writeOut32(pp, (uint32_t)(uintptr_t)&g_gsi); return 1; } + // 7: AddSec + virtual int __thiscall AddSec(uint16_t, uint16_t, uint32_t, uint32_t) { return 1; } + // 8: QueryModFromAddr + virtual int __thiscall QueryModFromAddr(uint16_t, uint32_t, void **, uint16_t *, uint32_t *, uint32_t *) { return 0; } + // 9: QuerySecMap(pb, pcb) + virtual int __thiscall QuerySecMap(void *, uint32_t *pcb) { writeOut32(pcb, 0); return 1; } + // 10: QueryFileInfo(pb, pcb) + virtual int __thiscall QueryFileInfo(void *, uint32_t *pcb) { writeOut32(pcb, 0); return 1; } + // 11: DumpMods + virtual void __thiscall DumpMods() {} + // 12: DumpSecContribs + virtual void __thiscall DumpSecContribs() {} + // 13: DumpSecMap + virtual void __thiscall DumpSecMap() {} + // 14: Close + virtual int __thiscall Close() { return 1; } + // 15: AddThunkMap(7 args) + virtual int __thiscall AddThunkMap(uint32_t *, uint32_t, uint32_t, void *, uint32_t, uint16_t, uint32_t) { return 1; } + // 16: AddPublic + virtual int __thiscall AddPublic(const char *, uint16_t, uint32_t) { return 1; } + // 17: getEnumContrib + virtual int __thiscall getEnumContrib(void *) { return 0; } + // 18: QueryTypeServer + virtual int __thiscall QueryTypeServer(uint32_t, void **) { return 0; } + // 19: QueryItsmForTi + virtual int __thiscall QueryItsmForTi(uint32_t, uint32_t *) { return 0; } + // 20: QueryNextItsm + virtual int __thiscall QueryNextItsm(uint32_t, uint32_t *) { return 0; } + // 21: QueryLazyTypes + virtual int __thiscall QueryLazyTypes() { return 0; } + // 22: SetLazyTypes + virtual int __thiscall SetLazyTypes(int) { return 1; } + // 23: FindTypeServers + virtual int __thiscall FindTypeServers(void *, char *) { return 1; } + // 24: DumpTypeServers + virtual void __thiscall DumpTypeServers() {} + // 25: OpenDbg(dbgtype, Dbg**) + virtual int __thiscall OpenDbg(uint32_t, void **pp) { writeOut32(pp, (uint32_t)(uintptr_t)&g_dbg); return 1; } + // 26: QueryDbgTypes(pdbgtype, pcDbgtype) + virtual int __thiscall QueryDbgTypes(uint32_t *, uint32_t *pcb) { writeOut32(pcb, 0); return 1; } + // 27: QueryAddrForSec + virtual int __thiscall QueryAddrForSec(uint16_t, uint32_t, uint16_t *, uint32_t *, uint32_t *, uint32_t *) { return 0; } + // 28: QueryAddrForSecEx + virtual int __thiscall QueryAddrForSecEx(uint16_t, uint32_t, uint32_t, uint16_t *, uint32_t *, uint32_t *, uint32_t *) { return 0; } + // 29: QuerySupportsEC + virtual int __thiscall QuerySupportsEC() { return 0; } + // 30: QueryPdb(PDB**) + virtual int __thiscall QueryPdb(void **pp) { writeOut32(pp, (uint32_t)(uintptr_t)&g_pdb); return 1; } + // 31: AddLinkInfo + virtual int __thiscall AddLinkInfo(void *) { return 1; } + // 32: QueryLinkInfo(pli, pcb) + virtual int __thiscall QueryLinkInfo(void *, uint32_t *pcb) { writeOut32(pcb, 0); return 1; } + // 33: QueryAge + virtual uint32_t __thiscall QueryAge() { return 1; } + // 34: QueryHeader + virtual void * __thiscall QueryHeader() { return nullptr; } + // 35: FlushTypeServers + virtual void __thiscall FlushTypeServers() {} + // 36: QueryTypeServerByPdb + virtual int __thiscall QueryTypeServerByPdb(const char *, uint32_t *) { return 0; } + // 37: OpenModW(szModule, szFile, Mod**) + virtual int __thiscall OpenModW(const wchar_t *, const wchar_t *, void **pp) { writeOut32(pp, (uint32_t)(uintptr_t)&g_mod); return 1; } + // 38: DeleteModW + virtual int __thiscall DeleteModW(const wchar_t *) { return 1; } + // 39: AddPublicW + virtual int __thiscall AddPublicW(const wchar_t *, uint16_t, uint32_t, uint32_t) { return 1; } + // 40: QueryTypeServerByPdbW + virtual int __thiscall QueryTypeServerByPdbW(const wchar_t *, uint32_t *) { return 0; } + // 41: AddLinkInfoW + virtual int __thiscall AddLinkInfoW(void *) { return 1; } + // 42: AddPublic2 + virtual int __thiscall AddPublic2(const char *, uint16_t, uint32_t, uint32_t) { return 1; } + // 43: QueryMachineType + virtual uint16_t __thiscall QueryMachineType() { return 0; } + // 44: SetMachineType + virtual void __thiscall SetMachineType(uint16_t) {} + // 45: RemoveDataForRva + virtual void __thiscall RemoveDataForRva(uint32_t, uint32_t) {} + // 46: FStripped + virtual int __thiscall FStripped() { return 0; } + // 47: QueryModFromAddr2 + virtual int __thiscall QueryModFromAddr2(uint16_t, uint32_t, void **, uint16_t *, uint32_t *, uint32_t *, uint32_t *) { return 0; } + // 48: QueryNoOfMods(pcMods) + virtual int __thiscall QueryNoOfMods(uint32_t *pcMods) { writeOut32(pcMods, 0); return 1; } + // 49: QueryMods + virtual int __thiscall QueryMods(void **, uint32_t) { return 1; } + // 50: QueryImodFromAddr + virtual int __thiscall QueryImodFromAddr(uint16_t, uint32_t, void **, uint16_t *, uint32_t *, uint32_t *, uint32_t *) { return 0; } + // 51: OpenModFromImod + virtual int __thiscall OpenModFromImod(uint32_t, void **) { return 0; } + // 52: QueryHeader2(cb, pb, pcbOut) + virtual int __thiscall QueryHeader2(uint32_t, void *, uint32_t *pcb) { writeOut32(pcb, 0); return 1; } + // 53: FAddSourceMappingItem + virtual int __thiscall FAddSourceMappingItem(const wchar_t *, const wchar_t *, uint32_t) { return 1; } + // 54: FSetPfnNotePdbUsed + virtual int __thiscall FSetPfnNotePdbUsed(void *, void *) { return 1; } + // 55: FCTypes + virtual int __thiscall FCTypes() { return 0; } + // 56: QueryFileInfo2(pb, pcb) + virtual int __thiscall QueryFileInfo2(void *, uint32_t *pcb) { writeOut32(pcb, 0); return 1; } + // 57: FSetPfnQueryCallback + virtual int __thiscall FSetPfnQueryCallback(void *, void *) { return 1; } + // 58: FSetPfnNoteTypeMismatch + virtual int __thiscall FSetPfnNoteTypeMismatch(void *, void *) { return 1; } + // 59: FSetPfnTmdTypeFilter + virtual int __thiscall FSetPfnTmdTypeFilter(void *, void *) { return 1; } + // 60: RemovePublic + virtual int __thiscall RemovePublic(const char *) { return 1; } + // 61: getEnumContrib2 + virtual int __thiscall getEnumContrib2(void *) { return 0; } + // 62: QueryModFromAddrEx + virtual int __thiscall QueryModFromAddrEx(uint16_t, uint32_t, void **, uint16_t *, uint32_t *, uint32_t *, uint32_t *, uint32_t *) { return 0; } + // 63: QueryImodFromAddrEx + virtual int __thiscall QueryImodFromAddrEx(uint16_t, uint32_t, void **, uint16_t *, uint32_t *, uint32_t *, uint32_t *, uint32_t *) { return 0; } +}; + +// --- Mod interface (64 vtable slots) --- +struct FakeMod { + // 0: QueryInterfaceVersion + virtual uint32_t __thiscall QueryInterfaceVersion() { return PDB_INTV; } + // 1: QueryImplementationVersion + virtual uint32_t __thiscall QueryImplementationVersion() { return PDB_IMPV; } + // 2: AddTypes + virtual int __thiscall AddTypes(void *, uint32_t) { return 1; } + // 3: AddSymbols + virtual int __thiscall AddSymbols(void *, uint32_t) { return 1; } + // 4: AddPublic + virtual int __thiscall AddPublic(const char *, uint16_t, uint32_t) { return 1; } + // 5: AddLines(8 args) + virtual int __thiscall AddLines(const char *, uint16_t, uint32_t, uint32_t, uint32_t, uint32_t, void *, uint32_t) { return 1; } + // 6: AddSecContrib + virtual int __thiscall AddSecContrib(uint16_t, uint32_t, uint32_t, uint32_t) { return 1; } + // 7: QueryCBName(pcb) + virtual int __thiscall QueryCBName(uint32_t *pcb) { writeOut32(pcb, 0); return 1; } + // 8: QueryName(szName, pcb) + virtual int __thiscall QueryName(char *sz, uint32_t *) { if (sz) *sz = 0; return 1; } + // 9: QuerySymbols(pbSym, pcb) + virtual int __thiscall QuerySymbols(void *, uint32_t *pcb) { writeOut32(pcb, 0); return 1; } + // 10: QueryLines(pbLines, pcb) + virtual int __thiscall QueryLines(void *, uint32_t *pcb) { writeOut32(pcb, 0); return 1; } + // 11: SetPvClient + virtual int __thiscall SetPvClient(void *) { return 1; } + // 12: GetPvClient(ppvClient) + virtual int __thiscall GetPvClient(void **pp) { writeOut32(pp, 0); return 1; } + // 13: QueryFirstCodeSecContrib + virtual int __thiscall QueryFirstCodeSecContrib(uint16_t *, uint32_t *, uint32_t *, uint32_t *) { return 0; } + // 14: QueryImod(pimod) + virtual int __thiscall QueryImod(uint32_t *p) { writeOut32(p, 0); return 1; } + // 15: QueryDBI(ppdbi) + virtual int __thiscall QueryDBI(void **pp) { writeOut32(pp, (uint32_t)(uintptr_t)&g_dbi); return 1; } + // 16: Close + virtual int __thiscall Close() { return 1; } + // 17: QueryCBFile(pcb) + virtual int __thiscall QueryCBFile(uint32_t *pcb) { writeOut32(pcb, 0); return 1; } + // 18: QueryFile(szFile, pcb) + virtual int __thiscall QueryFile(char *sz, uint32_t *) { if (sz) *sz = 0; return 1; } + // 19: QueryTpi(pptpi) + virtual int __thiscall QueryTpi(void **pp) { writeOut32(pp, (uint32_t)(uintptr_t)&g_tpi); return 1; } + // 20: AddSecContribEx + virtual int __thiscall AddSecContribEx(uint16_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) { return 1; } + // 21: QueryItsm(pitsm) + virtual int __thiscall QueryItsm(uint32_t *p) { writeOut32(p, 0); return 1; } + // 22: QuerySrcFile(szFile, pcb) + virtual int __thiscall QuerySrcFile(char *sz, uint32_t *) { if (sz) *sz = 0; return 1; } + // 23: QuerySupportsEC + virtual int __thiscall QuerySupportsEC() { return 0; } + // 24: QueryPdbFile(szFile, pcb) + virtual int __thiscall QueryPdbFile(char *sz, uint32_t *) { if (sz) *sz = 0; return 1; } + // 25: ReplaceLines + virtual int __thiscall ReplaceLines(void *, uint32_t) { return 1; } + // 26: GetEnumLines + virtual int __thiscall GetEnumLines(void *) { return 0; } + // 27: QueryLineFlags + virtual int __thiscall QueryLineFlags(uint32_t *) { return 0; } + // 28: QueryFileNameInfo + virtual int __thiscall QueryFileNameInfo(uint32_t, wchar_t *, uint32_t *, uint32_t *, uint32_t *) { return 0; } + // 29: AddPublicW + virtual int __thiscall AddPublicW(const wchar_t *, uint16_t, uint32_t, uint32_t) { return 1; } + // 30: AddLinesW(8 args) + virtual int __thiscall AddLinesW(const wchar_t *, uint16_t, uint32_t, uint32_t, uint32_t, uint32_t, void *, uint32_t) { return 1; } + // 31: QueryNameW(szName, pcb) + virtual int __thiscall QueryNameW(wchar_t *sz, uint32_t *) { if (sz) *sz = 0; return 1; } + // 32: QueryFileW(szFile, pcb) + virtual int __thiscall QueryFileW(wchar_t *sz, uint32_t *) { if (sz) *sz = 0; return 1; } + // 33: QuerySrcFileW(szFile, pcb) + virtual int __thiscall QuerySrcFileW(wchar_t *sz, uint32_t *) { if (sz) *sz = 0; return 1; } + // 34: QueryPdbFileW(szFile, pcb) + virtual int __thiscall QueryPdbFileW(wchar_t *sz, uint32_t *) { if (sz) *sz = 0; return 1; } + // 35: AddPublic2 + virtual int __thiscall AddPublic2(const char *, uint16_t, uint32_t, uint32_t) { return 1; } + // 36: InsertLines + virtual int __thiscall InsertLines(void *, uint32_t) { return 1; } + // 37: QueryLines2(cbLines, pbLines, pcbLines) + virtual int __thiscall QueryLines2(uint32_t, void *, uint32_t *pcb) { writeOut32(pcb, 0); return 1; } + // Padding 38-63 + virtual int __thiscall _pad38() { return 0; } + virtual int __thiscall _pad39() { return 0; } + virtual int __thiscall _pad40() { return 0; } + virtual int __thiscall _pad41() { return 0; } + virtual int __thiscall _pad42() { return 0; } + virtual int __thiscall _pad43() { return 0; } + virtual int __thiscall _pad44() { return 0; } + virtual int __thiscall _pad45() { return 0; } + virtual int __thiscall _pad46() { return 0; } + virtual int __thiscall _pad47() { return 0; } + virtual int __thiscall _pad48() { return 0; } + virtual int __thiscall _pad49() { return 0; } + virtual int __thiscall _pad50() { return 0; } + virtual int __thiscall _pad51() { return 0; } + virtual int __thiscall _pad52() { return 0; } + virtual int __thiscall _pad53() { return 0; } + virtual int __thiscall _pad54() { return 0; } + virtual int __thiscall _pad55() { return 0; } + virtual int __thiscall _pad56() { return 0; } + virtual int __thiscall _pad57() { return 0; } + virtual int __thiscall _pad58() { return 0; } + virtual int __thiscall _pad59() { return 0; } + virtual int __thiscall _pad60() { return 0; } + virtual int __thiscall _pad61() { return 0; } + virtual int __thiscall _pad62() { return 0; } + virtual int __thiscall _pad63() { return 0; } +}; + +// --- TPI interface (32 vtable slots) --- +struct FakeTPI { + // 0: QueryInterfaceVersion + virtual uint32_t __thiscall QueryInterfaceVersion() { return PDB_INTV; } + // 1: QueryImplementationVersion + virtual uint32_t __thiscall QueryImplementationVersion() { return PDB_IMPV; } + // 2-4: Ti16 queries + virtual int __thiscall QueryTi16ForCVRecord(void *, void *) { return 0; } + virtual int __thiscall QueryCVRecordForTi16(uint32_t, void *, uint32_t *) { return 0; } + virtual int __thiscall QueryPbCVRecordForTi16(uint32_t, void **) { return 0; } + // 5-7: Ti16 range + size + virtual uint16_t __thiscall QueryTi16Min() { return 0; } + virtual uint16_t __thiscall QueryTi16Mac() { return 0; } + virtual int32_t __thiscall QueryCb() { return 0; } + // 8: Close + virtual int __thiscall Close() { return 1; } + // 9: Commit + virtual int __thiscall Commit() { return 1; } + // 10: QueryTi16ForUDT + virtual int __thiscall QueryTi16ForUDT(const char *, int, void *) { return 0; } + // 11: SupportQueryTiForUDT + virtual int __thiscall SupportQueryTiForUDT() { return 0; } + // 12: fIs16bitTypePool + virtual int __thiscall fIs16bitTypePool() { return 0; } + // 13: QueryTiForUDT + virtual int __thiscall QueryTiForUDT(const char *, int, void *) { return 0; } + // 14: QueryTiForCVRecord + virtual int __thiscall QueryTiForCVRecord(void *, void *) { return 0; } + // 15: QueryCVRecordForTi + virtual int __thiscall QueryCVRecordForTi(uint32_t, void *, uint32_t *) { return 0; } + // 16: QueryPbCVRecordForTi + virtual int __thiscall QueryPbCVRecordForTi(uint32_t, void **) { return 0; } + // 17: QueryTiMin + virtual uint32_t __thiscall QueryTiMin() { return 0x1000; } + // 18: QueryTiMac + virtual uint32_t __thiscall QueryTiMac() { return 0x1000; } + // 19: AreTypesEqual + virtual int __thiscall AreTypesEqual(uint32_t, uint32_t) { return 0; } + // 20: IsTypeServed + virtual int __thiscall IsTypeServed(uint32_t) { return 0; } + // 21: QueryTiForUDTW + virtual int __thiscall QueryTiForUDTW(const wchar_t *, int, void *) { return 0; } + // 22: QueryModSrcLineForUDTDefn + virtual int __thiscall QueryModSrcLineForUDTDefn(uint32_t, void *, void *, uint32_t *) { return 0; } + // Padding 23-31 + virtual int __thiscall _pad23() { return 0; } + virtual int __thiscall _pad24() { return 0; } + virtual int __thiscall _pad25() { return 0; } + virtual int __thiscall _pad26() { return 0; } + virtual int __thiscall _pad27() { return 0; } + virtual int __thiscall _pad28() { return 0; } + virtual int __thiscall _pad29() { return 0; } + virtual int __thiscall _pad30() { return 0; } + virtual int __thiscall _pad31() { return 0; } +}; + +// --- GSI interface (16 vtable slots) --- +struct FakeGSI { + virtual uint32_t __thiscall QueryInterfaceVersion() { return PDB_INTV; } + virtual uint32_t __thiscall QueryImplementationVersion() { return PDB_IMPV; } + virtual void * __thiscall NextSym(void *) { return nullptr; } + virtual void * __thiscall HashSym(const char *, void *) { return nullptr; } + virtual void * __thiscall NearestSym(uint16_t, uint32_t, int32_t *) { return nullptr; } + virtual int __thiscall Close() { return 1; } + virtual int __thiscall getEnumThunk(uint16_t, uint32_t, void **) { return 0; } + virtual uint32_t __thiscall OffForSym(void *) { return 0; } + virtual void * __thiscall SymForOff(uint32_t) { return nullptr; } + virtual void * __thiscall HashSymW(const wchar_t *, void *) { return nullptr; } + virtual int __thiscall getEnumByAddr(void **) { return 0; } + // Padding 11-15 + virtual int __thiscall _pad11() { return 0; } + virtual int __thiscall _pad12() { return 0; } + virtual int __thiscall _pad13() { return 0; } + virtual int __thiscall _pad14() { return 0; } + virtual int __thiscall _pad15() { return 0; } +}; + +// --- Dbg interface (16 vtable slots) --- +struct FakeDbg { + virtual int __thiscall Close() { return 1; } + virtual int32_t __thiscall QuerySize() { return 0; } + virtual void __thiscall Reset() {} + virtual int __thiscall Skip(uint32_t) { return 1; } + virtual int __thiscall QueryNext(uint32_t, void *) { return 0; } + virtual int __thiscall Find(void *) { return 0; } + virtual int __thiscall Clear() { return 1; } + virtual int __thiscall Append(uint32_t, void *) { return 1; } + virtual int __thiscall ReplaceNext(uint32_t, void *) { return 1; } + virtual int __thiscall Clone(void **) { return 0; } + virtual int32_t __thiscall QueryElementSize() { return 0; } + // Padding 11-15 + virtual int __thiscall _pad11() { return 0; } + virtual int __thiscall _pad12() { return 0; } + virtual int __thiscall _pad13() { return 0; } + virtual int __thiscall _pad14() { return 0; } + virtual int __thiscall _pad15() { return 0; } +}; + +// --- NameMap interface (20 vtable slots) --- +struct FakeNameMap { + virtual int __thiscall close() { return 1; } + virtual int __thiscall reinitialize() { return 1; } + virtual int __thiscall getNi(const char *, uint32_t *pni) { writeOut32(pni, 0); return 1; } + virtual int __thiscall getName(uint32_t, const char **) { return 0; } + virtual int __thiscall getEnumNameMap(void **) { return 0; } + virtual int __thiscall contains(const char *, uint32_t *) { return 0; } + virtual int __thiscall commit() { return 1; } + virtual int __thiscall isValidNi(uint32_t) { return 0; } + virtual int __thiscall getNiW(const wchar_t *, uint32_t *pni) { writeOut32(pni, 0); return 1; } + virtual int __thiscall getNameW(uint32_t, const wchar_t *, uint32_t *) { return 0; } + virtual int __thiscall containsW(const wchar_t *, uint32_t *) { return 0; } + virtual int __thiscall containsUTF8(const char *, uint32_t *) { return 0; } + virtual int __thiscall getNiUTF8(const char *, uint32_t *pni) { writeOut32(pni, 0); return 1; } + virtual int __thiscall getNameA(uint32_t, const char **) { return 0; } + virtual int __thiscall getNameW2(uint32_t, const wchar_t **) { return 0; } + // Padding 15-19 + virtual int __thiscall _pad15() { return 0; } + virtual int __thiscall _pad16() { return 0; } + virtual int __thiscall _pad17() { return 0; } + virtual int __thiscall _pad18() { return 0; } + virtual int __thiscall _pad19() { return 0; } +}; + +// --- Stream interface (12 vtable slots) --- +struct FakeStream { + virtual int32_t __thiscall QueryCb() { return 0; } + virtual int __thiscall Read(uint32_t, void *, uint32_t *) { return 1; } + virtual int __thiscall Write(uint32_t, void *, uint32_t) { return 1; } + virtual int __thiscall Replace(void *, uint32_t) { return 1; } + virtual int __thiscall Append(void *, uint32_t) { return 1; } + virtual int __thiscall Delete() { return 1; } + virtual int __thiscall Release() { return 1; } + virtual int __thiscall Read2(uint32_t, void *, uint32_t) { return 1; } + virtual int __thiscall Truncate(uint32_t) { return 1; } + // Padding 9-11 + virtual int __thiscall _pad9() { return 0; } + virtual int __thiscall _pad10() { return 0; } + virtual int __thiscall _pad11() { return 0; } +}; + +// Global instances (matching extern declarations above) +FakePDB g_pdb; +FakeDBI g_dbi; +FakeMod g_mod; +FakeTPI g_tpi; +FakeGSI g_gsi; +FakeDbg g_dbg; +FakeNameMap g_namemap; +FakeStream g_stream; + +// Legacy sentinel for PDBOpenStreamEx +static uint32_t g_fakeStreamLegacy = 0; + +// --- C-style exports --- + +static int openPDB(int32_t *pec, void **ppPDB) { + if (pec) *pec = 0; + if (ppPDB) *(uint32_t *)ppPDB = (uint32_t)(uintptr_t)&g_pdb; + return 1; +} + +DLLEXPORT int __cdecl PDB_Open2W(const wchar_t *, const char *, int32_t *pec, wchar_t *, unsigned int, void **ppPDB) { + return openPDB(pec, ppPDB); +} + +DLLEXPORT int __cdecl PDB_Open3W(const wchar_t *, const char *, uint32_t, void *, uint32_t, int32_t *pec, + wchar_t *, unsigned int, void **ppPDB) { + return openPDB(pec, ppPDB); +} + +DLLEXPORT int __cdecl PDB_OpenValidate5(const wchar_t *, const wchar_t *, void *, void *, void *, uint32_t, + int32_t *pec, wchar_t *, unsigned int, void **ppPDB) { + return openPDB(pec, ppPDB); +} + +DLLEXPORT int __cdecl PDBExportValidateInterface(uint32_t) { + return 1; +} + +DLLEXPORT uint32_t __cdecl SigForPbCb(void *, uint32_t) { + return 0; +} + +DLLEXPORT const char * __cdecl SzCanonFilename(const char *sz) { + return sz; +} + +DLLEXPORT int __cdecl PDB_Open2W_C(const wchar_t *, const char *, int32_t *pec, wchar_t *, unsigned int, void **ppPDB) { + return openPDB(pec, ppPDB); +} + +DLLEXPORT int __cdecl PDBOpenStreamEx(void *, const char *, uint32_t, void **ppStream) { + if (ppStream) *(uint32_t *)ppStream = (uint32_t)(uintptr_t)&g_fakeStreamLegacy; + return 1; +} + +DLLEXPORT int __cdecl PDBCommit(void *) { + return 1; +} + +DLLEXPORT int __cdecl PDBClose(void *) { + return 1; +} + +DLLEXPORT int __cdecl NameMap_open(void *, int, void **ppNameMap) { + if (ppNameMap) *(uint32_t *)ppNameMap = (uint32_t)(uintptr_t)&g_namemap; + return 1; +} + +DLLEXPORT int __cdecl StreamAppend(void *, void *, int32_t) { + return 1; +} + +DLLEXPORT int32_t __cdecl StreamQueryCb(void *) { + return 0; +} + +DLLEXPORT int __cdecl StreamRead(void *, int32_t, void *, int32_t *pcb) { + if (pcb) *pcb = 0; + return 1; +} + +DLLEXPORT int __cdecl StreamRelease(void *) { + return 1; +} + +DLLEXPORT int __cdecl StreamReplace(void *, void *, int32_t) { + return 1; +} + +DLLEXPORT int __cdecl StreamTruncate(void *, int32_t) { + return 1; +} + +DLLEXPORT int __cdecl StreamWrite(void *, int32_t, void *, int32_t) { + return 1; +} diff --git a/dll/mspdb_embed.cpp b/dll/mspdb_embed.cpp new file mode 100644 index 0000000..deb526c --- /dev/null +++ b/dll/mspdb_embed.cpp @@ -0,0 +1,15 @@ +#include "macros.h" +#include "modules.h" + +INCLUDE_BIN(_mspdbDllData, EMBED_PATH) + +extern const wibo::ModuleStub lib_mspdb = { + .names = + (const char *[]){ + "mspdbxx", + "mspdb100", + "mspdb80", + nullptr, + }, + .dllData = INCLUDE_BIN_SPAN(_mspdbDllData), +}; diff --git a/dll/rpcrt4.cpp b/dll/rpcrt4.cpp index b147aea..4627315 100644 --- a/dll/rpcrt4.cpp +++ b/dll/rpcrt4.cpp @@ -17,7 +17,9 @@ namespace { constexpr RPC_STATUS RPC_S_OK = 0; constexpr RPC_STATUS RPC_S_INVALID_STRING_BINDING = 1700; constexpr RPC_STATUS RPC_S_INVALID_BINDING = 1702; +#ifndef __x86_64__ constexpr RPC_STATUS RPC_S_SERVER_UNAVAILABLE = 1722; +#endif constexpr RPC_STATUS RPC_S_INVALID_ARG = 87; constexpr RPC_STATUS RPC_S_OUT_OF_MEMORY = 14; @@ -227,6 +229,7 @@ RPC_STATUS WINAPI RpcStringFreeW(GUEST_PTR *string) { return RPC_S_OK; } +#ifndef __x86_64__ CLIENT_CALL_RETURN CDECL_NO_CONV NdrClientCall2(PMIDL_STUB_DESC stubDescriptor, PFORMAT_STRING format, ...) { DEBUG_LOG("STUB: NdrClientCall2 stubDescriptor=%p format=%p\n", stubDescriptor, format); CLIENT_CALL_RETURN result = {}; @@ -234,6 +237,7 @@ CLIENT_CALL_RETURN CDECL_NO_CONV NdrClientCall2(PMIDL_STUB_DESC stubDescriptor, DEBUG_LOG("NdrClientCall2 returning RPC_S_SERVER_UNAVAILABLE\n"); return result; } +#endif VOID WINAPI NdrServerCall2(PRPC_MESSAGE message) { HOST_CONTEXT_GUARD(); diff --git a/dll/rpcrt4.h b/dll/rpcrt4.h index 2a1db46..6a83631 100644 --- a/dll/rpcrt4.h +++ b/dll/rpcrt4.h @@ -33,7 +33,9 @@ RPC_STATUS WINAPI RpcBindingSetAuthInfoExW(RPC_BINDING_HANDLE binding, RPC_WSTR RPC_SECURITY_QOS *securityQos); RPC_STATUS WINAPI RpcBindingFree(GUEST_PTR *binding); RPC_STATUS WINAPI RpcStringFreeW(GUEST_PTR *string); +#ifndef __x86_64__ // TODO: varargs trampoline not supported on 64-bit CLIENT_CALL_RETURN CDECL_NO_CONV NdrClientCall2(PMIDL_STUB_DESC stubDescriptor, PFORMAT_STRING format, ...); +#endif VOID WINAPI NdrServerCall2(PRPC_MESSAGE message); } // namespace rpcrt4 diff --git a/src/files.cpp b/src/files.cpp index e4ea48b..65c12b7 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -17,13 +17,9 @@ #include #include #include -#include #include kernel32::FsObject::~FsObject() { - if (!canonicalPath.empty() && accessCategory != 0) { - files::unregisterOpenFile(canonicalPath, accessCategory, shareAccess); - } int fd = std::exchange(this->fd, -1); if (fd >= 0 && closeOnDestroy) { close(fd); @@ -37,12 +33,6 @@ kernel32::FsObject::~FsObject() { namespace files { -static std::vector> gPathAliases; - -void addPathAlias(const std::string &hostPrefix, const std::string &windowsPrefix) { - gPathAliases.push_back({hostPrefix, windowsPrefix}); -} - static std::vector splitList(const std::string &value, char delimiter) { std::vector entries; size_t start = 0; @@ -101,16 +91,6 @@ std::filesystem::path pathFromWindows(const char *inStr) { str.erase(0, 4); } - // Check path aliases (reverse mapping: windows prefix -> host prefix) - for (const auto &alias : gPathAliases) { - std::string winPrefix = alias.second; - std::replace(winPrefix.begin(), winPrefix.end(), '\\', '/'); - if (strncasecmp(str.c_str(), winPrefix.c_str(), winPrefix.size()) == 0) { - str = alias.first + str.substr(winPrefix.size()); - return std::filesystem::path(str).lexically_normal(); - } - } - // Remove the drive letter if (str.rfind("z:/", 0) == 0 || str.rfind("Z:/", 0) == 0 || str.rfind("c:/", 0) == 0 || str.rfind("C:/", 0) == 0) { str.erase(0, 2); @@ -156,15 +136,6 @@ std::filesystem::path pathFromWindows(const char *inStr) { std::string pathToWindows(const std::filesystem::path &path) { std::string str = path.lexically_normal(); - // Check path aliases before default Z: mapping - for (const auto &alias : gPathAliases) { - if (str.rfind(alias.first, 0) == 0) { - str = alias.second + str.substr(alias.first.size()); - std::replace(str.begin(), str.end(), '/', '\\'); - return str; - } - } - if (path.is_absolute()) { str.insert(0, "Z:"); } @@ -452,60 +423,6 @@ std::string windowsPathListToHost(const std::string &value) { } return result; } -static std::mutex gShareMutex; -static std::unordered_map>> gShareMap; - -bool checkShareViolation(const std::filesystem::path &path, uint32_t accessCategory, uint32_t shareMode) { - if (accessCategory == 0) { - return false; - } - std::string key = path.string(); - std::lock_guard lk(gShareMutex); - auto it = gShareMap.find(key); - if (it == gShareMap.end()) { - return false; - } - for (const auto &[existAccess, existShare] : it->second) { - // Existing handle does something the new opener doesn't allow - if ((existAccess & ~shareMode) != 0) { - return true; - } - // New opener wants to do something the existing handle doesn't allow - if ((accessCategory & ~existShare) != 0) { - return true; - } - } - return false; -} - -void registerOpenFile(const std::filesystem::path &path, uint32_t accessCategory, uint32_t shareMode) { - if (accessCategory == 0) { - return; - } - std::string key = path.string(); - std::lock_guard lk(gShareMutex); - gShareMap[key].emplace_back(accessCategory, shareMode); -} - -void unregisterOpenFile(const std::filesystem::path &path, uint32_t accessCategory, uint32_t shareMode) { - std::string key = path.string(); - std::lock_guard lk(gShareMutex); - auto it = gShareMap.find(key); - if (it == gShareMap.end()) { - return; - } - auto &vec = it->second; - for (auto jt = vec.begin(); jt != vec.end(); ++jt) { - if (jt->first == accessCategory && jt->second == shareMode) { - vec.erase(jt); - break; - } - } - if (vec.empty()) { - gShareMap.erase(it); - } -} - static std::mutex gMappedFileMutex; static std::map, int> gMappedFileCount; diff --git a/src/files.h b/src/files.h index 04098df..735859c 100644 --- a/src/files.h +++ b/src/files.h @@ -19,7 +19,6 @@ struct IOResult { }; void init(); -void addPathAlias(const std::string &hostPrefix, const std::string &windowsPrefix); std::filesystem::path pathFromWindows(const char *inStr); std::string pathToWindows(const std::filesystem::path &path); IOResult read(FileObject *file, void *buffer, size_t bytesToRead, const std::optional &offset, @@ -34,11 +33,6 @@ std::filesystem::path canonicalPath(const std::filesystem::path &path); std::string hostPathListToWindows(const std::string &value); std::string windowsPathListToHost(const std::string &value); -// Share violation tracking -bool checkShareViolation(const std::filesystem::path &path, uint32_t accessCategory, uint32_t shareMode); -void registerOpenFile(const std::filesystem::path &path, uint32_t accessCategory, uint32_t shareMode); -void unregisterOpenFile(const std::filesystem::path &path, uint32_t accessCategory, uint32_t shareMode); - // Memory-mapped file tracking (prevents truncation of mapped files, matching Windows behavior) void trackMappedFile(dev_t dev, ino_t ino); void untrackMappedFile(dev_t dev, ino_t ino); diff --git a/src/main.cpp b/src/main.cpp index 31819db..1780113 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -263,33 +263,6 @@ int main(int argc, char **argv) { optionDebug = true; continue; } - if (strncmp(arg, "--path-alias=", 13) == 0) { - std::string mapping = arg + 13; - auto eqPos = mapping.find('='); - if (eqPos == std::string::npos) { - fprintf(stderr, "Error: --path-alias requires format 'host_path=windows_path'\n"); - printHelp(argv[0], true); - return 1; - } - files::addPathAlias(mapping.substr(0, eqPos), mapping.substr(eqPos + 1)); - continue; - } - if (strcmp(arg, "--path-alias") == 0) { - if (i + 1 >= argc) { - fprintf(stderr, "Error: '%s' requires a mapping argument.\n", arg); - printHelp(argv[0], true); - return 1; - } - std::string mapping = argv[++i]; - auto eqPos = mapping.find('='); - if (eqPos == std::string::npos) { - fprintf(stderr, "Error: --path-alias requires format 'host_path=windows_path'\n"); - printHelp(argv[0], true); - return 1; - } - files::addPathAlias(mapping.substr(0, eqPos), mapping.substr(eqPos + 1)); - continue; - } if (strncmp(arg, "--chdir=", 8) == 0) { chdirPath = arg + 8; continue; diff --git a/src/modules.cpp b/src/modules.cpp index ff15b32..652947a 100644 --- a/src/modules.cpp +++ b/src/modules.cpp @@ -32,7 +32,9 @@ extern const wibo::ModuleStub lib_bcrypt; extern const wibo::ModuleStub lib_kernel32; extern const wibo::ModuleStub lib_lmgr; extern const wibo::ModuleStub lib_mscoree; +#if WIBO_HAS_MSPDB extern const wibo::ModuleStub lib_mspdb; +#endif #if WIBO_HAS_MSVCRT extern const wibo::ModuleStub lib_msvcrt; #endif @@ -188,8 +190,11 @@ LockedRegistry registry() { if (!reg.initialized) { reg.initialized = true; const wibo::ModuleStub *builtins[] = { - &lib_advapi32, &lib_bcrypt, &lib_kernel32, &lib_lmgr, &lib_mscoree, &lib_mspdb, + &lib_advapi32, &lib_bcrypt, &lib_kernel32, &lib_lmgr, &lib_mscoree, &lib_ntdll, &lib_ole32, &lib_rpcrt4, &lib_user32, &lib_vcruntime, &lib_version, &lib_ws2, +#if WIBO_HAS_MSPDB + &lib_mspdb, +#endif #if WIBO_HAS_MSVCRT &lib_msvcrt, #endif From b26f2a5dbb012ae721c2d5d4555a2bc800da887e Mon Sep 17 00:00:00 2001 From: freeqaz <00free@gmail.com> Date: Thu, 26 Feb 2026 01:53:17 +0000 Subject: [PATCH 5/6] Add X360 linker support: CRC hashing, path mapping, new Win32 APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement SigForPbCb with real CRC-32 (reflected polynomial) so the linker generates correct ??_C@_ string literal hashes. Add path map support (WIBO_PATH_MAP env var) for Windows↔host path translation with case-insensitive resolution and drive letter mapping. New Win32 APIs: InitOnceExecuteOnce, RtlComputeCrc32, SystemFunction036 (RtlGenRandom). Fix QueryPerformanceCounter/Frequency to return real monotonic clock values instead of stubs. Cleanup: factor out duplicated WIBO_PATH_MAP parsing into cached getPathMap() helper, remove goto in pathToWindows, remove duplicate PINIT_ONCE_FN typedef, revert incorrect PROT_WRITE in PAGE_EXECUTE_READ, remove redundant early debug detection in main(), add env var passthrough. --- dll/advapi32/wincrypt.cpp | 21 ++++ dll/advapi32/wincrypt.h | 1 + dll/kernel32/profileapi.cpp | 13 ++- dll/kernel32/synchapi.cpp | 33 +++++++ dll/kernel32/synchapi.h | 3 + dll/mspdb/mspdb_dll.cpp | 19 +++- dll/ntdll.cpp | 12 +++ dll/ntdll.h | 1 + src/files.cpp | 192 +++++++++++++++++++++++++++++++----- src/main.cpp | 20 +++- 10 files changed, 278 insertions(+), 37 deletions(-) diff --git a/dll/advapi32/wincrypt.cpp b/dll/advapi32/wincrypt.cpp index b927922..0a7d718 100644 --- a/dll/advapi32/wincrypt.cpp +++ b/dll/advapi32/wincrypt.cpp @@ -245,4 +245,25 @@ BOOL WINAPI CryptDestroyHash(HCRYPTHASH hHash) { return TRUE; } +BOOLEAN WINAPI SystemFunction036(PVOID RandomBuffer, ULONG RandomBufferLength) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("SystemFunction036(%p, %u)\n", RandomBuffer, RandomBufferLength); + if (RandomBuffer && RandomBufferLength > 0) { +#ifdef __APPLE__ + arc4random_buf(RandomBuffer, RandomBufferLength); +#else + ssize_t ret = getrandom(RandomBuffer, RandomBufferLength, 0); + if (ret < 0 || static_cast(ret) != RandomBufferLength) { + // fallback to /dev/urandom + FILE *f = fopen("/dev/urandom", "rb"); + if (f) { + fread(RandomBuffer, 1, RandomBufferLength, f); + fclose(f); + } + } +#endif + } + return TRUE; +} + } // namespace advapi32 diff --git a/dll/advapi32/wincrypt.h b/dll/advapi32/wincrypt.h index 476dcb7..add5714 100644 --- a/dll/advapi32/wincrypt.h +++ b/dll/advapi32/wincrypt.h @@ -24,5 +24,6 @@ BOOL WINAPI CryptCreateHash(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey, DWOR BOOL WINAPI CryptHashData(HCRYPTHASH hHash, const BYTE *pbData, DWORD dwDataLen, DWORD dwFlags); BOOL WINAPI CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam, BYTE *pbData, DWORD *pdwDataLen, DWORD dwFlags); BOOL WINAPI CryptDestroyHash(HCRYPTHASH hHash); +BOOLEAN WINAPI SystemFunction036(PVOID RandomBuffer, ULONG RandomBufferLength); } // namespace advapi32 diff --git a/dll/kernel32/profileapi.cpp b/dll/kernel32/profileapi.cpp index 2d849ac..087cdfb 100644 --- a/dll/kernel32/profileapi.cpp +++ b/dll/kernel32/profileapi.cpp @@ -5,28 +5,31 @@ #include "errors.h" #include "internal.h" +#include + namespace kernel32 { BOOL WINAPI QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount) { HOST_CONTEXT_GUARD(); - VERBOSE_LOG("STUB: QueryPerformanceCounter(%p)\n", lpPerformanceCount); + DEBUG_LOG("QueryPerformanceCounter(%p)\n", lpPerformanceCount); if (!lpPerformanceCount) { kernel32::setLastError(ERROR_INVALID_PARAMETER); return FALSE; } - lpPerformanceCount->QuadPart = 0; + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + lpPerformanceCount->QuadPart = static_cast(ts.tv_sec) * 1000000000LL + ts.tv_nsec; return TRUE; } BOOL WINAPI QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency) { HOST_CONTEXT_GUARD(); - VERBOSE_LOG("STUB: QueryPerformanceFrequency(%p)\n", lpFrequency); + DEBUG_LOG("QueryPerformanceFrequency(%p)\n", lpFrequency); if (!lpFrequency) { kernel32::setLastError(ERROR_INVALID_PARAMETER); return FALSE; } - lpFrequency->QuadPart = 1; + lpFrequency->QuadPart = 1000000000LL; return TRUE; } - } // namespace kernel32 diff --git a/dll/kernel32/synchapi.cpp b/dll/kernel32/synchapi.cpp index c349faa..700de44 100644 --- a/dll/kernel32/synchapi.cpp +++ b/dll/kernel32/synchapi.cpp @@ -1320,6 +1320,39 @@ BOOL WINAPI InitOnceComplete(LPINIT_ONCE lpInitOnce, DWORD dwFlags, LPVOID lpCon } } +BOOL WINAPI InitOnceExecuteOnce(PINIT_ONCE InitOnce, PINIT_ONCE_FN InitFn, PVOID Parameter, LPVOID *Context) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("InitOnceExecuteOnce(%p, %p, %p, %p)\n", InitOnce, InitFn, Parameter, Context); + if (!InitOnce || !InitFn) { + setLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + BOOL pending = FALSE; + GUEST_PTR context = GUEST_NULL; + if (!InitOnceBeginInitialize(InitOnce, 0, &pending, &context)) { + return FALSE; + } + + if (pending) { + LPVOID ctx = nullptr; + if (!InitFn(InitOnce, Parameter, &ctx)) { + InitOnceComplete(InitOnce, INIT_ONCE_INIT_FAILED, nullptr); + return FALSE; + } + InitOnceComplete(InitOnce, 0, ctx); + if (Context) { + *Context = ctx; + } + } else { + if (Context) { + *Context = fromGuestPtr(context); + } + } + + return TRUE; +} + void WINAPI AcquireSRWLockShared(PSRWLOCK SRWLock) { HOST_CONTEXT_GUARD(); VERBOSE_LOG("AcquireSRWLockShared(%p)\n", SRWLock); diff --git a/dll/kernel32/synchapi.h b/dll/kernel32/synchapi.h index 22a75df..b450fa2 100644 --- a/dll/kernel32/synchapi.h +++ b/dll/kernel32/synchapi.h @@ -63,6 +63,8 @@ using INIT_ONCE = RTL_RUN_ONCE; using PINIT_ONCE = INIT_ONCE *; using LPINIT_ONCE = INIT_ONCE *; +typedef BOOL(WINAPI *PINIT_ONCE_FN)(PINIT_ONCE, PVOID, PVOID *); + constexpr INIT_ONCE INIT_ONCE_STATIC_INIT{GUEST_NULL}; union RTL_SRWLOCK { @@ -103,6 +105,7 @@ void WINAPI LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection); BOOL WINAPI TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection); BOOL WINAPI InitOnceBeginInitialize(LPINIT_ONCE lpInitOnce, DWORD dwFlags, PBOOL fPending, GUEST_PTR *lpContext); BOOL WINAPI InitOnceComplete(LPINIT_ONCE lpInitOnce, DWORD dwFlags, LPVOID lpContext); +BOOL WINAPI InitOnceExecuteOnce(PINIT_ONCE InitOnce, PINIT_ONCE_FN InitFn, PVOID Parameter, LPVOID *Context); void WINAPI AcquireSRWLockShared(PSRWLOCK SRWLock); void WINAPI ReleaseSRWLockShared(PSRWLOCK SRWLock); void WINAPI AcquireSRWLockExclusive(PSRWLOCK SRWLock); diff --git a/dll/mspdb/mspdb_dll.cpp b/dll/mspdb/mspdb_dll.cpp index a5587f4..cd5d735 100644 --- a/dll/mspdb/mspdb_dll.cpp +++ b/dll/mspdb/mspdb_dll.cpp @@ -556,8 +556,23 @@ DLLEXPORT int __cdecl PDBExportValidateInterface(uint32_t) { return 1; } -DLLEXPORT uint32_t __cdecl SigForPbCb(void *, uint32_t) { - return 0; +// CRC-32 with reflected polynomial 0xEDB88320 (ISO 3309). +// Used by the linker to hash string literal content for ??_C@_ symbol names. +DLLEXPORT uint32_t __cdecl SigForPbCb(const unsigned char *pb, uint32_t cb, uint32_t dwInitial) { + uint32_t crc = dwInitial; + for (uint32_t i = 0; i < cb; i++) { + uint32_t index = (crc ^ pb[i]) & 0xff; + uint32_t entry = index; + for (int j = 0; j < 8; j++) { + if (entry & 1) { + entry = (entry >> 1) ^ 0xEDB88320; + } else { + entry >>= 1; + } + } + crc = (crc >> 8) ^ entry; + } + return crc; } DLLEXPORT const char * __cdecl SzCanonFilename(const char *sz) { diff --git a/dll/ntdll.cpp b/dll/ntdll.cpp index 17309d4..4661431 100644 --- a/dll/ntdll.cpp +++ b/dll/ntdll.cpp @@ -829,6 +829,18 @@ NTSTATUS WINAPI NtQueryInformationProcess(HANDLE ProcessHandle, PROCESSINFOCLASS } } +DWORD WINAPI RtlComputeCrc32(DWORD dwInitial, const BYTE *pData, int iLen) { + // Standard CRC32 (ISO 3309 / ITU-T V.42) + DWORD crc = dwInitial ^ 0xFFFFFFFF; + for (int i = 0; i < iLen; i++) { + crc ^= pData[i]; + for (int j = 0; j < 8; j++) { + crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1)); + } + } + return crc ^ 0xFFFFFFFF; +} + NTSTATUS WINAPI LdrAddRefDll(ULONG Flags, HMODULE Module) { DEBUG_LOG("STUB: LdrAddRefDll(%x, %p)\n", Flags, Module); (void)Flags; diff --git a/dll/ntdll.h b/dll/ntdll.h index 86fbe60..df8450d 100644 --- a/dll/ntdll.h +++ b/dll/ntdll.h @@ -54,6 +54,7 @@ NTSTATUS WINAPI RtlGetVersion(PRTL_OSVERSIONINFOW lpVersionInformation); NTSTATUS WINAPI NtQueryInformationProcess(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength, PULONG ReturnLength); +DWORD WINAPI RtlComputeCrc32(DWORD dwInitial, const BYTE *pData, int iLen); NTSTATUS WINAPI LdrAddRefDll(ULONG Flags, HMODULE Module); } // namespace ntdll diff --git a/src/files.cpp b/src/files.cpp index 65c12b7..3614043 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -81,60 +81,198 @@ static HANDLE stdinHandle; static HANDLE stdoutHandle; static HANDLE stderrHandle; -std::filesystem::path pathFromWindows(const char *inStr) { - // Convert to forward slashes - std::string str = inStr; - std::replace(str.begin(), str.end(), '\\', '/'); +static std::string getDriveMapping(char drive) { + char envVar[] = "WIBO_DRIVE_X"; + envVar[11] = toupper(drive); + const char *val = getenv(envVar); + return val ? val : ""; +} - // Remove "//?/" prefix - if (str.rfind("//?/", 0) == 0) { - str.erase(0, 4); +struct PathMapEntry { + std::string winPath; // Normalized Windows-style prefix (forward slashes, no trailing slash) + std::string hostPath; // Host filesystem path +}; + +// Parse WIBO_PATH_MAP once and cache the result. +// Format: "winPath=hostPath;winPath2=hostPath2;..." +static const std::vector &getPathMap() { + static std::vector entries; + static bool parsed = false; + if (!parsed) { + parsed = true; + const char *envVal = getenv("WIBO_PATH_MAP"); + if (envVal) { + std::string mapStr = envVal; + size_t start = 0; + while (start < mapStr.size()) { + size_t end = mapStr.find(';', start); + std::string entry = mapStr.substr(start, (end == std::string::npos) ? std::string::npos : end - start); + size_t sep = entry.find('='); + if (sep != std::string::npos) { + std::string winPart = entry.substr(0, sep); + std::string hostPart = entry.substr(sep + 1); + std::replace(winPart.begin(), winPart.end(), '\\', '/'); + while (winPart.size() > 1 && winPart.back() == '/') + winPart.pop_back(); + entries.push_back({std::move(winPart), std::move(hostPart)}); + } + if (end == std::string::npos) + break; + start = end + 1; + } + } } + return entries; +} - // Remove the drive letter - if (str.rfind("z:/", 0) == 0 || str.rfind("Z:/", 0) == 0 || str.rfind("c:/", 0) == 0 || str.rfind("C:/", 0) == 0) { - str.erase(0, 2); +static std::filesystem::path resolveCaseInsensitive(const std::filesystem::path &path) { + std::filesystem::path norm = path.lexically_normal(); + if (std::filesystem::exists(norm)) { + return norm; } - // Return as-is if it exists, else traverse the filesystem looking for - // a path that matches case insensitively - std::filesystem::path path = std::filesystem::path(str).lexically_normal(); - if (std::filesystem::exists(path)) { - return path; + std::filesystem::path newPath = "."; + if (norm.is_absolute()) { + newPath = norm.root_path(); } - std::filesystem::path newPath = "."; bool followingExisting = true; - for (const auto &component : path) { - std::filesystem::path newPath2 = newPath / component; - if (followingExisting && !std::filesystem::exists(newPath2) && + auto it = norm.begin(); + if (norm.is_absolute()) { + ++it; + } + + for (; it != norm.end(); ++it) { + const auto &component = *it; + std::filesystem::path nextPath = newPath / component; + if (followingExisting && !std::filesystem::exists(nextPath) && (component != ".." && component != "." && component != "")) { followingExisting = false; std::error_code ec; std::filesystem::directory_iterator iter{newPath, ec}; if (!ec) { for (std::filesystem::path entry : iter) { - if (strcasecmp(entry.filename().c_str(), component.c_str()) == 0) { + if (strcasecmp(entry.filename().c_str(), component.string().c_str()) == 0) { followingExisting = true; - newPath2 = entry; + nextPath = entry; break; } } } } - newPath = newPath2; + newPath = nextPath; } + if (followingExisting) { - DEBUG_LOG("Resolved case-insensitive path: %s\n", newPath.c_str()); - } else { - DEBUG_LOG("Failed to resolve path: %s\n", newPath.c_str()); + DEBUG_LOG("Resolved case-insensitive path: %s -> %s\n", path.c_str(), newPath.c_str()); + return newPath; + } + return norm; +} + +static std::filesystem::path applyPathMap(const std::string &inStr) { + std::string str = inStr; + std::replace(str.begin(), str.end(), '\\', '/'); + + const auto &entries = getPathMap(); + if (entries.empty()) + return {}; + + std::string strLower = str; + toLowerInPlace(strLower); + + for (const auto &e : entries) { + std::string winLower = e.winPath; + toLowerInPlace(winLower); + + if (strLower.rfind(winLower, 0) == 0 && + (strLower.size() == winLower.size() || strLower[winLower.size()] == '/')) { + std::string rest = str.substr(e.winPath.size()); + std::filesystem::path hostBase(e.hostPath); + std::filesystem::path result; + if (rest.empty() || (rest.size() == 1 && rest[0] == '/')) { + result = hostBase; + } else { + if (rest[0] == '/') + rest.erase(0, 1); + result = hostBase / rest; + } + return resolveCaseInsensitive(result); + } + } + DEBUG_LOG("applyPathMap: %s -> (no match)\n", str.c_str()); + return {}; +} + +std::filesystem::path pathFromWindows(const char *inStr) { + // Try path map first + std::filesystem::path mapped = applyPathMap(inStr); + if (!mapped.empty()) { + return mapped; } - return newPath; + // Normalize to forward slashes + std::string str = inStr; + std::replace(str.begin(), str.end(), '\\', '/'); + + // Remove "//?/" prefix + if (str.rfind("//?/", 0) == 0) { + str.erase(0, 4); + } + + // Handle drive letter mapping + if (str.size() >= 2 && str[1] == ':') { + std::string mapping = getDriveMapping(str[0]); + if (!mapping.empty()) { + std::string rest = (str.size() >= 3 && str[2] == '/') ? str.substr(3) : str.substr(2); + std::filesystem::path p = std::filesystem::path(mapping) / rest; + return resolveCaseInsensitive(p); + } + // Fallback: strip drive letter + if (str.size() >= 3 && str[2] == '/') { + str.erase(0, 2); + } + } + + return resolveCaseInsensitive(str); } std::string pathToWindows(const std::filesystem::path &path) { - std::string str = path.lexically_normal(); + std::string hostStr = std::filesystem::absolute(path).lexically_normal().string(); + + // Try path map first (most specific first) + const auto &entries = getPathMap(); + for (const auto &e : entries) { + std::string hostPart = std::filesystem::absolute(e.hostPath).lexically_normal().string(); + if (hostStr.rfind(hostPart, 0) == 0) { + std::string rest = hostStr.substr(hostPart.size()); + if (!rest.empty() && (rest[0] == '/' || rest[0] == '\\')) + rest.erase(0, 1); + std::string result = e.winPath; + if (!result.empty() && result.back() != '\\' && result.back() != '/' && !rest.empty()) + result += '\\'; + result += rest; + std::replace(result.begin(), result.end(), '/', '\\'); + return result; + } + } + + std::string str = path.lexically_normal().string(); + + // Check for mapped drives + for (char d = 'A'; d <= 'Z'; ++d) { + std::string mapping = getDriveMapping(d); + if (mapping.empty()) + continue; + std::string mappingNorm = std::filesystem::path(mapping).lexically_normal().string(); + if (str.rfind(mappingNorm, 0) == 0) { + std::string drivePrefix = "X:"; + drivePrefix[0] = d; + str.replace(0, mappingNorm.size(), drivePrefix); + std::replace(str.begin(), str.end(), '/', '\\'); + return str; + } + } if (path.is_absolute()) { str.insert(0, "Z:"); diff --git a/src/main.cpp b/src/main.cpp index 1780113..9213b8d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -285,6 +285,11 @@ int main(int argc, char **argv) { printHelp(argv[0], true); return 1; } + if (strchr(arg, '=') && arg[0] != '-' && arg[0] != '/' && arg[0] != '.') { + // Looks like an environment variable + putenv(strdup(arg)); + continue; + } } programIndex = i; @@ -335,8 +340,12 @@ int main(int argc, char **argv) { return 1; } - // Determine the guest program name + // Build guest arguments auto guestArgs = wibo::splitCommandLine(cmdLine.c_str()); + DEBUG_LOG("Split command line into %zu args:\n", guestArgs.size()); + for (size_t i = 0; i < guestArgs.size(); i++) { + DEBUG_LOG(" arg[%zu]: %s\n", i, guestArgs[i].c_str()); + } std::string programName; if (programIndex != -1) { programName = argv[programIndex]; @@ -378,7 +387,9 @@ int main(int argc, char **argv) { // Build a command line if (cmdLine.empty()) { + DEBUG_LOG("Reconstructing command line from %zu args\n", guestArgs.size()); for (size_t i = 0; i < guestArgs.size(); ++i) { + DEBUG_LOG(" arg[%zu]: %s\n", i, guestArgs[i].c_str()); if (i != 0) { cmdLine += ' '; } @@ -400,17 +411,20 @@ int main(int argc, char **argv) { if (c == '\0' || c == '"') cmdLine += '\\'; } - backslashes = 0; if (c == '\0') break; - if (c == '\"') + if (c == '"') cmdLine += '\\'; cmdLine += c; + backslashes = 0; } if (needQuotes) cmdLine += '"'; } + } else { + // Use verbatim + DEBUG_LOG("Using exact command line: %s\n", cmdLine.c_str()); } if (cmdLine.empty() || cmdLine.back() != '\0') { cmdLine.push_back('\0'); From a91b26e05df7e3ee67e684d96b266b1aba13f3e3 Mon Sep 17 00:00:00 2001 From: freeqaz <00free@gmail.com> Date: Thu, 26 Feb 2026 18:45:59 +0000 Subject: [PATCH 6/6] Add WIBO_COMPUTER_NAME, PDBOpenEx2W stub, SigForPbCb debug logging WIBO_COMPUTER_NAME env var overrides GetComputerNameA/W return value, allowing decomp builds to reproduce original anonymous namespace hashes (MSVC hashes computer name + file path via CRC-32). PDBOpenEx2W stub enables /Zi compiler flag support. WIBO_SIGFORPBCB_LOG env var logs all SigForPbCb CRC-32 calls with init/result hashes and input data for hash algorithm debugging. Minor: add DEBUG_LOG to GetFullPathNameW and GetShortPathNameW. --- dll/kernel32/fileapi.cpp | 3 +++ dll/kernel32/winbase.cpp | 54 +++++++++++++++++++++++++++++++--------- dll/mspdb/mspdb_dll.cpp | 44 ++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 12 deletions(-) diff --git a/dll/kernel32/fileapi.cpp b/dll/kernel32/fileapi.cpp index 5504d57..4f91c56 100644 --- a/dll/kernel32/fileapi.cpp +++ b/dll/kernel32/fileapi.cpp @@ -1615,10 +1615,12 @@ DWORD WINAPI GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength, LPWSTR lp } std::string narrow = wideStringToString(lpFileName); + DEBUG_LOG("GetFullPathNameW input: %s\n", narrow.c_str()); FullPathInfo info; if (!computeFullPath(narrow, info)) { return 0; } + DEBUG_LOG("GetFullPathNameW -> %s\n", info.path.c_str()); auto widePath = stringToWideString(info.path.c_str()); const size_t wideLen = widePath.size(); @@ -1682,6 +1684,7 @@ DWORD WINAPI GetShortPathNameW(LPCWSTR lpszLongPath, LPWSTR lpszShortPath, DWORD DEBUG_LOG("GetShortPathNameW(%s)\n", longPath.c_str()); std::filesystem::path absPath = std::filesystem::absolute(files::pathFromWindows(longPath.c_str())); std::string absStr = files::pathToWindows(absPath); + DEBUG_LOG("GetShortPathNameW -> %s\n", absStr.c_str()); auto absStrW = stringToWideString(absStr.c_str()); size_t len = wstrlen(absStrW.data()); DWORD required = static_cast(len + 1); diff --git a/dll/kernel32/winbase.cpp b/dll/kernel32/winbase.cpp index 7b4730a..e0ec878 100644 --- a/dll/kernel32/winbase.cpp +++ b/dll/kernel32/winbase.cpp @@ -337,10 +337,36 @@ bool resolveDiskFreeSpaceStat(const char *rootPathName, struct statvfs &outBuf, } } -constexpr DWORD kComputerNameLength = 8; -constexpr DWORD kComputerNameRequiredSize = kComputerNameLength + 1; -constexpr const char kComputerNameAnsi[] = "COMPNAME"; -const uint16_t kComputerNameWide[] = {u'C', u'O', u'M', u'P', u'N', u'A', u'M', u'E', 0}; +static const char* getComputerNameAnsi() { + static const char* name = nullptr; + if (!name) { + const char* env = getenv("WIBO_COMPUTER_NAME"); + if (env && env[0]) { + name = strdup(env); + } else { + name = "COMPNAME"; + } + } + return name; +} + +static DWORD getComputerNameLength() { + return static_cast(std::strlen(getComputerNameAnsi())); +} + +static const uint16_t* getComputerNameWide() { + static uint16_t* wideName = nullptr; + if (!wideName) { + const char* ansi = getComputerNameAnsi(); + size_t len = std::strlen(ansi); + wideName = new uint16_t[len + 1]; + for (size_t i = 0; i < len; i++) { + wideName[i] = static_cast(ansi[i]); + } + wideName[len] = 0; + } + return wideName; +} struct DllRedirectionEntry { std::string nameLower; @@ -765,14 +791,16 @@ BOOL WINAPI GetComputerNameA(LPSTR lpBuffer, LPDWORD nSize) { return FALSE; } - if (*nSize < kComputerNameRequiredSize) { - *nSize = kComputerNameRequiredSize; + DWORD nameLen = getComputerNameLength(); + DWORD requiredSize = nameLen + 1; + if (*nSize < requiredSize) { + *nSize = requiredSize; setLastError(ERROR_BUFFER_OVERFLOW); return FALSE; } - std::strcpy(lpBuffer, kComputerNameAnsi); - *nSize = kComputerNameLength; + std::strcpy(lpBuffer, getComputerNameAnsi()); + *nSize = nameLen; return TRUE; } @@ -787,14 +815,16 @@ BOOL WINAPI GetComputerNameW(LPWSTR lpBuffer, LPDWORD nSize) { return FALSE; } - if (*nSize < kComputerNameRequiredSize) { - *nSize = kComputerNameRequiredSize; + DWORD nameLen = getComputerNameLength(); + DWORD requiredSize = nameLen + 1; + if (*nSize < requiredSize) { + *nSize = requiredSize; setLastError(ERROR_BUFFER_OVERFLOW); return FALSE; } - wstrncpy(lpBuffer, kComputerNameWide, static_cast(kComputerNameRequiredSize)); - *nSize = kComputerNameLength; + wstrncpy(lpBuffer, getComputerNameWide(), static_cast(requiredSize)); + *nSize = nameLen; return TRUE; } diff --git a/dll/mspdb/mspdb_dll.cpp b/dll/mspdb/mspdb_dll.cpp index cd5d735..be1ec6f 100644 --- a/dll/mspdb/mspdb_dll.cpp +++ b/dll/mspdb/mspdb_dll.cpp @@ -6,6 +6,8 @@ // No virtual destructors (MSVC: 1 slot, GCC: 2 slots - would shift all indices). #include +#include +#include #include #define DLLEXPORT extern "C" __declspec(dllexport) @@ -552,12 +554,31 @@ DLLEXPORT int __cdecl PDB_OpenValidate5(const wchar_t *, const wchar_t *, void * return openPDB(pec, ppPDB); } +DLLEXPORT int __cdecl PDBOpenEx2W(const wchar_t *, const char *, int32_t *pec, wchar_t *, unsigned int, void **ppPDB) { + return openPDB(pec, ppPDB); +} + DLLEXPORT int __cdecl PDBExportValidateInterface(uint32_t) { return 1; } // CRC-32 with reflected polynomial 0xEDB88320 (ISO 3309). // Used by the linker to hash string literal content for ??_C@_ symbol names. +// Also used by c1xx.dll for anonymous namespace hash generation. + +static FILE *getSigLogFile() { + static FILE *logFile = nullptr; + static bool tried = false; + if (!tried) { + tried = true; + const char *path = getenv("WIBO_SIGFORPBCB_LOG"); + if (path && path[0]) { + logFile = fopen(path, "a"); + } + } + return logFile; +} + DLLEXPORT uint32_t __cdecl SigForPbCb(const unsigned char *pb, uint32_t cb, uint32_t dwInitial) { uint32_t crc = dwInitial; for (uint32_t i = 0; i < cb; i++) { @@ -572,6 +593,29 @@ DLLEXPORT uint32_t __cdecl SigForPbCb(const unsigned char *pb, uint32_t cb, uint } crc = (crc >> 8) ^ entry; } + + FILE *logFile = getSigLogFile(); + if (logFile) { + // Log: init hash data_hex_or_string -> result + fprintf(logFile, "0x%08x 0x%08x %u ", dwInitial, crc, cb); + if (cb <= 260) { + bool printable = true; + for (uint32_t i = 0; i < cb; i++) { + if (pb[i] < 0x20 || pb[i] > 0x7e) { printable = false; break; } + } + if (printable && cb > 0) { + fprintf(logFile, "\"%.*s\"", (int)cb, pb); + } else { + for (uint32_t i = 0; i < cb && i < 64; i++) fprintf(logFile, "%02x", pb[i]); + if (cb > 64) fprintf(logFile, "..."); + } + } else { + fprintf(logFile, "(%u bytes)", cb); + } + fprintf(logFile, "\n"); + fflush(logFile); + } + return crc; }