Repository navigation
Bind every unwinder entry point to the executable's libunwind - #7
Open
buyemune-sys wants to merge 2 commits into
Open
buyemune-sys wants to merge 2 commits into
buyemune-sys wants to merge 2 commits into
Conversation
Security updates replaced libapparmor1 4.0.1really4.0.1-0ubuntu0.24.04.7 with .9 and libexpat1 2.6.1-2ubuntu0.4 with ubuntu0.6, and the pool kept only the new files, so the cold-cache Fetch x86_64 job answers 404 and every x86-64 job behind it is skipped. Both pins move to the versions noble-updates ships now; their SHA256s match the archive's Packages index. The other twelve pins are still served.
The bridge overrode _Unwind_RaiseException and the accessors C++ personalities use, but not _Unwind_GetCFA, _Unwind_GetIP, _Unwind_GetGR, _Unwind_Backtrace, _Unwind_ForcedUnwind, _Unwind_FindEnclosingFunction, _Unwind_Find_FDE, nor __register_frame and __deregister_frame. A guest that links libgcc_s got those from the host's libgcc while the exception itself ran in the vendored libunwind: - a personality calling _Unwind_GetCFA read libunwind's context with libgcc's layout. LuaJIT does exactly that, so the first Lua error in a glibc-linked LuaJIT (Tarantool, for one) crashed in err_unwind; - unwind info a JIT registers for its generated code went into libgcc, which never walks the exception, so the search phase stopped at the first generated frame. All of them are now overrides. __register_frame also needs an adapter: libgcc's takes a whole .eh_frame section up to its zero terminator, libunwind's takes one FDE, so the bridge walks the section and registers each FDE. The guest test gains a personality of its own reading _Unwind_GetCFA, and raises through a generated frame registered the libgcc way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
buyemune-sys
force-pushed
the
fix/one-unwinder
branch
from
October 20, 2026 08:11
59f6062 to
a4e6d46
Compare
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.
The problem
The bridge overrides
_Unwind_RaiseExceptionand the accessors C++ personalities use (GetLanguageSpecificData,GetRegionStart,GetIPInfo,SetGR,SetIP, …). It does not override_Unwind_GetCFA,_Unwind_GetIP,_Unwind_GetGR,_Unwind_Backtrace,_Unwind_ForcedUnwind,_Unwind_FindEnclosingFunction,_Unwind_Find_FDE, nor__register_frame/__deregister_frame. In a guest linked againstlibgcc_s, those come from the host's libgcc while the exception itself is unwound by the vendored libunwind. Two things break:_Unwind_GetCFA(context)reads libunwind's_Unwind_Contextwith libgcc's layout and returns garbage. LuaJIT's personality does exactly this (lj_err_unwind_dwarf→_Unwind_GetCFA). So any glibc binary embedding LuaJIT (Tarantool, in our case) segfaults inerr_unwindundersolo runon its first Lua error, even one caught bypcall.__register_frame, which a JIT uses to register the.eh_frameof generated code, goes to libgcc. The exception, however, looks FDEs up in libunwind, so the search phase stops at the first generated frame (_URC_END_OF_STACKbefore the personality is ever reached).The table already says "_Unwind_Context is private to the unwinder that created it" (
lib/glibc_shim.cpp:4975); the list just wasn't complete.Reproduction
LD_DEBUG=bindings solo run tarantool -e 'pcall(error, "x")'onmain:A minimal C program does the same: its own personality via
.cfi_personality,_Unwind_RaiseException, and_Unwind_GetCFAinside the personality. Natively on glibc the CFA points into the stack next to the frame's locals; onmainunder solo it iscfa=7de00faf32fdwithanchor=7fff85d3df9f.The fix
__register_frameneeds an adapter. libgcc's takes a whole.eh_framesection (CIEs and FDEs up to a zero terminator), while LLVM libunwind 15's takes one FDE (ext/llvm/libunwind/src/UnwindLevel1-gcc-ext.c:235). The bridge walks the section and registers each FDE, skipping CIEs. That is the semantics LuaJIT and other JITs expect.Test
tst/glibc_guest_test.cgains:_Unwind_GetCFAand checks that the CFA sits in the stack just below the frame's locals (inside a personality, both libgcc and libunwind report the examined frame's SP at its call site);.eh_frameis built at run time and registered through__register_framewith libgcc semantics. There is code for both x86_64 and aarch64.Guests now link the sysroot's
libgcc_s.so.1, and the bundle carries it too, since the bundle runs withoutLD_LIBRARY_PATH. New needle:guest unwind personality=called cfa=ok.main(test only, no fix):solo run misses: guest unwind personality=called cfa=ok;./build testpasses (x86_64, Ubuntu, GCC). The aarch64 code is written but was not run locally.Context
We found this while running the static build of Tarantool (a glibc binary embedding LuaJIT) as a solo bundle. With this fix and the other PRs in the series, Tarantool passes its smoke test on Alpine, CentOS 6 (glibc 2.12) and
busybox:musl: memtx and vinyl, net.box, JIT, popen, http, C modules.