GH-126910: Add GNU backtrace support for unwinding JIT frames

This commit is contained in:
Diego Russo 2026-04-24 14:47:34 +01:00
parent efcac6f281
commit db967dca64
14 changed files with 366 additions and 63 deletions

View file

@ -16,11 +16,22 @@
# endif
#endif
#if defined(PY_HAVE_PERF_TRAMPOLINE) || defined(PY_HAVE_JIT_GDB_UNWIND)
#if defined(PY_HAVE_PERF_TRAMPOLINE) \
|| defined(PY_HAVE_JIT_GDB_UNWIND) \
|| defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND)
#if defined(PY_HAVE_JIT_GDB_UNWIND)
# include <elf.h>
#endif
#if defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND)
/*
* libgcc exposes frame registration entry points, but GCC's public headers
* on some distributions do not declare them even though the symbols are
* available in libgcc_s.
*/
void __register_frame(const void *);
void __deregister_frame(const void *);
#endif
#include <stdio.h>
#include <string.h>
@ -983,4 +994,56 @@ _PyJitUnwind_GdbUnregisterCode(void *handle)
#endif
}
#endif // defined(PY_HAVE_PERF_TRAMPOLINE) || defined(PY_HAVE_JIT_GDB_UNWIND)
#if defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND)
void *
_PyJitUnwind_GnuBacktraceRegisterCode(const void *code_addr, size_t code_size)
{
if (code_addr == NULL || code_size == 0) {
return NULL;
}
size_t eh_frame_size = _PyJitUnwind_EhFrameSize(1);
if (eh_frame_size == 0) {
return NULL;
}
size_t total_size = eh_frame_size + sizeof(uint32_t);
if (total_size < eh_frame_size) {
return NULL;
}
/*
* libgcc's __register_frame walks a .eh_frame section until it finds a
* zero-length terminator entry, so keep an extra zeroed word after the
* generated CIE/FDE pair.
*
* See GCC's libgcc/unwind-dw2-fde.c (__register_frame) and
* libgcc/unwind-dw2-fde.h (last_fde/next_fde):
* https://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-dw2-fde.c
* https://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-dw2-fde.h
*/
uint8_t *eh_frame = PyMem_RawCalloc(1, total_size);
if (eh_frame == NULL) {
return NULL;
}
if (_PyJitUnwind_BuildEhFrame(
eh_frame, eh_frame_size, code_addr, code_size, 1) == 0) {
PyMem_RawFree(eh_frame);
return NULL;
}
__register_frame(eh_frame);
return eh_frame;
}
void
_PyJitUnwind_GnuBacktraceUnregisterCode(void *handle)
{
if (handle == NULL) {
return;
}
__deregister_frame(handle);
PyMem_RawFree(handle);
}
#endif // defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND)
#endif // JIT unwind support