Add Xbox 360 linker support (fake mspdb PDB interface)#110
Add Xbox 360 linker support (fake mspdb PDB interface)#110freeqaz wants to merge 6 commits intodecompals:mainfrom
Conversation
dll/kernel32/memoryapi.cpp
Outdated
|
|
||
| errno = 0; | ||
| void *mapBase = mmap(requestedBase, mapLength, prot, mapFlags, mmapFd, alignedOffset); | ||
| #ifdef MAP_FIXED_NOREPLACE |
There was a problem hiding this comment.
It's possible we could just MAP_FIXED in the first place, as long as it properly checks against the allocated ranges properly
dll/kernel32/memoryapi.cpp
Outdated
| 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). |
dll/kernel32/internal.h
Outdated
| 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 |
| setLastError(ERROR_PATH_NOT_FOUND); | ||
| return INVALID_HANDLE_VALUE; | ||
| } | ||
| if (fInfoLevelId != FindExInfoStandard) { |
There was a problem hiding this comment.
need debug logs for all these branches to indicate unimplemented behavior
| } | ||
|
|
||
| std::string narrow = wideStringToString(lpFileName); | ||
| DEBUG_LOG("GetFullPathNameW input: %s\n", narrow.c_str()); |
src/main.cpp
Outdated
| optionDebug = true; | ||
| continue; | ||
| } | ||
| if (strncmp(arg, "--path-alias=", 13) == 0) { |
| { | ||
| struct sigaction sa = {}; | ||
| sa.sa_sigaction = [](int sig, siginfo_t *, void *) { | ||
| kernel32::flushAllFileViews(); |
There was a problem hiding this comment.
there are probably other things to call before exiting
| RPC_SECURITY_QOS *securityQos); | ||
| RPC_STATUS WINAPI RpcBindingFree(GUEST_PTR *binding); | ||
| RPC_STATUS WINAPI RpcStringFreeW(GUEST_PTR *string); | ||
| #ifndef __x86_64__ // TODO |
There was a problem hiding this comment.
should check the generated x64 stubs to see what this actually generates. it's probably very broken
dll/kernel32/fileapi.cpp
Outdated
|
|
||
| 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)) { |
There was a problem hiding this comment.
was this functionally necessary for some reason?
dll/mspdb.cpp
Outdated
There was a problem hiding this comment.
This implementation is technically impressive but incredibly scary. It's probably better to build as a separate DLL and include it (similar to our msvcrt), rather than manually writing x86 instructions to a buffer
There was a problem hiding this comment.
how would you package this up such that it could be included as a DLL? Do you have an example repo?
Also thanks for the in depth review. Much appreciated.
…piled 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
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).
- 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
…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.
…piled 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
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.
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.
0086de2 to
a91b26e
Compare
Motivation
The Dance Central 3 decompilation project needs to link decomp
.objfiles with original object code using the MSVC Xbox 360 linker (link.exe16.00.11886.00). Previously this required wine, which is a heavy dependency and harder to integrate into CI. It's also slow af. This PR lets the linker run natively under wibo.The linker hard-depends on
mspdb80.dllfor PDB/XDB output — if the PDB interface returns failure, the linker emits LNK1207 and deletes the output file. We need to satisfy the interface, not produce a real PDB. (at least today!)Approach
Fake PDB vtables — We provide 8 COM-style objects (PDB, DBI, Mod, TPI, GSI, Dbg, NameMap, Stream) with vtables full of tiny x86
__thiscallstubs generated at runtime into mmap'd executable memory. Each stub does the minimum to keep the linker happy: return success codes, write output pointers to sub-objects, or return empty query results. The vtable slot assignments and calling conventions were determined empirically against the linker binary, cross-referenced with microsoft-pdb.64-bit safe — All fake objects, vtable arrays, and sentinel values are allocated from the
MAP_32BITcode page (not static globals in host BSS). This ensuresaddr32()never truncates pointers on a 64-bit host where BSS can be at >4GB addresses.Supporting kernel32 changes — The linker also exercises file mapping and file I/O paths that wibo didn't previously cover:
MapViewOfFilewithFILE_MAP_ALL_ACCESS(needsMAP_SHARED, notMAP_PRIVATE)MAP_FIXEDfallback whenMAP_FIXED_NOREPLACEfailsSetEndOfFile,GetFileType,SetFilePointerExflushAllFileViews()on SIGSEGV to preserve partial PE outputConfidence
test_mspdb.cexercises the fullLoadLibrary→GetProcAddress→ function call path for all C-style mspdb exports, plus vtable dispatch through PDB and DBI objects (the exact code path the linker uses).addr32()are dereferenceable and that stubs execute correctly.Test plan
ctest --output-on-failure— 36/36 pass (including newtest_mspdb)QueryInterfaceVersion()andOpenDBI()through PDB vtable, then dispatches through the returned DBI object's vtableNote: I wrote a significant amount of this with Claude Code so if I got anything wrong, that's why. It does work though!