Skip to content

fix(relinker): prefer 0x400000 for Windows executables - #9332

Open
rooklz wants to merge 1 commit into
boykopovar:mainfrom
rooklz:fix/windows-console-base
Open

rooklz wants to merge 1 commit into
boykopovar:mainfrom
rooklz:fix/windows-console-base

Conversation

@rooklz

@rooklz rooklz commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

What

On Windows, Ruffle (PPSA68091), VLC (PPSA85300) and XPSemu (PPSA97358) only start when they're converted with --windows-fixed-base 0x400000. They read their GOT slots at 0x400000 plus the offsets in app0/imports.txt (#4013), but the default executable puts its lowest ELF load page at 0x140010000, so the first read faults. babyhuey on #5732, about VLC on main 64a9caa62: with the option it "runs the full 60 s at about 119 fps with no fault. Without --windows-fixed-base it still faults at start-up as before (#4013)". dantdmdarealguy measured the same for Ruffle on #4013. The web converter has no way to pass the option.

This makes 0x400000 the preferred address of the executable's lowest ELF load page, which is a PE image base of 0x3f0000, since the headers take the 64 KiB before it. Otherwise nothing changes:

  • The executable keeps its base relocations and still has no DYNAMIC_BASE, as since fix(relinker): load Windows executables at a fixed low base #4237. Windows maps the executable before anything else, so the range is normally free and it loads there. If the range is taken, Windows relocates it, as it did when 0x140000000 was taken. --windows-fixed-base is unchanged: an exact address, no relocations, and an occupied address fails to load.
  • It's still inside the 40-bit window that fix(relinker): load Windows executables at a fixed low base #4237 was about and below the guest arena at 0x200000000. An image big enough to reach the shared user data page at 0x7ffe0000 can't load there, so Windows relocates it; the debt entry says so.
  • Bundled modules keep DYNAMIC_BASE and their preferred base 0x140000000. macOS output is unchanged.
  • WindowsPeWriter::Write now takes the image base and whether it's fixed, instead of an optional fixed base, so the patcher can pass a preferred base.

#8973 does the same on Linux. The two don't depend on each other.

Tested

No title run on my side. ycbuzi ran VLC (PPSA85300) on this head merged onto main a849f2516e (comment), Windows 11, RTX 4070 SUPER: on main the default conversion crashed at start-up with 0xc0000005 and 0 presents (2 runs); with this PR the default conversion rendered at 72.6 to 118.9 fps (4 of 5 runs; the fifth stopped presenting after about 20 s, which also happened once with --windows-fixed-base 0x400000). ccleste (PPSA59322) and DriftCheck (PPSA98766) render on the new default too. Ruffle and XPSemu weren't run.

  • windows_fixed_base now converts its imports.txt-style fixture without any option and runs it. The fixture reads the slot at 0x400300 and returns 42 when the slot is filled. With only that check added to main 950388bfd, the converted executable exits with 0xC0000005 on a GitHub-hosted Windows runner (verify run on my fork), which is the fault in Ruffle, VLC and XPSemu read their GOT at a fixed 0x400000 base from app0/imports.txt and fault once all imports load #4013. With this change it passes. The header check there and in windows_tls_protected_entries now expects 0x3f0000.
  • Windows Server 2025 (GitHub-hosted), WinLibs GCC 15.2.0, relinker-only build, run on my fork: 233/233 with the change and 233/233 on its base 950388bfd. That includes windows_address_space, which runs the default executable 8 times and checks that its main thread stack stays below the arena.
  • macOS 26.2 (Apple M5), AppleClang 21, relinker-only build: 229/229.
  • check_conventions.py passes. pr_overlap.py reports one conflict: fix(relinker): trap inline SDK mount syscall #8729 changes the next line of docs/dev/debt/relinker.md, so whichever lands second needs a one-line rebase.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: PPSA85300 VLC (PPSA85300) #5732, PPSA97358 XPSemu (PPSA97358) #5600
  • GPUs tested: n/a
  • AI-assisted: yes

@github-actions github-actions Bot added the unblocks title Names a title ID on the Unblocks line label Oct 21, 2026
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 8729 docs/dev/debt/relinker.md L11-12

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 5021 docs/dev/debt/relinker.md
docs/user/USAGE.md
PR 5925 docs/user/USAGE.md
PR 6171 docs/user/USAGE.md
PR 6788 docs/user/USAGE.md
PR 6924 docs/user/USAGE.md
PR 7822 docs/dev/debt/relinker.md
PR 7918 docs/dev/debt/relinker.md
docs/user/USAGE.md
PR 8076 docs/dev/debt/relinker.md
PR 9408 docs/dev/debt/relinker.md
docs/user/USAGE.md

@ycbuzi

ycbuzi commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Windows run of 14ffbf9 merged onto main a849f25 (189a4bb233, clean), with #7264's tools/title_run.py merged on top to run the titles (it changes only tools/ and docs/dev/TRACING.md). Windows 11 Home 10.0.26200, MinGW-w64 GCC 15.2.0 (ucrt-posix-seh r7), Release, BUILD_TESTING=ON, RTX 4070 SUPER (NVIDIA 616.92), Ryzen 9 7900.

VLC (PPSA85300, 01.000.007), converted by title_run.py with --registry, 60 s per run:

Build Conversion Runs Outcome ImageBase Base relocations
main a849f25 default 2 crashed at start-up, FATAL: unhandled exception 0xc0000005, 0 presents 0x140000000 kept
this PR default 5 4 rendering, 1 stalled (below); 72.6 to 118.9 fps 0x3f0000 kept
this PR --windows-fixed-base 0x400000 4 3 rendering, 1 stalled (below); 99.4 to 119.8 fps 0x3f0000 stripped

DllCharacteristics is 0x100 (NX_COMPAT only, no DYNAMIC_BASE) in all three. Three of the default runs and three of the fixed-base ones alternated (A B B A A B): 118.9, 116.2 and 106.8 fps against 119.8, 99.4 and 107.2, so no difference beyond the run-to-run spread on this machine. The last two of those, one of each, stopped presenting after about 20 and 30 s with no error line (the runner's "stalled"), so it happens with the existing option too.

ccleste (PPSA59322) and DriftCheck (PPSA98766) on the new default: 2 of 2 rendering each (116.5 and 117.1 fps; 54.3 and 58.5 fps).

ctest --test-dir core\relinker (162 tests here), --repeat until-fail:2, -j 4: 159 pass on both repeats, including windows_fixed_base, windows_tls_protected_entries and windows_address_space. guest_none_relocations, guest_relative_section_symbols and overlapping_relocations reached their 20 and 30 s timeouts under that load; run one at a time they pass on both repeats in 1 to 14 s, on this PR and on main alike.

Not run: Ruffle (PPSA68091) and XPSemu (PPSA97358), which I don't have.

@rooklz

rooklz commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

Thanks, that's the title run this PR was missing. Good to see the default and --windows-fixed-base 0x400000 land in the same fps range, and that the stall shows up with the existing option too, so it isn't from this change. I've added your run to the description.

@wachi35676

Copy link
Copy Markdown
Contributor

Windows and Linux run of 14ffbf9 merged onto main a6dcea9 (merges cleanly). Windows 11 Home 10.0.26300, Ryzen 5 9600X, WinLibs MinGW-w64 GCC 15.2.0, Release relinker-only BUILD_TESTING=ON; Linux: WSL Ubuntu 24.04, GCC 13.3.0, same configuration.

  • ctest (relinker-only): Windows 233/233 (performance_compare hit its 20 s timeout in the parallel pass and passed on rerun; it passed on main in the same setup). Linux 237/238, the one failure being shader_cache_runtime_sources (its internal 15 s CMake configure timed out), which fails the same way on main here.
  • Negative control: the PR's test_windows_fixed_base.py against main's relinker fails; main's version of the test plus only the new default-conversion run(...) line fails with the converted fixture exiting 3221225477 (0xC0000005) after Transferring control to ELF entry point. With this PR both pass.
  • Four homebrew (PPSA99005, PPSA47402, PPSA99581, PPSA99995), each converted for --windows --to-intel, --macos --to-rosetta and Linux --to-intel: macOS and Linux outputs are byte-identical to main. On Windows only app.exe changes; every bundled module is byte-identical. In app.exe only ImageBase changes (0x140000000 to 0x3f0000); SizeOfImage, the size of .reloc, DllCharacteristics 0x100 (no DYNAMIC_BASE) and Characteristics 0x22 (relocations not stripped) match main.
  • Where Windows actually maps the image: each app.exe started with CREATE_SUSPENDED and PEB.ImageBaseAddress read back, 3 times each. main: 0x140000000 for all four. This PR: 0x3f0000 for all four, including PPSA99581 (SizeOfImage 0x6bf1000, so it ends at 0x6fe1000).

Not run: the converted titles themselves (my build is relinker-only); ycbuzi's VLC runs above cover that.

AI-assisted: yes (Claude Code); results above were run on this machine.

@rooklz
rooklz force-pushed the fix/windows-console-base branch from 14ffbf9 to 92fbd0a Compare October 22, 2026 13:33
@rooklz

rooklz commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current main (8b3e39832a): head 92fbd0adc1, same diff.

This branch has not been deployed

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

Labels

docs relinker unblocks title Names a title ID on the Unblocks line

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants