Add Xbox 360 linker support (fake mspdb PDB interface)#110
Add Xbox 360 linker support (fake mspdb PDB interface)#110freeqaz wants to merge 4 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
dll/kernel32/fileapi.cpp
Outdated
| } | ||
|
|
||
| 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.
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
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!