mirror of
https://github.com/libunwind/libunwind.git
synced 2026-01-12 00:04:03 +08:00
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:
committed by
Stephen M. Webb
parent
9fd5fe66ff
commit
585d9c75ad
@@ -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))
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
146
tests/Gtest-sig-context.c
Normal 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
32
tests/Ltest-sig-context.c
Normal 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
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user