summaryrefslogtreecommitdiff
path: root/libunwind
diff options
context:
space:
mode:
authorMartin Storsjö <martin@martin.st>2022-05-11 11:20:38 +0300
committerMartin Storsjö <martin@martin.st>2022-06-06 23:25:24 +0300
commit08d30c602bcf3bc25a02938f2663a1b9e96a7cbe (patch)
tree0e60935fe408e6080fe7ef59ad7cba4d288c3252 /libunwind
parent64778c002a77046e15a7ce7a7206f5c15355fc26 (diff)
downloadllvm-08d30c602bcf3bc25a02938f2663a1b9e96a7cbe.tar.gz
[libunwind] Don't store a predecremented PC when using SEH
This fixes unwinding in boundary cases on ARM with SEH. In the case of ARM/Thumb, disp->ControlPc points at the following instruction, with the thumb bit set. Thus by decrementing 1, it still points at the next instruction. To achieve the desired effect of pointing at the previous instruction, one first has to strip out the thumb bit, then do the decrement by 1 to reach the previous instruction. When libcxxabi looks for call site ranges, it already does `_Unwind_GetIP(context) - 1` (in `scan_eh_tab` in libcxxabi/src/cxa_personality.cpp), so we shouldn't do the corresponding `- 1` multiple times. In the case of libcxxabi on Thumb, `funcStart` (still in `scan_eh_tab`) may have the thumb bit set. If the program counter address is decremented both in libunwind (first removing the thumb bit, then decremented), and then libcxxabi decrements it further, and compares with a `funcStart` with the thumb bit set, it could point to one byte before the start of the call site. Thus: This modification makes libunwind with SEH work with libcxxabi on Thumb, in settings where libunwind and libcxxabi worked fine with Dwarf before. For existing cases with libunwind with SEH (on x86_64 and aarch64), this modification doesn't break any of my testcases. Differential Revision: https://reviews.llvm.org/D126869
Diffstat (limited to 'libunwind')
-rw-r--r--libunwind/src/Unwind-seh.cpp2
-rw-r--r--libunwind/src/UnwindCursor.hpp8
2 files changed, 9 insertions, 1 deletions
diff --git a/libunwind/src/Unwind-seh.cpp b/libunwind/src/Unwind-seh.cpp
index 64aaf0a383b8..c2c31045a37b 100644
--- a/libunwind/src/Unwind-seh.cpp
+++ b/libunwind/src/Unwind-seh.cpp
@@ -104,7 +104,7 @@ _GCC_specific_handler(PEXCEPTION_RECORD ms_exc, PVOID frame, PCONTEXT ms_ctx,
if (!ctx) {
__unw_init_seh(&cursor, disp->ContextRecord);
__unw_seh_set_disp_ctx(&cursor, disp);
- __unw_set_reg(&cursor, UNW_REG_IP, disp->ControlPc - 1);
+ __unw_set_reg(&cursor, UNW_REG_IP, disp->ControlPc);
ctx = (struct _Unwind_Context *)&cursor;
if (!IS_UNWINDING(ms_exc->ExceptionFlags)) {
diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index a46bbfbccdb5..e363f9e59c9a 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -518,6 +518,14 @@ private:
pint_t getLastPC() const { return _dispContext.ControlPc; }
void setLastPC(pint_t pc) { _dispContext.ControlPc = pc; }
RUNTIME_FUNCTION *lookUpSEHUnwindInfo(pint_t pc, pint_t *base) {
+#ifdef __arm__
+ // Remove the thumb bit; FunctionEntry ranges don't include the thumb bit.
+ pc &= ~1U;
+#endif
+ // If pc points exactly at the end of the range, we might resolve the
+ // next function instead. Decrement pc by 1 to fit inside the current
+ // function.
+ pc -= 1;
_dispContext.FunctionEntry = RtlLookupFunctionEntry(pc,
&_dispContext.ImageBase,
_dispContext.HistoryTable);