From 9afaef970b69c8f7e9194bb9956ba57215e97a4a Mon Sep 17 00:00:00 2001 From: kent-cheung-arm <40630626+kent-cheung-arm@users.noreply.github.com> Date: Sat, 13 Aug 2022 14:45:07 +0100 Subject: Add support for unwinding from the Linux vsyscall region This change fixes unwinding from the vsyscall region. Although vsyscall has been phased out, it is still possible to call into it at an address where libunwind is unable to step out of. --- configure.ac | 8 ++++---- src/x86_64/Gstep.c | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 9a4c887d..f535c86c 100644 --- a/configure.ac +++ b/configure.ac @@ -34,10 +34,10 @@ esac dnl Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS(asm/ptrace_offsets.h asm/ptrace.h endian.h sys/endian.h sys/param.h \ - execinfo.h ia64intrin.h sys/uc_access.h unistd.h signal.h sys/types.h \ - sys/procfs.h sys/ptrace.h sys/syscall.h byteswap.h elf.h sys/elf.h \ - link.h sys/link.h) +AC_CHECK_HEADERS(asm/ptrace_offsets.h asm/ptrace.h asm/vsyscall.h endian.h sys/endian.h \ + sys/param.h execinfo.h ia64intrin.h sys/uc_access.h unistd.h signal.h \ + sys/types.h sys/procfs.h sys/ptrace.h sys/syscall.h byteswap.h elf.h \ + sys/elf.h link.h sys/link.h) dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST diff --git a/src/x86_64/Gstep.c b/src/x86_64/Gstep.c index 4216d188..1dc440e3 100644 --- a/src/x86_64/Gstep.c +++ b/src/x86_64/Gstep.c @@ -25,6 +25,10 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#ifdef HAVE_ASM_VSYSCALL_H +#include +#endif + #include "libunwind_i.h" #include "unwind_i.h" #include @@ -53,6 +57,20 @@ is_plt_entry (struct dwarf_cursor *c) return ret; } +static int +is_vsyscall (struct dwarf_cursor *c) +{ +#if defined(VSYSCALL_START) && defined(VSYSCALL_END) + return c->ip >= VSYSCALL_START && c->ip < VSYSCALL_END; +#elif defined(VSYSCALL_ADDR) + /* Linux 3.16 removes `VSYSCALL_START` and `VSYSCALL_END`. Assume + a single page is mapped for vsyscalls. */ + return c->ip >= VSYSCALL_ADDR && c->ip < VSYSCALL_ADDR + sysconf(_SC_PAGESIZE); +#else + return 0; +#endif +} + int unw_step (unw_cursor_t *cursor) { @@ -141,6 +159,15 @@ unw_step (unw_cursor_t *cursor) c->dwarf.loc[RIP] = DWARF_LOC (c->dwarf.cfa, 0); c->dwarf.cfa += 8; } + else if (is_vsyscall (&c->dwarf)) + { + Debug (2, "in vsyscall region\n"); + c->frame_info.cfa_reg_offset = 8; + c->frame_info.cfa_reg_rsp = -1; + c->frame_info.frame_type = UNW_X86_64_FRAME_GUESSED; + c->dwarf.loc[RIP] = DWARF_LOC (c->dwarf.cfa, 0); + c->dwarf.cfa += 8; + } else if (DWARF_IS_NULL_LOC (c->dwarf.loc[RBP])) { for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) -- cgit v1.2.1