l4re-base-25.08.0

This commit is contained in:
2025-09-12 15:55:45 +02:00
commit d959eaab98
37938 changed files with 9382688 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
#include <stdio.h>
#include <stdlib.h>
/*
* Run the backtrace test using the ARM DWARF unwinder based on the
* .debug_frame section.
*/
__attribute__((constructor))
void set_dwarf(void) {
setenv("UNW_ARM_UNWIND_METHOD", "1", 1);
}
#include "Gtest-bt.c"

View File

@@ -0,0 +1,140 @@
/*
* Verify that unwinding from a signal handler works when variable width
* SVE registers are pushed onto the stack
*/
#if defined(__ARM_FEATURE_SVE) && defined(__ARM_FEATURE_SVE_VECTOR_OPERATORS)
#include <arm_sve.h>
#include <libunwind.h>
#include <signal.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#if defined(__linux__)
#include <sys/auxv.h>
#endif
int64_t z[100];
void signal_handler(int signum)
{
unw_cursor_t cursor;
unw_context_t context;
const char* expected[] = {
"signal_frame",
"kill",
"sum",
"square",
"main",
};
unw_getcontext(&context);
unw_init_local(&cursor, &context);
for (unsigned int depth = 0; depth < sizeof(expected) / sizeof(expected[0]); ++depth)
{
unw_word_t offset, pc;
int unw_rc = unw_step(&cursor);
if (unw_rc <= 0) {
printf("Frame: %d unw_step error: %d\n", depth, unw_rc);
exit(-1);
}
unw_rc = unw_get_reg(&cursor, UNW_REG_IP, &pc);
if (pc == 0 || unw_rc != 0) {
printf("Frame: %d unw_get_reg error: %d\n", depth, unw_rc);
exit(-1);
}
char sym[256];
unw_rc = unw_is_signal_frame(&cursor);
if (unw_rc > 0)
{
strcpy(sym, "signal_frame");
}
else if (unw_rc < 0)
{
printf("Frame: %d unw_is_signal_frame error: %d\n", depth, unw_rc);
exit(-1);
}
else
{
unw_rc = unw_get_proc_name(&cursor, sym, sizeof(sym), &offset);
if (unw_rc)
{
printf("Frame: %d unw_get_proc_name error: %d\n", depth, unw_rc);
exit(-1);
}
}
if (strcmp(sym, expected[depth]) != 0)
{
printf("Frame: %d expected %s but found %s\n", depth, expected[depth], sym);
exit(-1);
}
}
exit(0); /* PASS */
}
int64_t sum(svint64_t z0)
{
int64_t ret = svaddv_s64(svptrue_b64(), z0);
kill (getpid (), SIGUSR1);
return ret;
}
int64_t square(svint64_t z0)
{
int64_t res = 0;
for (int i = 0; i < 100; ++i)
{
z0 = svmul_s64_z(svptrue_b64(), z0, z0);
res += sum(z0);
}
return res;
}
bool has_sve(void) {
#if defined(__linux__)
return (getauxval(AT_HWCAP) & HWCAP_SVE) ? true : false;
#else
printf("Cannot determine if SVE is present, assuming it is not\n");
return false;
#endif
}
int main()
{
if (!has_sve()) {
printf("SVE not available, skipping\n");
return 77;
}
signal(SIGUSR1, signal_handler);
for (unsigned int i = 0; i < sizeof(z) / sizeof(z[0]); ++i)
z[i] = rand();
svint64_t z0 = svld1(svptrue_b64(), &z[0]);
square(z0);
/*
* Shouldn't get here, exit is called from signal handler
*/
printf("Signal handler wasn't called\n");
return -1;
}
#else /* !__ARM_FEATURE_SVE */
int
main ()
{
return 77; /* SKIP */
}
#endif

View File

@@ -0,0 +1,627 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2004-2005 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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. */
/* This file tests corner-cases of NaT-bit handling. */
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libunwind.h>
#include "compiler.h"
#ifdef HAVE_SYS_UC_ACCESS_H
# include <sys/uc_access.h>
#endif
#include "tdep-ia64/rse.h"
#define NUM_RUNS 1024
//#define NUM_RUNS 1
#define MAX_CHECKS 1024
//#define MAX_CHECKS 2
#define MAX_VALUES_PER_FUNC 4
#define panic(args...) \
do { printf (args); ++nerrors; } while (0)
typedef void save_func_t (void *funcs, unsigned long *vals);
typedef unw_word_t *check_func_t (unw_cursor_t *c, unsigned long *vals);
extern void flushrs (void);
extern save_func_t save_static_to_stacked;
static check_func_t check_static_to_stacked;
extern save_func_t save_static_to_fr;
static check_func_t check_static_to_fr;
extern save_func_t save_static_to_br;
static check_func_t check_static_to_br;
extern save_func_t save_static_to_mem;
static check_func_t check_static_to_mem;
extern save_func_t save_static_to_mem2;
static check_func_t check_static_to_mem2;
extern save_func_t save_static_to_mem3;
static check_func_t check_static_to_mem3;
extern save_func_t save_static_to_mem4;
static check_func_t check_static_to_mem4;
extern save_func_t save_static_to_mem5;
static check_func_t check_static_to_mem5;
extern save_func_t save_static_to_scratch;
static check_func_t check_static_to_scratch;
extern save_func_t rotate_regs;
static check_func_t check_rotate_regs;
extern save_func_t save_pr;
static check_func_t check_pr;
static int verbose;
static int nerrors;
static int num_checks;
static save_func_t *funcs[MAX_CHECKS + 1];
static check_func_t *checks[MAX_CHECKS];
static unw_word_t values[MAX_CHECKS*MAX_VALUES_PER_FUNC];
static struct
{
save_func_t *func;
check_func_t *check;
}
all_funcs[] =
{
{ save_static_to_stacked, check_static_to_stacked },
{ save_static_to_fr, check_static_to_fr },
{ save_static_to_br, check_static_to_br },
{ save_static_to_mem, check_static_to_mem },
{ save_static_to_mem2, check_static_to_mem2 },
{ save_static_to_mem3, check_static_to_mem3 },
{ save_static_to_mem4, check_static_to_mem4 },
{ save_static_to_mem5, check_static_to_mem5 },
{ save_static_to_scratch, check_static_to_scratch },
{ save_pr, check_pr },
{ rotate_regs, check_rotate_regs },
};
static unw_word_t
random_word (void)
{
unw_word_t val = random ();
if (sizeof (unw_word_t) > 4)
val |= ((unw_word_t) random ()) << 32;
return val;
}
void
sighandler (int signal, void *siginfo, void *context)
{
unsigned long *bsp, *arg1;
save_func_t **arg0;
ucontext_t *uc = context;
#if defined(__linux__)
{
long sof;
int sp;
if (verbose)
printf ("sighandler: signal %d sp=%p nat=%08lx pr=%lx\n",
signal, &sp, uc->uc_mcontext.sc_nat, uc->uc_mcontext.sc_pr);
sof = uc->uc_mcontext.sc_cfm & 0x7f;
bsp = (unsigned long *) rse_skip_regs (uc->uc_mcontext.sc_ar_bsp, -sof);
}
#elif defined(__hpux)
if (__uc_get_ar (uc, UNW_IA64_AR_BSP - UNW_IA64_AR, &bsp) != 0)
{
panic ("%s: reading of ar.bsp failed, errno=%d", __FUNCTION__, errno);
return;
}
#endif
flushrs ();
arg0 = (save_func_t **) *bsp;
bsp = (unsigned long *) rse_skip_regs ((uint64_t) bsp, 1);
arg1 = (unsigned long *) *bsp;
(*arg0[0]) (arg0 + 1, arg1);
/* skip over the instruction which triggered sighandler() */
#if defined(__linux__)
++uc->uc_mcontext.sc_ip;
#elif defined(HAVE_SYS_UC_ACCESS_H)
{
unsigned long ip;
if (__uc_get_ip (uc, &ip) != 0)
{
panic ("%s: reading of ip failed, errno=%d", __FUNCTION__, errno);
return;
}
if (__uc_set_ip (uc, ip) != 0)
{
panic ("%s: writing of ip failed, errno=%d", __FUNCTION__, errno);
return;
}
}
#endif
}
static void
enable_sighandler (void)
{
struct sigaction act;
memset (&act, 0, sizeof (act));
act.sa_handler = (void (*)(int)) sighandler;
act.sa_flags = SA_SIGINFO | SA_NODEFER;
if (sigaction (SIGSEGV, &act, NULL) < 0)
panic ("sigaction: %s\n", strerror (errno));
}
static void
disable_sighandler (void)
{
struct sigaction act;
memset (&act, 0, sizeof (act));
act.sa_handler = SIG_DFL;
act.sa_flags = SA_SIGINFO | SA_NODEFER;
if (sigaction (SIGSEGV, &act, NULL) < 0)
panic ("sigaction: %s\n", strerror (errno));
}
static unw_word_t *
check_static_to_stacked (unw_cursor_t *c, unw_word_t *vals)
{
unw_word_t r[4];
unw_word_t nat[4];
int i, ret;
if (verbose)
printf (" %s()\n", __FUNCTION__);
vals -= 4;
for (i = 0; i < 4; ++i)
if ((ret = unw_get_reg (c, UNW_IA64_GR + 4 + i, &r[i])) < 0)
panic ("%s: failed to read register r%d, error=%d\n",
__FUNCTION__, 4 + i, ret);
for (i = 0; i < 4; ++i)
if ((ret = unw_get_reg (c, UNW_IA64_NAT + 4 + i, &nat[i])) < 0)
panic ("%s: failed to read register nat%d, error=%d\n",
__FUNCTION__, 4 + i, ret);
for (i = 0; i < 4; ++i)
{
if (verbose)
printf (" r%d = %c%016lx (expected %c%016lx)\n",
4 + i, nat[i] ? '*' : ' ', r[i],
(vals[i] & 1) ? '*' : ' ', vals[i]);
if (vals[i] & 1)
{
if (!nat[i])
panic ("%s: r%d not a NaT!\n", __FUNCTION__, 4 + i);
}
else
{
if (nat[i])
panic ("%s: r%d a NaT!\n", __FUNCTION__, 4 + i);
if (r[i] != vals[i])
panic ("%s: r%d=%lx instead of %lx!\n",
__FUNCTION__, 4 + i, r[i], vals[i]);
}
}
return vals;
}
static unw_word_t *
check_static_to_fr (unw_cursor_t *c, unw_word_t *vals)
{
unw_word_t r4;
unw_word_t nat4;
int ret;
if (verbose)
printf (" %s()\n", __FUNCTION__);
vals -= 1;
if ((ret = unw_get_reg (c, UNW_IA64_GR + 4, &r4)) < 0)
panic ("%s: failed to read register r4, error=%d\n", __FUNCTION__, ret);
if ((ret = unw_get_reg (c, UNW_IA64_NAT + 4, &nat4)) < 0)
panic ("%s: failed to read register nat4, error=%d\n", __FUNCTION__, ret);
if (verbose)
printf (" r4 = %c%016lx (expected %c%016lx)\n",
nat4 ? '*' : ' ', r4, (vals[0] & 1) ? '*' : ' ', vals[0]);
if (vals[0] & 1)
{
if (!nat4)
panic ("%s: r4 not a NaT!\n", __FUNCTION__);
}
else
{
if (nat4)
panic ("%s: r4 a NaT!\n", __FUNCTION__);
if (r4 != vals[0])
panic ("%s: r4=%lx instead of %lx!\n", __FUNCTION__, r4, vals[0]);
}
return vals;
}
static unw_word_t *
check_static_to_br (unw_cursor_t *c, unw_word_t *vals)
{
unw_word_t r4, nat4;
int ret;
if (verbose)
printf (" %s()\n", __FUNCTION__);
vals -= 1;
if ((ret = unw_get_reg (c, UNW_IA64_GR + 4, &r4)) < 0)
panic ("%s: failed to read register r4, error=%d\n", __FUNCTION__, ret);
if ((ret = unw_get_reg (c, UNW_IA64_NAT + 4, &nat4)) < 0)
panic ("%s: failed to read register nat4, error=%d\n", __FUNCTION__, ret);
if (verbose)
printf (" r4 = %c%016lx (expected %c%016lx)\n",
nat4 ? '*' : ' ', r4, (vals[0] & 1) ? '*' : ' ', vals[0]);
if (vals[0] & 1)
{
if (!nat4)
panic ("%s: r4 not a NaT!\n", __FUNCTION__);
}
else
{
if (nat4)
panic ("%s: r4 a NaT!\n", __FUNCTION__);
if (r4 != vals[0])
panic ("%s: r4=%lx instead of %lx!\n", __FUNCTION__, r4, vals[0]);
}
return vals;
}
static unw_word_t *
check_static_to_mem (unw_cursor_t *c, unw_word_t *vals)
{
unw_word_t r5, nat5;
int ret;
if (verbose)
printf (" %s()\n", __FUNCTION__);
vals -= 1;
if ((ret = unw_get_reg (c, UNW_IA64_GR + 5, &r5)) < 0)
panic ("%s: failed to read register r5, error=%d\n", __FUNCTION__, ret);
if ((ret = unw_get_reg (c, UNW_IA64_NAT + 5, &nat5)) < 0)
panic ("%s: failed to read register nat5, error=%d\n", __FUNCTION__, ret);
if (verbose)
printf (" r5 = %c%016lx (expected %c%016lx)\n",
nat5 ? '*' : ' ', r5, (vals[0] & 1) ? '*' : ' ', vals[0]);
if (vals[0] & 1)
{
if (!nat5)
panic ("%s: r5 not a NaT!\n", __FUNCTION__);
}
else
{
if (nat5)
panic ("%s: r5 a NaT!\n", __FUNCTION__);
if (r5 != vals[0])
panic ("%s: r5=%lx instead of %lx!\n", __FUNCTION__, r5, vals[0]);
}
return vals;
}
static unw_word_t *
check_static_to_memN (unw_cursor_t *c, unw_word_t *vals, const char *func)
{
unw_word_t r6, nat6;
int ret;
if (verbose)
printf (" %s()\n", func);
vals -= 1;
if ((ret = unw_get_reg (c, UNW_IA64_GR + 6, &r6)) < 0)
panic ("%s: failed to read register r6, error=%d\n", __FUNCTION__, ret);
if ((ret = unw_get_reg (c, UNW_IA64_NAT + 6, &nat6)) < 0)
panic ("%s: failed to read register nat6, error=%d\n", __FUNCTION__, ret);
if (verbose)
printf (" r6 = %c%016lx (expected %c%016lx)\n",
nat6 ? '*' : ' ', r6, (vals[0] & 1) ? '*' : ' ', vals[0]);
if (vals[0] & 1)
{
if (!nat6)
panic ("%s: r6 not a NaT!\n", __FUNCTION__);
}
else
{
if (nat6)
panic ("%s: r6 a NaT!\n", __FUNCTION__);
if (r6 != vals[0])
panic ("%s: r6=%lx instead of %lx!\n", __FUNCTION__, r6, vals[0]);
}
return vals;
}
static unw_word_t *
check_static_to_mem2 (unw_cursor_t *c, unw_word_t *vals)
{
return check_static_to_memN (c, vals, __FUNCTION__);
}
static unw_word_t *
check_static_to_mem3 (unw_cursor_t *c, unw_word_t *vals)
{
return check_static_to_memN (c, vals, __FUNCTION__);
}
static unw_word_t *
check_static_to_mem4 (unw_cursor_t *c, unw_word_t *vals)
{
return check_static_to_memN (c, vals, __FUNCTION__);
}
static unw_word_t *
check_static_to_mem5 (unw_cursor_t *c, unw_word_t *vals)
{
return check_static_to_memN (c, vals, __FUNCTION__);
}
static unw_word_t *
check_static_to_scratch (unw_cursor_t *c, unw_word_t *vals)
{
unw_word_t r[4], nat[4], ec, expected;
unw_fpreg_t f4;
int i, ret;
if (verbose)
printf (" %s()\n", __FUNCTION__);
vals -= 4;
while (!unw_is_signal_frame (c))
if ((ret = unw_step (c)) < 0)
panic ("%s: unw_step (ret=%d): Failed to skip over signal handler\n",
__FUNCTION__, ret);
if ((ret = unw_step (c)) < 0)
panic ("%s: unw_step (ret=%d): Failed to skip over signal handler\n",
__FUNCTION__, ret);
for (i = 0; i < 4; ++i)
if ((ret = unw_get_reg (c, UNW_IA64_GR + 4 + i, &r[i])) < 0)
panic ("%s: failed to read register r%d, error=%d\n",
__FUNCTION__, 4 + i, ret);
for (i = 0; i < 4; ++i)
if ((ret = unw_get_reg (c, UNW_IA64_NAT + 4 + i, &nat[i])) < 0)
panic ("%s: failed to read register nat%d, error=%d\n",
__FUNCTION__, 4 + i, ret);
for (i = 0; i < 4; ++i)
{
if (verbose)
printf (" r%d = %c%016lx (expected %c%016lx)\n",
4 + i, nat[i] ? '*' : ' ', r[i],
(vals[i] & 1) ? '*' : ' ', vals[i]);
if (vals[i] & 1)
{
if (!nat[i])
panic ("%s: r%d not a NaT!\n", __FUNCTION__, 4 + i);
}
else
{
if (nat[i])
panic ("%s: r%d a NaT!\n", __FUNCTION__, 4 + i);
if (r[i] != vals[i])
panic ("%s: r%d=%lx instead of %lx!\n",
__FUNCTION__, 4 + i, r[i], vals[i]);
}
}
if ((ret = unw_get_fpreg (c, UNW_IA64_FR + 4, &f4)) < 0)
panic ("%s: failed to read f4, error=%d\n", __FUNCTION__, ret);
/* These tests are little-endian specific: */
if (nat[0])
{
if (f4.raw.bits[0] != 0 || f4.raw.bits[1] != 0x1fffe)
panic ("%s: f4=%016lx.%016lx instead of NaTVal!\n",
__FUNCTION__, f4.raw.bits[1], f4.raw.bits[0]);
}
else
{
if (f4.raw.bits[0] != r[0] || f4.raw.bits[1] != 0x1003e)
panic ("%s: f4=%016lx.%016lx instead of %lx!\n",
__FUNCTION__, f4.raw.bits[1], f4.raw.bits[0], r[0]);
}
if ((unw_get_reg (c, UNW_IA64_AR_EC, &ec)) < 0)
panic ("%s: failed to read register ar.ec, error=%d\n", __FUNCTION__, ret);
expected = vals[0] & 0x3f;
if (ec != expected)
panic ("%s: ar.ec=%016lx instead of %016lx!\n",
__FUNCTION__, ec, expected);
return vals;
}
static unw_word_t *
check_pr (unw_cursor_t *c, unw_word_t *vals)
{
unw_word_t pr, expected;
int ret;
# define BIT(n) ((unw_word_t) 1 << (n))
# define DONTCARE (BIT( 6) | BIT( 7) | BIT( 8) | BIT( 9) | BIT(10) \
| BIT(11) | BIT(12) | BIT(13) | BIT(14) | BIT(15))
if (verbose)
printf (" %s()\n", __FUNCTION__);
vals -= 1;
if ((ret = unw_get_reg (c, UNW_IA64_PR, &pr)) < 0)
panic ("%s: failed to read register pr, error=%d\n", __FUNCTION__, ret);
pr &= ~DONTCARE;
expected = (vals[0] & ~DONTCARE) | 1;
if (verbose)
printf (" pr = %016lx (expected %016lx)\n", pr, expected);
if (pr != expected)
panic ("%s: pr=%lx instead of %lx!\n", __FUNCTION__, pr, expected);
if ((ret = unw_set_reg (c, UNW_IA64_PR, vals[0])) < 0)
panic ("%s: failed to write register pr, error=%d\n", __FUNCTION__, ret);
if ((ret = unw_get_reg (c, UNW_IA64_PR, &pr)) < 0)
panic ("%s: failed to read register pr, error=%d\n", __FUNCTION__, ret);
if (pr != vals[0])
panic ("%s: secondary pr=%lx instead of %lx!\n",
__FUNCTION__, pr, vals[0]);
return vals;
}
static unw_word_t *
check_rotate_regs (unw_cursor_t *c, unw_word_t *vals)
{
if (verbose)
printf (" %s()\n", __FUNCTION__);
return check_pr (c, vals - 1);
}
static void
start_checks (void *funcs, unsigned long *vals)
{
unw_context_t uc;
unw_cursor_t c;
int i, ret;
disable_sighandler ();
unw_getcontext (&uc);
if ((ret = unw_init_local (&c, &uc)) < 0)
panic ("%s: unw_init_local (ret=%d)\n", __FUNCTION__, ret);
if ((ret = unw_step (&c)) < 0)
panic ("%s: unw_step (ret=%d)\n", __FUNCTION__, ret);
for (i = 0; i < num_checks; ++i)
{
vals = (*checks[num_checks - 1 - i]) (&c, vals);
if ((ret = unw_step (&c)) < 0)
panic ("%s: unw_step (ret=%d)\n", __FUNCTION__, ret);
}
}
static void
run_check (int test)
{
int index, i;
if (test == 1)
/* Make first test always go the full depth... */
num_checks = MAX_CHECKS;
else
num_checks = (random () % MAX_CHECKS) + 1;
for (i = 0; i < num_checks * MAX_VALUES_PER_FUNC; ++i)
values[i] = random_word ();
for (i = 0; i < num_checks; ++i)
{
if (test == 1)
/* Make first test once go through each test... */
index = i % (int) ARRAY_SIZE (all_funcs);
else
index = random () % (int) ARRAY_SIZE (all_funcs);
funcs[i] = all_funcs[index].func;
checks[i] = all_funcs[index].check;
}
funcs[num_checks] = start_checks;
enable_sighandler ();
(*funcs[0]) (funcs + 1, values);
}
int
main (int argc, char **argv)
{
int i;
if (argc > 1)
verbose = 1;
for (i = 0; i < NUM_RUNS; ++i)
{
if (verbose)
printf ("Run %d\n", i + 1);
run_check (i + 1);
}
if (nerrors > 0)
{
fprintf (stderr, "FAILURE: detected %d errors\n", nerrors);
exit (-1);
}
if (verbose)
printf ("SUCCESS.\n");
return 0;
}

View File

@@ -0,0 +1,193 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2003-2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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. */
/* This file tests corner-cases of unwinding across multiple stacks.
In particular, it verifies that the extreme case with a frame of 96
stacked registers that are all backed up by separate stacks works
as expected. */
#include <stdio.h>
#include <stdlib.h>
#include <libunwind.h>
#include "compiler.h"
#include "ia64-test-rbs.h"
#define panic(args...) \
do { fprintf (stderr, args); ++nerrors; return -9999; } while (0)
/* The loadrs field in ar.rsc is 14 bits wide, which limits all ia64
implementations to at most 2048 physical stacked registers
(actually, slightly less than that, because loadrs also counts RNaT
slots). Since we can dirty 93 stacked registers per recursion, we
need to recurse RECURSION_DEPTH times to ensure all physical
stacked registers are in use. */
#define MAX_PHYS_STACKED 2048
#define RECURSION_DEPTH ((MAX_PHYS_STACKED + 92) / 93)
typedef int spill_func_t (long iteration, int (*next_func[])());
extern int loadup (long iteration, int *values, int (*next_func[])());
extern char resumption_point_label;
#define DCL(n) \
extern int rbs_spill_##n (long iteration, int (*next_func[])())
DCL(2); DCL(3); DCL(4); DCL(5); DCL(6); DCL(7);
DCL(8); DCL(9); DCL(10); DCL(11); DCL(12); DCL(13); DCL(14); DCL(15);
DCL(16); DCL(17); DCL(18); DCL(19); DCL(20); DCL(21); DCL(22); DCL(23);
DCL(24); DCL(25); DCL(26); DCL(27); DCL(28); DCL(29); DCL(30); DCL(31);
DCL(32); DCL(33); DCL(34); DCL(35); DCL(36); DCL(37); DCL(38); DCL(39);
DCL(40); DCL(41); DCL(42); DCL(43); DCL(44); DCL(45); DCL(46); DCL(47);
DCL(48); DCL(49); DCL(50); DCL(51); DCL(52); DCL(53); DCL(54); DCL(55);
DCL(56); DCL(57); DCL(58); DCL(59); DCL(60); DCL(61); DCL(62); DCL(63);
DCL(64); DCL(65); DCL(66); DCL(67); DCL(68); DCL(69); DCL(70); DCL(71);
DCL(72); DCL(73); DCL(74); DCL(75); DCL(76); DCL(77); DCL(78); DCL(79);
DCL(80); DCL(81); DCL(82); DCL(83); DCL(84); DCL(85); DCL(86); DCL(87);
DCL(88); DCL(89); DCL(90); DCL(91); DCL(92); DCL(93); DCL(94);
#define SPL(n) rbs_spill_##n
spill_func_t *spill_funcs[] =
{
SPL(2), SPL(3), SPL(4), SPL(5), SPL(6), SPL(7),
SPL(8), SPL(9), SPL(10), SPL(11), SPL(12), SPL(13), SPL(14), SPL(15),
SPL(16), SPL(17), SPL(18), SPL(19), SPL(20), SPL(21), SPL(22), SPL(23),
SPL(24), SPL(25), SPL(26), SPL(27), SPL(28), SPL(29), SPL(30), SPL(31),
SPL(32), SPL(33), SPL(34), SPL(35), SPL(36), SPL(37), SPL(38), SPL(39),
SPL(40), SPL(41), SPL(42), SPL(43), SPL(44), SPL(45), SPL(46), SPL(47),
SPL(48), SPL(49), SPL(50), SPL(51), SPL(52), SPL(53), SPL(54), SPL(55),
SPL(56), SPL(57), SPL(58), SPL(59), SPL(60), SPL(61), SPL(62), SPL(63),
SPL(64), SPL(65), SPL(66), SPL(67), SPL(68), SPL(69), SPL(70), SPL(71),
SPL(72), SPL(73), SPL(74), SPL(75), SPL(76), SPL(77), SPL(78), SPL(79),
SPL(80), SPL(81), SPL(82), SPL(83), SPL(84), SPL(85), SPL(86), SPL(87),
SPL(88), SPL(89), SPL(90), SPL(91), SPL(92), SPL(93), SPL(94)
};
static int verbose;
static int nerrors;
static int unwind_count;
static int
unwind_and_resume (long iteration, int (*next_func[])())
{
unw_context_t uc;
unw_cursor_t c;
unw_word_t ip;
int i, ret;
if (verbose)
printf (" %s(iteration=%ld, next_func=%p)\n",
__FUNCTION__, iteration, next_func);
unw_getcontext (&uc);
if ((ret = unw_init_local (&c, &uc)) < 0)
panic ("unw_init_local (ret=%d)", ret);
for (i = 0; i < unwind_count; ++i)
if ((ret = unw_step (&c)) < 0)
panic ("unw_step (ret=%d)", ret);
if (unw_get_reg (&c, UNW_REG_IP, &ip) < 0
|| unw_set_reg (&c, UNW_REG_IP, (unw_word_t) &resumption_point_label) < 0
|| unw_set_reg (&c, UNW_REG_EH + 0, 0) /* ret val */
|| unw_set_reg (&c, UNW_REG_EH + 1, ip))
panic ("failed to redirect to resumption_point\n");
if (verbose)
{
unw_word_t bsp;
if (unw_get_reg (&c, UNW_IA64_BSP, &bsp) < 0)
panic ("unw_get_reg() failed\n");
printf (" bsp=%lx, old ip=%lx, new ip=%p\n", bsp,
ip, &resumption_point_label);
}
ret = unw_resume (&c);
panic ("unw_resume() returned (ret=%d)!!\n", ret);
return 0;
}
static int
run_check (int test)
{
int nfuncs, nspills, n, ret, i, reg_values[88];
spill_func_t *func[NSTACKS + 1];
/* First, generate a set of 88 random values which loadup() will load
into loc2-loc89 (r37-r124). */
for (i = 0; i < (int) ARRAY_SIZE (reg_values); ++i)
{
reg_values[i] = random ();
/* Generate NaTs with a reasonably probability (1/16th): */
if (reg_values[i] < 0x10000000)
reg_values[i] = 0;
}
nspills = 0;
nfuncs = 0;
do
{
n = random () % (int) ARRAY_SIZE (spill_funcs);
func[nfuncs++] = spill_funcs[n];
nspills += 2 + n;
}
while (nspills < 128);
func[nfuncs++] = unwind_and_resume;
unwind_count = 1 + (random () % (nfuncs + RECURSION_DEPTH - 1));
if (verbose)
printf ("test%d: nfuncs=%d, unwind_count=%d\n",
test, nfuncs, unwind_count);
ret = loadup (RECURSION_DEPTH, reg_values, func);
if (ret < 0)
panic ("test%d: load() returned %d\n", test, ret);
else if (ret != RECURSION_DEPTH + nfuncs - unwind_count)
panic ("test%d: resumed wrong frame: expected %d, got %d\n",
test, RECURSION_DEPTH + nfuncs - unwind_count, ret);
return 0;
}
int
main (int argc, char **argv)
{
int i;
if (argc > 1)
verbose = 1;
for (i = 0; i < 100000; ++i)
run_check (i + 1);
if (nerrors > 0)
{
fprintf (stderr, "FAILURE: detected %d errors\n", nerrors);
exit (-1);
}
if (verbose)
printf ("SUCCESS.\n");
return 0;
}

View File

@@ -0,0 +1,89 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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. */
/* This file verifies that read-only registers cannot be written to. */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libunwind.h>
#define panic(args...) \
do { printf (args); ++nerrors; } while (0)
static int verbose;
static int nerrors;
extern void test_func (void (*) (void));
void
checker (void)
{
unw_fpreg_t fpval;
unw_context_t uc;
unw_cursor_t c;
int ret;
fpval.raw.bits[0] = 100;
fpval.raw.bits[1] = 101;
unw_getcontext (&uc);
if ((ret = unw_init_local (&c, &uc)) < 0)
panic ("%s: unw_init_local (ret=%d)\n", __FUNCTION__, ret);
if ((ret = unw_step (&c)) < 0)
panic ("%s: unw_step (ret=%d)\n", __FUNCTION__, ret);
if ((ret = unw_step (&c)) < 0)
panic ("%s: unw_step (ret=%d)\n", __FUNCTION__, ret);
if ((ret = unw_set_reg (&c, UNW_IA64_IP, 99)) != -UNW_EREADONLYREG)
panic ("%s: unw_set_reg (ip) returned %d instead of %d\n",
__FUNCTION__, ret, -UNW_EREADONLYREG);
if ((ret = unw_set_reg (&c, UNW_IA64_AR_LC, 99)) != -UNW_EREADONLYREG)
panic ("%s: unw_set_reg (ar.lc) returned %d instead of %d\n",
__FUNCTION__, ret, -UNW_EREADONLYREG);
}
int
main (int argc, char **argv)
{
if (argc > 1)
verbose = 1;
test_func (checker);
if (nerrors > 0)
{
fprintf (stderr, "FAILURE: detected %d errors\n", nerrors);
exit (-1);
}
if (verbose)
printf ("SUCCESS.\n");
return 0;
}

View File

@@ -0,0 +1,176 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2003 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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. */
/* This file tests corner-cases of unwinding across multiple stacks.
In particular, it verifies that the extreme case with a frame of 96
stacked registers that are all backed up by separate stacks works
as expected. */
#include <libunwind.h>
#include <stdio.h>
#include <stdlib.h>
#include "ia64-test-stack.h"
#define panic(args...) \
{ printf (args); ++nerrors; }
/* The loadrs field in ar.rsc is 14 bits wide, which limits all ia64
implementations to at most 2048 physical stacked registers
(actually, slightly less than that, because loadrs also counts RNaT
slots). Since we can dirty 95 stacked registers per recursion, we
need to recurse RECURSION_DEPTH times to ensure all physical
stacked registers are in use. */
#define MAX_PHYS_STACKED 2048
#define RECURSION_DEPTH ((MAX_PHYS_STACKED + 94) / 95)
extern void touch_all (unsigned long recursion_depth);
extern void flushrs (void);
int nerrors;
int verbose;
void
do_unwind_tests (void)
{
unw_word_t ip, sp, bsp, v0, v1, v2, v3, n0, n1, n2, n3, cfm, sof, sol, r32;
int ret, reg, i, l;
unw_context_t uc;
unw_cursor_t c;
if (verbose)
printf ("do_unwind_tests: here we go!\n");
/* do a full stack-dump: */
unw_getcontext (&uc);
unw_init_local (&c, &uc);
i = 0;
do
{
if (verbose)
{
if ((ret = unw_get_reg (&c, UNW_IA64_IP, &ip)) < 0
|| (ret = unw_get_reg (&c, UNW_IA64_SP, &sp)) < 0
|| (ret = unw_get_reg (&c, UNW_IA64_BSP, &bsp)) < 0)
break;
printf ("ip=0x%16lx sp=0x%16lx bsp=0x%16lx\n", ip, sp, bsp);
for (reg = 32; reg < 128; reg += 4)
{
v0 = v1 = v2 = v3 = 0;
n0 = n1 = n2 = n3 = 0;
(void)
((ret = unw_get_reg (&c, UNW_IA64_GR + reg, &v0)) < 0
|| (ret = unw_get_reg (&c, UNW_IA64_NAT + reg, &n0)) < 0
|| (ret = unw_get_reg (&c, UNW_IA64_GR + reg + 1, &v1)) < 0
|| (ret = unw_get_reg (&c, UNW_IA64_NAT + reg + 1, &n1)) < 0
|| (ret = unw_get_reg (&c, UNW_IA64_GR + reg + 2, &v2)) < 0
|| (ret = unw_get_reg (&c, UNW_IA64_NAT + reg + 2, &n2)) < 0
|| (ret = unw_get_reg (&c, UNW_IA64_GR + reg + 3, &v3)) < 0
|| (ret = unw_get_reg (&c, UNW_IA64_NAT + reg + 3, &n3)) < 0);
if (reg < 100)
printf (" r%d", reg);
else
printf (" r%d", reg);
printf (" %c%016lx %c%016lx %c%016lx %c%016lx\n",
n0 ? '*' : ' ', v0, n1 ? '*' : ' ', v1,
n2 ? '*' : ' ', v2, n3 ? '*' : ' ', v3);
if (ret < 0)
break;
}
}
if (i >= 1 && i <= NSTACKS)
{
if ((ret = unw_get_reg (&c, UNW_IA64_CFM, &cfm)) < 0)
break;
sof = cfm & 0x7f;
if (sof != (unw_word_t) (i & 1))
panic ("\texpected sof=%d, found sof=%lu\n", i - 1, sof);
if (sof == 1)
{
if ((ret = unw_get_reg (&c, UNW_IA64_GR + 32, &r32)) < 0)
break;
if (r32 != (unw_word_t) (i - 1))
panic ("\texpected r32=%d, found r32=%lu\n", i - 1, r32);
}
}
else if (i > NSTACKS && i <= NSTACKS + RECURSION_DEPTH)
{
if ((ret = unw_get_reg (&c, UNW_IA64_CFM, &cfm)) < 0)
break;
sof = cfm & 0x7f;
sol = (cfm >> 7) & 0x7f;
if (sof != 96)
panic ("\texpected sof=96, found sof=%lu\n", sof);
if (sol != 95)
panic ("\texpected sol=95, found sol=%lu\n", sol);
for (l = 2; l <= 93; ++l)
{
if ((ret = unw_get_reg (&c, UNW_IA64_GR + 33 + l, &v0)) < 0
|| (ret = unw_get_reg (&c, UNW_IA64_NAT + 33 + l, &n0)) < 0)
break;
switch (l)
{
case 2: case 31: case 73: case 93:
if (!n0)
panic ("\texpected loc%d to be a NaT!\n", l);
break;
default:
if (n0)
panic ("\tloc%d is unexpectedly a NaT!\n", l);
v1 = ((unw_word_t) (i - NSTACKS) << 32) + l;
if (v0 != v1)
panic ("\tloc%d expected to be %lx, found to be %lx\n",
l, v1, v0);
}
}
}
++i;
}
while ((ret = unw_step (&c)) > 0);
if (ret < 0)
panic ("libunwind returned %d\n", ret);
}
int
main (int argc, char **argv)
{
if (argc > 1)
++verbose;
touch_all (RECURSION_DEPTH);
if (nerrors)
{
printf ("FAILURE: detected %d errors\n", nerrors);
exit (-1);
}
if (verbose)
printf ("SUCCESS\n");
return 0;
}

View File

@@ -0,0 +1,264 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2003-2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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 <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libunwind.h>
#include "compiler.h"
#include <sys/resource.h>
#include <sys/time.h>
#define panic(args...) \
do { fprintf (stderr, args); exit (-1); } while (0)
long dummy;
static long iterations = 10000;
#define KB 1024
#define MB (1024*1024)
static char big[64*MB]; /* should be >> max. cache size */
static inline double
gettime (void)
{
struct timeval tv;
gettimeofday (&tv, NULL);
return tv.tv_sec + 1e-6*tv.tv_usec;
}
static int NOINLINE
measure_unwind (int maxlevel, double *step)
{
double stop, start;
unw_cursor_t cursor;
unw_context_t uc;
int ret, level = 0;
unw_getcontext (&uc);
if (unw_init_local (&cursor, &uc) < 0)
panic ("unw_init_local() failed\n");
start = gettime ();
do
{
ret = unw_step (&cursor);
if (ret < 0)
panic ("unw_step() failed\n");
++level;
}
while (ret > 0);
stop = gettime ();
if (level <= maxlevel)
panic ("Unwound only %d levels, expected at least %d levels\n",
level, maxlevel);
*step = (stop - start) / (double) level;
return 0;
}
static int f1 (int, int, double *);
static int NOINLINE
g1 (int level, int maxlevel, double *step)
{
if (level == maxlevel)
return measure_unwind (maxlevel, step);
else
/* defeat last-call/sibcall optimization */
return f1 (level + 1, maxlevel, step) + level;
}
static int NOINLINE
f1 (int level, int maxlevel, double *step)
{
if (level == maxlevel)
return measure_unwind (maxlevel, step);
else
/* defeat last-call/sibcall optimization */
return g1 (level + 1, maxlevel, step) + level;
}
static void
doit (const char *label, int maxlevel)
{
double step, min_step, first_step, sum_step;
int i;
sum_step = first_step = 0.0;
min_step = 1e99;
for (i = 0; i < iterations; ++i)
{
f1 (0, maxlevel, &step);
sum_step += step;
if (step < min_step)
min_step = step;
if (i == 0)
first_step = step;
}
printf ("%s: unw_step : 1st=%9.3f min=%9.3f avg=%9.3f nsec\n", label,
1e9*first_step, 1e9*min_step, 1e9*sum_step/iterations);
}
static long
sum (void *buf, size_t size)
{
long s = 0;
char *cp = buf;
size_t i;
for (i = 0; i < size; i += 8)
s += cp[i];
return s;
}
static void
measure_init (void)
{
# define N 100
# define M 10 /* must be at least 2 to get steady-state */
double stop, start, get_cold, get_warm, init_cold, init_warm, delta;
struct
{
unw_cursor_t c;
char padding[1024]; /* should be > 2 * max. cacheline size */
}
cursor[N];
struct
{
unw_context_t uc;
char padding[1024]; /* should be > 2 * max. cacheline size */
}
uc[N];
int i, j;
/* Run each test M times and take the minimum to filter out noise
such dynamic linker resolving overhead, context-switches,
page-in, cache, and TLB effects. */
get_cold = 1e99;
for (j = 0; j < M; ++j)
{
dummy += sum (big, sizeof (big)); /* flush the cache */
for (i = 0; i < N; ++i)
uc[i].padding[511] = i; /* warm up the TLB */
start = gettime ();
for (i = 0; i < N; ++i)
unw_getcontext (&uc[i].uc);
stop = gettime ();
delta = (stop - start) / N;
if (delta < get_cold)
get_cold = delta;
}
init_cold = 1e99;
for (j = 0; j < M; ++j)
{
dummy += sum (big, sizeof (big)); /* flush cache */
for (i = 0; i < N; ++i)
uc[i].padding[511] = i; /* warm up the TLB */
start = gettime ();
for (i = 0; i < N; ++i)
unw_init_local (&cursor[i].c, &uc[i].uc);
stop = gettime ();
delta = (stop - start) / N;
if (delta < init_cold)
init_cold = delta;
}
get_warm = 1e99;
for (j = 0; j < M; ++j)
{
start = gettime ();
for (i = 0; i < N; ++i)
unw_getcontext (&uc[0].uc);
stop = gettime ();
delta = (stop - start) / N;
if (delta < get_warm)
get_warm = delta;
}
init_warm = 1e99;
for (j = 0; j < M; ++j)
{
start = gettime ();
for (i = 0; i < N; ++i)
unw_init_local (&cursor[0].c, &uc[0].uc);
stop = gettime ();
delta = (stop - start) / N;
if (delta < init_warm)
init_warm = delta;
}
printf ("unw_getcontext : cold avg=%9.3f nsec, warm avg=%9.3f nsec\n",
1e9 * get_cold, 1e9 * get_warm);
printf ("unw_init_local : cold avg=%9.3f nsec, warm avg=%9.3f nsec\n",
1e9 * init_cold, 1e9 * init_warm);
}
int
main (int argc, char **argv)
{
int maxlevel = 100;
struct rlimit rlim = {
.rlim_cur = RLIM_INFINITY,
.rlim_max = RLIM_INFINITY
};
setrlimit (RLIMIT_STACK, &rlim);
memset (big, 0xaa, sizeof (big));
if (argc > 1)
{
maxlevel = atol (argv[1]);
if (argc > 2)
iterations = atol (argv[2]);
}
measure_init ();
doit ("default ", maxlevel);
unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_NONE);
doit ("no cache ", maxlevel);
unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_GLOBAL);
doit ("global cache ", maxlevel);
unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_PER_THREAD);
doit ("per-thread cache", maxlevel);
return 0;
}

View File

@@ -0,0 +1,250 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2003-2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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 <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libunwind.h>
#include "compiler.h"
#include <sys/resource.h>
#include <sys/time.h>
#define panic(...) \
do { fprintf (stderr, __VA_ARGS__); exit (-1); } while (0)
long dummy;
static long iterations = 10000;
#define KB 1024
#define MB (1024*1024)
static char big[64*MB]; /* should be >> max. cache size */
static inline double
gettime (void)
{
struct timeval tv;
gettimeofday (&tv, NULL);
return tv.tv_sec + 1e-6*tv.tv_usec;
}
static int NOINLINE
measure_unwind (int maxlevel, double *step)
{
double stop, start;
int level = 0;
void *buffer[128];
start = gettime ();
level = unw_backtrace(buffer, 128);
stop = gettime ();
if (level <= maxlevel)
panic ("Unwound only %d levels, expected at least %d levels\n",
level, maxlevel);
*step = (stop - start) / (double) level;
return 0;
}
static int f1 (int, int, double *);
static int NOINLINE
g1 (int level, int maxlevel, double *step)
{
if (level == maxlevel)
return measure_unwind (maxlevel, step);
else
/* defeat last-call/sibcall optimization */
return f1 (level + 1, maxlevel, step) + level;
}
static int NOINLINE
f1 (int level, int maxlevel, double *step)
{
if (level == maxlevel)
return measure_unwind (maxlevel, step);
else
/* defeat last-call/sibcall optimization */
return g1 (level + 1, maxlevel, step) + level;
}
static void
doit (const char *label, int maxlevel)
{
double step, min_step, first_step, sum_step;
int i;
sum_step = first_step = 0.0;
min_step = 1e99;
for (i = 0; i < iterations; ++i)
{
f1 (0, maxlevel, &step);
sum_step += step;
if (step < min_step)
min_step = step;
if (i == 0)
first_step = step;
}
printf ("%s: unw_step : 1st=%9.3f min=%9.3f avg=%9.3f nsec\n", label,
1e9*first_step, 1e9*min_step, 1e9*sum_step/iterations);
}
static long
sum (void *buf, size_t size)
{
long s = 0;
char *cp = buf;
size_t i;
for (i = 0; i < size; i += 8)
s += cp[i];
return s;
}
static void
measure_init (void)
{
# define N 100
# define M 10 /* must be at least 2 to get steady-state */
double stop, start, get_cold, get_warm, init_cold, init_warm, delta;
struct
{
unw_cursor_t c;
char padding[1024]; /* should be > 2 * max. cacheline size */
}
cursor[N];
struct
{
unw_context_t uc;
char padding[1024]; /* should be > 2 * max. cacheline size */
}
uc[N];
int i, j;
/* Run each test M times and take the minimum to filter out noise
such dynamic linker resolving overhead, context-switches,
page-in, cache, and TLB effects. */
get_cold = 1e99;
for (j = 0; j < M; ++j)
{
dummy += sum (big, sizeof (big)); /* flush the cache */
for (i = 0; i < N; ++i)
uc[i].padding[511] = i; /* warm up the TLB */
start = gettime ();
for (i = 0; i < N; ++i)
unw_getcontext (&uc[i].uc);
stop = gettime ();
delta = (stop - start) / N;
if (delta < get_cold)
get_cold = delta;
}
init_cold = 1e99;
for (j = 0; j < M; ++j)
{
dummy += sum (big, sizeof (big)); /* flush cache */
for (i = 0; i < N; ++i)
uc[i].padding[511] = i; /* warm up the TLB */
start = gettime ();
for (i = 0; i < N; ++i)
unw_init_local (&cursor[i].c, &uc[i].uc);
stop = gettime ();
delta = (stop - start) / N;
if (delta < init_cold)
init_cold = delta;
}
get_warm = 1e99;
for (j = 0; j < M; ++j)
{
start = gettime ();
for (i = 0; i < N; ++i)
unw_getcontext (&uc[0].uc);
stop = gettime ();
delta = (stop - start) / N;
if (delta < get_warm)
get_warm = delta;
}
init_warm = 1e99;
for (j = 0; j < M; ++j)
{
start = gettime ();
for (i = 0; i < N; ++i)
unw_init_local (&cursor[0].c, &uc[0].uc);
stop = gettime ();
delta = (stop - start) / N;
if (delta < init_warm)
init_warm = delta;
}
printf ("unw_getcontext : cold avg=%9.3f nsec, warm avg=%9.3f nsec\n",
1e9 * get_cold, 1e9 * get_warm);
printf ("unw_init_local : cold avg=%9.3f nsec, warm avg=%9.3f nsec\n",
1e9 * init_cold, 1e9 * init_warm);
}
int
main (int argc, char **argv)
{
int maxlevel = 100;
struct rlimit rlim = {
.rlim_cur = RLIM_INFINITY,
.rlim_max = RLIM_INFINITY
};
setrlimit (RLIMIT_STACK, &rlim);
memset (big, 0xaa, sizeof (big));
if (argc > 1)
{
maxlevel = atol (argv[1]);
if (argc > 2)
iterations = atol (argv[2]);
}
measure_init ();
doit ("default ", maxlevel);
unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_NONE);
doit ("no cache ", maxlevel);
unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_GLOBAL);
doit ("global cache ", maxlevel);
unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_PER_THREAD);
doit ("per-thread cache", maxlevel);
return 0;
}

View File

@@ -0,0 +1,333 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2001-2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
Copyright 2022 Blackberry Limited.
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. */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "compiler.h"
#include <errno.h>
#if HAVE_EXECINFO_H
# include <execinfo.h>
#else
extern int backtrace (void **, int);
#endif
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <ucontext.h>
#include <unistd.h>
#include <libunwind.h>
#include "ident.h"
#define panic(...) \
{ fprintf (stderr, __VA_ARGS__); exit (-1); }
#define MIN_FRAMES 3
#define SIG_STACK_SIZE 0x100000
#if UNW_TARGET_ARM
#define unwi_unwind_method UNW_OBJ(unwind_method)
#define UNW_ARM_METHOD_DWARF 0x01
#define UNW_ARM_METHOD_EXIDX 0x04
#endif
int verbose;
int num_errors;
sigjmp_buf env;
/* These variables are global because they
* cause the signal stack to overflow */
char buf[512], name[256];
unw_cursor_t cursor;
unw_context_t uc;
static void
do_backtrace (void)
{
unw_word_t ip, sp, off;
unw_proc_info_t pi;
unsigned int num_frames = 0;
int ret;
if (verbose)
printf ("\texplicit backtrace:\n");
unw_getcontext (&uc);
if (unw_init_local (&cursor, &uc) < 0)
panic ("unw_init_local failed!\n");
do
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
unw_get_reg (&cursor, UNW_REG_SP, &sp);
buf[0] = '\0';
if (unw_get_proc_name (&cursor, name, sizeof (name), &off) == 0)
{
if (off)
snprintf (buf, sizeof (buf), "<%s+0x%lx>", name, (long) off);
else
snprintf (buf, sizeof (buf), "<%s>", name);
}
if (verbose)
{
printf ("%016lx %-32s (sp=%016lx)\n", (long) ip, buf, (long) sp);
if (unw_get_proc_info (&cursor, &pi) == 0)
{
printf ("\tproc=0x%lx-0x%lx\n\thandler=0x%lx lsda=0x%lx gp=0x%lx",
(long) pi.start_ip, (long) pi.end_ip,
(long) pi.handler, (long) pi.lsda, (long) pi.gp);
}
#if UNW_TARGET_IA64
{
unw_word_t bsp;
unw_get_reg (&cursor, UNW_IA64_BSP, &bsp);
printf (" bsp=%lx", bsp);
}
#endif
printf ("\n");
name[0] = '\0';
if (unw_get_elf_filename (&cursor, name, sizeof (name), &off) == UNW_ESUCCESS)
printf ("\t[%s+0x%lx]\n", name, (long) off);
}
ret = unw_step (&cursor);
if (ret < 0)
{
#if UNW_TARGET_ARM
/*
* On ARM, when using EXIDX, the last frame will return
* cantunwind. Stop unwinding and check later on if enough frames
* have been unwound.
*/
extern int unwi_unwind_method;
if (unwi_unwind_method & UNW_ARM_METHOD_EXIDX &&
ret == -UNW_ESTOPUNWIND)
break;
#endif
unw_get_reg (&cursor, UNW_REG_IP, &ip);
printf ("FAILURE: unw_step() returned %d for ip=%lx\n",
ret, (long) ip);
++num_errors;
}
++num_frames;
}
while (ret > 0);
if (num_frames < MIN_FRAMES) {
printf ("FAILURE: only found %u frames in the backtrace\n",
num_frames);
++num_errors;
}
{
void *buffer[20];
int i, n;
if (verbose)
printf ("\n\tvia backtrace():\n");
n = backtrace (buffer, 20);
if (verbose)
for (i = 0; i < n; ++i)
printf ("[%d] ip=%p\n", i, buffer[i]);
}
}
void
foo (long val UNUSED)
{
do_backtrace ();
}
void
bar (long v)
{
int arr[v];
arr[0] = 0;
/* This is a vain attempt to use up lots of registers to force
the frame-chain info to be saved on the memory stack on ia64.
It happens to work with gcc v3.3.4 and gcc v3.4.1 but perhaps
not with any other compiler. */
foo (f (arr[0]) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + f (v))
))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
)))))))))))))))))))))))))))))))))))))))))))))))))))))));
}
void
segv_handler (int signal, siginfo_t *siginfo UNUSED, void *context UNUSED)
{
if (verbose)
fprintf (stderr, "segv_handler: got signal %d\n", signal);
do_backtrace ();
siglongjmp (env, 42);
}
void
sighandler (int signal, siginfo_t *siginfo UNUSED, void *context)
{
ucontext_t *uc UNUSED;
int sp;
uc = context;
if (verbose)
{
printf ("sighandler: got signal %d, sp=%p", signal, (void *)&sp);
#if UNW_TARGET_IA64
# if defined(__linux__) || defined __sun
printf (" @ %lx", uc->uc_mcontext.sc_ip);
# else
{
uint16_t reason;
uint64_t ip;
__uc_get_reason (uc, &reason);
__uc_get_ip (uc, &ip);
printf (" @ %lx (reason=%d)", ip, reason);
}
# endif
#elif UNW_TARGET_X86
# if defined __linux__ || defined __sun
printf (" @ %lx", (unsigned long) uc->uc_mcontext.gregs[REG_EIP]);
# elif defined __FreeBSD__
printf (" @ %lx", (unsigned long) uc->uc_mcontext.mc_eip);
# endif
#elif UNW_TARGET_X86_64
# if defined __linux__ || defined __sun
printf (" @ %lx", (unsigned long) uc->uc_mcontext.gregs[REG_RIP]);
# elif defined __FreeBSD__
printf (" @ %lx", (unsigned long) uc->uc_mcontext.mc_rip);
# endif
#elif UNW_TARGET_AARCH64
# if defined(__QNX__)
fprintf (stderr, " @ %#010lx", (unsigned long) uc->uc_mcontext.cpu.elr);
# endif /* defined(__QNX__) */
#endif
printf ("\n");
}
do_backtrace();
}
int
main (int argc, char **argv UNUSED)
{
struct sigaction act;
#ifdef HAVE_SIGALTSTACK
stack_t stk;
#endif /* HAVE_SIGALTSTACK */
verbose = (argc > 1);
if (verbose)
printf ("Normal backtrace:\n");
bar (1);
memset (&act, 0, sizeof (act));
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = sighandler;
if (sigaction (SIGTERM, &act, NULL) < 0)
panic ("sigaction: %s\n", strerror (errno));
if (verbose)
printf ("\nBacktrace across signal handler:\n");
kill (getpid (), SIGTERM);
if (verbose)
printf ("\nBacktrace across SIGSEGV handler:\n");
act.sa_sigaction = segv_handler;
if (sigaction (SIGSEGV, &act, NULL) < 0)
panic ("sigaction: %s\n", strerror (errno));
if (sigsetjmp (env, 1) == 0)
{
/* Make a NULL pointer dereference. */
int *bad_ptr = NULL;
*bad_ptr = 0;
}
#ifdef HAVE_SIGALTSTACK
if (verbose)
printf ("\nBacktrace across signal handler on alternate stack:\n");
stk.ss_sp = malloc (SIG_STACK_SIZE);
if (!stk.ss_sp)
panic ("failed to allocate %u bytes\n", SIG_STACK_SIZE);
stk.ss_size = SIG_STACK_SIZE;
stk.ss_flags = 0;
if (sigaltstack (&stk, NULL) < 0)
panic ("sigaltstack: %s\n", strerror (errno));
memset (&act, 0, sizeof (act));
act.sa_flags = SA_ONSTACK | SA_SIGINFO;
act.sa_sigaction = sighandler;
if (sigaction (SIGTERM, &act, NULL) < 0)
panic ("sigaction: %s\n", strerror (errno));
kill (getpid (), SIGTERM);
#endif /* HAVE_SIGALTSTACK */
if (num_errors > 0)
{
fprintf (stderr, "FAILURE: detected %d errors\n", num_errors);
exit (-1);
}
if (verbose)
printf ("SUCCESS.\n");
signal (SIGTERM, SIG_DFL);
#ifdef HAVE_SIGALTSTACK
stk.ss_flags = SS_DISABLE;
sigaltstack (&stk, NULL);
free (stk.ss_sp);
#endif /* HAVE_SIGALTSTACK */
return 0;
}

View File

@@ -0,0 +1,136 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2003-2005 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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. */
/* Verify that multi-threaded concurrent unwinding works as expected. */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "compiler.h"
#include <libunwind.h>
#include <limits.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define NTHREADS 128
#define panic(...) \
do { fprintf (stderr, __VA_ARGS__); ++nerrors; } while (0)
int verbose;
int nerrors;
int got_usr1, got_usr2;
char *sigusr1_sp;
void
handler (int sig UNUSED)
{
unw_word_t ip;
unw_context_t uc;
unw_cursor_t c;
int ret;
unw_getcontext (&uc);
unw_init_local (&c, &uc);
do
{
unw_get_reg (&c, UNW_REG_IP, &ip);
if (verbose)
printf ("%lx: IP=%lx\n", (long) pthread_self (), (unsigned long) ip);
}
while ((ret = unw_step (&c)) > 0);
if (ret < 0)
panic ("unw_step() returned %d\n", ret);
}
void *
worker (void *arg UNUSED)
{
signal (SIGUSR1, handler);
if (verbose)
printf ("sending SIGUSR1\n");
pthread_kill (pthread_self (), SIGUSR1);
return NULL;
}
static void
doit (void)
{
pthread_t th[NTHREADS];
pthread_attr_t attr;
int i;
pthread_attr_init (&attr);
pthread_attr_setstacksize (&attr, PTHREAD_STACK_MIN + 64*1024);
for (i = 0; i < NTHREADS; ++i)
if (pthread_create (th + i, &attr, worker, NULL))
{
fprintf (stderr, "FAILURE: Failed to create %u threads "
"(after %u threads)\n",
NTHREADS, i);
exit (-1);
}
for (i = 0; i < NTHREADS; ++i)
pthread_join (th[i], NULL);
}
int
main (int argc, char **argv UNUSED)
{
if (argc > 1)
verbose = 1;
if (verbose)
printf ("Caching: none\n");
unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_NONE);
doit ();
if (verbose)
printf ("Caching: global\n");
unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_GLOBAL);
doit ();
if (verbose)
printf ("Caching: per-thread\n");
unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_PER_THREAD);
doit ();
if (nerrors)
{
fprintf (stderr, "FAILURE: detected %d errors\n", nerrors);
exit (-1);
}
if (verbose)
printf ("SUCCESS\n");
return 0;
}

View File

@@ -0,0 +1,223 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2002-2003 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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. */
/* This file tests dynamic code-generation via function-cloning. */
#include "flush-cache.h"
#include "compiler.h"
#include <libunwind.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/mman.h>
#if UNW_TARGET_ARM
#define MAX_FUNC_SIZE 96 /* FIXME: arch/compiler dependent */
#else
#define MAX_FUNC_SIZE 2048 /* max. size of cloned function */
#endif
#define panic(args...) \
{ fprintf (stderr, args); exit (-1); }
typedef void (*template_t) (int, void (*)(),
int (*)(const char *, ...), const char *,
const char **);
int verbose;
static const char *strarr[] =
{
"i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix", "x", NULL
};
#ifdef __ia64__
struct fdesc
{
long code;
long gp;
};
# define get_fdesc(fdesc,func) (fdesc = *(struct fdesc *) &(func))
# define get_funcp(fdesc) ((template_t) &(fdesc))
# define get_gp(fdesc) ((fdesc).gp)
#elif __arm__
struct fdesc
{
long code;
long is_thumb;
};
/* Workaround GCC bug: https://bugs.launchpad.net/gcc-linaro/+bug/721531 */
# define get_fdesc(fdesc,func) ({long tmp = (long) &(func); \
(fdesc).code = (long) &(func) & ~0x1; \
(fdesc).is_thumb = tmp & 0x1;})
/*# define get_fdesc(fdesc,func) ({(fdesc).code = (long) &(func) & ~0x1; \
(fdesc).is_thumb = (long) &(func) & 0x1;})*/
# define get_funcp(fdesc) ((template_t) ((fdesc).code | (fdesc).is_thumb))
# define get_gp(fdesc) (0)
#else
struct fdesc
{
long code;
};
# define get_fdesc(fdesc,func) (fdesc.code = (long) &(func))
# define get_funcp(fdesc) ((template_t) (fdesc).code)
# define get_gp(fdesc) (0)
#endif
void
template (int i, template_t self,
int (*printer)(const char *, ...), const char *fmt, const char **arr)
{
(*printer) (fmt, arr[11 - i][0], arr[11 - i] + 1);
if (i > 0)
(*self) (i - 1, self, printer, fmt, arr);
}
static void
sighandler (int signal)
{
unw_cursor_t cursor;
char name[128], off[32];
unw_word_t ip, offset;
unw_context_t uc;
int count;
if (verbose)
printf ("caught signal %d\n", signal);
unw_getcontext (&uc);
unw_init_local (&cursor, &uc);
count = 0;
while (!unw_is_signal_frame (&cursor))
{
if (unw_step (&cursor) < 0)
panic ("failed to find signal frame!\n");
if (count++ > 20)
{
panic ("Too many steps to the signal frame (%d)\n", count);
break;
}
}
unw_step (&cursor);
count = 0;
do
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
name[0] = '\0';
off[0] = '\0';
if (unw_get_proc_name (&cursor, name, sizeof (name), &offset) == 0
&& offset > 0)
snprintf (off, sizeof (off), "+0x%lx", (long) offset);
if (verbose)
printf ("ip = %lx <%s%s>\n", (long) ip, name, off);
++count;
if (count > 20)
{
panic ("Too many steps (%d)\n", count);
break;
}
}
while (unw_step (&cursor) > 0);
if (count != 13)
panic ("FAILURE: expected 13, not %d frames below signal frame\n", count);
if (verbose)
printf ("SUCCESS\n");
exit (0);
}
int
dev_null (const char *format UNUSED, ...)
{
return 0;
}
int
main (int argc, char *argv[] UNUSED)
{
unw_dyn_region_info_t *region;
unw_dyn_info_t di;
struct fdesc fdesc;
template_t funcp;
void *mem;
if (argc > 1)
++verbose;
mem = malloc (getpagesize ());
get_fdesc (fdesc, template);
if (verbose)
printf ("old code @ %p, new code @ %p\n", (void *) fdesc.code, mem);
memcpy (mem, (void *) fdesc.code, MAX_FUNC_SIZE);
mprotect ((void *) ((long) mem & ~(getpagesize () - 1)),
2*getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC);
flush_cache (mem, MAX_FUNC_SIZE);
signal (SIGSEGV, sighandler);
/* register the new function: */
region = alloca (_U_dyn_region_info_size (2));
region->next = NULL;
region->insn_count = 3 * (MAX_FUNC_SIZE / 16);
region->op_count = 2;
_U_dyn_op_alias (&region->op[0], 0, -1, fdesc.code);
_U_dyn_op_stop (&region->op[1]);
memset (&di, 0, sizeof (di));
di.start_ip = (long) mem;
di.end_ip = (long) mem + 16*region->insn_count/3;
di.gp = get_gp (fdesc);
di.format = UNW_INFO_FORMAT_DYNAMIC;
di.u.pi.name_ptr = (unw_word_t) "copy_of_template";
di.u.pi.regions = region;
_U_dyn_register (&di);
/* call new function: */
fdesc.code = (long) mem;
funcp = get_funcp (fdesc);
if (verbose)
(*funcp) (10, funcp, printf, "iteration %c%s\n", strarr);
else
(*funcp) (10, funcp, dev_null, "iteration %c%s\n", strarr);
_U_dyn_cancel (&di);
return -1;
}

View File

@@ -0,0 +1,180 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2001-2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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. */
/* This illustrates the basics of using the unwind interface for
exception handling. */
#include "compiler.h"
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libunwind.h>
#ifdef HAVE_IA64INTRIN_H
# include <ia64intrin.h>
#endif
#define panic(...) \
{ ++nerrors; fprintf (stderr, __VA_ARGS__); }
int nerrors = 0;
int verbose = 0;
int depth = 13;
volatile int got_here = 0;
extern void b (int);
void
raise_exception (void)
{
unw_cursor_t cursor;
unw_context_t uc;
int i;
unw_word_t ip;
unw_word_t sp;
unw_getcontext (&uc);
if (unw_init_local (&cursor, &uc) < 0)
{
panic ("unw_init_local() failed!\n");
return;
}
if (verbose)
{
printf ("raise_exception(): sp=%p\n", (void*) &sp);
unw_get_reg (&cursor, UNW_REG_IP, &ip);
unw_get_reg (&cursor, UNW_REG_SP, &sp);
printf ("\t #%-3d ip=%p sp=%p\n", 0, (void*) ip, (void*) sp);
}
/* unwind to top-most frame a(), skipping over b() and raise_exception(): */
for (i = 0; i < depth + 2; ++i)
if (unw_step (&cursor) < 0)
{
panic ("unw_step() failed!\n");
return;
}
else if (verbose)
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
unw_get_reg (&cursor, UNW_REG_SP, &sp);
printf ("\t #%-3d ip=%p sp=%p\n", i + 1, (void*) ip, (void*) sp);
}
unw_resume (&cursor); /* transfer control to exception handler */
}
uintptr_t
get_bsp (void)
{
#if UNW_TARGET_IA64
# ifdef __INTEL_COMPILER
return __getReg (_IA64_REG_AR_BSP);
# else
return (uintptr_t) __builtin_ia64_bsp ();
# endif
#else
return 0;
#endif
}
int NOINLINE
a (int n)
{
long stack;
int result = 99;
if (verbose)
printf ("a(n=%d): sp=%p bsp=0x%lx\n",
n, (void *)&stack, (unsigned long) get_bsp ());
if (n > 0)
a (n - 1);
else
b (16);
if (verbose)
{
printf ("exception handler: here we go (sp=%p, bsp=0x%lx)...\n",
(void *)&stack, (unsigned long) get_bsp ());
/* This call works around a bug in gcc (up-to pre3.4) which
causes invalid assembly code to be generated when
__builtin_ia64_bsp() gets predicated. */
getpid ();
}
if (n == depth)
{
result = 0;
got_here = 1;
}
return result;
}
void NOINLINE
b (int n)
{
if ((n & 1) == 0)
{
if (verbose)
printf ("b(n=%d) calling raise_exception()\n", n);
raise_exception ();
}
panic ("FAILURE: b() returned from raise_exception()!!\n");
}
int
main (int argc, char **argv)
{
int result;
if (argc > 1)
{
++verbose;
depth = atol (argv[1]);
if (depth < 1)
{
fprintf (stderr, "Usage: %s depth\n"
" depth must be >= 1\n", argv[0]);
exit (-1);
}
}
result = a (depth);
if (result != 0 || !got_here || nerrors > 0)
{
fprintf (stderr,
"FAILURE: test failed: result=%d got_here=%d nerrors=%d\n",
result, got_here, nerrors);
exit (-1);
}
if (verbose)
printf ("SUCCESS!\n");
return 0;
}

View File

@@ -0,0 +1,113 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2002-2003 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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. */
/* This file tests unwinding from a constructor from within an
atexit() handler. */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libunwind.h>
#include "compiler.h"
int verbose, errors;
#define panic(args...) \
{ ++errors; fprintf (stderr, args); return; }
class Test_Class {
public:
Test_Class (void);
};
static Test_Class t;
static void
do_backtrace (void)
{
char name[128], off[32];
char filename[256];
unw_word_t ip, offset, file_offset;
unw_cursor_t cursor;
unw_context_t uc;
int ret, count = 0;
unw_getcontext (&uc);
unw_init_local (&cursor, &uc);
do
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
name[0] = '\0';
off[0] = '\0';
if (unw_get_proc_name (&cursor, name, sizeof (name), &offset) == 0
&& offset > 0)
snprintf (off, sizeof (off), "+0x%lx", (long) offset);
if (verbose)
printf (" [%lx] <%s%s>\n", (long) ip, name, off);
if (unw_get_elf_filename (&cursor, filename, sizeof (filename), &file_offset) == 0)
if (verbose)
printf (" [%s+0x%lx]\n", filename, (long) file_offset);
if (++count > 32)
panic ("FAILURE: didn't reach beginning of unwind-chain\n");
}
while ((ret = unw_step (&cursor)) > 0);
if (ret < 0)
panic ("FAILURE: unw_step() returned %d\n", ret);
}
static void
b (void)
{
do_backtrace();
}
static void
a (void)
{
if (verbose)
printf ("do_backtrace() from atexit()-handler:\n");
b();
if (errors)
abort (); /* cannot portably call exit() from an atexit() handler */
}
Test_Class::Test_Class (void)
{
if (verbose)
printf ("do_backtrace() from constructor:\n");
b();
}
int
main (int argc, char **argv UNUSED)
{
verbose = argc > 1;
return atexit (a);
}

View File

@@ -0,0 +1,124 @@
/**
* @file tests/Gtest-nomalloc.c
*
* Verify that @c malloc() is not called during an unwinding operation.
*/
/*
* This file is part of libunwind.
* Copyright 2025 Stephen M. Webb <stephen.webb@bregmasoft.ca>
* Copyright (C) 2009 Google, Inc
* Contributed by Arun Sharma <arun.sharma@google.com>
*
* 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 <dlfcn.h>
#include <libunwind.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "unw_test.h"
int malloc_call_count;
int in_unwind;
/**
* Intercepted malloc() call.
*
* If invoked during unwinding this call will increment the test error count
* and indicate a failure by returning NULL. Otherwise it just calls the real
* malloc().
*/
void *
malloc (size_t sz)
{
typedef void *(*malloc_t) (size_t);
static malloc_t real_malloc = NULL;
if (real_malloc == NULL)
{
real_malloc = (malloc_t)(intptr_t)dlsym (RTLD_NEXT, "malloc");
if (real_malloc == NULL)
{
fprintf (stderr, "no malloc() found\n");
exit (UNW_TEST_EXIT_HARD_ERROR); \
}
}
if (in_unwind)
{
malloc_call_count++;
}
return real_malloc (sz);
}
static void
do_backtrace (void)
{
unw_cursor_t cursor;
unw_context_t uc;
int ret;
in_unwind = 1;
unw_getcontext (&uc);
if (unw_init_local (&cursor, &uc) < 0)
{
fprintf (stderr, "unw_init_local failed!\n");
exit (UNW_TEST_EXIT_HARD_ERROR); \
}
do
{
ret = unw_step (&cursor);
}
while (ret > 0);
in_unwind = 0;
}
void
foo3 (void)
{
do_backtrace ();
}
void
foo2 (void)
{
foo3 ();
}
void
foo1 (void)
{
foo2 ();
}
int
main (void)
{
foo1 ();
if (malloc_call_count > 0)
{
fprintf (stderr, "FAILURE: malloc called %d times, expected 0\n", malloc_call_count);
exit (UNW_TEST_EXIT_FAIL);
}
exit (UNW_TEST_EXIT_PASS);
}

View File

@@ -0,0 +1,31 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2003-2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
Copyright (C) 2012 Tommi Rantala <tt.rantala@gmail.com>
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. */
/* The purpose of this test is to invoke different code paths in libunwind (on
* some architectures), that are executed when the SA_SIGINFO sigaction() flag
* is used.
*/
#define TEST_WITH_SIGINFO 1
#include "Gtest-resume-sig.c"

View File

@@ -0,0 +1,195 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2003-2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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. */
/* Verify that unw_resume() restores the signal mask at proper time. */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "compiler.h"
#include <libunwind.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#ifdef HAVE_IA64INTRIN_H
# include <ia64intrin.h>
#endif
#define panic(...) \
do { fprintf (stderr, __VA_ARGS__); ++nerrors; } while (0)
int verbose;
int nerrors;
int got_usr1, got_usr2;
char *sigusr1_sp;
uintptr_t
get_bsp (void)
{
#if UNW_TARGET_IA64
# ifdef __INTEL_COMPILER
return __getReg (_IA64_REG_AR_BSP);
# else
return (uintptr_t) __builtin_ia64_bsp ();
# endif
#else
return 0;
#endif
}
#ifdef TEST_WITH_SIGINFO
void
handler (int sig,
siginfo_t *si UNUSED,
void *ucontext UNUSED)
#else
void
handler (int sig)
#endif
{
unw_word_t ip;
sigset_t mask;
unw_context_t uc;
unw_cursor_t c;
char foo;
int ret;
// The test rely on SIGUSR2 mask to be cleared when the handler returns.
// For local context from the signal handler, there doesn't seem to be a way
// currently to set it so just clear the whole struct to make sure the signal mask is cleared.
// This should probably be fixed to avoid signal mask being set to random values
// by `unw_resume` if the context was not pre-zeroed.,
// Using the signal ucontext direction should also work automatically but currently doesn't
// on ARM/AArch64 (or any other archs that doesn't have a proper sigreturn implementation)
memset(&uc, 0x0, sizeof(uc));
#if UNW_TARGET_IA64
if (verbose)
printf ("bsp = %llx\n", (unsigned long long) get_bsp ());
#endif
if (verbose)
printf ("got signal %d\n", sig);
if (sig == SIGUSR1)
{
++got_usr1;
sigusr1_sp = &foo;
sigemptyset (&mask);
sigaddset (&mask, SIGUSR2);
sigprocmask (SIG_BLOCK, &mask, NULL);
kill (getpid (), SIGUSR2); /* pend SIGUSR2 */
signal (SIGUSR1, SIG_IGN);
if ((ret = unw_getcontext (&uc)) < 0)
panic ("unw_getcontext() failed: ret=%d\n", ret);
if ((ret = unw_init_local (&c, &uc)) < 0)
panic ("unw_init_local() failed: ret=%d\n", ret);
if ((ret = unw_step (&c)) < 0) /* step to signal trampoline */
panic ("unw_step(1) failed: ret=%d\n", ret);
if ((ret = unw_step (&c)) < 0) /* step to kill() */
panic ("unw_step(2) failed: ret=%d\n", ret);
if ((ret = unw_get_reg (&c, UNW_REG_IP, &ip)) < 0)
panic ("unw_get_reg(IP) failed: ret=%d\n", ret);
if (verbose)
printf ("resuming at 0x%lx, with SIGUSR2 pending\n",
(unsigned long) ip);
unw_resume (&c);
}
else if (sig == SIGUSR2)
{
++got_usr2;
if (got_usr1)
{
if (verbose)
printf ("OK: stack still at %p\n", &foo);
}
signal (SIGUSR2, SIG_IGN);
}
else
panic ("Got unexpected signal %d\n", sig);
}
int
main (int argc, char **argv UNUSED)
{
struct sigaction sa;
float d = 1.0;
int n = 0;
if (argc > 1)
verbose = 1;
memset (&sa, 0, sizeof(sa));
#ifdef TEST_WITH_SIGINFO
sa.sa_sigaction = handler;
sa.sa_flags = SA_SIGINFO;
#else
sa.sa_handler = handler;
#endif
if (sigaction (SIGUSR1, &sa, NULL) != 0 ||
sigaction (SIGUSR2, &sa, NULL) != 0)
{
fprintf (stderr, "sigaction() failed: %s\n", strerror (errno));
return -1;
}
/* Use the FPU a bit; otherwise we get spurious errors should the
signal handler need to use the FPU for any reason. This seems to
happen on x86-64. */
while (d > 0.0)
{
d /= 2.0;
++n;
}
if (n > 9999)
return -1; /* can't happen, but don't tell the compiler... */
if (verbose)
printf ("sending SIGUSR1\n");
kill (getpid (), SIGUSR1);
if (!got_usr2)
panic ("failed to get SIGUSR2\n");
if (nerrors)
{
fprintf (stderr, "FAILURE: detected %d errors\n", nerrors);
exit (-1);
}
if (verbose)
printf ("SUCCESS\n");
return 0;
}

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);
}

View File

@@ -0,0 +1,413 @@
/**
* @file tests/Gtest-trace.c
*
* Exercise the unw_backtrace*() functions and ensire they produce expected
* results.
*/
/*
* Copyright (C) 2010, 2011 by FERMI NATIONAL ACCELERATOR LABORATORY
* 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.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "compiler.h"
#include <errno.h>
#if HAVE_EXECINFO_H
# include <execinfo.h>
#else
extern int backtrace (void **, int);
#endif
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <unistd.h>
#include <libunwind.h>
#include "ident.h"
#define panic(...) \
{ fprintf (stderr, __VA_ARGS__); exit (-1); }
#define SIG_STACK_SIZE 0x100000
int verbose; /**< enable verbose mode? */
int repeat_count = 9; /**< number of times to exercise the signal handler */
int num_errors = 0; /**< cumulatiove error count */
/* These variables are global because they
* can cause the signal stack to overflow */
enum test_id {
TEST_UNW_STEP,
TEST_BACKTRACE,
TEST_UNW_BACKTRACE,
TEST_UNW_BACKTRACE2
};
enum { MAX_BACKTRACE_SIZE = 128 };
struct {
char const *name;
void *addresses[MAX_BACKTRACE_SIZE];
} trace[] = {
{ "unw_step", {0} },
{ "backtrace", {0} },
{ "unw_backtrace", {0} },
{ "unw_backtrace2", {0} }
};
unw_cursor_t cursor;
unw_context_t uc;
/**
* Dump a backtrace (in verbose mode).
*/
static void
dump_backtrace(int bt_count, enum test_id id)
{
if (verbose)
{
printf ("\t%s() [depth is %d]:\n", trace[id].name, bt_count);
for (int i = 0; i < bt_count; ++i)
printf ("\t #%-3d ip=%p\n", i, trace[id].addresses[i]);
}
}
/**
* Compare a backtrace from a backtrace call with a backtrace from unw_step().
*/
static void
compare_backtraces(int step_count, int backtrace_count, enum test_id id)
{
if (step_count != backtrace_count)
{
printf ("FAILURE: unw_step() loop and %s() depths differ: %d vs. %d\n",
trace[id].name,
step_count,
backtrace_count);
++num_errors;
}
else
{
for (int i = 1; i < step_count; ++i)
{
if (labs (trace[TEST_UNW_STEP].addresses[i] - trace[id].addresses[i]) > 1)
{
printf ("FAILURE: unw_step() loop and %s() addresses differ at %d: %p vs. %p\n",
trace[id].name, i,
trace[TEST_UNW_STEP].addresses[i],
trace[id].addresses[i]);
++num_errors;
}
}
}
}
/**
* Exercises `unw_backtrace()`. Compares the result of a raw `unw_step()` loop
* with the result of calling the system-supplied `backtrace()` (if any) with the
* result of the `unw_backtrace()` call, which may have taken the "fast path"
* (using cached data).
*
* If there is no system-supplied `backtrace()` call its just an alias for
* `unw_backtrace()` so the result better match.
*
* Also exercises `unw_backtrace2()` using the same context, since the code
* differs from `unw_backtrace()`.
*/
static void
do_backtrace (void)
{
unw_word_t ip;
int ret = -UNW_ENOINFO;
int unw_step_depth = 0;
int backtrace_depth = 0;
int unw_backtrace_depth = 0;
if (verbose)
{
printf ("\nBacktrace with implicit context:\n");
}
/**
* Perform a naked `unw_step()` loop on the current context and save the IPs.
*/
ret = unw_getcontext (&uc);
if (ret != 0)
panic ("unw_getcontext failed\n");
if (unw_init_local (&cursor, &uc) < 0)
panic ("unw_init_local failed!\n");
do
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
trace[TEST_UNW_STEP].addresses[unw_step_depth++] = (void *) ip;
}
while ((ret = unw_step (&cursor)) > 0 && unw_step_depth < MAX_BACKTRACE_SIZE);
if (ret < 0)
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
printf ("FAILURE: unw_step() returned %d for ip=%#010lx\n", ret, (long) ip);
++num_errors;
}
dump_backtrace(unw_step_depth, TEST_UNW_STEP);
/*
* Call the system-supplied `backtrace()` (maybe) and compare with the
* `unw_step()`results. They should be identical.
*/
backtrace_depth = backtrace (trace[TEST_BACKTRACE].addresses, MAX_BACKTRACE_SIZE);
dump_backtrace(backtrace_depth, TEST_BACKTRACE);
compare_backtraces(unw_step_depth, backtrace_depth, TEST_BACKTRACE);
/*
* Call `unw_backtrace()` and compare with the `unw_step()`results. They
* should be identical.
*/
unw_backtrace_depth = unw_backtrace (trace[TEST_UNW_BACKTRACE].addresses,
MAX_BACKTRACE_SIZE);
dump_backtrace(unw_backtrace_depth, TEST_UNW_BACKTRACE);
compare_backtraces(unw_step_depth, unw_backtrace_depth, TEST_UNW_BACKTRACE);
/*
* Call `unw_backtrace2()` on the current context and compare with the
* `unw_step()`results. They should be identical.
*/
int unw_backtrace2_depth = unw_backtrace2 (trace[TEST_UNW_BACKTRACE2].addresses,
MAX_BACKTRACE_SIZE,
&uc,
0);
dump_backtrace(unw_backtrace2_depth, TEST_UNW_BACKTRACE2);
compare_backtraces(unw_step_depth, unw_backtrace2_depth, TEST_UNW_BACKTRACE2);
}
/**
* Exercises `unw_backtrace2()` using the context passed in to a signal
* handler. Compares the result of a raw `unw_step()` loop with the result of
* the `unw_backtrace2()` call, which may have taken the "fast path" (using
* cached data).
*/
void
do_backtrace_with_context(void *context)
{
unw_word_t ip;
int ret = -UNW_ENOINFO;
int unw_step_depth = 0;
int unw_backtrace2_depth;
if (verbose)
{
printf ("\nbacktrace with explicit context:\n");
}
/**
* Perform a naked `unw_step()` loop on the passed context and save the IPs.
*/
if (unw_init_local2 (&cursor, (unw_context_t*)context, UNW_INIT_SIGNAL_FRAME) < 0)
panic ("unw_init_local2 failed!\n");
do
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
trace[TEST_UNW_STEP].addresses[unw_step_depth++] = (void *) ip;
}
while ((ret = unw_step (&cursor)) > 0 && unw_step_depth < MAX_BACKTRACE_SIZE);
if (ret < 0)
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
printf ("FAILURE: unw_step() returned %d for ip=%#010lx\n", ret, (long) ip);
++num_errors;
}
dump_backtrace(unw_step_depth, TEST_UNW_STEP);
/*
* Call `unw_backtrace2()` on the passed context and compare with the
* `unw_step()`results. They should be identical.
*/
unw_backtrace2_depth = unw_backtrace2 (trace[TEST_UNW_BACKTRACE2].addresses,
MAX_BACKTRACE_SIZE,
(unw_context_t*)context,
UNW_INIT_SIGNAL_FRAME);
dump_backtrace(unw_backtrace2_depth, TEST_UNW_BACKTRACE2);
compare_backtraces(unw_step_depth, unw_backtrace2_depth, TEST_UNW_BACKTRACE2);
}
void
foo (long val UNUSED)
{
do_backtrace ();
}
void
bar (long v)
{
int arr[v];
arr[0] = 0;
/* This is a vain attempt to use up lots of registers to force
the frame-chain info to be saved on the memory stack on ia64.
It happens to work with gcc v3.3.4 and gcc v3.4.1 but perhaps
not with any other compiler. */
foo (f (arr[0]) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + f (v))
))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
)))))))))))))))))))))))))))))))))))))))))))))))))))))));
}
void
sighandler (int signal, siginfo_t *siginfo UNUSED, void *context)
{
if (verbose)
{
int sp;
ucontext_t *ctxt UNUSED = context;
printf ("sighandler: got signal %d, sp=%p", signal, (void *)&sp);
#if UNW_TARGET_IA64
# if defined(__linux__)
printf (" @ %#010lx", ctxt->uc_mcontext.sc_ip);
# else
{
uint16_t reason;
uint64_t ip;
__uc_get_reason (ctxt, &reason);
__uc_get_ip (ctxt, &ip);
printf (" @ %#010lx (reason=%d)", ip, reason);
}
# endif
#elif UNW_TARGET_X86
#if defined __linux__
printf (" @ %#010lx", (unsigned long) ctxt->uc_mcontext.gregs[REG_EIP]);
#elif defined __FreeBSD__
printf (" @ %#010lx", (unsigned long) ctxt->uc_mcontext.mc_eip);
#endif
#elif UNW_TARGET_X86_64
#if defined __linux__ || defined __sun
printf (" @ %#010lx", (unsigned long) ctxt->uc_mcontext.gregs[REG_RIP]);
#elif defined __FreeBSD__
printf (" @ %#010lx", (unsigned long) ctxt->uc_mcontext.mc_rip);
#endif
#elif defined UNW_TARGET_ARM
#if defined __linux__
printf (" @ %#010lx", (unsigned long) ctxt->uc_mcontext.arm_pc);
#elif defined __FreeBSD__
printf (" @ %#010lx", (unsigned long) ctxt->uc_mcontext.__gregs[_REG_PC]);
#endif
#endif
printf ("\n");
}
do_backtrace();
do_backtrace_with_context(context);
}
int
main (int argc, char **argv UNUSED)
{
struct sigaction act;
#ifdef HAVE_SIGALTSTACK
stack_t stk;
#endif /* HAVE_SIGALTSTACK */
verbose = (argc > 1);
if (verbose)
printf ("Normal backtrace:\n");
bar (1);
memset (&act, 0, sizeof (act));
act.sa_sigaction = sighandler;
act.sa_flags = SA_SIGINFO;
if (sigaction (SIGTERM, &act, NULL) < 0)
panic ("sigaction: %s\n", strerror (errno));
/*
* Repeatedly invoke the signal hander to make sure any global cache does not
* get corrupted.
*/
for (int i = 0; i < repeat_count; ++i)
{
if (verbose)
printf ("\nBacktrace across signal handler (iteration %d):\n", i+1);
kill (getpid (), SIGTERM);
}
#ifdef HAVE_SIGALTSTACK
if (verbose)
printf ("\nBacktrace across signal handler on alternate stack:\n");
stk.ss_sp = malloc (SIG_STACK_SIZE);
if (!stk.ss_sp)
panic ("failed to allocate %u bytes\n", SIG_STACK_SIZE);
stk.ss_size = SIG_STACK_SIZE;
stk.ss_flags = 0;
if (sigaltstack (&stk, NULL) < 0)
panic ("sigaltstack: %s\n", strerror (errno));
memset (&act, 0, sizeof (act));
act.sa_sigaction = sighandler;
act.sa_flags = SA_ONSTACK | SA_SIGINFO;
if (sigaction (SIGTERM, &act, NULL) < 0)
panic ("sigaction: %s\n", strerror (errno));
kill (getpid (), SIGTERM);
#endif /* HAVE_SIGALTSTACK */
if (num_errors > 0)
{
fprintf (stderr, "FAILURE: detected %d errors\n", num_errors);
exit (-1);
}
if (verbose)
printf ("SUCCESS.\n");
signal (SIGTERM, SIG_DFL);
#ifdef HAVE_SIGALTSTACK
stk.ss_flags = SS_DISABLE;
sigaltstack (&stk, NULL);
free (stk.ss_sp);
#endif /* HAVE_SIGALTSTACK */
return 0;
}

View File

@@ -0,0 +1,69 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <libunwind.h>
#include "compiler.h"
static int verbose;
static int nerrors;
#define panic(...) \
do { printf (__VA_ARGS__); ++nerrors; } while (0)
// Assembly routine which sets up the stack for the test then calls another one
// which clobbers the stack, and which in turn calls recover_register below
extern int64_t DW_CFA_expression_testcase(int64_t regnum, int64_t height);
// recover_register is called by the assembly routines. It returns the value of
// a register at a specified height from the inner-most frame. The return value
// is propagated back through the assembly routines to the testcase.
extern int64_t recover_register(int64_t regnum, int64_t height)
{
// Initialize cursor to current frame
int rc, i;
unw_cursor_t cursor;
unw_context_t context;
unw_getcontext(&context);
unw_init_local(&cursor, &context);
// Unwind frames until required height from inner-most frame (i.e. this one)
for (i = 0; i < height; ++i)
{
rc = unw_step(&cursor);
if (rc < 0)
panic("%s: unw_step failed on step %d with return code %d", __FUNCTION__, i, rc);
else if (rc == 0)
panic("%s: unw_step failed to reach the end of the stack", __FUNCTION__);
unw_word_t pc;
rc = unw_get_reg(&cursor, UNW_REG_IP, &pc);
if (rc < 0 || pc == 0)
panic("%s: unw_get_reg failed to locate the program counter", __FUNCTION__);
}
// We're now at the required height, extract register
uint64_t value;
if ((rc = unw_get_reg(&cursor, (unw_regnum_t) regnum, &value)) != 0)
panic("%s: unw_get_reg failed to retrieve register %lu", __FUNCTION__, regnum);
return value;
}
int
main (int argc, char **argv UNUSED)
{
if (argc > 1)
verbose = 1;
if (DW_CFA_expression_testcase(12, 1) != 0)
panic("r12 should be clobbered at height 1 (DW_CFA_expression_inner)");
if (DW_CFA_expression_testcase(12, 2) != 111222333)
panic("r12 should be restored at height 2 (DW_CFA_expression_testcase)");
if (nerrors > 0)
{
fprintf (stderr, "FAILURE: detected %d errors\n", nerrors);
exit (-1);
}
if (verbose)
printf ("SUCCESS.\n");
return 0;
}

View File

@@ -0,0 +1,5 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#if !defined(UNW_REMOTE_ONLY)
#include "Garm-test-debug-frame-bt.c"
#endif

View File

@@ -0,0 +1,5 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#if !defined(UNW_REMOTE_ONLY)
#include "Garm64-test-sve-signal.c"
#endif

View File

@@ -0,0 +1,5 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#if !defined(UNW_REMOTE_ONLY)
#include "Gia64-test-nat.c"
#endif

View File

@@ -0,0 +1,5 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#if !defined(UNW_REMOTE_ONLY)
#include "Gia64-test-rbs.c"
#endif

View File

@@ -0,0 +1,5 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#if !defined(UNW_REMOTE_ONLY)
#include "Gia64-test-readonly.c"
#endif

View File

@@ -0,0 +1,5 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#if !defined(UNW_REMOTE_ONLY)
#include "Gia64-test-stack.c"
#endif

View File

@@ -0,0 +1,5 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#if !defined(UNW_REMOTE_ONLY)
#include "Gperf-simple.c"
#endif

View File

@@ -0,0 +1,5 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#if !defined(UNW_REMOTE_ONLY)
#include "Gperf-trace.c"
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#if !defined(UNW_REMOTE_ONLY)
#include "Gtest-bt.c"
#endif

View File

@@ -0,0 +1,5 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#if !defined(UNW_REMOTE_ONLY)
#include "Gtest-concurrent.c"
#endif

View File

@@ -0,0 +1,80 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2010 stefan.demharter@gmx.net
Copyright (C) 2010 arun.sharma@google.com
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 <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <libunwind.h>
#include "compiler.h"
#define panic(args...) \
{ fprintf (stderr, args); exit (-1); }
static int verbose;
struct Test
{
public: // --- ctor/dtor ---
Test() { ++counter_; }
~Test() { -- counter_; }
Test(const Test&) { ++counter_; }
public: // --- static members ---
static int counter_;
};
int Test::counter_ = 0;
// Called by foo
extern "C" void bar()
{
Test t;
try {
Test t;
throw 5;
} catch (...) {
Test t;
if (verbose)
printf("Throwing an int\n");
throw 6;
}
}
int main(int argc, char **argv UNUSED)
{
if (argc > 1)
verbose = 1;
try {
Test t;
bar();
} catch (int) {
// Dtor of all Test-object has to be called.
if (Test::counter_ != 0)
panic("Counter non-zero\n");
return Test::counter_;
} catch (...) {
// An int was thrown - we should not get here.
panic("Int was thrown why are we here?\n");
}
exit(0);
}

View File

@@ -0,0 +1,5 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#if !defined(UNW_REMOTE_ONLY)
#include "Gtest-dyn1.c"
#endif

View File

@@ -0,0 +1,5 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#if !defined(UNW_REMOTE_ONLY)
#include "Gtest-exc.c"
#endif

View File

@@ -0,0 +1,6 @@
#include <stdio.h>
/* To prevent inlining and optimizing away */
int foo(volatile int* f) {
return *f;
}

View File

@@ -0,0 +1,70 @@
#include "libunwind.h"
#include "compiler.h"
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <assert.h>
static const int max_steps = 10;
#if defined __FreeBSD__
#define TRAMPOLINE_DEPTH 4
#else
#define TRAMPOLINE_DEPTH 2
#endif
int stepper(unw_cursor_t* c) {
int steps = 0;
int ret = 1;
while (ret) {
ret = unw_step(c);
if (!ret) {
break;
}
steps++;
if (steps > max_steps) break;
}
return steps;
}
/* Verify that we can step from both ucontext, and from getcontext()
* roughly the same. This tests that the IP from ucontext is used
* correctly (see impl of unw_init_local2) */
void handler(int num UNUSED, siginfo_t* info UNUSED, void* ucontext) {
unw_cursor_t c;
unw_context_t context;
unw_getcontext(&context);
int ret = unw_init_local2(&c, ucontext, UNW_INIT_SIGNAL_FRAME);
assert(!ret);
int ucontext_steps = stepper(&c);
ret = unw_init_local(&c, &context);
(void)ret;
assert(!ret);
int getcontext_steps = stepper(&c);
if (ucontext_steps == getcontext_steps - TRAMPOLINE_DEPTH) {
exit(0);
}
printf("unw_getcontext steps was %i, ucontext steps was %i, should be %i\n",
getcontext_steps, ucontext_steps, getcontext_steps - TRAMPOLINE_DEPTH);
exit(-1);
}
int foo(volatile int* f);
int main(){
struct sigaction a;
memset(&a, 0, sizeof(struct sigaction));
a.sa_sigaction = &handler;
a.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &a, NULL);
foo(NULL);
return 0;
}

View File

@@ -0,0 +1,5 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#if !defined(UNW_REMOTE_ONLY)
#include "Gtest-init.cxx"
#endif

View File

@@ -0,0 +1,176 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2003-2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
This file is part of libunwind.
Copyright (c) 2003 Hewlett-Packard Co.
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/resource.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#define panic(...) \
{ fprintf (stderr, __VA_ARGS__); exit (-1); }
void * stack_start;
long page_size;
#define STEPS 5
#define STACK_SLICE (sizeof(unw_cursor_t) + sizeof(unw_context_t))
void do_backtrace (void)
{
/*
We make the assumption that we are able to rewind far enough
(steps > 5) before touching the forbidden region in the stack,
at which point the unwinding should stop gracefully.
*/
mprotect(stack_start, page_size, PROT_NONE);
unw_cursor_t cursor;
unw_word_t ip, sp;
unw_context_t uc;
int ret;
int steps = 0;
unw_getcontext (&uc);
if (unw_init_local (&cursor, &uc) < 0)
panic ("unw_init_local failed!\n");
do
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
unw_get_reg (&cursor, UNW_REG_SP, &sp);
ret = unw_step (&cursor);
printf("ip=%p, sp=%p -> %d\n", (void *)ip, (void *)sp, ret);
if (ret < 0)
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
}
steps ++;
}
while (ret > 0);
if (steps < STEPS)
{
printf("not enough steps: %d, need %d\n", steps, STEPS);
exit(-1);
}
printf("success, steps: %d\n", steps);
mprotect(stack_start, page_size, PROT_READ|PROT_WRITE);
}
void NOINLINE consume_and_run (int depth)
{
unw_cursor_t cursor;
unw_context_t uc;
char string[64];
sprintf (string, "hello %p %p\n", (void *)&cursor, (void *)&uc);
if (depth == 0) {
do_backtrace();
} else {
consume_and_run(depth - 1);
}
}
static int NOINLINE is_stack_downward (int *val)
{
int start;
return val > &start;
}
int
main (int argc UNUSED, char **argv UNUSED)
{
int start, count;
unw_context_t uc;
unw_cursor_t cursor;
/*
* We need to make the frame at least the size protected by
* the mprotect call so we are not forbidding access to
* unrelated regions.
* mprotect consume_and_run stack area.
* Check whether stack grows downward or upward.
*/
page_size = sysconf(_SC_PAGESIZE);
if (is_stack_downward(&start))
{
stack_start = (void *)(((uintptr_t)&start & ~(page_size - 1)) - page_size);
count = (uintptr_t)&start - (uintptr_t)stack_start;
}
else
{
stack_start = (void *)(((uintptr_t)&start & ~(page_size - 1)) + page_size);
count = (uintptr_t)stack_start - (uintptr_t)&start;
}
count = count / STACK_SLICE + STEPS;
// Initialize pipe mem validate check, opens file descriptors
unw_getcontext(&uc);
if (unw_init_local (&cursor, &uc) < 0)
panic ("unw_init_local failed!\n");
int i;
for (i = 3; i < 10; i++)
{
pid_t childpid = fork();
if (!childpid)
{
/* Close fds and make sure we still work */
close(i);
}
int status;
if (childpid)
{
wait(&status);
if (WIFEXITED(status))
return WEXITSTATUS(status);
else
return -1;
}
else
{
consume_and_run (count);
return 0;
}
}
return 0;
}

View File

@@ -0,0 +1,133 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2011 Google, Inc
Contributed by Paul Pluzhnikov <ppluzhnikov@google.com>
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>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <pthread.h>
#define panic(args...) \
{ fprintf (stderr, args); exit (-1); }
int num_mallocs;
int num_callocs;
int in_unwind;
void *
calloc(size_t n, size_t s)
{
static void * (*func)(size_t, size_t);
#ifdef __GLIBC__
/* In glibc, dlsym() calls calloc. Calling dlsym(RTLD_NEXT, "calloc") here
causes infinite recursion. Instead, we simply use it by its other
name. */
extern void *__libc_calloc(size_t, size_t);
if (!func)
func = &__libc_calloc;
#else
if(!func)
func = dlsym(RTLD_NEXT, "calloc");
#endif
if (in_unwind) {
num_callocs++;
}
return func(n, s);
}
void *
malloc(size_t s)
{
static void * (*func)(size_t);
if(!func)
func = dlsym(RTLD_NEXT, "malloc");
if (in_unwind) {
num_mallocs++;
}
return func(s);
}
static void
do_backtrace (void)
{
const int num_levels = 100;
void *pc[num_levels];
in_unwind = 1;
unw_backtrace(pc, num_levels);
in_unwind = 0;
}
void
foo3 (void)
{
do_backtrace ();
}
void
foo2 (void)
{
foo3 ();
}
void
foo1 (void)
{
foo2 ();
}
int
main (void)
{
int i, num_errors;
/* Create (and leak) 100 TSDs, then call backtrace()
and check that it doesn't call malloc()/calloc(). */
for (i = 0; i < 100; ++i) {
pthread_key_t key;
if (pthread_key_create (&key, NULL))
panic ("FAILURE: unable to create key %d\n", i);
}
/* Call backtrace right after thread creation,
* where we are sure that we're not inside malloc */
do_backtrace();
num_mallocs = num_callocs = 0;
foo1 ();
num_errors = num_mallocs + num_callocs;
if (num_errors > 0)
{
fprintf (stderr,
"FAILURE: detected %d error%s (malloc: %d, calloc: %d)\n",
num_errors, num_errors > 1 ? "s" : "",
num_mallocs, num_callocs);
exit (-1);
}
return 0;
}

View File

@@ -0,0 +1,5 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#if !defined(UNW_REMOTE_ONLY)
#include "Gtest-nomalloc.c"
#endif

View File

@@ -0,0 +1,5 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#if !defined(UNW_REMOTE_ONLY)
#include "Gtest-resume-sig-rt.c"
#endif

View File

@@ -0,0 +1,5 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#if !defined(UNW_REMOTE_ONLY)
#include "Gtest-resume-sig.c"
#endif

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

@@ -0,0 +1,5 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#if !defined(UNW_REMOTE_ONLY)
#include "Gtest-trace.c"
#endif

View File

@@ -0,0 +1,84 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#include "compiler.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int ok;
int verbose;
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 3)
void a (int, ...) __attribute__((optimize(0)));
void b (void) __attribute__((optimize(0)));
void c (void) __attribute__((optimize(0)));
#endif
void NOINLINE
b (void)
{
void *v[20];
int i, n;
n = unw_backtrace(v, 20);
/* Check that the number of addresses given by unw_backtrace() looks
* reasonable. If the compiler inlined everything, then this check will also
* break. */
if (n >= 7)
ok = 1;
if (verbose)
for (i = 0; i < n; ++i)
printf ("[%d] %p\n", i, v[i]);
}
void NOINLINE
c (void)
{
b ();
}
void NOINLINE
a (int d, ...)
{
switch (d)
{
case 5:
a (4, 2,4);
break;
case 4:
a (3, 1,3,5);
break;
case 3:
a (2, 11, 13, 17, 23);
break;
case 2:
a (1);
break;
case 1:
c ();
}
}
int
main (int argc, char **argv UNUSED)
{
if (argc > 1)
verbose = 1;
a (5, 3, 4, 5, 6);
if (!ok)
{
fprintf (stderr, "FAILURE: expected deeper backtrace.\n");
return 1;
}
if (verbose)
printf ("SUCCESS.\n");
return 0;
}

View File

@@ -0,0 +1,5 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#if !defined(UNW_REMOTE_ONLY)
#include "Gx64-test-dwarf-expressions.c"
#endif

View File

@@ -0,0 +1,380 @@
##
## tests/Makefile.am
##
## Process this file with **automake** to produce a Makefile.in
##
#
# 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.
#
# Test binaries and scripts get installed here
testdir = ${pkglibexecdir}
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) -g -fno-optimize-sibling-calls
LOG_DRIVER = $(SHELL) $(UNW_TESTDRIVER)
EXTRA_DIST = run-ia64-test-dyn1 run-ptrace-mapper run-ptrace-misc \
run-coredump-unwind \
run-coredump-unwind-mdi check-namespace.sh.in \
test-runner.in \
Gtest-nomalloc.c
CLEANFILES = test-runner
MAINTAINERCLEANFILES = Makefile.in
noinst_PROGRAMS_arch =
noinst_PROGRAMS_cdep =
noinst_PROGRAMS_common =
check_PROGRAMS_arch =
check_PROGRAMS_cdep =
check_PROGRAMS_common = test-proc-info test-static-link \
test-strerror
check_SCRIPTS_arch =
check_SCRIPTS_cdep =
check_SCRIPTS_common = check-namespace.sh
XFAIL_TESTS =
if REMOTE_ONLY
perf:
else
LIBUNWIND_local = $(top_builddir)/src/libunwind.la
if ARCH_IA64
noinst_PROGRAMS_arch += ia64-test-dyn1
check_SCRIPTS_arch += run-ia64-test-dyn1
check_PROGRAMS_arch += Gia64-test-stack Lia64-test-stack \
Gia64-test-nat Lia64-test-nat \
Gia64-test-rbs Lia64-test-rbs \
Gia64-test-readonly Lia64-test-readonly \
ia64-test-setjmp ia64-test-sig
else #!ARCH_IA64
if ARCH_PPC64
if USE_ALTIVEC
noinst_PROGRAMS_arch += ppc64-test-altivec
endif #USE_ALTIVEC
check_PROGRAMS_arch += ppc64-test-plt
else #!ARCH_PPC64
if ARCH_X86_64
check_PROGRAMS_arch += Gx64-test-dwarf-expressions Lx64-test-dwarf-expressions x64-unwind-badjmp-signal-frame
endif #ARCH X86_64
endif #!ARCH_PPC64
endif #!ARCH_IA64
if ARCH_ARM
check_PROGRAMS_arch += Garm-test-debug-frame-bt Larm-test-debug-frame-bt
endif
if ARCH_AARCH64
check_PROGRAMS_arch += Garm64-test-sve-signal Larm64-test-sve-signal \
aarch64-test-plt aarch64-test-frame-record
endif
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 \
test-mem test-reg-state Ltest-varargs \
Ltest-nomalloc Ltest-nocalloc Lrs-race
noinst_PROGRAMS_cdep += forker Gperf-simple Lperf-simple \
Gperf-trace Lperf-trace
# unw_init_local2() is not implemented on ia64
if !ARCH_IA64
check_PROGRAMS_cdep += Ltest-init-local-signal
endif
if ARCH_PPC32
# https://github.com/libunwind/libunwind/issues/560
XFAIL_TESTS += Ltest-init-local-signal
# https://github.com/libunwind/libunwind/issues/670
XFAIL_TESTS += test-async-sig
endif #ARCH_PPC32
# Tests that exercise unw_resume, which is only unsupported on some targets
if ENABLE_UNW_RESUME_TESTS
check_PROGRAMS_cdep += Gtest-exc Ltest-exc \
Gtest-resume-sig Ltest-resume-sig \
Gtest-resume-sig-rt Ltest-resume-sig-rt
if ARCH_PPC32
# https://github.com/libunwind/libunwind/issues/559
XFAIL_TESTS += Gtest-exc Ltest-exc
endif #ARCH_PPC32
endif
if BUILD_PTRACE
check_SCRIPTS_cdep += run-ptrace-mapper run-ptrace-misc
check_PROGRAMS_cdep += test-ptrace
noinst_PROGRAMS_cdep += mapper test-ptrace-misc
if ARCH_X86
# https://github.com/libunwind/libunwind/issues/392
XFAIL_TESTS += test-ptrace
endif
endif
if BUILD_SETJMP
check_PROGRAMS_cdep += test-setjmp
endif
if SUPPORT_CXX_EXCEPTIONS
check_PROGRAMS_cdep += Ltest-cxx-exceptions
endif
if ARCH_PPC32
# https://github.com/libunwind/libunwind/issues/561
XFAIL_TESTS += Ltest-cxx-exceptions
endif
if ARCH_IA64
check_PROGRAMS_cdep += Gtest-dyn1 Ltest-dyn1
endif
if OS_LINUX
if BUILD_COREDUMP
check_SCRIPTS_cdep += run-coredump-unwind
noinst_PROGRAMS_cdep += crasher test-coredump-unwind
if HAVE_LZMA
check_SCRIPTS_cdep += run-coredump-unwind-mdi
endif # HAVE_LZMA
endif # BUILD_COREDUMP
endif # OS_LINUX
perf: perf-startup Gperf-simple Lperf-simple Lperf-trace
@echo "########## Basic performance of generic libunwind:"
@./Gperf-simple
@echo "########## Basic performance of local-only libunwind:"
@./Lperf-simple
@echo "########## Performance of fast unwind:"
@./Lperf-trace
@echo "########## Startup overhead:"
@$(srcdir)/perf-startup @arch@
endif
check_PROGRAMS = $(check_PROGRAMS_common) $(check_PROGRAMS_cdep) \
$(check_PROGRAMS_arch)
check_SCRIPTS = $(check_SCRIPTS_common) $(check_SCRIPTS_cdep) \
$(check_SCRIPTS_arch)
TESTS = $(check_PROGRAMS)
if !CROSS_BUILD
TESTS += $(check_SCRIPTS)
endif
# Use if arch defines but does not support PTRACE_SINGLESTEP
# ptrace request used in the tests.
XFAIL_TESTS_PTRACE_SINGLESTEP = run-ptrace-mapper run-ptrace-misc
if ARCH_MIPS
XFAIL_TESTS += $(XFAIL_TESTS_PTRACE_SINGLESTEP)
endif
if ARCH_RISCV
XFAIL_TESTS += $(XFAIL_TESTS_PTRACE_SINGLESTEP)
endif
if ARCH_ARM
# ARM Linux kernel >=2.6.39 removed PTRACE_SINGLESTEP emulation
XFAIL_TESTS += $(XFAIL_TESTS_PTRACE_SINGLESTEP)
endif
if ARCH_LOONGARCH64
XFAIL_TESTS += $(XFAIL_TESTS_PTRACE_SINGLESTEP)
endif
# This is meant for multilib binaries, -m32.
# ptrace gives EBADREG when testing,
# but generally everything else works.
if XFAIL_PTRACE_TEST
XFAIL_TESTS += run-ptrace-mapper
endif
noinst_PROGRAMS = $(noinst_PROGRAMS_common) $(noinst_PROGRAMS_cdep) \
$(noinst_PROGRAMS_arch)
noinst_HEADERS = ident.h unw_test.h
do_test_subst = sed -e 's,[@]TESTS[@],$(TESTS),g' \
-e 's,[@]XFAIL_TESTS[@],$(XFAIL_TESTS),g' \
-e 's,[@]LIBDIR[@],$(libdir),g' \
-e 's,[@]ARCH[@],$(arch),g'
test-runner: test-runner.in Makefile
$(AM_V_GEN)$(do_test_subst) <$(srcdir)/test-runner.in >$@
@chmod +x test-runner
# Make the test binaries installable
test_PROGRAMS = ${check_PROGRAMS} ${noinst_PROGRAMS}
test_SCRIPTS = ${check_SCRIPTS} test-runner
Lia64_test_readonly_SOURCES = Lia64-test-readonly.c ia64-test-readonly-asm.S
Gia64_test_readonly_SOURCES = Gia64-test-readonly.c ia64-test-readonly-asm.S
Lia64_test_stack_SOURCES = Lia64-test-stack.c ia64-test-stack-asm.S \
ia64-test-stack.h
Gia64_test_stack_SOURCES = Gia64-test-stack.c ia64-test-stack-asm.S \
ia64-test-stack.h
Lia64_test_rbs_SOURCES = Lia64-test-rbs.c ia64-test-rbs-asm.S ia64-test-rbs.h
Gia64_test_rbs_SOURCES = Gia64-test-rbs.c ia64-test-rbs-asm.S ia64-test-rbs.h
Lia64_test_nat_SOURCES = Lia64-test-nat.c ia64-test-nat-asm.S
Gia64_test_nat_SOURCES = Gia64-test-nat.c ia64-test-nat-asm.S
ia64_test_dyn1_SOURCES = ia64-test-dyn1.c ia64-dyn-asm.S flush-cache.S \
flush-cache.h
ppc64_test_altivec_SOURCES = ppc64-test-altivec.c ppc64-test-altivec-utils.c
ppc64_test_plt_SOURCES = ppc64-test-plt.c
Gx64_test_dwarf_expressions_SOURCES = Gx64-test-dwarf-expressions.c \
x64-test-dwarf-expressions.S
Lx64_test_dwarf_expressions_SOURCES = Lx64-test-dwarf-expressions.c \
x64-test-dwarf-expressions.S
Garm_test_debug_frame_bt_SOURCES = Garm-test-debug-frame-bt.c ident.c
Larm_test_debug_frame_bt_SOURCES = Larm-test-debug-frame-bt.c ident.c
Garm64_test_sve_signal_SOURCES = Garm64-test-sve-signal.c
Larm64_test_sve_signal_SOURCES = Larm64-test-sve-signal.c
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 = $(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
Gtest_init_SOURCES = Gtest-init.cxx
Ltest_init_SOURCES = Ltest-init.cxx
Ltest_cxx_exceptions_SOURCES = Ltest-cxx-exceptions.cxx
Ltest_init_local_signal_SOURCES = Ltest-init-local-signal.c Ltest-init-local-signal-lib.c
x64_unwind_badjmp_signal_frame_SOURCES = x64-unwind-badjmp-signal-frame.c
Gtest_dyn1_SOURCES = Gtest-dyn1.c flush-cache.S flush-cache.h
Ltest_dyn1_SOURCES = Ltest-dyn1.c flush-cache.S flush-cache.h
test_static_link_SOURCES = test-static-link-loc.c test-static-link-gen.c
test_static_link_LDFLAGS = -static
forker_LDFLAGS = -static
Gtest_bt_SOURCES = Gtest-bt.c ident.c
Ltest_bt_SOURCES = Ltest-bt.c ident.c
test_ptrace_misc_SOURCES = test-ptrace-misc.c ident.c
Ltest_nomalloc_SOURCES = Ltest-nomalloc.c
Ltest_nocalloc_SOURCES = Ltest-nocalloc.c
Gtest_trace_SOURCES = Gtest-trace.c ident.c
Ltest_trace_SOURCES = Ltest-trace.c ident.c
Ltest_mem_validate_SOURCES = Ltest-mem-validate.c
LIBUNWIND = $(top_builddir)/src/libunwind-$(arch).la
LIBUNWIND_ptrace = $(top_builddir)/src/libunwind-ptrace.la
LIBUNWIND_coredump = $(top_builddir)/src/libunwind-coredump.la
if USE_ELF32
LIBUNWIND_ELF = $(top_builddir)/src/libunwind-elf32.la
endif
if USE_ELF64
LIBUNWIND_ELF = $(top_builddir)/src/libunwind-elf64.la
endif
if USE_ELFXX
LIBUNWIND_ELF = $(top_builddir)/src/libunwind-elfxx.la
endif
LIBUNWIND_setjmp = $(top_builddir)/src/libunwind-setjmp.la \
$(LIBUNWIND_ELF) $(LIBUNWIND)
test_async_sig_LDADD = $(LIBUNWIND_local) $(PTHREADS_LIB)
test_flush_cache_LDADD = $(LIBUNWIND_local)
test_init_remote_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
test_mem_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
test_reg_state_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
test_ptrace_LDADD = $(LIBUNWIND_ptrace) $(LIBUNWIND)
test_proc_info_LDADD = $(LIBUNWIND)
test_static_link_LDADD = $(LIBUNWIND)
test_strerror_LDADD = $(LIBUNWIND)
Lrs_race_LDADD = $(LIBUNWIND_local) $(PTHREADS_LIB)
Ltest_varargs_LDADD = $(LIBUNWIND_local)
Ltest_init_local_signal_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
Gtest_bt_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
Gtest_concurrent_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) $(PTHREADS_LIB)
x64_unwind_badjmp_signal_frame_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
Gtest_dyn1_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
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)
Ltest_bt_LDADD = $(LIBUNWIND_local)
Ltest_concurrent_LDADD = $(LIBUNWIND_local) $(PTHREADS_LIB)
Ltest_cxx_exceptions_LDADD = $(LIBUNWIND_local)
Ltest_dyn1_LDADD = $(LIBUNWIND_local)
Ltest_exc_LDADD = $(LIBUNWIND_local)
Ltest_init_LDADD = $(LIBUNWIND_local)
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)
Ltest_mem_validate_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
test_setjmp_LDADD = $(LIBUNWIND_setjmp)
ia64_test_setjmp_LDADD = $(LIBUNWIND_setjmp)
if BUILD_COREDUMP
test_coredump_unwind_LDADD = $(LIBUNWIND_coredump) $(LIBUNWIND)
endif
Gia64_test_nat_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
Gia64_test_stack_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
Gia64_test_rbs_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
Gia64_test_readonly_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
Lia64_test_nat_LDADD = $(LIBUNWIND_local)
Lia64_test_stack_LDADD = $(LIBUNWIND_local)
Lia64_test_rbs_LDADD = $(LIBUNWIND_local)
Lia64_test_readonly_LDADD = $(LIBUNWIND_local)
ia64_test_dyn1_LDADD = $(LIBUNWIND)
ia64_test_sig_LDADD = $(LIBUNWIND)
ppc64_test_altivec_LDADD = $(LIBUNWIND)
ppc64_test_plt_LDADD = $(LIBUNWIND)
Gx64_test_dwarf_expressions_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
Lx64_test_dwarf_expressions_LDADD = $(LIBUNWIND_local)
Garm_test_debug_frame_bt_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
Larm_test_debug_frame_bt_LDADD = $(LIBUNWIND_local)
Garm64_test_sve_signal_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
Larm64_test_sve_signal_LDADD = $(LIBUNWIND_local)
aarch64_test_plt_LDADD = $(LIBUNWIND)
aarch64_test_frame_record_LDADD = $(LIBUNWIND)

View File

@@ -0,0 +1,24 @@
Testing libunwind
=================
Testing in the build tree
-------------------------
Testing in a self-hosted build environment is as simple as `make check`
Testing staged builds
---------------------
```
$ STAGINGDIR=/tmp/gh-705
$ make install DESTDIR=$STAGINGDIR
$ LIBUNWIND=$STAGINGDIR/usr/lib/libunwind.so LIBUNWIND_GENERIC=$STAGINGDIR/usr/lib/libunwind-x86_64.so LD_LIBRARY_PATH=$STAGINGDIR/usr/lib $STAGINGDIR/usr/libexec/libunwind/test-runner
```
Testing installed builds
------------------------
```
$ /usr/libexec/libunwind/test-runner
```

View File

@@ -0,0 +1,390 @@
/*
* Unittest AArch64 get_frame_state function by inspecting output at
* different points in example address spaces (from python2.7)
*/
#include "dwarf.h"
#include "libunwind_i.h"
int unw_is_signal_frame (unw_cursor_t *cursor) { return 0; }
int dwarf_step (struct dwarf_cursor *c) { return 0; }
#include "aarch64/Gstep.c"
static int procedure_size;
/* Mock access_mem implementation */
static int
access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
void *arg)
{
if (write != 0)
return -1;
const size_t mem_size = procedure_size * sizeof(uint32_t);
const void *mem_start = arg;
const void *mem_end = (const char*) arg + mem_size;
const unw_word_t *paddr = (const unw_word_t*) addr;
if ((void*) paddr < mem_start || (void*) paddr > mem_end)
{
return -1;
}
*val = *paddr;
return 0;
}
//! Stub implementation of get_proc_name - returns offset to start of procedure
static int
get_proc_name (unw_addr_space_t as, unw_word_t ip, char *buf, size_t len, unw_word_t *offp,
void *arg)
{
*offp = ip - (unw_word_t) arg;
return 0;
}
int
main ()
{
struct unw_addr_space mock_address_space;
mock_address_space.acc.access_mem = &access_mem;
mock_address_space.acc.get_proc_name = &get_proc_name;
frame_state_t fs;
unw_cursor_t cursor;
struct cursor *c = (struct cursor *) &cursor;
c->dwarf.as = &mock_address_space;
/* STP_MOV_start procedure */
{
int IpStp = 0;
int IpMov = 1;
int IpLdp1 = 4;
int IpLdp2 = 7;
// 0000000000418254 <copy_absolute>:
unsigned int instructions[9] = {
0xa9be7bfd, // stp x29, x30, [sp,#-32]! <= FP+LR stored
0x910003fd, // mov x29, sp <= FP updated
0xa90153f3, // stp x19, x20, [sp,#16]
// some instructions skipped
0xa94153f3, // ldp x19, x20, [sp,#16]
0xa8c27bfd, // ldp x29, x30, [sp],#32 <= FP+LR retrieved
0x17ffff33, // b 417f50 <strcpy@plt>
// some instructions skipped
0xa94153f3, // ldp x19, x20, [sp,#16]
0xa8c27bfd, // ldp x29, x30, [sp],#32 <= FP+LR retrieved
0x1400de12, // b 44fb08 <joinpath>
};
procedure_size = 9;
c->dwarf.as_arg = &instructions;
/* IP is pointing to instruction that stores FP and LR */
c->dwarf.ip = (unw_word_t) (instructions+IpStp);
fs = get_frame_state(&cursor);
if (fs.loc != NONE || fs.offset != 0) return -1;
/* IP is pointing to instruction that updates the FP */
c->dwarf.ip = (unw_word_t) (instructions+IpMov);
fs = get_frame_state(&cursor);
if (fs.loc != AT_SP_OFFSET || fs.offset != 0) return -1;
/* IP is pointing to instruction after FP was updated */
c->dwarf.ip = (unw_word_t) (instructions+IpMov+1);
fs = get_frame_state(&cursor);
if (fs.loc != AT_FP || fs.offset != 0) return -1;
/* IP is pointing to first instruction that retrieves FP and LR */
c->dwarf.ip = (unw_word_t) (instructions+IpLdp1);
fs = get_frame_state(&cursor);
if (fs.loc != AT_FP || fs.offset != 0) return -1;
/* IP is pointing to instruction after first retrieval of FP and LR */
c->dwarf.ip = (unw_word_t) (instructions+IpLdp1+1);
fs = get_frame_state(&cursor);
if (fs.loc != NONE || fs.offset != 0) return -1;
/* IP is pointing to second instruction that retrieves FP and LR */
c->dwarf.ip = (unw_word_t) (instructions+IpLdp2);
fs = get_frame_state(&cursor);
if (fs.loc != NONE || fs.offset != 0) return -1;
/* IP is pointing to instruction after second retrieval of FP and LR */
c->dwarf.ip = (unw_word_t) (instructions+IpLdp2+1);
fs = get_frame_state(&cursor);
if (fs.loc != NONE || fs.offset != 0) return -1;
}
/* STP_start_MOV_later procedure */
{
int IpStp = 0;
int IpMov = 2;
int IpLdp = 5;
// 00000000004181b4 <get_time>:
unsigned int instructions[7] = {
0xa9bd7bfd, // stp x29, x30, [sp,#-48]! <= FP+LR stored
0xf0001740, // adrp x0, 703000
0x910003fd, // mov x29, sp <= FP updated
0xf943cc00, // ldr x0, [x0,#1944]
// some instructions skipped
0xf9400bf3, // ldr x19, [sp,#16]
0xa8c37bfd, // ldp x29, x30, [sp],#48 <= FP+LR retrieved
0xd65f03c0, // ret
};
procedure_size = 7;
c->dwarf.as_arg = &instructions;
/* IP is pointing to instruction that stores FP and LR */
c->dwarf.ip = (unw_word_t) (instructions+IpStp);
fs = get_frame_state(&cursor);
if (fs.loc != NONE || fs.offset != 0) return -1;
/* IP is pointing to instruction after FP and LR are stored */
c->dwarf.ip = (unw_word_t) (instructions+IpStp+1);
fs = get_frame_state(&cursor);
if (fs.loc != AT_SP_OFFSET || fs.offset != 0) return -1;
/* IP is pointing to instruction that updates the FP */
c->dwarf.ip = (unw_word_t) (instructions+IpMov);
fs = get_frame_state(&cursor);
if (fs.loc != AT_SP_OFFSET || fs.offset != 0) return -1;
/* IP is pointing to instruction after FP was updated */
c->dwarf.ip = (unw_word_t) (instructions+IpMov+1);
fs = get_frame_state(&cursor);
if (fs.loc != AT_FP || fs.offset != 0) return -1;
/* IP is pointing to instruction that retrieves FP and LR */
c->dwarf.ip = (unw_word_t) (instructions+IpLdp);
fs = get_frame_state(&cursor);
if (fs.loc != AT_FP || fs.offset != 0) return -1;
/* IP is pointing to instruction after retrieval of FP and LR */
c->dwarf.ip = (unw_word_t) (instructions+IpLdp+1);
fs = get_frame_state(&cursor);
if (fs.loc != NONE || fs.offset != 0) return -1;
}
/* STP_MOV_later procedure */
{
int IpStp = 3;
int IpMov = 4;
int IpLdp = 7;
// 0000000000418370 <indenterror>:
uint32_t instructions[9] = {
0xb941fc01, // ldr w1, [x0,#508]
// some instructions skipped
0xd65f03c0, // ret
// some instructions skipped
0x34ffffa4, // cbz w4, 41838c <indenterror+0x1c>
0xa9be7bfd, // stp x29, x30, [sp,#-32] <= FP+LR stored
0x910003fd, // mov x29, sp <= FP updated
0xf9000bf3, // str x19, [sp,#16]
0xf9400bf3, // ldr x19, [sp,#16]
0xa8c27bfd, // ldp x29, x30, [sp],#32 <= FP+LR retrieved
0xd65f03c0, // ret
};
procedure_size = 9;
c->dwarf.as_arg = &instructions;
/* IP is pointing to start of procedure */
c->dwarf.ip = (unw_word_t) (instructions);
fs = get_frame_state(&cursor);
if (fs.loc != NONE || fs.offset != 0) return -1;
/* IP is pointing to instruction that stores FP and LR */
c->dwarf.ip = (unw_word_t) (instructions+IpStp);
fs = get_frame_state(&cursor);
if (fs.loc != NONE || fs.offset != 0) return -1;
/* IP is pointing to instruction that updates the FP */
c->dwarf.ip = (unw_word_t) (instructions+IpMov);
fs = get_frame_state(&cursor);
if (fs.loc != AT_SP_OFFSET || fs.offset != 0) return -1;
/* IP is pointing to instruction after FP was updated */
c->dwarf.ip = (unw_word_t) (instructions+IpMov+1);
fs = get_frame_state(&cursor);
if (fs.loc != AT_FP || fs.offset != 0) return -1;
/* IP is pointing to instruction that retrieves FP and LR */
c->dwarf.ip = (unw_word_t) (instructions+IpLdp);
fs = get_frame_state(&cursor);
if (fs.loc != AT_FP || fs.offset != 0) return -1;
/* IP is pointing to instruction after FP and LR are retrieved */
c->dwarf.ip = (unw_word_t) (instructions+IpLdp+1);
fs = get_frame_state(&cursor);
if (fs.loc != NONE || fs.offset != 0) return -1;
}
/* STP_POS_OFFSET procedure */
{
int IpStp = 3;
int IpAdd = 4;
int IpLdp = 7;
// 000000000046d1d8 <PyEval_EvalCode>:
unsigned int instructions[9] = {
0xd10083ff, // sub sp, sp, #0x20
0xd2800007, // mov x7, #0x0 // #0
// some instructions skipped
0xd2800003, // mov x3, #0x0 // #0
0xa9017bfd, // stp x29, x30, [sp,#16]
0x910043fd, // add x29, sp, #0x10
0xb90003ff, // str wzr, [sp]
// some instructions skipped
0x910003bf, // mov sp, x29
0xa8c17bfd, // ldp x29, x30, [sp],#16
0xd65f03c0, // ret
};
procedure_size = 9;
c->dwarf.as_arg = &instructions;
/* IP is pointing to start of procedure */
c->dwarf.ip = (unw_word_t) (instructions);
fs = get_frame_state(&cursor);
if (fs.loc != NONE || fs.offset != 0) return -1;
/* IP is pointing to instruction that stores FP and LR */
c->dwarf.ip = (unw_word_t) (instructions+IpStp);
fs = get_frame_state(&cursor);
if (fs.loc != NONE || fs.offset != 0) return -1;
/* IP is pointing to instruction that updates the FP */
c->dwarf.ip = (unw_word_t) (instructions+IpAdd);
fs = get_frame_state(&cursor);
if (fs.loc != AT_SP_OFFSET || fs.offset != 16) return -1;
/* IP is pointing to instruction after FP was updated */
c->dwarf.ip = (unw_word_t) (instructions+IpAdd+1);
fs = get_frame_state(&cursor);
if (fs.loc != AT_FP || fs.offset != 0) return -1;
/* IP is pointing to instruction that retrieves FP and LR */
c->dwarf.ip = (unw_word_t) (instructions+IpLdp);
fs = get_frame_state(&cursor);
if (fs.loc != AT_FP || fs.offset != 0) return -1;
/* IP is pointing to instruction after FP and LR are retrieved */
c->dwarf.ip = (unw_word_t) (instructions+IpLdp+1);
fs = get_frame_state(&cursor);
if (fs.loc != NONE || fs.offset != 0) return -1;
}
/* STP_NEG_OFFSET procedure */
{
int IpStp = 3;
int IpAdd = 4;
int IpLdp = 7;
// Artificial example based on PyEval_EvalCode with negative offset STP
unsigned int instructions[9] = {
0xd10083ff, // sub sp, sp, #0x20
0xd2800007, // mov x7, #0x0 // #0
// some instructions skipped
0xd2800003, // mov x3, #0x0 // #0
0xa9217bfd, // stp x29, x30, [sp,#-16]
0x910043fd, // add x29, sp, #0x10
0xb90003ff, // str wzr, [sp]
// some instructions skipped
0x910003bf, // mov sp, x29
0xa8c17bfd, // ldp x29, x30, [sp],#16
0xd65f03c0, // ret
};
procedure_size = 9;
c->dwarf.as_arg = &instructions;
/* IP is pointing to start of procedure */
c->dwarf.ip = (unw_word_t) (instructions);
fs = get_frame_state(&cursor);
if (fs.loc != NONE || fs.offset != 0) return -1;
/* IP is pointing to instruction that stores FP and LR */
c->dwarf.ip = (unw_word_t) (instructions+IpStp);
fs = get_frame_state(&cursor);
if (fs.loc != NONE || fs.offset != 0) return -1;
/* IP is pointing to instruction that updates the FP */
c->dwarf.ip = (unw_word_t) (instructions+IpAdd);
fs = get_frame_state(&cursor);
if (fs.loc != AT_SP_OFFSET || fs.offset != -16) return -1;
/* IP is pointing to instruction after FP was updated */
c->dwarf.ip = (unw_word_t) (instructions+IpAdd+1);
fs = get_frame_state(&cursor);
if (fs.loc != AT_FP || fs.offset != 0) return -1;
/* IP is pointing to instruction that retrieves FP and LR */
c->dwarf.ip = (unw_word_t) (instructions+IpLdp);
fs = get_frame_state(&cursor);
if (fs.loc != AT_FP || fs.offset != 0) return -1;
/* IP is pointing to instruction after FP and LR are retrieved */
c->dwarf.ip = (unw_word_t) (instructions+IpLdp+1);
fs = get_frame_state(&cursor);
if (fs.loc != NONE || fs.offset != 0) return -1;
}
/* PLT_entry procedure */
{
// 0000000000417760 <fork@plt>:
unsigned int instructions[4] = {
0x900013d0, // adrp x16, 68f000
0xf942a211, // ldr x17, [x16,#1344]
0x91150210, // add x16, x16, #0x540
0xd61f0220, // br x17
};
procedure_size = 4;
c->dwarf.as_arg = &instructions;
/* IP is pointing to start of procedure */
c->dwarf.ip = (unw_word_t) (instructions);
fs = get_frame_state(&cursor);
if (fs.loc != NONE || fs.offset != 0) return -1;
/* IP is pointing to end of procedure */
c->dwarf.ip = (unw_word_t) (instructions+procedure_size-1);
fs = get_frame_state(&cursor);
if (fs.loc != NONE || fs.offset != 0) return -1;
}
/* no_STP_MOV procedure */
{
// 000000000041ddf0 <init_bisect>:
uint32_t instructions[9] = {
0xb0001641, // adrp x1, 6e6000 <ioctl_doc+0x550>
0x90000d00, // adrp x0, 5bd000 <_PyImport_StandardFiletab+0x1468>
0x913d4023, // add x3, x1, #0xf50
0x911b4000, // add x0, x0, #0x6d0
0x91330062, // add x2, x3, #0xcc0
0x91374061, // add x1, x3, #0xdd0
0x52807ea4, // mov w4, #0x3f5
0xd2800003, // mov x3, #0x0
0x1400bccc, // b 44d140 <Py_InitModule4_64>
};
procedure_size = 9;
c->dwarf.as_arg = &instructions;
/* IP is pointing to start of procedure */
c->dwarf.ip = (unw_word_t) (instructions);
fs = get_frame_state(&cursor);
if (fs.loc != NONE || fs.offset != 0) return -1;
/* IP is pointing to end of procedure */
c->dwarf.ip = (unw_word_t) (instructions+procedure_size-1);
fs = get_frame_state(&cursor);
if (fs.loc != NONE || fs.offset != 0) return -1;
}
return 0;
}

View File

@@ -0,0 +1,197 @@
/**
* Unittest AArch64 unw_is_plt_entry function by inspecting output at
* different points in a mock PLT address space.
*/
/*
* 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 "dwarf.h"
#include "libunwind_i.h"
int unw_is_signal_frame (unw_cursor_t *cursor) { return 0; }
int dwarf_step (struct dwarf_cursor *c) { return 0; }
enum
{
ip_guard0,
ip_adrp,
ip_ldr,
ip_add,
ip_br,
ip_guard1,
ip_program_end
};
/* Mock access_mem implementation */
static int
access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
void *arg)
{
if (write != 0)
return -1;
const size_t mem_size = ip_program_end * sizeof(uint32_t);
const void *mem_start = arg;
const void *mem_end = (const char*) arg + mem_size;
const unw_word_t *paddr = (const unw_word_t*) addr;
if ((void*) paddr < mem_start || (void*) paddr > mem_end)
{
return -1;
}
*val = *paddr;
return 0;
}
int
main ()
{
if (target_is_big_endian())
return 77;
const uint32_t plt_instructions[ip_program_end] = {
0xDEADBEEF,
0xf0000990, // adrp x16, 540000
0xf9400a11, // ldr x17, [x16,#16]
0x91004210, // add x16, x16, #0x10
0xd61f0220, // br x17
0xDEADBEEF,
};
uint32_t test_instructions[ip_program_end];
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
struct unw_addr_space mock_address_space;
mock_address_space.big_endian = 0;
mock_address_space.acc.access_mem = &access_mem;
struct cursor cursor;
struct dwarf_cursor *dwarf = &cursor.dwarf;
struct unw_cursor *c = (struct unw_cursor *)(&cursor);
dwarf->as = &mock_address_space;
dwarf->as_arg = &test_instructions;
/* ip at adrp */
dwarf->ip = (unw_word_t) (test_instructions + ip_adrp);
if (unw_is_plt_entry(c) == 0) return -1;
/* adrp uses different offset */
test_instructions[ip_adrp] = 0x90272990;
if (unw_is_plt_entry(c) == 0) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ldr uses different offset */
test_instructions[ip_ldr] = 0xf948be11;
if (unw_is_plt_entry(c) == 0) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* add uses different offset */
test_instructions[ip_add] = 0x91726210;
if (unw_is_plt_entry(c) == 0) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip_ldr is not a ldr instruction */
test_instructions[ip_ldr] = 0xf154f00d;
if (unw_is_plt_entry(c) != 0) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip_add is not an add instruction */
test_instructions[ip_add] = 0xf154f00d;
if (unw_is_plt_entry(c) != 0) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip_br is not a br instruction */
test_instructions[ip_br] = 0xf154f00d;
if (unw_is_plt_entry(c) != 0) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip at ldr */
dwarf->ip = (unw_word_t) (test_instructions + ip_ldr);
if (unw_is_plt_entry(c) == 0) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip_adrp is not an adrp instruction */
test_instructions[ip_adrp] = 0xf154f00d;
if (unw_is_plt_entry(c) != 0) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip_add is not an add instruction */
test_instructions[ip_add] = 0xf154f00d;
if (unw_is_plt_entry(c) != 0) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip_br is not a br instruction */
test_instructions[ip_br] = 0xf154f00d;
if (unw_is_plt_entry(c) != 0) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip at add */
dwarf->ip = (unw_word_t) (test_instructions + ip_add);
if (unw_is_plt_entry(c) == 0) return -1;
/* ip_adrp is not an adrp instruction */
test_instructions[ip_adrp] = 0xf154f00d;
if (unw_is_plt_entry(c) != 0) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip_ldr is not a ldr instruction */
test_instructions[ip_ldr] = 0xf154f00d;
if (unw_is_plt_entry(c) != 0) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip_br is not a br instruction */
test_instructions[ip_br] = 0xf154f00d;
if (unw_is_plt_entry(c) != 0) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip at br */
dwarf->ip = (unw_word_t) (test_instructions + ip_br);
if (unw_is_plt_entry(c) == 0) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip_adrp is not an adrp instruction */
test_instructions[ip_adrp] = 0xf154f00d;
if (unw_is_plt_entry(c) != 0) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip_ldr is not a ldr instruction */
test_instructions[ip_ldr] = 0xf154f00d;
if (unw_is_plt_entry(c) != 0) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip_add is not an add instruction */
test_instructions[ip_add] = 0xf154f00d;
if (unw_is_plt_entry(c) != 0) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip at non-PLT instruction */
dwarf->ip = (unw_word_t) (test_instructions + ip_guard0);
if (unw_is_plt_entry(c)) return -1;
/* ip at another non-PLT instruction */
dwarf->ip = (unw_word_t) (test_instructions + ip_guard1);
if (unw_is_plt_entry(c)) return -1;
return 0;
}

View File

@@ -0,0 +1,446 @@
#!/bin/sh
#
# 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.
#
verbose=false
if [ "$1" = "-v" ]; then
verbose=true
shift
fi
build_plat=@build_arch@
plat=@arch@
os=@target_os@
num_errors=0
: ${LIBUNWIND:=../src/.libs/libunwind.so}
: ${LIBUNWIND_GENERIC:=../src/.libs/libunwind-${plat}.so}
fetch_symtab () {
filename=$1
if [ ! -r $filename ]; then
return
fi
if $verbose; then
echo "Checking $filename..."
fi
#
# Unfortunately, "nm --defined" is a GNU-extension. For portability,
# build the list of defined symbols by hand.
#
symtab=`nm -g $filename`
saved_IFS="$IFS"
IFS=""
undef=`nm -g -u $filename`
for line in $undef; do
symtab=`echo "$symtab" | grep -v "^${line}"\$`
done;
IFS="$saved_IFS"
}
ignore () {
sym=$1
symtab=`echo "$symtab" | grep -v " ${sym}\$"`
}
match () {
sym=$1
if `echo "$symtab" | grep -q " ${sym}\$"`; then
symtab=`echo "$symtab" | grep -v " ${sym}\$"`
else
echo " ERROR: Symbol \"$sym\" missing."
num_errors=`expr $num_errors + 1`
fi
}
#
# Filter out miscellaneous symbols that get defined by the
# linker for each shared object.
#
filter_misc () {
ignore _DYNAMIC
ignore _GLOBAL_OFFSET_TABLE_
ignore __bss_start
ignore _edata
ignore _end
ignore _Uelf32_get_proc_name
ignore _Uelf32_valid_object
ignore _Uelf64_get_proc_name
ignore _Uelf64_valid_object
ignore _U.*debug_level
ignore ICRT.INTERNAL # ICC 8.x defines this
# Ignore symbols generated by the ARM Linux default linker script.
# For details see the binutils sources (src/ld/emulparams/armelf_linux.sh).
case "$plat}" in
arm*|aarch64*)
ignore __bss_start__
ignore __bss_end__
ignore __end__
ignore _bss_end__
;;
mips*)
ignore _fbss
ignore _fdata
ignore _ftext
ignore _gp
;;
loongarch64)
ignore _fbss
ignore _fdata
ignore _ftext
ignore _gp
;;
esac
case "${os}" in
solaris2.11)
ignore _PROCEDURE_LINKAGE_TABLE_
ignore _etext
;;
nto*)
ignore _btext
;;
esac
}
check_local_unw_abi () {
match _UL${plat}_apply_reg_state
match _UL${plat}_reg_states_iterate
match _UL${plat}_create_addr_space
match _UL${plat}_destroy_addr_space
match _UL${plat}_get_fpreg
match _UL${plat}_get_proc_info
match _UL${plat}_get_proc_info_by_ip
match _UL${plat}_get_proc_info_in_range
match _UL${plat}_get_proc_name
match _UL${plat}_get_proc_name_by_ip
match _UL${plat}_get_elf_filename
match _UL${plat}_get_elf_filename_by_ip
match _UL${plat}_get_reg
match _UL${plat}_get_save_loc
match _UL${plat}_init_local
match _UL${plat}_init_local2
match _UL${plat}_init_remote
match _UL${plat}_is_plt_entry
match _UL${plat}_is_signal_frame
match _UL${plat}_local_addr_space
match _UL${plat}_resume
match _UL${plat}_set_iterate_phdr_function
match _UL${plat}_set_caching_policy
match _UL${plat}_set_cache_size
match _UL${plat}_set_reg
match _UL${plat}_set_fpreg
match _UL${plat}_step
match _U${plat}_flush_cache
match _U${plat}_get_accessors
match _U${plat}_get_elf_image
match _U${plat}_get_exe_image_path
match _U${plat}_regname
match _U${plat}_strerror
match _U_dyn_cancel
match _U_dyn_info_list_addr
match _U_dyn_register
match unw_backtrace
@CONFIG_WEAK_BACKTRACE_TRUE@match backtrace
match unw_backtrace2
case ${plat} in
arm)
match _U${plat}_getcontext
match _U${plat}_is_fpreg
match _UL${plat}_unwind_method
match _UL${plat}_search_unwind_table
match _UL${plat}_dwarf_search_unwind_table
match _UL${plat}_dwarf_find_unwind_table
;;
hppa)
match _U${plat}_getcontext
match _UL${plat}_dwarf_search_unwind_table
match _UL${plat}_dwarf_find_unwind_table
match _U${plat}_setcontext
;;
ia64)
match _U${plat}_getcontext
match _UL${plat}_search_unwind_table
;;
x86)
match _U${plat}_getcontext
match _U${plat}_is_fpreg
match _UL${plat}_dwarf_search_unwind_table
match _UL${plat}_dwarf_find_unwind_table
;;
x86_64)
match _U${plat}_getcontext
match _U${plat}_is_fpreg
match _UL${plat}_dwarf_search_unwind_table
match _UL${plat}_dwarf_find_unwind_table
match _U${plat}_setcontext
;;
ppc*)
match _U${plat}_getcontext
match _U${plat}_get_func_addr
match _U${plat}_is_fpreg
match _UL${plat}_dwarf_search_unwind_table
match _UL${plat}_dwarf_find_unwind_table
;;
s390x)
match _U${plat}_getcontext
match _U${plat}_is_fpreg
match _UL${plat}_dwarf_search_unwind_table
match _UL${plat}_dwarf_find_unwind_table
match _U${plat}_setcontext
;;
riscv)
match _U${plat}_getcontext
match _U${plat}_is_fpreg
match _UL${plat}_dwarf_search_unwind_table
match _UL${plat}_dwarf_find_unwind_table
match _U${plat}_setcontext
;;
loongarch64)
match _U${plat}_getcontext
match _U${plat}_is_fpreg
match _UL${plat}_dwarf_search_unwind_table
match _UL${plat}_dwarf_find_unwind_table
;;
*)
match _U${plat}_is_fpreg
match _UL${plat}_dwarf_search_unwind_table
match _UL${plat}_dwarf_find_unwind_table
;;
esac
if [ x@enable_debug_frame@ = xyes ]; then
match _UL${plat}_dwarf_find_debug_frame
fi
}
check_generic_unw_abi () {
match _U${plat}_apply_reg_state
match _U${plat}_reg_states_iterate
match _U${plat}_create_addr_space
match _U${plat}_destroy_addr_space
match _U${plat}_flush_cache
match _U${plat}_get_accessors
match _U${plat}_get_fpreg
match _U${plat}_get_proc_info
match _U${plat}_get_proc_info_by_ip
match _U${plat}_get_proc_info_in_range
match _U${plat}_get_proc_name
match _U${plat}_get_proc_name_by_ip
match _U${plat}_get_elf_filename
match _U${plat}_get_elf_filename_by_ip
match _U${plat}_get_reg
match _U${plat}_get_save_loc
match _U${plat}_init_local
match _U${plat}_init_local2
match _U${plat}_init_remote
match _U${plat}_is_plt_entry
match _U${plat}_is_signal_frame
match _U${plat}_local_addr_space
match _U${plat}_regname
match _U${plat}_resume
match _U${plat}_set_iterate_phdr_function
match _U${plat}_set_caching_policy
match _U${plat}_set_cache_size
match _U${plat}_set_fpreg
match _U${plat}_set_reg
match _U${plat}_step
match _U${plat}_strerror
case ${plat} in
aarch64)
match _U${plat}_is_fpreg
match _U${plat}_get_elf_image
match _U${plat}_get_exe_image_path
match _U${plat}_dwarf_search_unwind_table
match _U${plat}_dwarf_find_unwind_table
;;
arm)
match _U${plat}_is_fpreg
match _U${plat}_unwind_method
match _U${plat}_get_elf_image
match _U${plat}_get_exe_image_path
match _U${plat}_search_unwind_table
match _U${plat}_dwarf_search_unwind_table
match _U${plat}_dwarf_find_unwind_table
;;
hppa)
match _U${plat}_dwarf_search_unwind_table
match _U${plat}_dwarf_find_unwind_table
match _U${plat}_get_elf_image
match _U${plat}_get_exe_image_path
;;
ia64)
match _U${plat}_search_unwind_table
match _U${plat}_find_dyn_list
if [ $plat = $build_plat ]; then
match _U${plat}_get_elf_image
match _U${plat}_get_exe_image_path
case $os in
linux*)
match _U${plat}_get_kernel_table
;;
esac
fi
;;
x86)
match _U${plat}_get_elf_image
match _U${plat}_get_exe_image_path
match _U${plat}_is_fpreg
match _U${plat}_dwarf_search_unwind_table
match _U${plat}_dwarf_find_unwind_table
;;
x86_64)
match _U${plat}_get_elf_image
match _U${plat}_get_exe_image_path
match _U${plat}_is_fpreg
match _U${plat}_dwarf_search_unwind_table
match _U${plat}_dwarf_find_unwind_table
;;
ppc*)
match _U${plat}_get_elf_image
match _U${plat}_get_exe_image_path
match _U${plat}_get_func_addr
match _U${plat}_is_fpreg
match _U${plat}_dwarf_search_unwind_table
match _U${plat}_dwarf_find_unwind_table
;;
s390x)
match _U${plat}_is_fpreg
match _U${plat}_get_elf_image
match _U${plat}_get_exe_image_path
match _U${plat}_dwarf_search_unwind_table
match _U${plat}_dwarf_find_unwind_table
;;
riscv)
match _U${plat}_get_elf_image
match _U${plat}_get_exe_image_path
match _U${plat}_is_fpreg
match _U${plat}_dwarf_search_unwind_table
match _U${plat}_dwarf_find_unwind_table
;;
loongarch64)
match _U${plat}_get_elf_image
match _U${plat}_get_exe_image_path
match _U${plat}_is_fpreg
match _U${plat}_dwarf_search_unwind_table
match _U${plat}_dwarf_find_unwind_table
;;
*)
match _U${plat}_is_fpreg
match _U${plat}_dwarf_search_unwind_table
match _U${plat}_dwarf_find_unwind_table
match _U${plat}_get_elf_image
match _U${plat}_get_exe_image_path
;;
esac
if [ x@enable_debug_frame@ = xyes ]; then
match _U${plat}_dwarf_find_debug_frame
fi
}
check_cxx_abi () {
match _Unwind_Backtrace
match _Unwind_DeleteException
match _Unwind_FindEnclosingFunction
match _Unwind_ForcedUnwind
match _Unwind_GetBSP
match _Unwind_GetCFA
match _Unwind_GetDataRelBase
match _Unwind_GetGR
match _Unwind_GetIP
match _Unwind_GetIPInfo
match _Unwind_GetLanguageSpecificData
match _Unwind_GetRegionStart
match _Unwind_GetTextRelBase
match _Unwind_RaiseException
match _Unwind_Resume
match _Unwind_Resume_or_Rethrow
match _Unwind_SetGR
match _Unwind_SetIP
match __libunwind_Unwind_Backtrace
match __libunwind_Unwind_DeleteException
match __libunwind_Unwind_FindEnclosingFunction
match __libunwind_Unwind_ForcedUnwind
match __libunwind_Unwind_GetBSP
match __libunwind_Unwind_GetCFA
match __libunwind_Unwind_GetDataRelBase
match __libunwind_Unwind_GetGR
match __libunwind_Unwind_GetIP
match __libunwind_Unwind_GetIPInfo
match __libunwind_Unwind_GetLanguageSpecificData
match __libunwind_Unwind_GetRegionStart
match __libunwind_Unwind_GetTextRelBase
match __libunwind_Unwind_RaiseException
match __libunwind_Unwind_Resume
match __libunwind_Unwind_Resume_or_Rethrow
match __libunwind_Unwind_SetGR
match __libunwind_Unwind_SetIP
case $os in
linux*)
# needed only for Intel 8.0 bug-compatibility
match _ReadSLEB
match _ReadULEB
;;
esac
}
check_empty () {
if [ -n "$symtab" ]; then
printf " ERROR: Extraneous symbols:\n$symtab\n"
num_errors=`expr $num_errors + 1`
fi
}
if [ $plat = $build_plat ]; then
fetch_symtab $LIBUNWIND
filter_misc
check_local_unw_abi
if [ x@enable_cxx_exceptions@ = xyes ]; then
check_cxx_abi
fi
check_empty
fi
fetch_symtab $LIBUNWIND_GENERIC
filter_misc
check_generic_unw_abi
check_empty
if [ $num_errors -gt 0 ]; then
echo "FAILURE: Detected $num_errors errors"
exit 1
fi
if $verbose; then
echo " SUCCESS: all checks passed"
fi
exit 0

View File

@@ -0,0 +1,129 @@
/* This program should crash and produce coredump */
#include "compiler.h"
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#ifdef __FreeBSD__
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#endif
#if defined(__linux__)
void write_maps(char *fname)
{
char buf[512], path[128];
char exec;
uintmax_t addr;
FILE *maps = fopen("/proc/self/maps", "r");
FILE *out = fopen(fname, "w");
if (!maps || !out)
exit(EXIT_FAILURE);
while (fgets(buf, sizeof(buf), maps))
{
if (sscanf(buf, "%jx-%*x %*c%*c%c%*c %*x %*s %*d /%126[^\n]", &addr, &exec, path+1) != 3)
continue;
if (exec != 'x')
continue;
path[0] = '/';
fprintf(out, "0x%jx:%s ", addr, path);
}
fprintf(out, "\n");
fclose(out);
fclose(maps);
}
#elif defined(__FreeBSD__)
void
write_maps(char *fname)
{
FILE *out;
char *buf, *bp, *eb;
struct kinfo_vmentry *kv;
int mib[4], error;
size_t len;
out = fopen(fname, "w");
if (out == NULL)
exit(EXIT_FAILURE);
len = 0;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_VMMAP;
mib[3] = getpid();
error = sysctl(mib, 4, NULL, &len, NULL, 0);
if (error == -1)
exit(EXIT_FAILURE);
len = len * 4 / 3;
buf = malloc(len);
if (buf == NULL)
exit(EXIT_FAILURE);
error = sysctl(mib, 4, buf, &len, NULL, 0);
if (error == -1)
exit(EXIT_FAILURE);
for (bp = buf, eb = buf + len; bp < eb; bp += kv->kve_structsize) {
kv = (struct kinfo_vmentry *)(uintptr_t)bp;
if (kv->kve_type == KVME_TYPE_VNODE &&
(kv->kve_protection & KVME_PROT_EXEC) != 0) {
fprintf(out, "0x%jx:%s ", kv->kve_start, kv->kve_path);
}
}
fprintf(out, "\n");
fclose(out);
free(buf);
}
#else
#error Port me
#endif
#ifdef __GNUC__
#ifndef __clang__
// Gcc >= 8 became too good at inlining alias c into b when using -O2 or -O3,
// so force -O1 in all cases, otherwise a frame will be missing in the tests.
#pragma GCC optimize "-O1"
#endif
int c(int x) NOINLINE ALIAS(b);
#define compiler_barrier() __asm__ __volatile__ ("");
#else
int c(int x);
#define compiler_barrier()
#endif
int NOINLINE a(void)
{
*(volatile int *)32 = 1;
return 1;
}
int NOINLINE b(int x)
{
int r;
compiler_barrier();
if (x)
r = a();
else
r = c(1);
return r + 1;
}
int
main (int argc, char **argv)
{
if (argc > 1)
write_maps(argv[1]);
b(0);
return 0;
}

View File

@@ -0,0 +1,88 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifndef HAVE__BUILTIN___CLEAR_CACHE
#if defined(__ia64__)
.global flush_cache
.proc flush_cache
flush_cache:
.prologue
alloc r2=ar.pfs,2,0,0,0
add r8=31,in1 // round up to 32 byte-boundary
;;
shr.u r8=r8,5 // we flush 32 bytes per iteration
;;
add r8=-1,r8
.save ar.lc, r3
mov r3=ar.lc // save ar.lc
;;
.body
mov ar.lc=r8
;;
.loop: fc in0 // issuable on M0 only
add in0=32,in0
br.cloop.sptk.few .loop
;;
sync.i
;;
srlz.i
;;
mov ar.lc=r3 // restore ar.lc
br.ret.sptk.many rp
.endp flush_cache
#elif defined(__i386__) || defined (__x86_64__)
.globl flush_cache
flush_cache:
ret
#elif defined(__hppa__)
# warning FIX ME!!
.globl flush_cache
flush_cache:
.proc
.callinfo
bv %r0(%rp)
.procend
#elif defined(__powerpc64__)
# warning IMPLEMENT ME FOR PPC64!!
.globl flush_cache
flush_cache:
lwz 11, 0(1) ;
lwz 0, 4(11) ;
mtlr 0 ;
lwz 31, -4(11) ;
mr 1, 11 ;
blr
#elif defined(__powerpc__)
# warning IMPLEMENT ME FOR PPC32!!
.globl flush_cache
flush_cache:
lwz 11, 0(1) ;
lwz 0, 4(11) ;
mtlr 0 ;
lwz 31, -4(11) ;
mr 1, 11 ;
blr
#elif defined(__arm__)
.text
.globl flush_cache
flush_cache:
bx lr
# error Need flush_cache code for this architecture.
#endif
#if defined ( __linux__) && !defined (__arm__)
/* We do not need executable stack. */
.section .note.GNU-stack,"",@progbits
#endif
#endif

View File

@@ -0,0 +1,38 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2012 Tommi Rantala <tt.rantala@gmail.com>
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. */
#ifndef FLUSH_CACHE_H
#define FLUSH_CACHE_H
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE__BUILTIN___CLEAR_CACHE
#define flush_cache(ADDR, LEN) \
__builtin___clear_cache((ADDR), (ADDR) + (LEN))
#else
#include <stddef.h>
extern void flush_cache (void *addr, size_t len);
#endif
#endif /* FLUSH_CACHE_H */

View File

@@ -0,0 +1,76 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
int
main (int argc, char **argv, char **envp)
{
char *program, **child_argv;
struct timeval start, stop;
double secs;
int status, i;
long count;
pid_t pid;
count = atol (argv[1]);
program = argv[2];
child_argv = alloca ((argc - 1) * sizeof (char *));
for (i = 0; i < argc - 2; ++i)
child_argv[i] = argv[2 + i];
child_argv[i] = NULL;
gettimeofday (&start, NULL);
for (i = 0; i < count; ++i)
{
pid = fork ();
if (pid == 0)
{
execve (program, child_argv, envp);
_exit (-1);
}
else
{
waitpid (pid, &status, 0);
if (!WIFEXITED (status) || WEXITSTATUS (status) != 0)
{
fprintf (stderr, "%s: child failed\n", argv[0]);
exit (-1);
}
}
}
gettimeofday (&stop, NULL);
secs = ((stop.tv_sec + 1e-6 * stop.tv_usec)
- (start.tv_sec + 1e-6 * start.tv_usec));
printf ("%lu nsec/execution\n",
(unsigned long) (1e9 * secs / (double) count));
return 0;
}

View File

@@ -0,0 +1,102 @@
.globl func_add1, func_add1_end
.proc func_add1
func_add1:
{.mib; add r8 = 1, r32
nop.i 0
br.ret.sptk.many rp
}
func_add1_end:
.endp func_add1
.globl func_add3, func_add3_end
.proc func_add3
func_add3:
{.mmi; alloc loc0 = ar.pfs, 2, 1, 2, 0
mov r2 = sp
add sp = -16, sp
} ;;
{.mii; ld8 r8 = [in1], 8 // load the function pointer
mov r3 = rp
mov rp = loc0 // trash rp
} ;;
{.mmi; ld8 r9 = [r8], 8 // load the entry-point
st8 [r2] = r3
mov out0 = in0
} ;;
{.mii; ld8 gp = [r8] // load the gp
mov b6 = r9
mov out1 = in1
}
{.mib; nop 0
nop 0
br.call.sptk rp = b6
}
{.mmi; add r2 = 16, sp
;;
ld8 r3 = [r2] // r3 = saved rp
mov ar.pfs = loc0
} ;;
{.mii; nop 0
mov rp = r3
adds sp = 16, sp
} ;;
{.mib; st8 [sp] = in0 // trash rp save location
add r8 = 2, r8
br.ret.sptk.many rp
}
func_add3_end:
.endp func_add3
.globl func_vframe, func_vframe_end
.proc func_vframe
func_vframe:
{.mii; alloc r16 = ar.pfs, 1, 2, 0, 0 // 0
mov loc0 = rp
mov loc1 = sp
} ;;
{.mmi; sub sp = sp, in0
st8 [loc1] = r16
mov r2 = -99 // 0
} ;;
{.mii; nop 0
mov rp = r2
mov ar.pfs = r0
}
{.mib; mov r16 = r2
tbit.nz p6, p0 = in0, 4
(p6) br.cond.sptk.many .exit
} ;;
{.mmi; ld8 r16 = [loc1]
;;
mov r3 = loc0 // 8 move saved rp to r3
mov ar.pfs = r16
} ;;
{.mmi; mov sp = loc1 // 10
st8 [loc1] = r0 // trash saved pfs
mov loc0 = r2
} ;;
{.mib; mov r8 = 10
mov rp = r3
br.ret.sptk.many rp
}
.exit:
{.mmi; ld8 r16 = [loc1]
;;
sub sp = 32, sp
mov ar.pfs = r16
} ;;
{.mmi; mov sp = loc1
st8 [loc1] = r0 // trash saved pfs
mov rp = loc0
}
{.mib; nop 0
mov r8 = 4
br.ret.sptk.many rp
}
func_vframe_end:
.endp func_vframe
#ifdef __linux__
/* We do not need executable stack. */
.section .note.GNU-stack,"",@progbits
#endif

View File

@@ -0,0 +1,223 @@
#include "flush-cache.h"
#include <assert.h>
#include <libunwind.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
int verbose;
#ifdef __ia64__
# define GET_ENTRY(fdesc) (((uintptr_t *) (fdesc))[0])
# define GET_GP(fdesc) (((uintptr_t *) (fdesc))[0])
# define EXTRA 16
#else
# define GET_ENTRY(fdesc) ((uintptr_t ) (fdesc))
# define GET_GP(fdesc) (0)
# define EXTRA 0
#endif
int
make_executable (void *addr, size_t len)
{
if (mprotect ((void *) (((long) addr) & -getpagesize ()), len,
PROT_READ | PROT_WRITE | PROT_EXEC) < 0)
{
perror ("mprotect");
return -1;
}
flush_cache (addr, len);
return 0;
}
void *
create_func (unw_dyn_info_t *di, const char *name, long (*func) (),
void *end, unw_dyn_region_info_t *region)
{
void *mem, *memend, *addr, *fptr;
unw_word_t gp = 0;
size_t len;
len = (uintptr_t) end - GET_ENTRY (func) + EXTRA;
mem = malloc (len);
if (verbose)
printf ("%s: cloning %s at %p (%zu bytes)\n",
__FUNCTION__, name, mem, len);
memend = (char *) mem + len;
#ifdef __ia64__
addr = (void *) GET_ENTRY (func);
/* build function descriptor: */
((long *) mem)[0] = (long) mem + 16; /* entry point */
((long *) mem)[1] = GET_GP (func); /* global-pointer */
fptr = mem;
mem = (void *) ((long) mem + 16);
#else
fptr = mem;
#endif
len = (char *) memend - (char *) mem;
memcpy (mem, addr, len);
if (make_executable (mem, len) < 0)
return NULL;
if (di)
{
memset (di, 0, sizeof (*di));
di->start_ip = (unw_word_t) mem;
di->end_ip = (unw_word_t) memend;
di->gp = gp;
di->format = UNW_INFO_FORMAT_DYNAMIC;
di->u.pi.name_ptr = (unw_word_t) name;
di->u.pi.regions = region;
}
return fptr;
}
int
main (int argc, char **argv)
{
extern long func_add1 (long);
extern char func_add1_end[];
extern long func_add3 (long, long (*[])());
extern char func_add3_end[];
extern long func_vframe (long);
extern char func_vframe_end[];
unw_dyn_region_info_t *r_pro, *r_epi, *r, *rtmp;
unw_dyn_info_t di0, di1, di2, di3;
long (*add1) (long);
long (*add3_0) (long);
long (*add3_1) (long, void *[]);
long (*vframe) (long);
void *flist[2];
long ret;
int i;
signal (SIGUSR1, SIG_IGN);
signal (SIGUSR2, SIG_IGN);
if (argc != 1)
verbose = 1;
add1 = (long (*)(long))
create_func (&di0, "func_add1", func_add1, func_add1_end, NULL);
/* Describe the epilogue of func_add3: */
i = 0;
r_epi = alloca (_U_dyn_region_info_size (5));
r_epi->op_count = 5;
r_epi->next = NULL;
r_epi->insn_count = -9;
_U_dyn_op_pop_frames (&r_epi->op[i++],
_U_QP_TRUE, /* when=*/ 5, /* num_frames=*/ 1);
_U_dyn_op_stop (&r_epi->op[i++]);
assert ((unsigned) i <= r_epi->op_count);
/* Describe the prologue of func_add3: */
i = 0;
r_pro = alloca (_U_dyn_region_info_size (4));
r_pro->op_count = 4;
r_pro->next = r_epi;
r_pro->insn_count = 8;
_U_dyn_op_save_reg (&r_pro->op[i++], _U_QP_TRUE, /* when=*/ 0,
/* reg=*/ UNW_IA64_AR_PFS, /* dst=*/ UNW_IA64_GR + 34);
_U_dyn_op_add (&r_pro->op[i++], _U_QP_TRUE, /* when=*/ 2,
/* reg= */ UNW_IA64_SP, /* val=*/ -16);
_U_dyn_op_save_reg (&r_pro->op[i++], _U_QP_TRUE, /* when=*/ 4,
/* reg=*/ UNW_IA64_RP, /* dst=*/ UNW_IA64_GR + 3);
_U_dyn_op_spill_sp_rel (&r_pro->op[i++], _U_QP_TRUE, /* when=*/ 7,
/* reg=*/ UNW_IA64_RP, /* off=*/ 16);
assert ((unsigned) i <= r_pro->op_count);
/* Create regions for func_vframe: */
i = 0;
r = alloca (_U_dyn_region_info_size (16));
r->op_count = 16;
r->next = NULL;
r->insn_count = 4;
_U_dyn_op_label_state (&r->op[i++], /* label=*/ 100402);
_U_dyn_op_pop_frames (&r->op[i++], _U_QP_TRUE, /* when=*/ 3, /* frames=*/ 1);
_U_dyn_op_stop (&r->op[i++]);
assert ((unsigned) i <= r->op_count);
i = 0;
rtmp = r;
r = alloca (_U_dyn_region_info_size (16));
r->op_count = 16;
r->next = rtmp;
r->insn_count = 16;
_U_dyn_op_save_reg (&r->op[i++], _U_QP_TRUE, /* when=*/ 8,
/* reg=*/ UNW_IA64_RP, /* dst=*/ UNW_IA64_GR + 3);
_U_dyn_op_pop_frames (&r->op[i++], _U_QP_TRUE, /* when=*/ 10,
/* num_frames=*/ 1);
_U_dyn_op_stop (&r->op[i++]);
assert ((unsigned) i <= r->op_count);
i = 0;
rtmp = r;
r = alloca (_U_dyn_region_info_size (16));
r->op_count = 16;
r->next = rtmp;
r->insn_count = 5;
_U_dyn_op_save_reg (&r->op[i++], _U_QP_TRUE, /* when=*/ 1,
/* reg=*/ UNW_IA64_RP, /* dst=*/ UNW_IA64_GR + 33);
_U_dyn_op_save_reg (&r->op[i++], _U_QP_TRUE, /* when=*/ 2,
/* reg=*/ UNW_IA64_SP, /* dst=*/ UNW_IA64_GR + 34);
_U_dyn_op_spill_fp_rel (&r->op[i++], _U_QP_TRUE, /* when=*/ 4,
/* reg=*/ UNW_IA64_AR_PFS, /* off=*/ 16);
_U_dyn_op_label_state (&r->op[i++], /* label=*/ 100402);
_U_dyn_op_stop (&r->op[i++]);
assert ((unsigned) i <= r->op_count);
/* Create two functions which can share the region-list: */
add3_0 = (long (*) (long))
create_func (&di1, "func_add3/0", func_add3, func_add3_end, r_pro);
add3_1 = (long (*) (long, void *[]))
create_func (&di2, "func_add3/1", func_add3, func_add3_end, r_pro);
vframe = (long (*) (long))
create_func (&di3, "func_vframe", func_vframe, func_vframe_end, r);
_U_dyn_register (&di1);
_U_dyn_register (&di2);
_U_dyn_register (&di3);
_U_dyn_register (&di0);
flist[0] = add3_0;
flist[1] = add1;
kill (getpid (), SIGUSR1); /* do something ptmon can latch onto */
ret = (*add3_1) (13, flist);
if (ret != 18)
{
fprintf (stderr, "FAILURE: (*add3_1)(13) returned %ld\n", ret);
exit (-1);
}
ret = (*vframe) (48);
if (ret != 4)
{
fprintf (stderr, "FAILURE: (*vframe)(16) returned %ld\n", ret);
exit (-1);
}
ret = (*vframe) (64);
if (ret != 10)
{
fprintf (stderr, "FAILURE: (*vframe)(32) returned %ld\n", ret);
exit (-1);
}
kill (getpid (), SIGUSR2); /* do something ptmon can latch onto */
_U_dyn_cancel (&di0);
_U_dyn_cancel (&di1);
_U_dyn_cancel (&di3);
_U_dyn_cancel (&di2);
return 0;
}

View File

@@ -0,0 +1,508 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2004-2005 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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. */
.text
#define CALL_NEXT_PTR(gp_save_reg, arg0, arg1) \
ld8 r2 = [arg0], 8;; /* read the next function pointer */ \
ld8 r3 = [r2], 8;; /* read the function's entry-point */ \
ld8 r2 = [r2];; /* read the function's gp */ \
mov b6 = r3; \
mov gp_save_reg = gp; \
mov out0 = arg0; \
mov out1 = arg1; \
mov gp = r2; \
br.call.sptk.many rp = b6;; \
mov gp = gp_save_reg
#define CALL_NEXT(gp_save_reg) CALL_NEXT_PTR(gp_save_reg, in0, in1)
#define LOAD_VAL(reg) \
ld8 reg = [in1], 8;; \
tbit.nz p15, p0 = reg, 0;; \
(p15) ld8.s reg = [r0]
.global flushrs
.proc flushrs
flushrs:
flushrs;;
br.ret.sptk.many rp
.endp flushrs
/* Save r4-r7 into stacked registers, load them up with the
values passed via the pointer in in1 and then call the
function passed via the pointer in in0. */
.global save_static_to_stacked
.proc save_static_to_stacked
save_static_to_stacked:
.prologue
.regstk 2, 7, 2, 0
.save ar.pfs, loc0
alloc loc0 = ar.pfs, 2, 7, 2, 0
.save rp, loc1
mov loc1 = rp
.spillreg r4, loc2
mov loc2 = r4
.spillreg r5, loc3
mov loc3 = r5
.spillreg r6, loc4
mov loc4 = r6
.spillreg r7, loc5
mov loc5 = r7
.body
LOAD_VAL(r4)
LOAD_VAL(r5)
LOAD_VAL(r6)
LOAD_VAL(r7)
CALL_NEXT(loc6)
mov r4 = loc2
mov r5 = loc3
mov r6 = loc4
mov r7 = loc5
mov ar.pfs = loc0
mov rp = loc1
br.ret.sptk.many rp
.endp save_static_to_stacked
/* Save f2 to the memory stack, save r4 to f2, then load
r4 with the value passed via in1 and call the function
passed via in0. */
.global save_static_to_fr
.proc save_static_to_fr
save_static_to_fr:
.prologue
.regstk 2, 3, 2, 0
.save ar.pfs, loc0
alloc loc0 = ar.pfs, 2, 3, 2, 0
.save rp, loc1
mov loc1 = rp
.fframe 16
.spillpsp f2, 0
stf.spill [sp] = f2, -16
.spillreg r4, f2
setf.sig f2 = r4
.body
ld8 r4 = [in1], 8;;
tbit.nz p6, p0 = r4, 0;;
(p6) ld8.s r4 = [r0]
CALL_NEXT(loc2)
getf.sig r4 = f2 // restore r4
.restore sp
add sp = 16, sp;;
ldf.fill f2 = [sp] // restore r2
mov ar.pfs = loc0
mov rp = loc1
br.ret.sptk.many rp
.endp save_static_to_fr
/* If r4 is not a NaT, save b3 to a stacked register and
then save r4 in b3. The non-NaTness of r4 is saved in
p1. */
.global save_static_to_br
.proc save_static_to_br
save_static_to_br:
.prologue
.regstk 2, 6, 2, 0
.save ar.pfs, loc0
alloc loc0 = ar.pfs, 2, 6, 2, 0
.save rp, loc1
mov loc1 = rp
.save pr, loc2
mov loc2 = pr // save predicates
.spillreg b3, loc3
mov loc3 = b3
tnat.z p1, p2 = r4;;
.spillreg.p p1, r4, b3
(p1) mov b3 = r4
.spillreg.p p2, r4, loc4
(p2) mov loc4 = r4
.body
LOAD_VAL(r4)
CALL_NEXT(loc5)
.pred.rel.mutex p1, p2
(p1) mov r4 = b3 // restore r4
(p2) mov r4 = loc4
mov ar.pfs = loc0
mov rp = loc1
mov pr = loc2, -1
mov b3 = loc3 // restore b3
br.ret.sptk.many rp
.endp save_static_to_br
/* Spill r4 into memory and then save r5 in r4. */
.global save_static_to_mem
.proc save_static_to_mem
save_static_to_mem:
.prologue
.regstk 2, 4, 2, 0
.save ar.pfs, loc0
alloc loc0 = ar.pfs, 2, 4, 2, 0
.save rp, loc1
mov loc1 = rp
.save ar.unat, loc2
mov loc2 = ar.unat
.fframe 16
.spillpsp r4, 0
st8.spill [sp] = r4, -16
.spillreg r5, r4
mov r4 = r5
.body
LOAD_VAL(r5)
CALL_NEXT(loc3)
mov r5 = r4 // restore r5
.restore sp
add sp = 16, sp;;
ld8.fill r4 = [sp] // restore r4
mov ar.pfs = loc0
mov rp = loc1
mov ar.unat = loc2 // restore ar.unat
br.ret.sptk.many rp
.endp save_static_to_mem
/* Spill r6 into memory and save primary ar.unat in a register. */
.global save_static_to_mem2
.proc save_static_to_mem2
save_static_to_mem2:
.prologue
.regstk 2, 5, 2, 0
.save ar.pfs, loc0
alloc loc0 = ar.pfs, 2, 5, 2, 0
.save rp, loc1
mov loc1 = rp
.save ar.unat, loc2
mov loc2 = ar.unat
.fframe 16
.spillpsp r6, 0
st8.spill [sp] = r6, -16;;
.save @priunat, loc3
mov loc3 = ar.unat
mov ar.unat = 0 // trash ar.unat
.body
LOAD_VAL(r6)
CALL_NEXT(loc4)
mov ar.unat = loc3 // restore primary UNaT
.restore sp
add sp = 16, sp;;
ld8.fill r6 = [sp] // restore r6
mov ar.pfs = loc0
mov rp = loc1
mov ar.unat = loc2 // restore ar.unat
br.ret.sptk.many rp
.endp save_static_to_mem2
/* Spill r6 into memory and save primary ar.unat in memory. */
.global save_static_to_mem3
.proc save_static_to_mem3
save_static_to_mem3:
.prologue
.regstk 2, 5, 2, 0
.save ar.pfs, loc0
alloc loc0 = ar.pfs, 2, 5, 2, 0
.save rp, loc1
mov loc1 = rp
.save ar.unat, loc2
mov loc2 = ar.unat
add r2 = 8, sp
.fframe 16
.spillpsp r6, 0
st8.spill [sp] = r6, -16;;
mov r3 = ar.unat;;
.savepsp @priunat, -8
st8 [r2] = r3
mov ar.unat = 0 // trash ar.unat
.body
LOAD_VAL(r6)
CALL_NEXT(loc4)
add r2 = 24, sp;;
ld8 r3 = [r2];;
mov ar.unat = r3 // restore primary UNaT
.restore sp
add sp = 16, sp;;
ld8.fill r6 = [sp] // restore r6
mov ar.pfs = loc0
mov rp = loc1
mov ar.unat = loc2 // restore ar.unat
br.ret.sptk.many rp
.endp save_static_to_mem3
/* Spill r6 into memory and save primary ar.unat in register,
then in memory. */
.global save_static_to_mem4
.proc save_static_to_mem4
save_static_to_mem4:
.prologue
.regstk 2, 5, 2, 0
.save ar.pfs, loc0
alloc loc0 = ar.pfs, 2, 5, 2, 0
.save rp, loc1
mov loc1 = rp
.save ar.unat, loc2
mov loc2 = ar.unat
add r2 = 8, sp
.fframe 16
.spillpsp r6, 0
st8.spill [sp] = r6, -16;;
.save @priunat, r3
mov r3 = ar.unat;;
mov ar.unat = 0 // trash ar.unat
.savepsp @priunat, -8
st8 [r2] = r3
mov r3 = r0 // trash register pri UNaT location
.body
LOAD_VAL(r6)
CALL_NEXT(loc4)
add r2 = 24, sp;;
ld8 r3 = [r2];;
mov ar.unat = r3 // restore primary UNaT
.restore sp
add sp = 16, sp;;
ld8.fill r6 = [sp] // restore r6
mov ar.pfs = loc0
mov rp = loc1
mov ar.unat = loc2 // restore ar.unat
br.ret.sptk.many rp
.endp save_static_to_mem4
/* Spill r6 into memory and save primary ar.unat in register,
then in memory. */
.global save_static_to_mem5
.proc save_static_to_mem5
save_static_to_mem5:
.prologue
.regstk 2, 5, 2, 0
.save ar.pfs, loc0
alloc loc0 = ar.pfs, 2, 5, 2, 0
.save rp, loc1
mov loc1 = rp
.save ar.unat, loc2
mov loc2 = ar.unat
add r2 = 8, sp
.fframe 16
.spillpsp r6, 0
st8.spill [sp] = r6, -16;;
mov r3 = ar.unat;;
mov ar.unat = 0 // trash ar.unat
.savepsp @priunat, -8
st8 [r2] = r3
.save @priunat, loc3
mov loc3 = r3
st8 [r2] = r0 // trash memory pri UNaT location
.body
LOAD_VAL(r6)
CALL_NEXT(loc4)
add r2 = 24, sp;;
ld8 r3 = [r2];;
mov ar.unat = loc3 // restore primary UNaT
.restore sp
add sp = 16, sp;;
ld8.fill r6 = [sp] // restore r6
mov ar.pfs = loc0
mov rp = loc1
mov ar.unat = loc2 // restore ar.unat
br.ret.sptk.many rp
.endp save_static_to_mem5
/* Save r4-r7 to various scratch registers, then trigger
a segfault. */
.global save_static_to_scratch
.proc save_static_to_scratch
save_static_to_scratch:
.prologue
.spillreg r4, r16
mov r16 = r4 // save r4 in r16
tnat.nz p6, p7 = r5;;
.spillreg.p p6, r5, f31
(p6) setf.sig f31 = r5 // save r5 in f31 if it's a NaT
.spillreg.p p7, r5, b6
(p7) mov b6 = r5 // in b6 if it not
.spillreg r6, f32
setf.sig f32 = r6 // save r6 in f32 (fph partition)
.spillsp r7, 0
st8.spill [sp] = r7 // save r7 in the scratch stack space
.spillreg f4, f6
mov f6 = f4;;
.body
ld8 r2 = [in1]
;;
mov ar.ec = r2
LOAD_VAL(r4)
LOAD_VAL(r5)
LOAD_VAL(r6)
LOAD_VAL(r7)
setf.sig f4 = r4
/* Now force a SIGSEGV. Make sure the ld8 is at the beginning of a
bundle, so the signal-handler can skip over it simply by
incrementing the IP. */
{
.mmi
ld8 r2 = [r0]
nop.m 0
nop.i 0 ;;
}
mov f4 = f6
mov r4 = r16
.pred.rel.mutex p6, p7
(p6) getf.sig r5 = f31
(p7) mov r5 = b6
getf.sig r6 = f32
ld8.fill r7 = [sp]
br.ret.sptk.many rp
.endp save_static_to_scratch
/* Rotate registers a bit in a vain attempt to sow some confusion.
Care must be taken not to write any rotating general register
after rotation, because we keep the preserved state
there... */
.global rotate_regs
.proc rotate_regs
rotate_regs:
.prologue
.regstk 2, 14, 2, 16
.save ar.pfs, loc0
alloc loc0 = ar.pfs, 2, 14, 2, 16
.save rp, loc1
mov loc1 = rp
.save pr, loc2
mov loc2 = pr
.save ar.lc, loc3
mov loc3 = ar.lc
.spillreg r4, loc4
mov loc4 = r4
ld8 r2 = [in1], 8;;
mov pr = r2, -1
ld8 r2 = [in1], 8;;
mov r8 = in0
mov r9 = in1
and r2 = 127, r2;;
mov ar.ec = 0
mov ar.lc = r2;;
// use p6 to preserve p63 as it gets rotated into p16:
(p16) cmp.eq.unc p6,p0 = r0,r0;;
1:
(p6) cmp.eq.unc p16,p0 = r0,r0
(p63) cmp.eq.unc p6,p0 = r0,r0
br.ctop.dptk.few 1b;;
(p6) cmp.eq.unc p63,p0 = r0,r0
CALL_NEXT_PTR(r4, r8, r9)
clrrrb
mov ar.pfs = loc0
mov rp = loc1
mov pr = loc2, -1
mov ar.lc = loc3
mov r4 = loc4
br.ret.sptk.many rp
.endp rotate_regs
.global save_pr
.proc save_pr
save_pr:
.prologue
.regstk 2, 4, 2, 0
.save ar.pfs, loc0
alloc loc0 = ar.pfs, 2, 4, 2, 0
.save rp, loc1
mov loc1 = rp
.save pr, loc2
mov loc2 = pr
ld8 r2 = [in1], 8;;
mov pr = r2, -1
CALL_NEXT(loc3)
mov ar.pfs = loc0
mov rp = loc1
mov pr = loc2, -1
br.ret.sptk.many rp
.endp save_pr
#ifdef __linux__
/* We do not need executable stack. */
.section .note.GNU-stack,"",@progbits
#endif

View File

@@ -0,0 +1,275 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2003 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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 "ia64-test-rbs.h"
.common stackmem, NSTACKS*STACK_SIZE, 16
.text
#define SAVED_SP_OFF 0
#define SAVED_RP_OFF 8
#define SAVED_PFS_OFF 16
#define SAVED_RNAT_OFF 24
#define SAVED_BSP_OFF 32
#define SAVED_BSPSTORE_OFF 40
#define FRAME_SIZE 48
#define SPILL(n) \
/* int rbs_spill_#n(long iteration, int (*next_func[])()) */ \
.globl rbs_spill_##n; \
.proc rbs_spill_##n; \
rbs_spill_##n: \
.prologue; \
alloc r18 = ar.pfs, 2, (n)-2, 2, 0;/* read ar.pfs */ \
/* first, calculate address of new stack: */ \
addl r2 = @ltoff(stackmem), gp; \
add r8 = 1, in0; \
;; \
ld8 r2 = [r2]; /* r2 = &stackmem */ \
shl r3 = in0, STACK_SIZE_SHIFT; \
shladd r8 = r8, 3, in1; /* r8 = &next_func[iteration+1] */ \
;; \
ld8 r8 = [r8]; /* r8 = next_func[iteration+1] */ \
add r2 = r2, r3; /* r2 = stackmem[iteration] */ \
;; \
ld8 r9 = [r8], 8;; /* r9 = target's entry-point */ \
ld8 gp = [r8]; /* r22 = target's gp */ \
addl r3 = STACK_SIZE-FRAME_SIZE, r2; /* r3 = &stackframe */ \
;; \
mov b6 = r9; \
st8 [r3] = sp; \
.vframesp SAVED_SP_OFF+16; \
adds sp = -16, r3; /* switch the memory stack */ \
;; \
adds r3 = (SAVED_RP_OFF - SAVED_SP_OFF), r3; \
mov r16 = rp; \
;; \
.savesp rp, SAVED_RP_OFF+16; \
st8 [r3] = r16, (SAVED_PFS_OFF - SAVED_RP_OFF); \
;; \
.savesp ar.pfs, SAVED_PFS_OFF+16; \
st8 [r3] = r18, (SAVED_BSP_OFF - SAVED_PFS_OFF); \
mov r16 = ar.bsp; \
mov r17 = ar.bspstore; \
mov r18 = ar.rnat; \
;; \
.savesp ar.bsp, SAVED_BSP_OFF+16; \
st8 [r3] = r16, (SAVED_BSPSTORE_OFF - SAVED_BSP_OFF); \
;; \
.savesp ar.bspstore, SAVED_BSPSTORE_OFF+16; \
st8 [r3] = r17, (SAVED_RNAT_OFF - SAVED_BSPSTORE_OFF); \
mov out1 = in1; \
;; \
.savesp ar.rnat, SAVED_RNAT_OFF+16; \
st8 [r3] = r18; \
.body; \
mov ar.bspstore = r2; /* switch the backing store */ \
adds out0 = 1, in0; \
;; \
br.call.sptk.many rp = b6; \
1: /* switch back to stack: */ \
adds r3 = SAVED_SP_OFF+16, sp; \
cmp.ge p8, p0 = r8, r0; \
;; \
(p8) add r8 = 1, r8; \
ld8 r16 = [r3], (SAVED_RP_OFF-SAVED_SP_OFF);; /* saved sp */ \
ld8 r17 = [r3], (SAVED_PFS_OFF-SAVED_RP_OFF);; /* saved rp */ \
ld8 r18 = [r3], (SAVED_RNAT_OFF-SAVED_PFS_OFF);;/* saved pfs */ \
ld8 r19 = [r3], (SAVED_BSP_OFF-SAVED_RNAT_OFF);;/* saved rnat */ \
ld8 r20 = [r3], (SAVED_BSPSTORE_OFF-SAVED_BSP_OFF);;/* saved bsp */ \
ld8 r21 = [r3];; /* saved bspstore */ \
mov rp = r17; \
mov ar.pfs = r18; \
shl r3 = in0, STACK_SIZE_SHIFT; \
addl r2 = @ltoff(stackmem), gp;; \
ld8 r2 = [r2];; /* r2 = &stackmem */ \
add r2 = r2, r3; /* r2 = stackmem[iteration] */ \
mov r3 = ar.bsp;; \
sub r2 = r3, r2;; /* r2 = dirty_size */ \
shl r2 = r2, 16;; \
mov ar.rsc = r2;; \
alloc r3 = ar.pfs, 0, 0, 0, 0;; \
loadrs;; \
mov ar.bspstore = r21;; /* this also restores ar.bsp */ \
mov ar.rnat = r19; \
.restore sp; \
mov sp = r16; \
br.ret.sptk.many rp; \
.endp rbs_spill_##n
SPILL(2); SPILL(3)
SPILL(4); SPILL(5); SPILL(6); SPILL(7)
SPILL(8); SPILL(9); SPILL(10); SPILL(11)
SPILL(12); SPILL(13); SPILL(14); SPILL(15)
SPILL(16); SPILL(17); SPILL(18); SPILL(19)
SPILL(20); SPILL(21); SPILL(22); SPILL(23)
SPILL(24); SPILL(25); SPILL(26); SPILL(27)
SPILL(28); SPILL(29); SPILL(30); SPILL(31)
SPILL(32); SPILL(33); SPILL(34); SPILL(35)
SPILL(36); SPILL(37); SPILL(38); SPILL(39)
SPILL(40); SPILL(41); SPILL(42); SPILL(43)
SPILL(44); SPILL(45); SPILL(46); SPILL(47)
SPILL(48); SPILL(49); SPILL(50); SPILL(51)
SPILL(52); SPILL(53); SPILL(54); SPILL(55)
SPILL(56); SPILL(57); SPILL(58); SPILL(59)
SPILL(60); SPILL(61); SPILL(62); SPILL(63)
SPILL(64); SPILL(65); SPILL(66); SPILL(67)
SPILL(68); SPILL(69); SPILL(70); SPILL(71)
SPILL(72); SPILL(73); SPILL(74); SPILL(75)
SPILL(76); SPILL(77); SPILL(78); SPILL(79)
SPILL(80); SPILL(81); SPILL(82); SPILL(83)
SPILL(84); SPILL(85); SPILL(86); SPILL(87)
SPILL(88); SPILL(89); SPILL(90); SPILL(91)
SPILL(92); SPILL(93); SPILL(94)
#define LD_LOC(n) \
ld4 loc##n = [in1], 4;; \
cmp.eq p8, p9 = r0, loc##n;; \
(p9) or loc##n = loc##n, r8; \
(p8) ld4.s loc##n = [r0]
#define CK_LOC(n) \
ld4 r16 = [in1], 4;; \
cmp.eq p8, p9 = r0, r16; \
or r16 = r16, r9;; \
(p8) tnat.z p10, p0 = loc##n; \
(p9) cmp.ne p10, p0 = r16, loc##n; \
;; \
(p10) mov r8 = -n; \
(p10) br.cond.spnt.many .fail
/* int loadup(long iteration, int *values, next_func[]) */
.global loadup
.proc loadup
loadup:
.prologue
.save ar.pfs, r36
alloc loc1 = ar.pfs, 3, 90, 3, 0
.save rp, loc0
mov loc0 = rp
.body
cmp.eq p6, p7 = 1, in0
;;
mov ar.rsc = 0 // put RSE into enforced lazy mode
(p6) mov out1 = in2
(p7) mov out2 = in2
(p6) ld8 r17 = [in2] // get address of function descriptor
(p7) add out0 = -1, in0
(p7) mov out1 = in1
;;
(p6) ld8 r16 = [r17], 8 // load entry point
shl r8 = in0, 32 // store iteration # in top 32 bits
mov r18 = in1
;;
(p6) ld8 r1 = [r17] // load gp
(p6) mov b6 = r16
(p6) mov out0 = 0
;;
LD_LOC( 2); LD_LOC( 3)
LD_LOC( 4); LD_LOC( 5); LD_LOC( 6); LD_LOC( 7)
LD_LOC( 8); LD_LOC( 9); LD_LOC(10); LD_LOC(11)
LD_LOC(12); LD_LOC(13); LD_LOC(14); LD_LOC(15)
LD_LOC(16); LD_LOC(17); LD_LOC(18); LD_LOC(19)
LD_LOC(20); LD_LOC(21); LD_LOC(22); LD_LOC(23)
LD_LOC(24); LD_LOC(25); LD_LOC(26); LD_LOC(27)
LD_LOC(28); LD_LOC(29); LD_LOC(30); LD_LOC(31)
LD_LOC(32); LD_LOC(33); LD_LOC(34); LD_LOC(35)
LD_LOC(36); LD_LOC(37); LD_LOC(38); LD_LOC(39)
LD_LOC(40); LD_LOC(41); LD_LOC(42); LD_LOC(43)
LD_LOC(44); LD_LOC(45); LD_LOC(46); LD_LOC(47)
LD_LOC(48); LD_LOC(49); LD_LOC(50); LD_LOC(51)
LD_LOC(52); LD_LOC(53); LD_LOC(54); LD_LOC(55)
LD_LOC(56); LD_LOC(57); LD_LOC(58); LD_LOC(59)
LD_LOC(60); LD_LOC(61); LD_LOC(62); LD_LOC(63)
LD_LOC(64); LD_LOC(65); LD_LOC(66); LD_LOC(67)
LD_LOC(68); LD_LOC(69); LD_LOC(70); LD_LOC(71)
LD_LOC(72); LD_LOC(73); LD_LOC(74); LD_LOC(75)
LD_LOC(76); LD_LOC(77); LD_LOC(78); LD_LOC(79)
LD_LOC(80); LD_LOC(81); LD_LOC(82); LD_LOC(83)
LD_LOC(84); LD_LOC(85); LD_LOC(86); LD_LOC(87)
LD_LOC(88); LD_LOC(89)
;;
{ .mbb
mov in1 = r18
(p6) br.call.sptk.many rp = b6
(p7) br.call.sptk.many rp = loadup
}
cmp.lt p8, p9 = r8, r0
shl r9 = in0, 32 // store iteration # in top 32 bits
(p8) br.cond.spnt.few .fail
;;
add r8 = 1, r8
CK_LOC( 2); CK_LOC( 3)
CK_LOC( 4); CK_LOC( 5); CK_LOC( 6); CK_LOC( 7)
CK_LOC( 8); CK_LOC( 9); CK_LOC(10); CK_LOC(11)
CK_LOC(12); CK_LOC(13); CK_LOC(14); CK_LOC(15)
CK_LOC(16); CK_LOC(17); CK_LOC(18); CK_LOC(19)
CK_LOC(20); CK_LOC(21); CK_LOC(22); CK_LOC(23)
CK_LOC(24); CK_LOC(25); CK_LOC(26); CK_LOC(27)
CK_LOC(28); CK_LOC(29); CK_LOC(30); CK_LOC(31)
CK_LOC(32); CK_LOC(33); CK_LOC(34); CK_LOC(35)
CK_LOC(36); CK_LOC(37); CK_LOC(38); CK_LOC(39)
CK_LOC(40); CK_LOC(41); CK_LOC(42); CK_LOC(43)
CK_LOC(44); CK_LOC(45); CK_LOC(46); CK_LOC(47)
CK_LOC(48); CK_LOC(49); CK_LOC(50); CK_LOC(51)
CK_LOC(52); CK_LOC(53); CK_LOC(54); CK_LOC(55)
CK_LOC(56); CK_LOC(57); CK_LOC(58); CK_LOC(59)
CK_LOC(60); CK_LOC(61); CK_LOC(62); CK_LOC(63)
CK_LOC(64); CK_LOC(65); CK_LOC(66); CK_LOC(67)
CK_LOC(68); CK_LOC(69); CK_LOC(70); CK_LOC(71)
CK_LOC(72); CK_LOC(73); CK_LOC(74); CK_LOC(75)
CK_LOC(76); CK_LOC(77); CK_LOC(78); CK_LOC(79)
CK_LOC(80); CK_LOC(81); CK_LOC(82); CK_LOC(83)
CK_LOC(84); CK_LOC(85); CK_LOC(86); CK_LOC(87)
CK_LOC(88); CK_LOC(89)
.fail:
mov rp = loc0
mov ar.pfs = loc1
br.ret.sptk.many rp
.endp loadup
.global resumption_point_label
.proc resumption_point
resumption_point:
resumption_point_label:
.prologue
.save rp, r16
.save ar.pfs, r0
.body
mov r8 = r15
mov b6 = r16
;;
br.cond.sptk.many b6
.endp resumption_point
#ifdef __linux__
/* We do not need executable stack. */
.section .note.GNU-stack,"",@progbits
#endif

View File

@@ -0,0 +1,3 @@
#define NSTACKS 128
#define STACK_SIZE_SHIFT 17
#define STACK_SIZE (1 << STACK_SIZE_SHIFT)

View File

@@ -0,0 +1,55 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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. */
.text
.global test_func
.proc test_func
test_func:
.prologue
.regstk 1, 3, 0, 0
.save ar.pfs, loc0
alloc loc0 = ar.pfs, 1, 3, 0, 0
mov loc1 = rp
.save rp, r0
.save ar.lc, r0
.body
mov loc2 = gp
ld8 r2 = [in0], 8;;
ld8 r1 = [in0];;
mov b6 = r2
br.call.sptk.many rp = b6
mov gp = loc2
mov rp = loc1
mov ar.pfs = loc0
br.ret.sptk.many rp
.endp test_func
#ifdef __linux__
/* We do not need executable stack. */
.section .note.GNU-stack,"",@progbits
#endif

View File

@@ -0,0 +1,155 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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. */
/* Test to verify that we can siglongjmp() into a frame whose register
window is not backed by valid memory. */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <setjmp.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#ifdef HAVE_IA64INTRIN_H
# include <ia64intrin.h>
#endif
static sigjmp_buf env;
static int return_level;
static uintptr_t return_bsp;
static int verbose;
uintptr_t
get_bsp (void)
{
#ifdef __INTEL_COMPILER
return __getReg (_IA64_REG_AR_BSP);
#else
return (uintptr_t) __builtin_ia64_bsp ();
#endif
}
static void
sighandler (int signal, void *siginfo, void *sigcontext)
{
ucontext_t *uc = sigcontext;
int local = 0;
if (verbose)
printf ("got signal, stack at %p, saved bsp=0x%lx\n",
&local, uc->uc_mcontext.sc_ar_bsp);
siglongjmp (env, 1);
}
/* Direct call of doit () at the end of doit () would get optimized by GCC to
a branch. */
static void doit (int n);
typedef void (*doit_type) (int);
static volatile doit_type doit_pointer = doit;
static void
doit (int n)
{
uintptr_t guard_page_addr, bsp = get_bsp ();
void *ret;
if (n == 0)
{
size_t page_size = getpagesize ();
guard_page_addr = (bsp + page_size - 1) & -page_size;
if (verbose)
printf ("guard_page_addr = 0x%lx\n", (unsigned long) guard_page_addr);
ret = mmap ((void *) guard_page_addr, page_size, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ret != (void *) guard_page_addr)
{
if (ret == MAP_FAILED)
perror ("mmap");
else
fprintf (stderr, "mmap() returned %p, expected 0x%lx\n",
ret, guard_page_addr);
exit (EXIT_FAILURE);
}
}
if (sigsetjmp (env, 1))
{
return_level = n;
return_bsp = bsp;
}
else
(*doit_pointer) (n + 1);
}
int
main (int argc, char **argv)
{
struct sigaction sa;
stack_t ss;
if (argc > 1)
verbose = 1;
ss.ss_sp = malloc (2 * SIGSTKSZ);
if (ss.ss_sp == NULL)
{
puts ("failed to allocate alternate stack");
return EXIT_FAILURE;
}
ss.ss_flags = 0;
ss.ss_size = 2 * SIGSTKSZ;
if (sigaltstack (&ss, NULL) < 0)
{
printf ("sigaltstack failed: %s\n", strerror (errno));
return EXIT_FAILURE;
}
sa.sa_handler = (void (*) (int)) sighandler;
sigemptyset (&sa.sa_mask);
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
if (sigaction (SIGSEGV, &sa, NULL) < 0)
{
printf ("sigaction failed: %s\n", strerror (errno));
exit (1);
}
doit (0);
if (verbose)
{
printf ("sigsetjmp returned at level %d bsp=0x%lx\n",
return_level, return_bsp);
puts ("Test succeeded!");
}
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,102 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2001-2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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. */
/* This test uses the unwind interface to modify the IP in an ancestor
frame while still returning to the parent frame. */
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <libunwind-ia64.h>
#define panic(args...) \
{ fprintf (stderr, args); exit (-1); }
int verbose;
static void
sighandler (int signal)
{
unw_cursor_t cursor, cursor2;
unw_word_t ip;
unw_context_t uc;
if (verbose)
printf ("caught signal %d\n", signal);
unw_getcontext (&uc);
if (unw_init_local (&cursor, &uc) < 0)
panic ("unw_init() failed!\n");
/* get cursor for caller of sighandler: */
if (unw_step (&cursor) < 0)
panic ("unw_step() failed!\n");
cursor2 = cursor;
while (!unw_is_signal_frame (&cursor2))
if (unw_step (&cursor2) < 0)
panic ("failed to find signal frame!\n");
if (unw_step (&cursor2) < 0)
panic ("unw_step() failed!\n");
if (unw_get_reg (&cursor2, UNW_REG_IP, &ip) < 0)
panic ("failed to get IP!\n");
/* skip faulting instruction (doesn't handle MLX template) */
++ip;
if ((ip & 0x3) == 0x3)
ip += 13;
if (unw_set_reg (&cursor2, UNW_REG_IP, ip) < 0)
panic ("failed to set IP!\n");
unw_resume (&cursor); /* update context & return to caller of sighandler() */
panic ("unexpected return from unw_resume()!\n");
}
static void
doit (volatile char *p)
{
int ch;
ch = *p; /* trigger SIGSEGV */
if (verbose)
printf ("doit: finishing execution!\n");
}
int
main (int argc, char **argv)
{
if (argc > 1)
verbose = 1;
signal (SIGSEGV, sighandler);
doit (0);
if (verbose)
printf ("SUCCESS\n");
return 0;
}

View File

@@ -0,0 +1,183 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2003 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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 "ia64-test-stack.h"
.common stackmem, NSTACKS*STACK_SIZE, 16
.global do_unwind_tests
.text
#define SAVED_SP_OFF 0
#define SAVED_RP_OFF 8
#define SAVED_PFS_OFF 16
#define SAVED_RNAT_OFF 24
#define SAVED_BSP_OFF 32
#define SAVED_BSPSTORE_OFF 40
#define FRAME_SIZE 48
.proc stack_it
stack_it:
.prologue
alloc r18 = ar.pfs, 0, 0, 0, 0 // read ar.pfs
// first, calculate address of new stack:
addl r2 = @ltoff(stackmem), gp
shl r3 = r8, STACK_SIZE_SHIFT
;;
ld8 r2 = [r2] // r2 = &stackmem
;;
add r2 = r2, r3 // r2 = stackmem[iteration]
;;
addl r3 = STACK_SIZE-FRAME_SIZE, r2 // r3 = &stackframe
;;
st8 [r3] = sp
.vframesp SAVED_SP_OFF+16
adds sp = -16, r3 // switch the memory stack
;;
adds r3 = (SAVED_RP_OFF - SAVED_SP_OFF), r3
mov r16 = rp
;;
.savesp rp, SAVED_RP_OFF+16
st8 [r3] = r16, (SAVED_PFS_OFF - SAVED_RP_OFF)
;;
.savesp ar.pfs, SAVED_PFS_OFF+16
st8 [r3] = r18, (SAVED_BSP_OFF - SAVED_PFS_OFF)
mov r16 = ar.bsp
mov r17 = ar.bspstore
mov r18 = ar.rnat
;;
.savesp ar.bsp, SAVED_BSP_OFF+16
st8 [r3] = r16, (SAVED_BSPSTORE_OFF - SAVED_BSP_OFF)
;;
.savesp ar.bspstore, SAVED_BSPSTORE_OFF+16
st8 [r3] = r17, (SAVED_RNAT_OFF - SAVED_BSPSTORE_OFF)
;;
.savesp ar.rnat, SAVED_RNAT_OFF+16
st8 [r3] = r18
;;
mov ar.bspstore = r2 // switch the backing store
.body
// for even iterations, allocate a local variable:
tbit.nz p6, p0 = r8, 0
(p6) br.cond.sptk.few .skip
;;
alloc r2 = ar.pfs, 0, 1, 0, 0
mov loc0 = r8
;;
.skip: cmp.ne p6, p7 = 0, r8
;;
{ .mbb
(p6) adds r8 = -1, r8
(p6) br.call.sptk.many rp = stack_it // next iteration
(p7) br.call.sptk.many rp = do_unwind_tests // time for introspection...
}
// switch back to stack:
adds r3 = SAVED_SP_OFF+16, sp
;;
ld8 r16 = [r3], (SAVED_RP_OFF-SAVED_SP_OFF);; // saved sp
ld8 r17 = [r3], (SAVED_PFS_OFF-SAVED_RP_OFF);; // saved rp
ld8 r18 = [r3], (SAVED_RNAT_OFF-SAVED_PFS_OFF);; // saved pfs
ld8 r19 = [r3], (SAVED_BSP_OFF-SAVED_RNAT_OFF);; // saved rnat
ld8 r20 = [r3], (SAVED_BSPSTORE_OFF-SAVED_BSP_OFF);; // saved bsp
ld8 r21 = [r3];; // saved bspstore
mov rp = r17
mov ar.pfs = r18
mov ar.bspstore = r21 // this also restores ar.bsp
;;
mov ar.rnat = r19
.restore sp
mov sp = r16
br.ret.sptk.many rp
.endp stack_it
#define SET_LOC(n) add loc##n = n, r8
#define SET_NAT(n) ld8.s loc##n = [r0]
.global touch_all
.proc touch_all
touch_all:
.prologue
.save ar.pfs, r34
alloc loc1 = ar.pfs, 1, 94, 1, 0
.save rp, loc0
mov loc0 = rp
.body
mov ar.rsc = 0 // put RSE into enforced lazy mode
shl r8 = in0, 32 // store iteration # in top 32 bits
add out0 = -1, in0
cmp.eq p6, p7 = 1, in0
;;
SET_LOC( 2); SET_LOC( 3)
SET_LOC( 4); SET_LOC( 5); SET_LOC( 6); SET_LOC( 7)
SET_LOC( 8); SET_LOC( 9); SET_LOC(10); SET_LOC(11)
SET_LOC(12); SET_LOC(13); SET_LOC(14); SET_LOC(15)
SET_LOC(16); SET_LOC(17); SET_LOC(18); SET_LOC(19)
SET_LOC(20); SET_LOC(21); SET_LOC(22); SET_LOC(23)
SET_LOC(24); SET_LOC(25); SET_LOC(26); SET_LOC(27)
SET_LOC(28); SET_LOC(29); SET_LOC(30); SET_LOC(31)
SET_LOC(32); SET_LOC(33); SET_LOC(34); SET_LOC(35)
SET_LOC(36); SET_LOC(37); SET_LOC(38); SET_LOC(39)
SET_LOC(40); SET_LOC(41); SET_LOC(42); SET_LOC(43)
SET_LOC(44); SET_LOC(45); SET_LOC(46); SET_LOC(47)
SET_LOC(48); SET_LOC(49); SET_LOC(50); SET_LOC(51)
SET_LOC(52); SET_LOC(53); SET_LOC(54); SET_LOC(55)
SET_LOC(56); SET_LOC(57); SET_LOC(58); SET_LOC(59)
SET_LOC(60); SET_LOC(61); SET_LOC(62); SET_LOC(63)
SET_LOC(64); SET_LOC(65); SET_LOC(66); SET_LOC(67)
SET_LOC(68); SET_LOC(69); SET_LOC(70); SET_LOC(71)
SET_LOC(72); SET_LOC(73); SET_LOC(74); SET_LOC(75)
SET_LOC(76); SET_LOC(77); SET_LOC(78); SET_LOC(79)
SET_LOC(80); SET_LOC(81); SET_LOC(82); SET_LOC(83)
SET_LOC(84); SET_LOC(85); SET_LOC(86); SET_LOC(87)
SET_LOC(88); SET_LOC(89); SET_LOC(90); SET_LOC(91)
SET_LOC(92); SET_LOC(93)
;;
SET_NAT(2); SET_NAT(31); SET_NAT(73); SET_NAT(93)
;;
{ .mbb
mov r8=NSTACKS-1
(p6) br.call.sptk.many rp = stack_it
(p7) br.call.sptk.many rp = touch_all
}
;;
mov rp = loc0
mov ar.pfs = loc1
br.ret.sptk.many rp
.endp touch_all
#ifdef __linux__
/* We do not need executable stack. */
.section .note.GNU-stack,"",@progbits
#endif

View File

@@ -0,0 +1,3 @@
#define NSTACKS 1024
#define STACK_SIZE_SHIFT 17
#define STACK_SIZE (1 << STACK_SIZE_SHIFT)

View File

@@ -0,0 +1,29 @@
/* libunwind - a platform-independent unwind library
*
* 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 "ident.h"
long
f (long val)
{
return val;
}

View File

@@ -0,0 +1,28 @@
/* libunwind - a platform-independent unwind library
*
* 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.
*/
#ifndef LIBUNWIND_TESTS_IDENT_H
#define LIBUNWIND_TESTS_IDENT_H
extern long f (long val);
#endif /* LIBUNWIND_TESTS_IDENT_H */

View File

@@ -0,0 +1,87 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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. */
/* This program creates lots of mappings such that on Linux the
reading of /proc/PID/maps gets very slow. With proper caching,
test-ptrace should still run at acceptable speed once
/proc/PID/maps has been scanned. If the program dies with a
SIGALRM, it means it was running unexpectedly slow. */
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
# define MAP_ANONYMOUS MAP_ANON
#endif
#if !defined(MAP_NORESERVE)
# define MAP_NORESERVE 0
#endif
void __attribute__((noinline)) push_some_stacks(int n)
{
if (n >= 1)
{
push_some_stacks(n - 1);
push_some_stacks(n - 1);
}
}
int
main (void)
{
long n = 0;
signal (SIGUSR1, SIG_IGN);
signal (SIGUSR2, SIG_IGN);
printf ("Starting mmap test...\n");
for (n = 0; n < 30000; ++n)
{
if (mmap (NULL, 1, (n & 1) ? PROT_READ : PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS
#ifdef MAP_NORESERVE
| MAP_NORESERVE
#endif
,
-1, 0) == MAP_FAILED)
{
printf ("Failed after %ld successful maps\n", n - 1);
exit (0);
}
}
alarm (80); /* die if we don't finish in 80 seconds */
printf ("Turning on single-stepping...\n");
kill (getpid (), SIGUSR1); /* tell test-ptrace to start single-stepping */
push_some_stacks (4);
kill (getpid (), SIGUSR2); /* tell test-ptrace to stop single-stepping */
printf ("Turned single-stepping off...\n");
return 0;
}

View File

@@ -0,0 +1,19 @@
#!/bin/sh
platform=$1
LIBUNWIND=../src/.libs/libunwind.so
LIBUNWIND_PLAT=../src/.libs/libunwind-$platform.so
warmup=$(./forker 2000 /bin/true | cut -f1 -d' ')
nsec1=$(./forker 2000 /bin/true | cut -f1 -d' ')
printf "\"/bin/true\"\t\t\t\t\t\t: $nsec1 nsec/execution\n"
nsec2=$(LD_PRELOAD=$LIBUNWIND ./forker 2000 /bin/true | cut -f1 -d' ')
printf "\"LD_PRELOAD=$LIBUNWIND /bin/true\"\t: $nsec2 nsec/execution\n"
nsec3=$(LD_PRELOAD=$LIBUNWIND_PLAT ./forker 2000 /bin/true | cut -f1 -d' ')
printf "\"LD_PRELOAD=$LIBUNWIND_PLAT /bin/true\"\t: $nsec3 nsec/execution\n"
echo
printf "Overhead of preloading $LIBUNWIND\t: $(($nsec2 - $nsec1)) nsec\n"
printf "Overhead of preloading $LIBUNWIND_PLAT\t: $(($nsec3 - $nsec1)) nsec\n"

View File

@@ -0,0 +1,32 @@
#include <stdio.h>
#include <altivec.h>
union si_overlay
{
vector signed int v;
int ints[4];
};
vector signed int
vec_init ()
{
vector signed int v;
static int count = 1;
((union si_overlay *) &v)->ints[0] = count++;
((union si_overlay *) &v)->ints[1] = count++;
((union si_overlay *) &v)->ints[2] = count++;
((union si_overlay *) &v)->ints[3] = count++;
return v;
}
void
vec_print (vector signed int v)
{
printf ("%08x %08x %08x %08x",
((union si_overlay *) &v)->ints[0],
((union si_overlay *) &v)->ints[1],
((union si_overlay *) &v)->ints[2],
((union si_overlay *) &v)->ints[3]);
}

View File

@@ -0,0 +1,177 @@
#include <libunwind.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <altivec.h>
#include <sys/resource.h>
#define panic(args...) { fprintf (stderr, args); abort(); }
extern vector signed int vec_init ();
extern void vec_print (vector signed int v);
vector signed int vec_stack (int count);
int
main ()
{
printf ("&vec_stack = %016lx\n", (unsigned long) vec_stack);
vec_stack (3);
return 0;
}
vector signed int
vec_stack (int count)
{
register vector signed int v1;
register vector signed int v2;
register vector signed int v3;
register vector signed int v4;
register vector signed int v5;
register vector signed int v6;
register vector signed int v7;
register vector signed int v8;
register vector signed int v9;
unw_fpreg_t vr;
unw_cursor_t cursor;
unw_word_t ip, sp;
unw_context_t uc;
int ret;
int verbose = 1;
/* if (count == 0) return vec_init(); */
if (count == 0)
{
unw_getcontext (&uc);
if (unw_init_local (&cursor, &uc) < 0)
{
panic ("unw_init_local failed!\n");
}
else
{
do
{
if ((ret = unw_get_reg (&cursor, UNW_REG_IP, &ip)) < 0)
{
panic ("FAILURE: unw_get_reg returned %d for UNW_REG_IP\n",
ret);
}
if ((ret = unw_get_reg (&cursor, UNW_REG_SP, &sp)) < 0)
{
panic ("FAILURE: unw_get_reg returned %d for UNW_REG_SP\n",
ret);
}
if ((ret = unw_get_fpreg (&cursor, UNW_PPC64_V30, &vr)) < 0)
{
panic
("FAILURE: unw_get_vreg returned %d for UNW_PPC64_V30\n",
ret);
}
if (verbose)
{
const char *regname = unw_regname (UNW_PPC64_V30);
char proc_name_buffer[256];
unw_word_t offset;
unsigned int * vec_half1, * vec_half2;
vec_half1 = (unsigned int *)&vr;
vec_half2 = vec_half1 + 1;
printf ("ip = %016lx, sp=%016lx\n", (long) ip, (long) sp);
printf ("vr30 = %08x %08x %08x %08x\n",
(unsigned int) (*vec_half1 >> 16),
(unsigned int) (*vec_half1 & 0xffffffff),
(unsigned int) (*vec_half2 >> 16),
(unsigned int) (*vec_half2 & 0xffffffff));
ret =
unw_get_proc_name (&cursor, proc_name_buffer,
sizeof (proc_name_buffer), &offset);
if (ret == 0)
{
printf ("proc name = %s, offset = %lx\n",
proc_name_buffer, offset);
}
else
{
panic ("unw_get_proc_name returned %d\n", ret);
}
printf ("unw_regname(UNW_PPC_V30) = %s\n\n", regname);
}
ret = unw_step (&cursor);
if (ret < 0)
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
panic ("FAILURE: unw_step() returned %d for ip=%lx\n", ret,
(long) ip);
}
}
while (ret > 0);
}
}
v1 = vec_init ();
v2 = vec_init ();
v3 = vec_init ();
v4 = vec_init ();
v5 = vec_init ();
v6 = vec_init ();
/* make use of all of the registers in some calculation */
v7 =
vec_nor (v1, vec_add (v2, vec_sub (v3, vec_and (v4, vec_or (v5, v6)))));
/*
* "force" the registers to be non-volatile by making a call and also
* using the registers after the call.
*/
v8 = vec_stack (count - 1);
/*
* Use the result from the previous call, plus all of the non-volatile
* registers in another calculation.
*/
v9 =
vec_nor (v1,
vec_add (v2,
vec_sub (v3,
vec_and (v4, vec_or (v5, vec_xor (v6, v8))))));
printf ("v1 - ");
vec_print (v1);
printf ("\n");
printf ("v2 - ");
vec_print (v2);
printf ("\n");
printf ("v3 - ");
vec_print (v3);
printf ("\n");
printf ("v4 - ");
vec_print (v4);
printf ("\n");
printf ("v5 - ");
vec_print (v5);
printf ("\n");
printf ("v6 - ");
vec_print (v6);
printf ("\n");
printf ("v7 - ");
vec_print (v7);
printf ("\n");
printf ("v8 - ");
vec_print (v8);
printf ("\n");
printf ("v9 - ");
vec_print (v9);
printf ("\n");
return v9;
}

View File

@@ -0,0 +1,185 @@
/**
* Unittest PPC64 is_plt_entry function by inspecting output at
* different points in a mock PLT address space.
*/
/*
* 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 "dwarf.h"
#include "libunwind_i.h"
#undef unw_get_accessors_int
unw_accessors_t *unw_get_accessors_int (unw_addr_space_t unused) { return NULL; }
int dwarf_step (struct dwarf_cursor* unused) { return 0; }
enum
{
ip_guard0,
ip_std,
ip_ld,
ip_mtctr,
ip_bctr,
ip_guard1,
ip_program_end
};
/* Mock access_mem implementation */
static int
access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
void *arg)
{
if (write != 0)
return -1;
const size_t mem_size = ip_program_end * sizeof(uint32_t);
const void *mem_start = arg;
const void *mem_end = (const char*) arg + mem_size;
const unw_word_t *paddr = (const unw_word_t*) addr;
if ((void*) paddr < mem_start || (void*) paddr > mem_end)
{
return -1;
}
*val = *paddr;
return 0;
}
int
main ()
{
if (target_is_big_endian())
return 77;
const uint32_t plt_instructions[ip_program_end] =
{
0xdeadbeef,
0xf8410018, // std r2,24(r1)
0xe9828730, // ld r12,-30928(r2)
0x7d8903a6, // mtctr r12
0x4e800420, // bctr
0xdeadbeef,
};
uint32_t test_instructions[ip_program_end];
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
struct unw_addr_space mock_address_space;
mock_address_space.big_endian = 0;
mock_address_space.acc.access_mem = &access_mem;
struct cursor cursor;
struct dwarf_cursor *dwarf = &cursor.dwarf;
struct unw_cursor *c = (struct unw_cursor *)(&cursor);
dwarf->as = &mock_address_space;
dwarf->as_arg = &test_instructions;
/* ip at std r2,24(r1) */
dwarf->ip = (unw_word_t) (test_instructions + ip_std);
if (!unw_is_plt_entry(c)) return -1;
/* ld uses a different offset */
test_instructions[ip_ld] = 0xe9820000;
if (!unw_is_plt_entry(c)) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip_ld is not a ld instruction */
test_instructions[ip_ld] = 0xf154f00d;
if (unw_is_plt_entry(c)) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip_mtctr is not a mtctr instruction */
test_instructions[ip_mtctr] = 0xf154f00d;
if (unw_is_plt_entry(c)) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip_bctr is not a bctr instruction */
test_instructions[ip_bctr] = 0xf154f00d;
if (unw_is_plt_entry(c)) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip at ld r12,-30928(r2) */
dwarf->ip = (unw_word_t) (test_instructions + ip_ld);
if (!unw_is_plt_entry(c)) return -1;
/* ip_std is not a std instruction */
test_instructions[ip_std] = 0xf154f00d;
if (unw_is_plt_entry(c)) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip_mtctr is not a mtctr instruction */
test_instructions[ip_mtctr] = 0xf154f00d;
if (unw_is_plt_entry(c)) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip_bctr is not a bctr instruction */
test_instructions[ip_bctr] = 0xf154f00d;
if (unw_is_plt_entry(c)) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip at mtctr r12 */
dwarf->ip = (unw_word_t) (test_instructions + ip_mtctr);
if (!unw_is_plt_entry(c)) return -1;
/* ip_std is not a std instruction */
test_instructions[ip_std] = 0xf154f00d;
if (unw_is_plt_entry(c)) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip_ld is not a ld instruction */
test_instructions[ip_ld] = 0xf154f00d;
if (unw_is_plt_entry(c)) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip_bctr is not a bctr instruction */
test_instructions[ip_bctr] = 0xf154f00d;
if (unw_is_plt_entry(c)) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip at bctr */
dwarf->ip = (unw_word_t) (test_instructions + ip_bctr);
if (!unw_is_plt_entry(c)) return -1;
/* ip_std is not a std instruction */
test_instructions[ip_std] = 0xf154f00d;
if (unw_is_plt_entry(c)) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip_ld is not a ld instruction */
test_instructions[ip_ld] = 0xf154f00d;
if (unw_is_plt_entry(c)) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip_mtctr is not a mtctr instruction */
test_instructions[ip_mtctr] = 0xf154f00d;
if (unw_is_plt_entry(c)) return -1;
memcpy(test_instructions, plt_instructions, sizeof(test_instructions));
/* ip at non-PLT instruction */
dwarf->ip = (unw_word_t) (test_instructions + ip_guard0);
if (unw_is_plt_entry(c)) return -1;
/* ip at another non-PLT instruction */
dwarf->ip = (unw_word_t) (test_instructions + ip_guard1);
if (unw_is_plt_entry(c)) return -1;
return 0;
}

View File

@@ -0,0 +1,58 @@
#!/bin/sh
# this function is slight modification of the one used in RPM
# found at https://bugzilla.redhat.com/show_bug.cgi?id=834073
# written by Alexander Larsson <alexl@redhat.com>
add_minidebug()
{
debuginfo="$1" ## we don't have separate debuginfo file
binary="$1"
dynsyms=`mktemp`
funcsyms=`mktemp`
keep_symbols=`mktemp`
mini_debuginfo=`mktemp`
# Extract the dynamic symbols from the main binary, there is no need to also have these
# in the normal symbol table
nm -D "$binary" --format=posix --defined-only | awk '{ print $1 }' | sort > "$dynsyms"
# Extract all the text (i.e. function) symbols from the debuginfo
nm "$debuginfo" --format=posix --defined-only | awk '{ if ($2 == "T" || $2 == "t") print $1 }' | sort > "$funcsyms"
# Keep all the function symbols not already in the dynamic symbol table
comm -13 "$dynsyms" "$funcsyms" > "$keep_symbols"
# Copy the full debuginfo, keeping only a minimal set of symbols and removing some unnecessary sections
objcopy -S --remove-section .gdb_index --remove-section .comment --keep-symbols="$keep_symbols" "$debuginfo" "$mini_debuginfo" &> /dev/null
wait
#Inject the compressed data into the .gnu_debugdata section of the original binary
xz "$mini_debuginfo"
mini_debuginfo="${mini_debuginfo}.xz"
objcopy --add-section .gnu_debugdata="$mini_debuginfo" "$binary"
rm -f "$dynsyms" "$funcsyms" "$keep_symbols" "$mini_debuginfo"
strip "$binary" ## throw away the symbol table
}
TESTDIR=`pwd`
TEMPDIR=`mktemp --tmpdir -d libunwind-test-XXXXXXXXXX`
trap "rm -r -- $TEMPDIR" EXIT
cp crasher $TEMPDIR/crasher
if [ "$1" = "-minidebuginfo" ]; then
add_minidebug $TEMPDIR/crasher
fi
# create core dump
(
cd $TEMPDIR
ulimit -c 10000
./crasher backing_files
) 2>/dev/null
COREFILE=$TEMPDIR/core*
if ! test -f "$COREFILE"; then
echo "crasher process did not produce coredump, test skipped"
exit 77
fi
# magic option -testcase enables checking for the specific contents of the stack
./test-coredump-unwind $COREFILE -testcase `cat $TEMPDIR/backing_files`

View File

@@ -0,0 +1,8 @@
#!/bin/sh
# This test intends to test the unw_get_proc_name function on binaries without
# the symbol table but with so called MiniDebuginfo available. In particular,
# it is tested using the coredump accessors. For more info about MiniDebugInfo
# see e.g. http://fedoraproject.org/wiki/Features/MiniDebugInfo
${0%/*}/run-coredump-unwind -minidebuginfo

View File

@@ -0,0 +1,2 @@
#!/bin/sh
./test-ptrace -t ./ia64-test-dyn1

View File

@@ -0,0 +1,24 @@
#!/bin/sh
#
# 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.
#
bindir="$(pwd)"
"${bindir}/test-ptrace" -c -n -t "${bindir}/mapper" $*

View File

@@ -0,0 +1,24 @@
#!/bin/sh
#
# 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.
#
bindir=$(pwd)
"${bindir}/test-ptrace" -c -t "${bindir}/test-ptrace-misc"

View File

@@ -0,0 +1,193 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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. */
/* Check whether basic unwinding truly is async-signal safe. */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "compiler.h"
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#define UNW_LOCAL_ONLY
#include <libunwind.h>
static const int nerrors_max = 100;
struct itimerval interval =
{
.it_interval = { .tv_sec = 0, .tv_usec = 0 },
.it_value = { .tv_sec = 0, .tv_usec = 1000 }
};
int verbose;
int nerrors;
int sigcount;
#ifndef CONFIG_BLOCK_SIGNALS
/* When libunwind is configured with --enable-block-signals=no, the caller
is responsible for preventing recursion via signal handlers.
We use a simple global here. In a multithreaded program, one would use
a thread-local variable. */
int recurcount;
#endif
#define panic(...) \
{ ++nerrors; fprintf (stderr, __VA_ARGS__); return; }
static void
do_backtrace (int may_print, int get_proc_name)
{
char buf[512], name[256];
unw_cursor_t cursor;
unw_word_t ip, sp, off;
unw_context_t uc;
int ret;
int depth = 0;
#ifndef CONFIG_BLOCK_SIGNALS
if (recurcount > 0)
return;
recurcount += 1;
#endif
unw_getcontext (&uc);
if (unw_init_local (&cursor, &uc) < 0)
panic ("unw_init_local failed!\n");
do
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
unw_get_reg (&cursor, UNW_REG_SP, &sp);
buf[0] = '\0';
if (get_proc_name || (may_print && verbose))
{
ret = unw_get_proc_name (&cursor, name, sizeof (name), &off);
if (ret == 0 && (may_print && verbose))
{
if (off)
snprintf (buf, sizeof (buf), "<%s+0x%lx>", name, (long) off);
else
{
size_t len = strlen (name);
buf[0] = '<';
memcpy (buf + 1, name, len);
buf[len + 1] = '>';
buf[len + 2] = '\0';
}
}
}
if (may_print && verbose)
printf ("%016lx %-32s (sp=%016lx)\n", (long) ip, buf, (long) sp);
ret = unw_step (&cursor);
if (ret < 0)
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
panic ("FAILURE: unw_step() returned %d for ip=%lx\n",
ret, (long) ip);
}
if (depth++ > 100)
{
panic ("FAILURE: unw_step() looping over %d iterations\n", depth);
break;
}
}
while (ret > 0);
#ifndef CONFIG_BLOCK_SIGNALS
recurcount -= 1;
#endif
}
void
sighandler (int signal)
{
if (verbose)
printf ("sighandler(signal=%d, count=%d)\n", signal, sigcount);
do_backtrace (1, 1);
++sigcount;
if (sigcount == 100)
unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_GLOBAL);
else if (sigcount == 200)
unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_PER_THREAD);
else if (sigcount == 300 || nerrors > nerrors_max)
{
if (nerrors > nerrors_max)
panic ("Too many errors (%d)\n", nerrors);
if (nerrors)
{
fprintf (stderr, "FAILURE: detected %d errors\n", nerrors);
exit (-1);
}
if (verbose)
printf ("SUCCESS.\n");
exit (0);
}
setitimer (ITIMER_REAL, &interval, NULL);
}
int
main (int argc, char **argv UNUSED)
{
struct sigaction act;
long i = 0;
if (argc > 1)
verbose = 1;
unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_NONE);
memset (&act, 0, sizeof (act));
act.sa_handler = sighandler;
act.sa_flags = SA_SIGINFO;
sigaction (SIGALRM, &act, NULL);
setitimer (ITIMER_REAL, &interval, NULL);
while (1)
{
if (verbose)
printf ("%s: starting backtrace\n", __FUNCTION__);
do_backtrace (0, (i++ % 100) == 0);
if (nerrors > nerrors_max)
{
fprintf (stderr, "Too many errors (%d)\n", nerrors);
exit (-1);
}
}
return (0);
}

View File

@@ -0,0 +1,265 @@
/*
* Example program for unwinding core dumps.
*
* Compile a-la:
* gcc -Os -Wall \
* -Wl,--start-group \
* -lunwind -lunwind-x86 -lunwind-coredump \
* example-core-unwind.c \
* -Wl,--end-group \
* -oexample-core-unwind
*
* Run:
* eu-unstrip -n --core COREDUMP
* figure out which virtual addresses in COREDUMP correspond to which mapped executable files
* (binary and libraries), then supply them like this:
* ./example-core-unwind COREDUMP 0x400000:/bin/crashed_program 0x3458600000:/lib/libc.so.6 [...]
*
* Note: Program eu-unstrip is part of elfutils, virtual addresses of shared
* libraries can be determined by ldd (at least on linux).
*/
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <unistd.h>
#include "compiler.h"
#include <libunwind-coredump.h>
/* Utility logging functions */
enum {
LOGMODE_NONE = 0,
LOGMODE_STDIO = (1 << 0),
};
const char *msg_prefix = "";
const char *msg_eol = "\n";
int logmode = LOGMODE_STDIO;
int xfunc_error_retval = EXIT_FAILURE;
void xfunc_die(void)
{
exit(xfunc_error_retval);
}
static void verror_msg_helper(const char *s,
va_list p,
const char* strerr,
int flags)
{
char *msg;
int prefix_len, strerr_len, msgeol_len, used;
if (!logmode)
return;
used = vasprintf(&msg, s, p);
if (used < 0)
return;
/* This is ugly and costs +60 bytes compared to multiple
* fprintf's, but is guaranteed to do a single write.
* This is needed for e.g. when multiple children
* can produce log messages simultaneously. */
prefix_len = msg_prefix[0] ? strlen(msg_prefix) + 2 : 0;
strerr_len = strerr ? strlen(strerr) : 0;
msgeol_len = strlen(msg_eol);
/* +3 is for ": " before strerr and for terminating NUL */
char *msg1 = (char*) realloc(msg, prefix_len + used + strerr_len + msgeol_len + 3);
if (!msg1)
{
free(msg);
return;
}
msg = msg1;
/* TODO: maybe use writev instead of memmoving? Need full_writev? */
if (prefix_len)
{
char *p;
memmove(msg + prefix_len, msg, used);
used += prefix_len;
p = stpcpy(msg, msg_prefix);
p[0] = ':';
p[1] = ' ';
}
if (strerr)
{
if (s[0])
{
msg[used++] = ':';
msg[used++] = ' ';
}
strcpy(&msg[used], strerr);
used += strerr_len;
}
strcpy(&msg[used], msg_eol);
if (flags & LOGMODE_STDIO)
{
fflush(stdout);
ssize_t written UNUSED = write(STDERR_FILENO, msg, used + msgeol_len);
}
msg[used] = '\0'; /* remove msg_eol (usually "\n") */
free(msg);
}
void log_msg(const char *s, ...)
{
va_list p;
va_start(p, s);
verror_msg_helper(s, p, NULL, logmode);
va_end(p);
}
/* It's a macro, not function, since it collides with log() from math.h */
#undef log
#define log(...) log_msg(__VA_ARGS__)
void error_msg_and_die(const char *s, ...)
{
va_list p;
va_start(p, s);
verror_msg_helper(s, p, NULL, logmode);
va_end(p);
xfunc_die();
}
/* End of utility logging functions */
int
main(int argc UNUSED, char **argv)
{
unw_addr_space_t as;
struct UCD_info *ui;
unw_cursor_t c;
int ret;
#define TEST_FRAMES 4
#define TEST_NAME_LEN 32
int testcase = 0;
int test_cur = 0;
long test_start_ips[TEST_FRAMES];
char test_names[TEST_FRAMES][TEST_NAME_LEN];
const char *progname = strrchr(argv[0], '/');
if (progname)
progname++;
else
progname = argv[0];
if (!argv[1])
error_msg_and_die("Usage: %s COREDUMP [VADDR:BINARY_FILE]...", progname);
msg_prefix = progname;
as = unw_create_addr_space(&_UCD_accessors, 0);
if (!as)
error_msg_and_die("unw_create_addr_space() failed");
ui = _UCD_create(argv[1]);
if (!ui)
error_msg_and_die("_UCD_create('%s') failed", argv[1]);
ret = unw_init_remote(&c, as, ui);
if (ret < 0)
error_msg_and_die("unw_init_remote() failed: ret=%d\n", ret);
argv += 2;
/* Enable checks for the crasher test program? */
if (*argv && !strcmp(*argv, "-testcase"))
{
testcase = 1;
logmode = LOGMODE_NONE;
argv++;
}
for (;;)
{
unw_word_t ip;
ret = unw_get_reg(&c, UNW_REG_IP, &ip);
if (ret < 0)
error_msg_and_die("unw_get_reg(UNW_REG_IP) failed: ret=%d\n", ret);
unw_proc_info_t pi;
ret = unw_get_proc_info(&c, &pi);
if (ret < 0)
error_msg_and_die("unw_get_proc_info(ip=0x%lx) failed: ret=%d\n", (long) ip, ret);
if (!testcase)
{
char proc_name[128];
unw_word_t off;
unw_get_proc_name(&c, proc_name, sizeof(proc_name), &off);
printf("\tip=0x%08lx proc=%08lx-%08lx handler=0x%08lx lsda=0x%08lx %s\n",
(long) ip,
(long) pi.start_ip, (long) pi.end_ip,
(long) pi.handler, (long) pi.lsda, proc_name);
char filename[PATH_MAX];
unw_word_t file_offset;
ret = unw_get_elf_filename (&c, filename, sizeof (filename), &file_offset);
if (ret == UNW_ESUCCESS)
printf ("\t[%s+0x%lx]\n", filename, (long) file_offset);
}
if (testcase && test_cur < TEST_FRAMES)
{
unw_word_t off;
test_start_ips[test_cur] = (long) pi.start_ip;
if (unw_get_proc_name(&c, test_names[test_cur], sizeof(test_names[0]), &off) != 0)
{
test_names[test_cur][0] = '\0';
}
test_cur++;
}
log("step");
ret = unw_step(&c);
log("step done:%d", ret);
if (ret < 0)
error_msg_and_die("FAILURE: unw_step() returned %d", ret);
if (ret == 0)
break;
}
log("stepping ended");
/* Check that the second and third frames are equal, but distinct of the
* others */
if (testcase &&
(test_cur != 4
|| test_start_ips[1] != test_start_ips[2]
|| test_start_ips[0] == test_start_ips[1]
|| test_start_ips[2] == test_start_ips[3]
)
)
{
fprintf(stderr, "FAILURE: start IPs incorrect\n");
return -1;
}
if (testcase &&
( strcmp(test_names[0], "a")
|| strcmp(test_names[1], "b")
|| strcmp(test_names[2], "b")
|| strcmp(test_names[3], "main")
)
)
{
fprintf(stderr, "FAILURE: procedure names are missing/incorrect\n");
return -1;
}
_UCD_destroy(ui);
unw_destroy_addr_space(as);
return 0;
}

View File

@@ -0,0 +1,143 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2003 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
This file is part of libunwind.
Copyright (c) 2003 Hewlett-Packard Co.
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 <stdio.h>
#include <string.h>
#define UNW_LOCAL_ONLY /* must define this for consistency with backtrace() */
#include <libunwind.h>
int verbose;
int
f257 (void)
{
void *buffer[300];
int i, n;
if (verbose)
printf ("First backtrace:\n");
n = unw_backtrace (buffer, 300);
if (verbose)
for (i = 0; i < n; ++i)
printf ("[%d] ip=%p\n", i, buffer[i]);
unw_set_cache_size (unw_local_addr_space, 1023, 0);
unw_flush_cache (unw_local_addr_space, 0, 0);
if (verbose)
printf ("\nSecond backtrace:\n");
n = unw_backtrace (buffer, 300);
if (verbose)
for (i = 0; i < n; ++i)
printf ("[%d] ip=%p\n", i, buffer[i]);
return 0;
}
#define F(n,m) \
int \
f##n (void) \
{ \
return f##m (); \
}
/* Here, we rely on the fact that the script-cache's hash-table is 256
entries big. With 257 functions, we're guaranteed to get at least
one hash-collision. */
F(256,257) F(255,256) F(254,255) F(253,254)
F(252,253) F(251,252) F(250,251) F(249,250)
F(248,249) F(247,248) F(246,247) F(245,246)
F(244,245) F(243,244) F(242,243) F(241,242)
F(240,241) F(239,240) F(238,239) F(237,238)
F(236,237) F(235,236) F(234,235) F(233,234)
F(232,233) F(231,232) F(230,231) F(229,230)
F(228,229) F(227,228) F(226,227) F(225,226)
F(224,225) F(223,224) F(222,223) F(221,222)
F(220,221) F(219,220) F(218,219) F(217,218)
F(216,217) F(215,216) F(214,215) F(213,214)
F(212,213) F(211,212) F(210,211) F(209,210)
F(208,209) F(207,208) F(206,207) F(205,206)
F(204,205) F(203,204) F(202,203) F(201,202)
F(200,201) F(199,200) F(198,199) F(197,198)
F(196,197) F(195,196) F(194,195) F(193,194)
F(192,193) F(191,192) F(190,191) F(189,190)
F(188,189) F(187,188) F(186,187) F(185,186)
F(184,185) F(183,184) F(182,183) F(181,182)
F(180,181) F(179,180) F(178,179) F(177,178)
F(176,177) F(175,176) F(174,175) F(173,174)
F(172,173) F(171,172) F(170,171) F(169,170)
F(168,169) F(167,168) F(166,167) F(165,166)
F(164,165) F(163,164) F(162,163) F(161,162)
F(160,161) F(159,160) F(158,159) F(157,158)
F(156,157) F(155,156) F(154,155) F(153,154)
F(152,153) F(151,152) F(150,151) F(149,150)
F(148,149) F(147,148) F(146,147) F(145,146)
F(144,145) F(143,144) F(142,143) F(141,142)
F(140,141) F(139,140) F(138,139) F(137,138)
F(136,137) F(135,136) F(134,135) F(133,134)
F(132,133) F(131,132) F(130,131) F(129,130)
F(128,129) F(127,128) F(126,127) F(125,126)
F(124,125) F(123,124) F(122,123) F(121,122)
F(120,121) F(119,120) F(118,119) F(117,118)
F(116,117) F(115,116) F(114,115) F(113,114)
F(112,113) F(111,112) F(110,111) F(109,110)
F(108,109) F(107,108) F(106,107) F(105,106)
F(104,105) F(103,104) F(102,103) F(101,102)
F(100,101) F(99,100) F(98,99) F(97,98)
F(96,97) F(95,96) F(94,95) F(93,94)
F(92,93) F(91,92) F(90,91) F(89,90)
F(88,89) F(87,88) F(86,87) F(85,86)
F(84,85) F(83,84) F(82,83) F(81,82)
F(80,81) F(79,80) F(78,79) F(77,78)
F(76,77) F(75,76) F(74,75) F(73,74)
F(72,73) F(71,72) F(70,71) F(69,70)
F(68,69) F(67,68) F(66,67) F(65,66)
F(64,65) F(63,64) F(62,63) F(61,62)
F(60,61) F(59,60) F(58,59) F(57,58)
F(56,57) F(55,56) F(54,55) F(53,54)
F(52,53) F(51,52) F(50,51) F(49,50)
F(48,49) F(47,48) F(46,47) F(45,46)
F(44,45) F(43,44) F(42,43) F(41,42)
F(40,41) F(39,40) F(38,39) F(37,38)
F(36,37) F(35,36) F(34,35) F(33,34)
F(32,33) F(31,32) F(30,31) F(29,30)
F(28,29) F(27,28) F(26,27) F(25,26)
F(24,25) F(23,24) F(22,23) F(21,22)
F(20,21) F(19,20) F(18,19) F(17,18)
F(16,17) F(15,16) F(14,15) F(13,14)
F(12,13) F(11,12) F(10,11) F(9,10)
F(8,9) F(7,8) F(6,7) F(5,6)
F(4,5) F(3,4) F(2,3) F(1,2)
int
main (int argc, char **argv)
{
if (argc > 1 && strcmp (argv[1], "-v") == 0)
verbose = 1;
return f1 ();
}

View File

@@ -0,0 +1,108 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
This file is part of libunwind.
Copyright (c) 2002 Hewlett-Packard Co.
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. */
/* This test simply verifies that unw_init_remote() can be used in
lieu of unw_init_local(). This was broken for a while on ia64. */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "compiler.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libunwind.h>
#define panic(...) \
{ fprintf (stderr, __VA_ARGS__); exit (-1); }
int verbose;
static int
do_backtrace (void)
{
char buf[512], name[256];
unw_word_t ip, sp, off;
unw_cursor_t cursor;
unw_context_t uc;
int ret;
unw_getcontext (&uc);
if (unw_init_remote (&cursor, unw_local_addr_space, &uc) < 0)
panic ("unw_init_remote failed!\n");
do
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
unw_get_reg (&cursor, UNW_REG_SP, &sp);
buf[0] = '\0';
if (unw_get_proc_name (&cursor, name, sizeof (name), &off) == 0)
{
if (off)
snprintf (buf, sizeof (buf), "<%s+0x%lx>", name, (long) off);
else
snprintf (buf, sizeof (buf), "<%s>", name);
}
if (verbose)
printf ("%016lx %-32s (sp=%016lx)\n", (long) ip, buf, (long) sp);
char filename[128];
unw_word_t file_offset;
if (unw_get_elf_filename (&cursor,filename, sizeof (filename), &file_offset) == UNW_ESUCCESS)
printf (" [%s+0x%lx]\n", filename, (long) file_offset);
ret = unw_step (&cursor);
if (ret < 0)
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
printf ("FAILURE: unw_step() returned %d for ip=%lx\n",
ret, (long) ip);
return -1;
}
}
while (ret > 0);
return 0;
}
static int
foo (void)
{
return do_backtrace ();
}
int
main (int argc, char **argv UNUSED)
{
verbose = (argc > 1);
if (verbose)
printf ("Normal backtrace:\n");
return foo ();
}

View File

@@ -0,0 +1,105 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2003-2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
This file is part of libunwind.
Copyright (c) 2003 Hewlett-Packard Co.
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/resource.h>
#define panic(...) \
{ fprintf (stderr, __VA_ARGS__); exit (-1); }
int verbose;
static void
do_backtrace (void)
{
unw_cursor_t cursor;
unw_word_t ip, sp;
unw_context_t uc;
int ret;
unw_getcontext (&uc);
if (unw_init_local (&cursor, &uc) < 0)
panic ("unw_init_local failed!\n");
do
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
unw_get_reg (&cursor, UNW_REG_SP, &sp);
if (verbose)
printf ("%016lx (sp=%016lx)\n", (long) ip, (long) sp);
ret = unw_step (&cursor);
if (ret < 0)
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
panic ("FAILURE: unw_step() returned %d for ip=%lx\n",
ret, (long) ip);
}
}
while (ret > 0);
}
int
consume_some_stack_space (void)
{
unw_cursor_t cursor;
unw_context_t uc;
char string[1024];
memset (&cursor, 0, sizeof (cursor));
memset (&uc, 0, sizeof (uc));
return sprintf (string, "hello %p %p\n", (void *)&cursor, (void *)&uc);
}
int
main (int argc, char **argv UNUSED)
{
struct rlimit rlim UNUSED;
verbose = argc > 1;
if (consume_some_stack_space () > 9999)
exit (-1); /* can't happen, but don't let the compiler know... */
#if !defined(__QNX__)
rlim.rlim_cur = 0;
rlim.rlim_max = RLIM_INFINITY;
setrlimit (RLIMIT_DATA, &rlim);
setrlimit (RLIMIT_AS, &rlim);
#endif /* !defined(__QNX__) */
do_backtrace ();
return 0;
}

View File

@@ -0,0 +1,171 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2003 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
This file is part of libunwind.
Copyright (c) 2003 Hewlett-Packard Co.
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. */
/* This test program checks whether proc_info lookup failures are
cached. They must NOT be cached because it could otherwise turn
temporary failures into permanent ones. Furthermore, we allow apps
to return -UNW_ESTOPUNWIND to terminate unwinding (though this
feature is deprecated and dynamic unwind info should be used
instead). */
#include <stdio.h>
#include <string.h>
#include <libunwind.h>
#include "compiler.h"
int errors;
#define panic(...) \
{ ++errors; fprintf (stderr, __VA_ARGS__); return -1; }
static int
find_proc_info (unw_addr_space_t as UNUSED,
unw_word_t ip UNUSED,
unw_proc_info_t *pip UNUSED,
int need_unwind_info UNUSED,
void *arg UNUSED)
{
return -UNW_ESTOPUNWIND;
}
static int
access_mem (unw_addr_space_t as UNUSED,
unw_word_t addr UNUSED,
unw_word_t *valp, int write,
void *arg UNUSED)
{
if (!write)
*valp = 0;
return 0;
}
static int
access_reg (unw_addr_space_t as UNUSED,
unw_regnum_t regnum UNUSED,
unw_word_t *valp, int write,
void *arg UNUSED)
{
if (!write)
*valp = 32;
return 0;
}
static int
access_fpreg (unw_addr_space_t as UNUSED,
unw_regnum_t regnum UNUSED,
unw_fpreg_t *valp, int write,
void *arg UNUSED)
{
if (!write)
memset (valp, 0, sizeof (*valp));
return 0;
}
static int
get_dyn_info_list_addr (unw_addr_space_t as UNUSED,
unw_word_t *dilap UNUSED,
void *arg UNUSED)
{
return -UNW_ENOINFO;
}
static void
put_unwind_info (unw_addr_space_t as UNUSED,
unw_proc_info_t *pi UNUSED,
void *arg UNUSED)
{
++errors;
fprintf (stderr, "%s() got called!\n", __FUNCTION__);
}
static int
resume (unw_addr_space_t as UNUSED,
unw_cursor_t *reg UNUSED,
void *arg UNUSED)
{
panic ("%s() got called!\n", __FUNCTION__);
}
static int
get_proc_name (unw_addr_space_t as UNUSED,
unw_word_t ip UNUSED,
char *buf UNUSED,
size_t buf_len UNUSED,
unw_word_t *offp UNUSED,
void *arg UNUSED)
{
panic ("%s() got called!\n", __FUNCTION__);
}
int
main (int argc, char **argv)
{
unw_accessors_t acc;
unw_addr_space_t as;
int ret, verbose = 0;
unw_cursor_t c;
if (argc > 1 && strcmp (argv[1], "-v") == 0)
verbose = 1;
memset (&acc, 0, sizeof (acc));
acc.find_proc_info = find_proc_info;
acc.put_unwind_info = put_unwind_info;
acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
acc.access_mem = access_mem;
acc.access_reg = access_reg;
acc.access_fpreg = access_fpreg;
acc.resume = resume;
acc.get_proc_name = get_proc_name;
as = unw_create_addr_space (&acc, 0);
if (!as)
panic ("unw_create_addr_space() failed\n");
unw_set_caching_policy (as, UNW_CACHE_GLOBAL);
ret = unw_init_remote (&c, as, NULL);
if (ret < 0)
panic ("unw_init_remote() returned %d instead of 0\n", ret);
ret = unw_step (&c);
if (ret != -UNW_ESTOPUNWIND)
panic ("First call to unw_step() returned %d instead of %d\n",
ret, -UNW_ESTOPUNWIND);
ret = unw_step (&c);
if (ret != -UNW_ESTOPUNWIND)
panic ("Second call to unw_step() returned %d instead of %d\n",
ret, -UNW_ESTOPUNWIND);
unw_destroy_addr_space (as);
if (verbose)
printf ("SUCCESS\n");
return 0;
}

View File

@@ -0,0 +1,121 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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 <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include "ident.h"
pid_t self;
int global[64];
int
func (int arg)
{
int sum = 0, i, max, arr[1024];
if (arg == 0)
{
sum = global[2];
sum += sum + sum * getppid ();
return sum;
}
else
{
max = arg;
if (max >= 64)
max = 64;
for (i = 0; i < max; ++i)
arr[i] = func (arg - 1);
for (i = 0; i < max; ++i)
if (arr[i] > 16)
sum += arr[i];
else
sum -= arr[i];
}
return sum;
}
int
bar (int v)
{
int arr[1] = { v };
uintptr_t r;
/* This is a vain attempt to use up lots of registers to force
the frame-chain info to be saved on the memory stack on ia64.
It happens to work with gcc v3.3.4 and gcc v3.4.1 but perhaps
not with any other compiler. */
r = (uintptr_t) malloc(f (arr[0])
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + f (v))
))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
)))))))))))))))))))))))))))))))))))))))))))))))))))))));
if (r < 2)
v = r;
kill (self, SIGUSR1); /* tell test-ptrace to start single-stepping */
v = func (v);
kill (self, SIGUSR2); /* tell test-ptrace to stop single-stepping */
return v;
}
int
main (int argc, char **argv UNUSED)
{
int val = argc;
signal (SIGUSR1, SIG_IGN);
signal (SIGUSR2, SIG_IGN);
self = getpid ();
printf ("sum = %d\n", bar (val));
return 0;
}

View File

@@ -0,0 +1,399 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2003-2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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 <config.h>
#ifdef HAVE_TTRACE
int
main (void)
{
printf ("FAILURE: ttrace() not supported yet\n");
return -1;
}
#else /* !HAVE_TTRACE */
#include <errno.h>
#include <fcntl.h>
#include <libunwind-ptrace.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
extern char **environ;
static const int nerrors_max = 100;
int nerrors;
int verbose;
int print_names = 1;
int print_elf_filename;
enum
{
INSTRUCTION,
SYSCALL,
TRIGGER
}
trace_mode = SYSCALL;
#define panic(...) \
do { fprintf (stderr, __VA_ARGS__); ++nerrors; } while (0)
static unw_addr_space_t as;
static struct UPT_info *ui;
static int killed;
void
do_backtrace (void)
{
unw_word_t ip, sp, start_ip = 0, off;
int n = 0, ret;
unw_proc_info_t pi;
unw_cursor_t c;
char buf[512];
size_t len;
ret = unw_init_remote (&c, as, ui);
if (ret < 0)
panic ("unw_init_remote() failed: ret=%d\n", ret);
do
{
if ((ret = unw_get_reg (&c, UNW_REG_IP, &ip)) < 0
|| (ret = unw_get_reg (&c, UNW_REG_SP, &sp)) < 0)
panic ("unw_get_reg/unw_get_proc_name() failed: ret=%d\n", ret);
if (n == 0)
start_ip = ip;
buf[0] = '\0';
if (print_names)
unw_get_proc_name (&c, buf, sizeof (buf), &off);
if (verbose)
{
if (off)
{
len = strlen (buf);
if (len >= sizeof (buf) - 32)
len = sizeof (buf) - 32;
sprintf (buf + len, "+0x%lx", (unsigned long) off);
}
printf ("%016lx %-32s (sp=%016lx)\n", (long) ip, buf, (long) sp);
}
if ((ret = unw_get_proc_info (&c, &pi)) < 0 && ret != -UNW_ENOINFO) /* It's possible unw_get_proc_info don't return information */
panic ("unw_get_proc_info(ip=0x%lx) failed: ret=%d\n", (long) ip, ret);
else if (verbose)
printf ("\tproc=%016lx-%016lx\n\thandler=%lx lsda=%lx",
(long) pi.start_ip, (long) pi.end_ip,
(long) pi.handler, (long) pi.lsda);
#if UNW_TARGET_IA64
{
unw_word_t bsp;
if ((ret = unw_get_reg (&c, UNW_IA64_BSP, &bsp)) < 0)
panic ("unw_get_reg() failed: ret=%d\n", ret);
else if (verbose)
printf (" bsp=%lx", bsp);
}
#endif
if (verbose)
printf ("\n");
if (print_elf_filename)
{
if ((ret = unw_get_elf_filename(&c, buf, sizeof (buf), &off)) != UNW_ESUCCESS)
panic ("unw_get_elf_filename(ip=0x%lx) failed: ret=%d\n", (long) ip, ret);
else if (verbose)
printf ("\t[%s+0x%lx]\n", buf, (long) off);
}
ret = unw_step (&c);
if (ret < 0)
{
unw_get_reg (&c, UNW_REG_IP, &ip);
panic ("FAILURE: unw_step() returned %d for ip=%lx (start ip=%lx)\n",
ret, (long) ip, (long) start_ip);
}
if (++n > 64)
{
/* guard against bad unwind info in old libraries... */
panic ("too deeply nested---assuming bogus unwind (start ip=%lx)\n",
(long) start_ip);
break;
}
if (nerrors > nerrors_max)
{
panic ("Too many errors (%d)!\n", nerrors);
break;
}
}
while (ret > 0);
if (ret < 0)
panic ("unwind failed with ret=%d\n", ret);
if (verbose)
printf ("================\n\n");
}
static pid_t target_pid;
static void target_pid_kill (void)
{
kill (target_pid, SIGKILL);
}
int
main (int argc, char **argv)
{
int status, pid, pending_sig, optind = 1, state = 1;
as = unw_create_addr_space (&_UPT_accessors, 0);
if (!as)
panic ("unw_create_addr_space() failed");
if (argc == 1)
{
#ifdef HAVE_EXECVPE
static char *args[] = { "self", "ls", "/", NULL };
#else
static char *args[] = { "self", "/bin/ls", "/", NULL };
#endif
/* automated test case */
argv = args;
/* Unless the args array is 'walked' the child
process is unable to access it and dies with a segfault */
fprintf(stderr, "Automated test (%s,%s,%s,%s)\n",
args[0],args[1],args[2],args[3]);
}
else if (argc > 1)
while (argv[optind][0] == '-')
{
if (strcmp (argv[optind], "-v") == 0)
++optind, verbose = 1;
else if (strcmp (argv[optind], "-i") == 0)
++optind, trace_mode = INSTRUCTION; /* backtrace at each insn */
else if (strcmp (argv[optind], "-s") == 0)
++optind, trace_mode = SYSCALL; /* backtrace at each syscall */
else if (strcmp (argv[optind], "-t") == 0)
/* Execute until raise(SIGUSR1), then backtrace at each insn
until raise(SIGUSR2). */
++optind, trace_mode = TRIGGER;
else if (strcmp (argv[optind], "-c") == 0)
/* Enable caching of unwind-info. */
++optind, unw_set_caching_policy (as, UNW_CACHE_GLOBAL);
else if (strcmp (argv[optind], "-n") == 0)
/* Don't look-up and print symbol names. */
++optind, print_names = 0;
else if (strcmp (argv[optind], "-f") == 0)
/* Print elf filenames. */
++optind, print_elf_filename = 1;
else
fprintf(stderr, "unrecognized option: %s\n", argv[optind++]);
if (optind >= argc)
break;
}
target_pid = fork ();
if (!target_pid)
{
/* child */
if (!verbose)
dup2 (open ("/dev/null", O_WRONLY), 1);
#if HAVE_DECL_PTRACE_TRACEME
long stat = ptrace (PTRACE_TRACEME, 0, 0, 0);
#elif HAVE_DECL_PT_TRACE_ME
int stat = ptrace (PT_TRACE_ME, 0, 0, 0);
#else
#error Trace me
#endif
if (stat == -1)
{
if (verbose)
{
fprintf(stderr, "ptrace() returned %ld errno=%d (%s)\n", (long)stat, errno, strerror(errno));
}
_exit(77);
}
if ((argc > 1) && (optind == argc)) {
fprintf(stderr, "Need to specify a command line for the child\n");
exit (-1);
}
#ifdef HAVE_EXECVPE
execvpe (argv[optind], argv + optind, environ);
#else
execve (argv[optind], argv + optind, environ);
#endif
_exit (-1);
}
atexit (target_pid_kill);
ui = _UPT_create (target_pid);
while (nerrors <= nerrors_max)
{
pid = wait4 (-1, &status, 0, NULL);
if (pid == -1)
{
if (errno == EINTR)
continue;
panic ("wait4() failed (errno=%d)\n", errno);
}
pending_sig = 0;
if (WIFSIGNALED (status) || WIFEXITED (status)
|| (WIFSTOPPED (status) && WSTOPSIG (status) != SIGTRAP))
{
if (WIFEXITED (status))
{
if (WEXITSTATUS (status) != 0)
panic ("child's exit status %d\n", WEXITSTATUS (status));
if (WEXITSTATUS (status) == 77)
_exit(77);
break;
}
else if (WIFSIGNALED (status))
{
if (!killed)
panic ("child terminated by signal %d\n", WTERMSIG (status));
break;
}
else
{
pending_sig = WSTOPSIG (status);
/* Avoid deadlock: */
if (WSTOPSIG (status) == SIGKILL)
break;
if (trace_mode == TRIGGER)
{
if (WSTOPSIG (status) == SIGUSR1)
state = 0;
else if (WSTOPSIG (status) == SIGUSR2)
state = 1;
}
if (WSTOPSIG (status) != SIGUSR1 && WSTOPSIG (status) != SIGUSR2)
{
static int count = 0;
if (count++ > 100)
{
panic ("Too many child unexpected signals (now %d)\n",
WSTOPSIG (status));
killed = 1;
}
}
}
}
switch (trace_mode)
{
case TRIGGER:
if (state)
#if HAVE_DECL_PTRACE_CONT
ptrace (PTRACE_CONT, target_pid, 0, 0);
#elif HAVE_DECL_PT_CONTINUE
ptrace (PT_CONTINUE, target_pid, (caddr_t)1, 0);
#else
#error Port me
#endif
else
{
do_backtrace ();
#if HAVE_DECL_PTRACE_SINGLESTEP
if (ptrace (PTRACE_SINGLESTEP, target_pid, 0, pending_sig) < 0)
{
panic ("ptrace(PTRACE_SINGLESTEP) failed (errno=%d)\n", errno);
killed = 1;
}
#elif HAVE_DECL_PT_STEP
if (ptrace (PT_STEP, target_pid, (caddr_t)1, pending_sig) < 0)
{
panic ("ptrace(PT_STEP) failed (errno=%d)\n", errno);
killed = 1;
}
#else
#error Singlestep me
#endif
}
break;
case SYSCALL:
if (!state)
do_backtrace ();
state ^= 1;
#if HAVE_DECL_PT_SYSCALL
ptrace (PT_SYSCALL, target_pid, (caddr_t)1, pending_sig);
#elif HAVE_DECL_PTRACE_SYSCALL
ptrace (PTRACE_SYSCALL, target_pid, 0, pending_sig);
#else
#error Syscall me
#endif
break;
case INSTRUCTION:
do_backtrace ();
#if HAVE_DECL_PTRACE_SINGLESTEP
ptrace (PTRACE_SINGLESTEP, target_pid, 0, pending_sig);
#elif HAVE_DECL_PT_STEP
ptrace (PT_STEP, target_pid, (caddr_t)1, pending_sig);
#else
#error Singlestep me
#endif
break;
}
if (killed)
kill (target_pid, SIGKILL);
}
_UPT_destroy (ui);
unw_destroy_addr_space (as);
if (nerrors)
{
printf ("FAILURE: detected %d errors\n", nerrors);
exit (-1);
}
if (verbose)
printf ("SUCCESS\n");
return 0;
}
#endif /* !HAVE_TTRACE */

View File

@@ -0,0 +1,135 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2003-2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
This file is part of libunwind.
Copyright (c) 2003 Hewlett-Packard Co.
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/resource.h>
#define panic(...) \
{ fprintf (stderr, __VA_ARGS__); exit (-1); }
int verbose;
struct cb_data
{
unw_word_t ip;
void* reg_state;
size_t len;
};
static int
dwarf_reg_states_callback(void *token,
void *rs,
size_t size,
unw_word_t start_ip, unw_word_t end_ip)
{
struct cb_data *data = token;
if (start_ip <= data->ip && data->ip < end_ip)
{
data->reg_state = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
memcpy(data->reg_state, rs, size);
data->len = size;
}
return 0;
}
static void
do_backtrace (void)
{
unw_cursor_t cursor;
unw_word_t ip, sp;
unw_context_t uc;
int ret;
unw_getcontext (&uc);
if (unw_init_local (&cursor, &uc) < 0)
panic ("unw_init_local failed!\n");
do
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
unw_get_reg (&cursor, UNW_REG_SP, &sp);
if (verbose)
printf ("%016lx (sp=%016lx)\n", (long) ip, (long) sp);
struct cb_data data = {.ip = ip, .reg_state = NULL};
ret = unw_reg_states_iterate(&cursor, dwarf_reg_states_callback, &data);
if (ret > 0)
{
ret = unw_apply_reg_state (&cursor, data.reg_state);
munmap(data.reg_state, data.len);
}
if (ret < 0)
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
panic ("FAILURE: unw_step() returned %d for ip=%lx\n",
ret, (long) ip);
}
}
while (ret > 0);
}
int
consume_some_stack_space (void)
{
unw_cursor_t cursor;
unw_context_t uc;
char string[1024];
memset (&cursor, 0, sizeof (cursor));
memset (&uc, 0, sizeof (uc));
return sprintf (string, "hello %p %p\n", (void *)&cursor, (void *)&uc);
}
int
main (int argc, char **argv UNUSED)
{
struct rlimit rlim;
verbose = argc > 1;
if (consume_some_stack_space () > 9999)
exit (-1); /* can't happen, but don't let the compiler know... */
#if !defined(__QNX__)
rlim.rlim_cur = 0;
rlim.rlim_max = RLIM_INFINITY;
setrlimit (RLIMIT_DATA, &rlim);
#endif /* !defined(__QNX__) */
do_backtrace ();
return 0;
}

View File

@@ -0,0 +1,45 @@
#!/bin/sh
#
# 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.
#
test_dir="$(dirname $0)"
: ${LIBUNWIND:=@LIBDIR@/libunwind.so}
: ${LIBUNWIND_GENERIC:=@LIBDIR@/libunwind-@ARCH@.so}
count=0
printf "TAP version 14\n"
for t in @TESTS@; do
"$test_dir/$t" >$t.out 2>$t.err
status=$?
if [ $status = 77 ]; then
printf "%d ok - %s # SKIP\n" $count "$t"
elif [ $status = 0 ]; then
printf "%d ok - %s\n" $count "$t"
elif (echo @XFAIL_TESTS@ | grep -Fqw "$t"); then
printf "%d not ok - %s # TODO\n" $count "$t"
else
printf "%d not ok - %s returned %d\n" $count "$t" $status
fi
count=$(expr $count + 1)
done
printf "1..%d\n" $count

View File

@@ -0,0 +1,289 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2003 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
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. */
/* The setjmp()/longjmp(), sigsetjmp()/siglongjmp(). */
#include "compiler.h"
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int nerrors;
int verbose;
static jmp_buf jbuf;
static sigjmp_buf sigjbuf;
static sigset_t sigset4;
NORETURN void
raise_longjmp (jmp_buf jbuf, int i, int n)
{
while (i < n)
raise_longjmp (jbuf, i + 1, n);
longjmp (jbuf, n);
}
void
test_setjmp (void)
{
volatile int i;
jmp_buf jbuf;
int ret;
for (i = 0; i < 10; ++i)
{
if ((ret = setjmp (jbuf)))
{
if (verbose)
printf ("%s: secondary setjmp () return, ret=%d\n",
__FUNCTION__, ret);
if (ret != i + 1)
{
fprintf (stderr, "%s: setjmp() returned %d, expected %d\n",
__FUNCTION__, ret, i + 1);
++nerrors;
}
continue;
}
if (verbose)
printf ("%s.%d: done with setjmp(); calling children\n",
__FUNCTION__, i + 1);
raise_longjmp (jbuf, 0, i + 1);
fprintf (stderr, "%s: raise_longjmp() returned unexpectedly\n",
__FUNCTION__);
++nerrors;
}
}
NORETURN void
raise_siglongjmp (sigjmp_buf jbuf, int i, int n)
{
while (i < n)
raise_siglongjmp (jbuf, i + 1, n);
siglongjmp (jbuf, n);
}
void
test_sigsetjmp (void)
{
sigjmp_buf jbuf;
volatile int i;
int ret;
for (i = 0; i < 10; ++i)
{
if ((ret = sigsetjmp (jbuf, 1)))
{
if (verbose)
printf ("%s: secondary sigsetjmp () return, ret=%d\n",
__FUNCTION__, ret);
if (ret != i + 1)
{
fprintf (stderr, "%s: sigsetjmp() returned %d, expected %d\n",
__FUNCTION__, ret, i + 1);
++nerrors;
}
continue;
}
if (verbose)
printf ("%s.%d: done with sigsetjmp(); calling children\n",
__FUNCTION__, i + 1);
raise_siglongjmp (jbuf, 0, i + 1);
fprintf (stderr, "%s: raise_siglongjmp() returned unexpectedly\n",
__FUNCTION__);
++nerrors;
}
}
void
sighandler (int signal)
{
if (verbose)
printf ("%s: got signal %d\n", __FUNCTION__, signal);
sigprocmask (SIG_BLOCK, NULL, (sigset_t *) &sigset4);
if (verbose)
printf ("%s: back from sigprocmask\n", __FUNCTION__);
siglongjmp (sigjbuf, 1);
printf ("%s: siglongjmp() returned unexpectedly!\n", __FUNCTION__);
}
int
main (int argc, char **argv UNUSED)
{
sigset_t sigset1, sigset2, sigset3;
volatile struct sigaction act;
if (argc > 1)
verbose = 1;
memset (&sigset1, 0, sizeof (sigset1));
memset (&sigset2, 0, sizeof (sigset2));
memset (&sigset3, 0, sizeof (sigset3));
sigemptyset ((sigset_t *) &sigset1);
sigaddset ((sigset_t *) &sigset1, SIGUSR1);
sigemptyset ((sigset_t *) &sigset2);
sigaddset ((sigset_t *) &sigset2, SIGUSR2);
memset ((void *) &act, 0, sizeof (act));
act.sa_handler = sighandler;
sigaction (SIGTERM, (struct sigaction *) &act, NULL);
test_setjmp ();
test_sigsetjmp ();
/* _setjmp() MUST NOT change signal mask: */
sigprocmask (SIG_SETMASK, (sigset_t *) &sigset1, NULL);
if (_setjmp (jbuf))
{
sigemptyset ((sigset_t *) &sigset3);
sigprocmask (SIG_BLOCK, NULL, (sigset_t *) &sigset3);
if (memcmp ((sigset_t *) &sigset3, (sigset_t *) &sigset2,
sizeof (sigset_t)) != 0)
{
fprintf (stderr, "FAILURE: _longjmp() manipulated signal mask!\n");
++nerrors;
}
else if (verbose)
printf ("OK: _longjmp() seems not to change signal mask\n");
}
else
{
sigprocmask (SIG_SETMASK, (sigset_t *) &sigset2, NULL);
_longjmp (jbuf, 1);
}
/* sigsetjmp(jbuf, 1) MUST preserve signal mask: */
sigprocmask (SIG_SETMASK, (sigset_t *) &sigset1, NULL);
if (sigsetjmp (sigjbuf, 1))
{
sigemptyset ((sigset_t *) &sigset3);
sigprocmask (SIG_BLOCK, NULL, (sigset_t *) &sigset3);
if (memcmp ((sigset_t *) &sigset3, (sigset_t *) &sigset1,
sizeof (sigset_t)) != 0)
{
fprintf (stderr,
"FAILURE: siglongjmp() didn't restore signal mask!\n");
++nerrors;
}
else if (verbose)
printf ("OK: siglongjmp() restores signal mask when asked to\n");
}
else
{
sigprocmask (SIG_SETMASK, (sigset_t *) &sigset2, NULL);
siglongjmp (sigjbuf, 1);
}
/* sigsetjmp(jbuf, 0) MUST NOT preserve signal mask: */
sigprocmask (SIG_SETMASK, (sigset_t *) &sigset1, NULL);
if (sigsetjmp (sigjbuf, 0))
{
sigemptyset ((sigset_t *) &sigset3);
sigprocmask (SIG_BLOCK, NULL, (sigset_t *) &sigset3);
if (memcmp ((sigset_t *) &sigset3, (sigset_t *) &sigset2,
sizeof (sigset_t)) != 0)
{
fprintf (stderr,
"FAILURE: siglongjmp() changed signal mask!\n");
++nerrors;
}
else if (verbose)
printf ("OK: siglongjmp() leaves signal mask alone when asked to\n");
}
else
{
sigprocmask (SIG_SETMASK, (sigset_t *) &sigset2, NULL);
siglongjmp (sigjbuf, 1);
}
/* sigsetjmp(jbuf, 1) MUST preserve signal mask: */
sigprocmask (SIG_SETMASK, (sigset_t *) &sigset1, NULL);
if (sigsetjmp (sigjbuf, 1))
{
sigemptyset ((sigset_t *) &sigset3);
sigprocmask (SIG_BLOCK, NULL, (sigset_t *) &sigset3);
if (memcmp ((sigset_t *) &sigset3, (sigset_t *) &sigset1,
sizeof (sigset_t)) != 0)
{
fprintf (stderr,
"FAILURE: siglongjmp() didn't restore signal mask!\n");
++nerrors;
}
else if (verbose)
printf ("OK: siglongjmp() restores signal mask when asked to\n");
}
else
{
sigprocmask (SIG_SETMASK, (sigset_t *) &sigset2, NULL);
kill (getpid (), SIGTERM);
fprintf (stderr, "FAILURE: unexpected return from kill()\n");
++nerrors;
}
/* sigsetjmp(jbuf, 0) MUST NOT preserve signal mask: */
sigprocmask (SIG_SETMASK, (sigset_t *) &sigset1, NULL);
if (sigsetjmp (sigjbuf, 0))
{
sigemptyset ((sigset_t *) &sigset3);
sigprocmask (SIG_BLOCK, NULL, (sigset_t *) &sigset3);
if (memcmp ((sigset_t *) &sigset3, (sigset_t *) &sigset4,
sizeof (sigset_t)) != 0)
{
fprintf (stderr,
"FAILURE: siglongjmp() changed signal mask!\n");
++nerrors;
}
else if (verbose)
printf ("OK: siglongjmp() leaves signal mask alone when asked to\n");
}
else
{
sigprocmask (SIG_SETMASK, (sigset_t *) &sigset2, NULL);
kill (getpid (), SIGTERM);
fprintf (stderr, "FAILURE: unexpected return from kill()\n");
++nerrors;
}
if (nerrors > 0)
{
fprintf (stderr, "FAILURE: detected %d failures\n", nerrors);
exit (-1);
}
if (verbose)
printf ("SUCCESS\n");
return 0;
}

View File

@@ -0,0 +1,74 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
This file is part of libunwind.
Copyright (c) 2003 Hewlett-Packard Co.
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 <stdio.h>
#include <libunwind.h>
extern int verbose;
static void *funcs[] =
{
(void *) &unw_get_reg,
(void *) &unw_get_fpreg,
(void *) &unw_set_reg,
(void *) &unw_set_fpreg,
(void *) &unw_resume,
(void *) &unw_create_addr_space,
(void *) &unw_destroy_addr_space,
(void *) &unw_get_accessors,
(void *) &unw_flush_cache,
(void *) &unw_set_caching_policy,
(void *) &unw_set_cache_size,
(void *) &unw_regname,
(void *) &unw_get_proc_info,
(void *) &unw_get_save_loc,
(void *) &unw_is_signal_frame,
(void *) &unw_get_proc_name
};
int
test_generic (void)
{
if (verbose)
printf (__FILE__": funcs[0]=%p\n", funcs[0]);
#ifndef UNW_REMOTE_ONLY
{
unw_context_t uc;
unw_cursor_t c;
unw_getcontext (&uc);
unw_init_local (&c, &uc);
unw_init_remote (&c, unw_local_addr_space, &uc);
return unw_step (&c);
}
#else
return 0;
#endif
}

View File

@@ -0,0 +1,102 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
This file is part of libunwind.
Copyright (c) 2003 Hewlett-Packard Co.
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. */
/* The purpose of this program is simply to link in all libunwind-API
functions both in their local-only and generic variants and to make
sure that the final result can be linked statically. */
#include <stdio.h>
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#include "compiler.h"
extern int test_generic (void);
int verbose;
#ifdef UNW_REMOTE_ONLY
int
test_local (void)
{
return 0;
}
#else /* !UNW_REMOTE_ONLY */
static void *funcs[] =
{
(void *) &unw_get_reg,
(void *) &unw_get_fpreg,
(void *) &unw_set_reg,
(void *) &unw_set_fpreg,
(void *) &unw_resume,
(void *) &unw_create_addr_space,
(void *) &unw_destroy_addr_space,
(void *) &unw_get_accessors,
(void *) &unw_flush_cache,
(void *) &unw_set_caching_policy,
(void *) &unw_set_cache_size,
(void *) &unw_regname,
(void *) &unw_get_proc_info,
(void *) &unw_get_save_loc,
(void *) &unw_is_signal_frame,
(void *) &unw_get_proc_name,
(void *) &_U_dyn_register,
(void *) &_U_dyn_cancel
};
int
test_local (void)
{
unw_context_t uc;
unw_cursor_t c;
if (verbose)
printf (__FILE__": funcs[0]=%p\n", funcs[0]);
unw_getcontext (&uc);
unw_init_local (&c, &uc);
unw_init_remote (&c, unw_local_addr_space, &uc);
return unw_step (&c);
}
#endif /* !UNW_REMOTE_ONLY */
int
main (int argc, char **argv UNUSED)
{
if (argc > 1)
verbose = 1;
if (test_local () < 0)
return -1;
if (test_generic () < 0)
return -1;
return 0;
}

View File

@@ -0,0 +1,18 @@
#include "compiler.h"
#include <libunwind.h>
#include <stdio.h>
int
main (int argc, char **argv UNUSED)
{
int i, verbose = argc > 1;
const char *msg;
for (i = 0; i < 16; ++i)
{
msg = unw_strerror (-i);
if (verbose)
printf ("%6d -> %s\n", -i, msg);
}
return 0;
}

View File

@@ -0,0 +1,47 @@
/**
* @file tests/unw_test.h
*
* Common unit test API for libunwind.
*/
/*
* This file is part of libunwind.
* Copyright 2025 Stephen M. Webb <stephen.webb@bregmasoft.ca>
*
* 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.
*/
#ifndef LIBUNWIND_UNW_TEST_H
#define LIBUNWIND_UNW_TEST_H 1
/**
* Exit values for test programs.
* Based on https://www.gnu.org/software/automake/manual/html_node/Scripts_002dbased-Testsuites.html
*
* These are used to interact with the test harness (eg. a TAP-based harness,
* CTest, or automake).
*/
enum {
UNW_TEST_EXIT_PASS = 0, /* Item under test is a PASS */
UNW_TEST_EXIT_FAIL = 1, /* Item under test is a FAIL */
UNW_TEST_EXIT_BAD_COMMAND = 2, /* Test program is invoked with invalid arguments */
UNW_TEST_EXIT_SKIP = 77, /* Test should be skipped */
UNW_TEST_EXIT_HARD_ERROR = 99 /* Test program itself has failed */
};
#endif /* LIBUNWIND_UNW_TEST_H */

View File

@@ -0,0 +1,81 @@
.global DW_CFA_expression_testcase
.extern recover_register
.text
# CFI expressions were added in DWARF v3 to allow compilers to specify memory
# locations or register values using DWARF programs. These programs are simple
# stack-based operations which allow the compiler to encode integer mathematics
# and other complex logic. CFI expressions are therefore more powerful than the
# conventional register + offset schemes.
#
# These tests capture a bug we have fixed in libunwind. CFI expression programs
# always start with the current CFA pushed onto the stack. This file contains a
# pair of routines which test CFI expression parsing. Specifically they test
# DW_CFA_expression logic, which uses DWARF expressions to compute the address
# where a non-volatile register was stored.
#
# Main calls DW_CFA_expression_testcase, which sets up known state in a
# non-volatile (caller-saved) register. We use r12 for this purpose. After this
# DW_CFA_expression_testcase then calls DW_CFA_expression_inner, which clobbers
# r12 after stashing its value on the stack. This routine contains a DWARF3 CFI
# expression to restore the value of r12 on unwind which should allow libunwind
# to recover clobbered state. DW_CFA_expression_inner calls recover_register to
# retrieve the cached register value. This function recovers the register value
# by using libunwind to unwind the stack through DW_CFA_expression_inner and up
# to the call site in DW_CFA_expression_testcase. If our expression is correct,
# libunwind will be able to restore r12 from the stack.
#
# BE CAREFUL WITH rdi, rsi, rax HERE! The arguments to recover_register are
# passed in via rdi, rsi and I just let them flow through unchanged. Similarly
# RAX flows back unchanged. Adding any function calls to the below may clobber
# these registers and cause this test to fail mysteriously.
########################################################
# Test: Restoring a register using a DW_CFA_expression #
# which uses implicit CFA pushed onto stack. #
########################################################
.type DW_CFA_expression_testcase STT_FUNC
DW_CFA_expression_testcase:
.cfi_startproc
push %r12
.cfi_adjust_cfa_offset 8
# Move our sentinel (known) value into non-volatile (Callee-saved) r12
mov $111222333, %r12
.cfi_rel_offset %r12, 0
call DW_CFA_expression_inner
pop %r12
.cfi_restore %r12
.cfi_adjust_cfa_offset -8
ret
.cfi_endproc
.size DW_CFA_expression_testcase,.-DW_CFA_expression_testcase
.type DW_CFA_expression_inner STT_FUNC
DW_CFA_expression_inner:
.cfi_startproc
push %r12
.cfi_adjust_cfa_offset 8
# !! IMPORTANT BIT !! The test is all about how we parse the following bytes.
# Now we use an expression to describe where our sentinel value is stored:
# DW_CFA_expression(0x10), r12(0x0c), Length(0x02), (preamble)
# DW_OP_lit16(0x40), DW_OP_minus(0x1c) (instructions)
# Parsing starts with the CFA on the stack, then pushes 16, then does a minus
# which is equivalent to a=pop(), b=pop(), push(b-a), leaving us with a value
# of cfa-16 (cfa points at old rsp, cfa-8 is our rip, so we stored r12 at
# cfa-16).
xor %r12, %r12 # Trash r12
.cfi_escape 0x10, 0x0c, 0x2, 0x40, 0x1c # DW_CFA_expression for recovery
call recover_register
pop %r12
.cfi_restore %r12
.cfi_adjust_cfa_offset -8
ret
.cfi_endproc
.size DW_CFA_expression_inner,.-DW_CFA_expression_inner
/* We do not need executable stack. */
.section .note.GNU-stack,"",@progbits

View File

@@ -0,0 +1,124 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2019 Brock York <twunknown AT gmail.com>
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 <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <execinfo.h>
#include <sys/types.h>
#include <sys/ucontext.h>
#include <unistd.h>
#ifdef HAVE_SYS_PTRACE_H
#include <sys/ptrace.h>
#endif
#define UNW_LOCAL_ONLY
#include <libunwind.h>
/*
* unwind in the signal handler checking the backtrace is correct
* after a bad jump.
*/
void handle_sigsegv(int signal, siginfo_t *info, void *ucontext)
{
/*
* 0 = success
* !0 = general failure
* 77 = test skipped
* 99 = complete failure
*/
int test_status = 0;
unw_cursor_t cursor; unw_context_t uc;
unw_word_t ip, sp, offset;
char name[1000];
int found_signal_frame = 0;
int i = 0;
char *names[] = {
"",
"main",
};
int names_count = sizeof(names) / sizeof(*names);
unw_getcontext(&uc);
unw_init_local(&cursor, &uc);
while (unw_step(&cursor) > 0 && !test_status)
{
if (unw_is_signal_frame(&cursor))
{
found_signal_frame = 1;
}
if (found_signal_frame)
{
unw_get_reg(&cursor, UNW_REG_IP, &ip);
unw_get_reg(&cursor, UNW_REG_SP, &sp);
memset(name, 0, sizeof(char) * 1000);
unw_get_proc_name(&cursor, name, sizeof(char) * 1000, &offset);
printf("ip = %lx, sp = %lx offset = %lx name = %s\n", (long) ip, (long) sp, (long) offset, name);
if (i < names_count)
{
if (strcmp(names[i], name) != 0)
{
test_status = 1;
printf("frame %s doesn't match expected frame %s\n", name, names[i]);
}
else
{
i += 1;
}
}
}
}
if (i != names_count) //Make sure we found all the frames!
{
printf("Failed to find all frames i:%d != names_count:%d\n", i, names_count);
test_status = 1;
}
/*return test_status to test harness*/
exit(test_status);
}
void (*invalid_function)() = (void*)1;
int main(int argc, char *argv[])
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = handle_sigsegv;
sa.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &sa, NULL);
invalid_function();
/*
* 99 is the hard error exit status for automake tests:
* https://www.gnu.org/software/automake/manual/html_node/Scripts_002dbased-Testsuites.html#Scripts_002dbased-Testsuites
* If we dont end up in the signal handler something went horribly wrong.
*/
return 99;
}