Author: Ryan Prichard
Date: 2021-01-13T16:38:36-08:00
New Revision: c82deed6764cbc63966374baf9721331901ca958
URL:
https://github.com/llvm/llvm-project/commit/c82deed6764cbc63966374baf9721331901ca958
DIFF:
https://github.com/llvm/llvm-project/commit/c82deed6764cbc63966374baf9721331901ca958.diff
LOG: [libunwind] Unwind through aarch64/Linux sigreturn frame
An AArch64 sigreturn trampoline frame can't currently be described
in a DWARF .eh_frame section, because the AArch64 DWARF spec currently
doesn't define a constant for the PC register. (PC and LR may need to
be restored to different values.)
Instead, use the same technique as libgcc or github.com/libunwind and
detect the sigreturn frame by looking for the sigreturn instructions:
mov x8, #0x8b
svc #0x0
If a sigreturn frame is detected, libunwind restores all the GPRs by
assuming that sp points at an rt_sigframe Linux kernel struct. This
behavior is a fallback mode that is only used if there is no ordinary
unwind info for sigreturn.
If libunwind can't find unwind info for a PC, it assumes that the PC is
readable, and would crash if it isn't. This could happen if:
- The PC points at a function compiled without unwind info, and which
is part of an execute-only mapping (e.g. using -Wl,--execute-only).
- The PC is invalid and happens to point to unreadable or unmapped
memory.
In the tests, ignore a failed dladdr call so that the tests can run on
user-mode qemu for AArch64, which uses a stack-allocated trampoline
instead of a vDSO.
Reviewed By: danielkiss, compnerd, #libunwind
Differential Revision: https://reviews.llvm.org/D90898
Added:
Modified:
libunwind/include/__libunwind_config.h
libunwind/src/UnwindCursor.hpp
libunwind/test/signal_unwind.pass.cpp
libunwind/test/unwind_leaffunction.pass.cpp
Removed:
diff --git a/libunwind/include/__libunwind_config.h
b/libunwind/include/__libunwind_config.h
index 71d77ca65118..80be357496c4 100644
--- a/libunwind/include/__libunwind_config.h
+++ b/libunwind/include/__libunwind_config.h
@@ -27,6 +27,9 @@
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_RISCV 64
#if defined(_LIBUNWIND_IS_NATIVE_ONLY)
+# if defined(__linux__)
+# define _LIBUNWIND_TARGET_LINUX 1
+# endif
# if defined(__i386__)
# define _LIBUNWIND_TARGET_I386
# define _LIBUNWIND_CONTEXT_SIZE 8
diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index 9f8fa65107b4..e537ed84dd93 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -925,6 +925,25 @@ class UnwindCursor : public AbstractUnwindCursor{
}
#endif
+#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64)
+ bool setInfoForSigReturn() {
+R dummy;
+return setInfoForSigReturn(dummy);
+ }
+ int stepThroughSigReturn() {
+R dummy;
+return stepThroughSigReturn(dummy);
+ }
+ bool setInfoForSigReturn(Registers_arm64 &);
+ int stepThroughSigReturn(Registers_arm64 &);
+ template bool setInfoForSigReturn(Registers &) {
+return false;
+ }
+ template int stepThroughSigReturn(Registers &) {
+return UNW_STEP_END;
+ }
+#endif
+
#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
bool getInfoFromFdeCie(const typename CFI_Parser::FDE_Info &fdeInfo,
const typename CFI_Parser::CIE_Info &cieInfo,
@@ -1179,6 +1198,9 @@ class UnwindCursor : public AbstractUnwindCursor{
unw_proc_info_t _info;
bool _unwindInfoMissing;
bool _isSignalFrame;
+#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64)
+ bool _isSigReturn = false;
+#endif
};
@@ -1873,7 +1895,11 @@ bool UnwindCursor::getInfoFromSEH(pint_t pc) {
template
void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) {
- pint_t pc = (pint_t)this->getReg(UNW_REG_IP);
+#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64)
+ _isSigReturn = false;
+#endif
+
+ pint_t pc = static_cast(this->getReg(UNW_REG_IP));
#if defined(_LIBUNWIND_ARM_EHABI)
// Remove the thumb bit so the IP represents the actual instruction address.
// This matches the behaviour of _Unwind_GetIP on arm.
@@ -1971,10 +1997,77 @@ void UnwindCursor::setInfoBasedOnIPRegister(bool
isReturnAddress) {
}
#endif // #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
+#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64)
+ if (setInfoForSigReturn())
+return;
+#endif
+
// no unwind info, flag that we can't reliably unwind
_unwindInfoMissing = true;
}
+#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64)
+template
+bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) {
+ // Look for the sigreturn trampoline. The trampoline's body is two
+ // specific instructions (see below). Typically the trampoline comes from the
+ // vDSO[1] (i.e. the __kernel_rt_sigreturn func