Repository navigation
fix(relinker): keep PT_GNU_EH_FRAME in the relinked executable - #1586
Merged
oneandonlydean merged 1 commit intoOct 9, 2026
Merged
Conversation
The segment filter dropped every header in the PT_LOOS..PT_HIOS range, which includes PT_GNU_EH_FRAME. The unwinders find the executable's unwind tables through that header, so no C++ exception thrown in the main executable could be caught on Linux. The header is kept only when the output program header table still fits in the input's table; otherwise it is dropped as before, with a warning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8
9 tasks done
9 tasks done
9 tasks done
9 tasks done
Contributor
|
Windows run, since the description says it wasn't built there: Windows 11 Pro (26200), MinGW-w64 GCC 15.2.0 (WinLibs r7, the CI build), Release, configured like CI, Ryzen 9 5900X. This commit merged onto
To check "the change does not touch the PE output" on a real input, I relinked DOOM Shareware PPSA99666 v1.1.1 (
No slot warning for this input. In code, AI-assisted: yes (Claude Code). |
9 tasks done
7 tasks done
This was referenced Oct 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The relinker dropped the
PT_GNU_EH_FRAMEprogram header from the Linux executable it writes.SegmentFilterskips every header inPT_LOOS..PT_HIOS(0x61000000..0x6fffffff) to remove the SCE-specific ones, andPT_GNU_EH_FRAME(0x6474e550) is in that range. The.eh_frame_hdrdata itself stayed in the image, but nothing pointed to it any more.Everything that finds unwind tables for the main executable looks for that header:
FindFrameincore/libs/prx/libc/src/exception/Unwind.cpp, throughdl_iterate_phdr);sceKernelGetModuleInfoFromAddron Linux (ModuleInfo.cpp), which fillseh_frame_hdr_addr,eh_frame_addrandeh_frame_sizefrom it.Without it, a C++ exception thrown in the main executable cannot be caught: the unwinder finds no frame description and the process ends in
std::terminate. Guest modules were not affected:GuestModuleWriter::WriteLinuxalready keepsPT_GNU_EH_FRAME.The fix keeps
PT_GNU_EH_FRAMEinSegmentFilter::ShouldSkip, the same wayPT_OS_PROCPARAMis kept. The header is copied unchanged and points to data already inside a keptPT_LOAD.The relinker writes the new program headers into the input's own table, so keeping this header needs one more slot.
ProgramHeaderLayoutBuilder::WriteLayoutkeeps it only when the whole output table, with the five headers it adds (PT_PHDR, twoPT_LOAD,PT_DYNAMIC,PT_INTERP), still fits in the input's table. Otherwise it dropsPT_GNU_EH_FRAMEas before and prints aWARNING:line on stderr. An input that relinks today still relinks, with the same headers. (The existing "Not enough program header slots" check counts four added headers,kSyntheticProgramHeaderCount, while five are written; this PR leaves that check as it is.)Windows is not affected:
SegmentFilteris only used byLinuxElfPatcher. The PE path (WindowsPePatcher,WindowsGuestModuleWriter) readsPT_GNU_EH_FRAMEstraight from the input headers to build its unwind data.This is one of the fixes needed for Hollow Knight Silksong on Linux; discussion #1519 has the full stack. Silksong ships its own guest libc, whose unwinder asks
sceKernelGetModuleInfoForUnwind. That function also needs a Linux fix, which comes in a separate PR. The two are independent: this one alone fixes titles that use the project's libc, and the other one needs this header to find the executable's tables.New test
linux_eh_frame_header(core/relinker/relinker/tests/test_linux_eh_frame_header.py, same style aslinux_load_alignment): it relinks a small ELF with aPT_GNU_EH_FRAMEheader for Linux and checks that the output has the same header, that the bytes it points to are unchanged, and that the SCE-specific headers are still removed. Two more cases give the input one or two slots fewer: they still relink as before, the output has noPT_GNU_EH_FRAME, and the warning is printed.Tested
Arch Linux, i7-8750H, GTX 1050 Ti Max-Q (NVIDIA 580.178.04). Base:
mainat cba8d86, Release build with gcc, compared with an unmodified build of the same commit.ctest -j4: 453 of 453 pass, 0***Failed, 0***Skipped(unmodified build: 452 of 452, 0 failed, 0 skipped). The only difference is the new test.VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation ctest -j2 -V): the same singleVUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605message (counted 2) as the unmodified build, no new VUIDs; 453 of 453 pass.SegmentFilterline, the test fails (noPT_GNU_EH_FRAMEin the output); without the fallback, the 5-slot input fails to relink with "Not enough program header slots"; with the slot check written as<=, the 6-slot input writes 7 headers. With the fix restored, it passes.eboot.bin, 14 program headers each,PT_GNU_EH_FRAMEpresent), with the unmodified relinker and with this branch: both succeed both times. The output keepsPT_GNU_EH_FRAMEwith this branch (14 headers instead of 13), and every guest module is byte-identical (1 for Horizon, 7 for Silksong).python3 tools/check_conventions.py --base cba8d86d: passes.sceKernelGetModuleInfoForUnwindfix: before, the game ended right after the intro cinematic withTerminating due to uncaught exception of type Il2CppExceptionWrapper(an IL2CPPSystem.Reflection.AmbiguousMatchExceptionthat the game normally catches). With both fixes (on a local branch that also merges other open PRs), the cinematic starts at about t+112 s and finishes, and the game goes into gameplay, saves, and keeps running until the run was stopped at t+500 s. Stopping the run ends in a separate shutdown error that is already known.Not built or run on Windows or macOS; the change does not touch the PE output.
Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt.mdfiles or images added to the repository (attach them to this PR)🤖 Generated with Claude Code
https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8