Skip to content

Convert the wide uninitialized_copy thunk at 0x0000DF10 to clean C++ - #3

Open
HugoDijkstra wants to merge 2 commits into
Open-BFME:masterfrom
HugoDijkstra:convert-wide-uninitialized-copy
Open

HugoDijkstra wants to merge 2 commits into
Open-BFME:masterfrom
HugoDijkstra:convert-wide-uninitialized-copy

Conversation

@HugoDijkstra

Copy link
Copy Markdown
Collaborator

Summary

  • Retail's body at RVA 0x0000DF10 (?uninitialized_copy@_STL@@YAPAGPAG00@Z, the wide basic_string<unsigned short>::reserve copy helper) is a five-byte jmp into __copy_trivial at 0x000179B0 (Ghidra: thunk_FUN_004179b0) — the linker folded this instantiation onto the narrow one and left a stub for the one external caller that still reaches it by address.
  • Replaces the naked-asm dump row with a plain forwarding definition (__cdecl, three pointers passed straight through) that MSVC 7.1 sibling-call optimizes into that exact jmp.
  • Given its own translation unit rather than placed next to the existing declaration in stlport_wide_string_reserve.cpp, because merely having a body for this function visible anywhere in that TU perturbed reserve()'s own already-matched register allocation for the call site.

Test plan

  • ./build.sh Code/Libraries/Source/WWVegas/WWLib/stlport_wide_uninitialized_copy.cpp — byte-exact match
  • ./build.sh Code/Libraries/Source/WWVegas/WWLib/stlport_wide_string_reserve.cpp — unaffected, still byte-exact
  • python3 tools/check_csv.py — clean

🤖 Generated with Claude Code

Hugo Dijkstra and others added 2 commits September 5, 2026 14:40
Retail's body here is a five-byte jmp into __copy_trivial at 0x000179B0
(Ghidra: thunk_FUN_004179b0) -- the linker folded this wchar_t instantiation
onto the narrow one and left a stub for the one external caller,
basic_string<unsigned short>::reserve, that still reaches it by address.

A plain forwarding definition reproduces the stub without hand-writing the
jump: __cdecl, three pointers passed straight through with nothing else live
across the call, so MSVC 7.1 sibling-call optimizes the whole function into
that one jmp. It lands in its own translation unit rather than next to the
existing declaration in stlport_wide_string_reserve.cpp, because merely
having a body for this function visible anywhere in that TU perturbed
reserve()'s own already-matched register allocation for the call.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017n3PU2fXgkEdmRzrtDmsCU
Retail's body at 0x0000BDA0 turned out to match vendor/stlport/stl/_monetary.h's
own base-class do_put(long double) exactly: it ignores the units value and
passes an uninitialized 64-byte buffer to the do_put(string) overload through
the vtable, confirmed against the real vendored header rather than guessed.

The vendored-header instantiation route (stlport_money_put.cpp) gets close but
routes free() through the CRT import table, which can't reproduce retail's
direct call. A hand-rolled shim lands 174 of 174 bytes with only five load/store
displacements differing, fixing three traps already known elsewhere in this
codebase: free() needs C++ linkage (not extern "C") for the byte EH-state store;
allocator<CharT> needs a user-declared default constructor to avoid a value-init
zero-store; and the string temporary's allocator-storage member has to be
dropped entirely, not just emptied, or it pads the frame four bytes and shifts
every later local.

What's left is exactly which stack slot the locale temporary lands in -- retail
reuses the dead incoming str-parameter slot, this body gives it a fresh one.
Declaration order doesn't move it; next attempt should look at what else is
competing for that slot.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0135fAF2xVRZjiD7q7gF9fUX
@HugoDijkstra

Copy link
Copy Markdown
Collaborator Author

Added a second commit: banked a 0.95-score partial for money_put::do_put(long double) at 0x0000BDA0 (reverse/attempts/0x0000bda0.cpp, logged in reverse/re_attempts.log). It's byte-count-exact (174/174) with only one stack-slot placement still differing from retail -- documented in the commit message and the stash file for whoever picks it up next. Doesn't change matched-byte progress on its own (it's a partial, not a landed match), but leaves the next attempt much closer than the prior "attempted, no stash" state.

@dginovker

Copy link
Copy Markdown
Contributor

Thanks for the PR! I gave you write permissions so you can push to master directly now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants