summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkent-cheung-arm <40630626+kent-cheung-arm@users.noreply.github.com>2022-08-13 14:45:07 +0100
committerGitHub <noreply@github.com>2022-08-13 09:45:07 -0400
commit9afaef970b69c8f7e9194bb9956ba57215e97a4a (patch)
tree0af60207fc0b4f1d1df94e4a2e528aef1213b227
parent67fb02721cbd846c8cc13a18e071cbf9c4dd1c1c (diff)
downloadlibunwind-9afaef970b69c8f7e9194bb9956ba57215e97a4a.tar.gz
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.
-rw-r--r--configure.ac8
-rw-r--r--src/x86_64/Gstep.c27
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 <asm/vsyscall.h>
+#endif
+
#include "libunwind_i.h"
#include "unwind_i.h"
#include <signal.h>
@@ -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)