Skip to content

feat(relinker): add --trace-imports for Windows executables - #4547

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
codingreapermaps:feat/relinker-trace-imports
Oct 13, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
codingreapermaps:feat/relinker-trace-imports

Conversation

@codingreapermaps

@codingreapermaps codingreapermaps commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

What

--trace-imports makes the Windows entry stub pass each function import through TraceImport_nid_no_patch in libc.prx (#4546) before writing its slot, so the calls a title makes to the libraries named in APS5_TRACE_CALLS are recorded and shown by the crash and abort reports. Without the option the output is unchanged. It covers part of #3815: the entry of a call, on Windows; arguments, results and Linux are not done.

Traced: every PLT slot, and a GOT slot only when the original symbol is a function. The second rule matters: the rebuilt dynamic symbol table types every import as a function, so the original type is carried from the input relocation. Without it a data import such as __stdoutp got a thunk and a homebrew title crashed when it wrote through it.

The option conflicts with --lazy-binding and needs libc.prx among the libraries; a libc.prx without the export fails at start-up with a message. Weak imports, imports of modules loaded at run time and functions looked up with sceKernelDlsym are not traced. Limits are in docs/dev/debt/libc.md and docs/dev/debt/relinker.md, usage in USAGE.md.

This branch contains the commit of #4546; only the second commit belongs to this PR.

Tested

Windows 11, MinGW-w64 GCC 15.2.0 (WinLibs r7), on main 43e3a70.

  • windows_trace_imports (new): a relinked fixture reaches the hook with the import's name and library and runs the address the hook returns; a data-typed GOT import is left alone; a weak import still resolves; a libc.prx without the export fails with the message; the option is rejected without --windows, with --lazy-binding and without libc.prx.
  • All 116 relinker tests pass.
  • A fixture importing getpagesize from the built libkernel.prx returns 0x4000 without the option, with it and the variable unset, and with it and APS5_TRACE_CALLS=libkernel; only the last prints the call.
  • DOOM Shareware (PPSA99666, lowbit/ps5-doom v1.1.1; eboot.bin and sce_module/libc.prx unwrapped from fSELF, relinked with --windows --to-intel --trace-imports; Intel i7-1165G7; before the rebase onto current main): with every library traced it reaches the same point as the untraced run (WAD loaded, audio opened at 48000 Hz, ST_Init) and ends with a normal process exit after 901,307,599 recorded calls. The last 64 were 62 sin, one pthread_mutex_unlock and one sceAudioOutOutput, which is why feat(libc): record traced import calls on Windows #4546 also counts calls per import.
  • Yamagi Quake II (PPSA99007, v0.2.0-alpha.1), relinked with --windows --to-intel --trace-imports: the option was used, together with APS5_HANG_DUMP_SECS, to confirm the start-up hang described in fix(libc): find mspace chunks for minimum-aligned allocations by index #4280 (main thread inside the mspace allocator, no window), now addressed by fix(libc): round mspace block sizes to the 32-byte alignment #4407. The report from that run was not kept. With local changes outside this PR the title goes on to load demo1 and present frames; that part does not depend on this option.
  • tools/check_conventions.py and tools/check_test_registration.py pass.

Not tested: either title after the rebase, a commercial title, Linux or macOS output (the option is rejected there).

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 in TechnicalDebt
  • 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: feat(libc): record traced import calls on Windows #4546
  • Unblocks: none
  • AI-assisted: yes (Claude Code)

🤖 Generated with Claude Code

@codingreapermaps

Copy link
Copy Markdown
Contributor Author

On the overlap comment: none of the four implements the same thing, and this PR does not build on any of them, so Depends on stays #4546 only. They are edits to neighbouring lines. Each merges cleanly into main and conflicts with this branch, so whichever goes second needs a rebase; I will rebase this one as they land.

AI-assisted (Claude Code).

@codingreapermaps
codingreapermaps force-pushed the feat/relinker-trace-imports branch 2 times, most recently from 7450d06 to ff34ff9 Compare October 12, 2026 22:31
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 12, 2026
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 13, 2026
@codingreapermaps
codingreapermaps force-pushed the feat/relinker-trace-imports branch from ff34ff9 to c73d027 Compare October 13, 2026 09:17
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 13, 2026
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 13, 2026
With --trace-imports the entry stub resolves TraceImport_nid_no_patch
in libc.prx and passes each function import through it before writing
the slot: every PLT slot, and a GOT slot only when the original symbol
is a function, so data imports such as __stdoutp keep their address.
Without the option the output is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@adriadam10

Copy link
Copy Markdown
Collaborator

Rebased onto current main: the branch still carried the first commit of #4546, which merged separately, so I dropped it, and resolved the conflicts with the refactored main.cpp/WindowsPePatcher (traceImports now goes through ConversionOptions into Conversion.cpp) plus the USAGE.md/debt/libc.md neighbours.

Checked the entry-stub change, the function/data split of imports (FunctionImports) and the option validation; relinker tests pass (125/125) and windows_trace_imports fails without the non-test changes. The Windows execution part of that test is covered by the full Build; merging once it is green.

@adriadam10
adriadam10 force-pushed the feat/relinker-trace-imports branch from c73d027 to 430e5c8 Compare October 13, 2026 14:59
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 13, 2026
@github-actions

github-actions Bot commented Oct 13, 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
#4593 core/relinker/cli/include/Cli.hpp L26
core/relinker/cli/src/CliArgs.cpp L13, L126
docs/user/USAGE.md L53
#4774 core/relinker/elfpatcher/include/elfpatcher/windows/WindowsEntryStubBuilder.hpp L23
core/relinker/elfpatcher/src/windows/WindowsEntryStubBuilder.cpp L69
core/relinker/elfpatcher/src/windows/WindowsPePatcher.cpp L90
#4780 docs/user/USAGE.md L159
#5021 docs/dev/debt/relinker.md L15
#5052 core/relinker/cli/src/CliArgs.cpp L13
#5087 docs/dev/debt/libc.md L67
#5229 core/relinker/domain/include/domain/Types.hpp L64
core/relinker/relinker/src/output/SysVDynamicSectionBuilder.cpp L96

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants