Fix signal handling for aarch64-nto-qnx*

QNX does not put the signal context at a fixed offset in the signal
stack. Need to get the pointer to the context passed by the kernel to
the trampoline instead.

Added unit tests to detect this situation (`[GL]test-sig-context`).
This commit is contained in:
Stephen Webb
2024-06-17 13:14:11 -04:00
committed by Stephen M. Webb
parent 9fd5fe66ff
commit 585d9c75ad
6 changed files with 231 additions and 11 deletions

View File

@@ -2,7 +2,7 @@
Copyright (C) 2008 CodeSourcery
Copyright (C) 2011-2013 Linaro Limited
Copyright (C) 2012 Tommi Rantala <tt.rantala@gmail.com>
Copyright 2022 Blackberry Limited.
Copyright 2022,2024 Blackberry Limited.
This file is part of libunwind.
@@ -518,18 +518,45 @@ aarch64_handle_signal_frame (unw_cursor_t *cursor)
{
struct cursor *c = (struct cursor *) cursor;
int i, ret;
unw_word_t sc_addr, sp, sp_addr = c->dwarf.cfa;
struct dwarf_loc sp_loc = DWARF_LOC (sp_addr, 0);
if ((ret = dwarf_get (&c->dwarf, sp_loc, &sp)) < 0)
return -UNW_EUNSPEC;
unw_word_t sp_addr = c->dwarf.cfa;
unw_word_t sc_addr;
ret = unw_is_signal_frame (cursor);
Debug(1, "unw_is_signal_frame()=%d\n", ret);
if (ret > 0)
{
c->sigcontext_format = SCF_FORMAT;
#if defined(__QNXNTO__)
/*
* The QNX signal trampoline receives a pointer to a _sighandler_info
* struct in r19, which it breaks apart and passes the relevant args to
* the signal handler in r0, r1, and r2 folowing the ARM AAPCS64
* convention, regardless of whether SA_SIGINFO is specified in sa_flags.
* Since r0-2 are not preserved registers, their value is
* lost by the time the unwinder gets here, but r19 is preserved so it
* should still be valid. If full DWARF unwinding has happened.
*/
unw_word_t x19;
ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_X19], &x19);
if ((ret < 0) || (x19 == 0UL))
{
return -UNW_EUNSPEC;
}
ret = dwarf_get (&c->dwarf,
DWARF_MEM_LOC (c->dwarf, x19 + SI_UCONTEXT_OFF),
&sc_addr);
if (ret < 0)
{
return -UNW_EUNSPEC;
}
sc_addr += UC_MCONTEXT_OFF;
#else
/*
* For non-QNX OSes the mcontext is expected to be on the signal stack,
*/
sc_addr = sp_addr + sizeof (siginfo_t) + UC_MCONTEXT_OFF;
#endif
}
else
return -UNW_EUNSPEC;
@@ -611,6 +638,7 @@ unw_step (unw_cursor_t *cursor)
/* Check if this is a signal frame. */
ret = unw_is_signal_frame (cursor);
Debug(1, "unw_is_signal_frame()=%d\n", ret);
if (ret > 0)
return aarch64_handle_signal_frame (cursor);
else if (unlikely (ret < 0))

View File

@@ -290,6 +290,16 @@ trace_lookup (unw_cursor_t *cursor,
uint64_t slot = ((pc * 0x9e3779b97f4a7c16) >> 43) & (cache_size-1);
unw_tdep_frame_t *frame;
#if defined(__QNXNTO__)
/**
* Without slow DWARF unwinding the signal context gets lost, so bail.
*/
if (unw_is_signal_frame (cursor))
{
return NULL;
}
#endif
for (i = 0; i < 16; ++i)
{
frame = &cache->frames[slot];

View File

@@ -58,6 +58,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#elif defined(__QNX__)
#define SI_UCONTEXT_OFF 0x38
#define UC_MCONTEXT_OFF 48
#define SC_GPR_OFF 0
#define SC_X29_OFF 232

146
tests/Gtest-sig-context.c Normal file
View File

@@ -0,0 +1,146 @@
/**
* Verify that unwinding in a signal handler gets a sane result past the signal
* trampoline.
*
* The sanity of unwinding past the signal trampoline is a matter of making sure
* the symbol `main` is seen.
*
* Both unwinding through the context passed to the signal handler and (if
* configured for local unwinding) the context retrieved through
* unw_getcontext() are exercised.
*
* This test needs to be built with `-g` (and * unstripped) in order to pass.
*/
/*
* Copyright 2024 Stephen M. Webb <stephen.webb@bregmasoft.ca>
*
* This file is part of libunwind.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE 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.
*/
#include "compiler.h"
#include "libunwind.h"
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static const int max_steps = 10;
int verbose = 0;
void stepper (unw_cursor_t *cursor)
{
for (int steps = 0; steps < max_steps; ++steps)
{
int ret = unw_step (cursor);
if (ret <= 0)
{
fprintf (stderr, "unw_step returned %d after %d steps\n", ret, steps);
break;
}
if (unw_is_signal_frame (cursor))
{
if (verbose)
{
fprintf (stdout, " stepping through signal trampoline\n");
}
}
else
{
char sym[256];
unw_word_t offset;
ret = unw_get_proc_name (cursor, sym, sizeof(sym), &offset);
if (ret == 0)
{
if (verbose)
{
fprintf (stdout, " stepping through \"%s\"\n", sym);
}
if (strcmp(sym, "main") == 0)
{
if (verbose)
{
fprintf (stdout, " symbol \"main\" found after %d steps\n", steps);
}
return;
}
}
}
}
fprintf (stderr, "symbol \"main\" not found after %d steps\n", max_steps);
exit (EXIT_FAILURE);
}
void
handler (int signo UNUSED, siginfo_t *info UNUSED, void *sigcontext)
{
if (verbose)
{
fprintf (stdout, "using signal context:\n");
}
unw_cursor_t sig_cursor;
int ret = unw_init_local2 (&sig_cursor, sigcontext, UNW_INIT_SIGNAL_FRAME);
if (ret != 0)
{
fprintf (stderr, "error %d in unw_init_local2()\n", ret);
exit (EXIT_FAILURE);
}
stepper (&sig_cursor);
#if defined(UNW_LOCAL_ONLY)
if (verbose)
{
fprintf (stdout, "using unw_context:\n");
}
unw_context_t context;
ret = unw_getcontext (&context);
if (ret != 0)
{
fprintf (stderr, "error %d in unw_getcontext()\n", ret);
exit (EXIT_FAILURE);
}
unw_cursor_t cursor;
ret = unw_init_local (&cursor, &context);
if (ret != 0)
{
fprintf (stderr, "error %d in unw_init_local()\n", ret);
exit (EXIT_FAILURE);
}
stepper (&cursor);
#endif /* !defined(UNW_LOCAL_ONLY) */
}
int main (int argc, char *argv[] UNUSED)
{
struct sigaction a;
memset (&a, 0, sizeof (struct sigaction));
a.sa_sigaction = &handler;
a.sa_flags = SA_SIGINFO;
sigaction (SIGHUP, &a, NULL);
verbose = (argc > 1);
kill (getpid (), SIGHUP);
exit (EXIT_SUCCESS);
}

32
tests/Ltest-sig-context.c Normal file
View File

@@ -0,0 +1,32 @@
/**
* Verify that unwinding in a signal handler gets a sane result past the signal
* trampoline (local-only version).
*/
/*
* Copyright 2024 Stephen M. Webb <stephen.webb@bregmasoft.ca>
*
* This file is part of libunwind.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE 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.
*/
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#if !defined(UNW_REMOTE_ONLY)
# include "Gtest-sig-context.c"
#endif

View File

@@ -33,7 +33,7 @@ AM_CPPFLAGS = $(UNW_DEBUG_CPPFLAGS) \
$(UNW_REMOTE_CPPFLAGS) \
$(UNW_TARGET_CPPFLAGS) \
-I$(top_srcdir)/include -I$(top_srcdir)/include/tdep-$(arch) -I$(top_srcdir)/src
AM_CFLAGS = $(UNW_EXTRA_CFLAGS) -fno-optimize-sibling-calls
AM_CFLAGS = $(UNW_EXTRA_CFLAGS) -g -fno-optimize-sibling-calls
LOG_DRIVER = $(SHELL) $(UNW_TESTDRIVER)
@@ -91,9 +91,10 @@ if ARCH_AARCH64
aarch64-test-plt aarch64-test-frame-record
endif
check_PROGRAMS_cdep += Gtest-bt Ltest-bt \
check_PROGRAMS_cdep += Gtest-bt Ltest-bt \
Gtest-init Ltest-init \
Gtest-concurrent Ltest-concurrent \
Gtest-sig-context Ltest-sig-context \
Gtest-trace Ltest-trace \
Ltest-mem-validate \
test-async-sig test-flush-cache test-init-remote \
@@ -254,8 +255,8 @@ aarch64_test_plt_SOURCES = aarch64-test-plt.c
aarch64_test_frame_record_SOURCES = aarch64-test-frame-record.c
if COMPILER_SUPPORTS_MARCH_ARMV8_A_SVE
Garm64_test_sve_signal_CFLAGS = -fno-inline -march=armv8-a+sve
Larm64_test_sve_signal_CFLAGS = -fno-inline -march=armv8-a+sve
Garm64_test_sve_signal_CFLAGS = $(AM_CFLAGS) -fno-inline -march=armv8-a+sve
Larm64_test_sve_signal_CFLAGS = $(AM_CFLAGS) -fno-inline -march=armv8-a+sve
endif
# https://github.com/libunwind/libunwind/issues/512
XFAIL_TESTS += Garm64-test-sve-signal Larm64-test-sve-signal
@@ -319,6 +320,7 @@ Gtest_exc_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
Gtest_init_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
Gtest_resume_sig_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
Gtest_resume_sig_rt_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
Gtest_sig_context_LDADD = $(LIBUNWIND)
Gperf_simple_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
Gtest_trace_LDADD=$(LIBUNWIND) $(LIBUNWIND_local)
Gperf_trace_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
@@ -333,6 +335,7 @@ Ltest_nomalloc_LDADD = $(LIBUNWIND_local) $(DLLIB)
Ltest_nocalloc_LDADD = $(LIBUNWIND_local) $(DLLIB) $(PTHREADS_LIB)
Ltest_resume_sig_LDADD = $(LIBUNWIND_local)
Ltest_resume_sig_rt_LDADD = $(LIBUNWIND_local)
Ltest_sig_context_LDADD = $(LIBUNWIND_local)
Lperf_simple_LDADD = $(LIBUNWIND_local)
Ltest_trace_LDADD = $(LIBUNWIND_local)
Lperf_trace_LDADD = $(LIBUNWIND_local)