Skip to content

Commit e662eb6

Browse files
Document LLVM libunwind JIT frame warning
1 parent e99b319 commit e662eb6

2 files changed

Lines changed: 90 additions & 0 deletions

File tree

Lib/test/test_c_stack_unwind.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727

2828

2929
STACK_DEPTH = 10
30+
BAD_DYNAMIC_FDE_WARNING = (
31+
"libunwind: __unw_add_dynamic_fde: bad fde: FDE is really a CIE"
32+
)
3033

3134

3235
def _manual_unwind_expected(machine):
@@ -218,6 +221,8 @@ def _run_unwind_helper(helper_name, unwinder_name, **env):
218221
# Surface the output for debugging/visibility when running this test
219222
if proc.stdout:
220223
print(proc.stdout, end="")
224+
if BAD_DYNAMIC_FDE_WARNING in proc.stderr:
225+
raise RuntimeError(proc.stderr)
221226
if proc.returncode:
222227
raise RuntimeError(
223228
f"unwind helper failed (rc={proc.returncode}): {proc.stderr or proc.stdout}"

Python/jit_unwind.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,18 @@
3131
*/
3232
void __register_frame(const void *);
3333
void __deregister_frame(const void *);
34+
35+
# if defined(__linux__) && defined(__clang__) && \
36+
(__clang_major__ >= 22) && (__clang_major__ <= 23)
37+
# define PY_JIT_GNU_BACKTRACE_REGISTER_FDE
38+
# endif
39+
40+
# if defined(PY_JIT_GNU_BACKTRACE_REGISTER_FDE)
41+
typedef struct JitGnuBacktraceRegistration {
42+
uint8_t *eh_frame;
43+
const void *registered_frame;
44+
} JitGnuBacktraceRegistration;
45+
# endif
3446
#endif
3547
#include <stdio.h>
3648
#include <string.h>
@@ -1008,6 +1020,50 @@ _PyJitUnwind_GdbUnregisterCode(void *handle)
10081020
}
10091021

10101022
#if defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND)
1023+
#if defined(PY_JIT_GNU_BACKTRACE_REGISTER_FDE)
1024+
static const void *
1025+
gnu_backtrace_registration_frame(const uint8_t *eh_frame, size_t eh_frame_size)
1026+
{
1027+
/*
1028+
* LLVM libunwind 22 and 23 implement __register_frame as a single-FDE
1029+
* registration API. The compiler-version check above is intentionally a
1030+
* narrow toolchain guard requested for those releases; it is not a
1031+
* general runtime unwinder capability probe.
1032+
*/
1033+
uint32_t cie_length;
1034+
if (eh_frame == NULL || eh_frame_size < 2 * sizeof(uint32_t)) {
1035+
return NULL;
1036+
}
1037+
memcpy(&cie_length, eh_frame, sizeof(cie_length));
1038+
if (cie_length == 0 || cie_length == UINT32_MAX) {
1039+
return NULL;
1040+
}
1041+
1042+
size_t fde_offset = sizeof(uint32_t) + (size_t)cie_length;
1043+
if (fde_offset > eh_frame_size - 2 * sizeof(uint32_t)) {
1044+
return NULL;
1045+
}
1046+
1047+
uint32_t fde_length;
1048+
memcpy(&fde_length, eh_frame + fde_offset, sizeof(fde_length));
1049+
if (fde_length == 0 || fde_length == UINT32_MAX) {
1050+
return NULL;
1051+
}
1052+
if ((size_t)fde_length > eh_frame_size - fde_offset - sizeof(uint32_t)) {
1053+
return NULL;
1054+
}
1055+
1056+
uint32_t cie_pointer;
1057+
memcpy(&cie_pointer, eh_frame + fde_offset + sizeof(fde_length),
1058+
sizeof(cie_pointer));
1059+
if (cie_pointer == 0) {
1060+
return NULL;
1061+
}
1062+
1063+
return eh_frame + fde_offset;
1064+
}
1065+
#endif
1066+
10111067
void *
10121068
_PyJitUnwind_GnuBacktraceRegisterCode(const void *code_addr, size_t code_size)
10131069
{
@@ -1044,8 +1100,29 @@ _PyJitUnwind_GnuBacktraceRegisterCode(const void *code_addr, size_t code_size)
10441100
return NULL;
10451101
}
10461102

1103+
#if defined(PY_JIT_GNU_BACKTRACE_REGISTER_FDE)
1104+
const void *registered_frame = gnu_backtrace_registration_frame(
1105+
eh_frame, eh_frame_size);
1106+
if (registered_frame == NULL) {
1107+
PyMem_RawFree(eh_frame);
1108+
return NULL;
1109+
}
1110+
1111+
JitGnuBacktraceRegistration *registration =
1112+
PyMem_RawMalloc(sizeof(*registration));
1113+
if (registration == NULL) {
1114+
PyMem_RawFree(eh_frame);
1115+
return NULL;
1116+
}
1117+
registration->eh_frame = eh_frame;
1118+
registration->registered_frame = registered_frame;
1119+
1120+
__register_frame(registered_frame);
1121+
return registration;
1122+
#else
10471123
__register_frame(eh_frame);
10481124
return eh_frame;
1125+
#endif
10491126
}
10501127

10511128
void
@@ -1054,8 +1131,16 @@ _PyJitUnwind_GnuBacktraceUnregisterCode(void *handle)
10541131
if (handle == NULL) {
10551132
return;
10561133
}
1134+
#if defined(PY_JIT_GNU_BACKTRACE_REGISTER_FDE)
1135+
JitGnuBacktraceRegistration *registration =
1136+
(JitGnuBacktraceRegistration *)handle;
1137+
__deregister_frame(registration->registered_frame);
1138+
PyMem_RawFree(registration->eh_frame);
1139+
PyMem_RawFree(registration);
1140+
#else
10571141
__deregister_frame(handle);
10581142
PyMem_RawFree(handle);
1143+
#endif
10591144
}
10601145
#endif // defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND)
10611146

0 commit comments

Comments
 (0)