1 /* Test child for parent backtrace test.
2 Copyright (C) 2013 Red Hat, Inc.
3 This file is part of elfutils.
5 This file is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 elfutils is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 /* Command line syntax: ./backtrace-child [--ptraceme|--gencore]
19 --ptraceme will call ptrace (PTRACE_TRACEME) in the two threads.
20 --gencore will call abort () at its end.
21 Main thread will signal SIGUSR2. Other thread will signal SIGUSR1.
23 PC will get changed to function 'jmp' by backtrace.c function
24 prepare_thread. Then SIGUSR2 will be signalled to backtrace-child
25 which will invoke function sigusr2.
26 This is all done so that signal interrupts execution of the very first
27 instruction of a function. Properly handled unwind should not slip into
28 the previous unrelated function.
29 The tested functionality is arch-independent but the code reproducing it
30 has to be arch-specific.
32 sigusr2 gets called by normal function call from function stdarg.
33 On any arch then sigusr2 calls raise (SIGUSR1) for --ptraceme.
34 abort () is called otherwise, expected for --gencore core dump.
36 Expected x86_64 output:
38 # 0 0x7f7ab61e9e6b raise
39 # 1 0x7f7ab661af47 - 1 main
40 # 2 0x7f7ab5e3bb45 - 1 __libc_start_main
41 # 3 0x7f7ab661aa09 - 1 _start
43 # 0 0x7f7ab61e9e6b raise
44 # 1 0x7f7ab661ab3c - 1 sigusr2
45 # 2 0x7f7ab5e4fa60 __restore_rt
46 # 3 0x7f7ab661ab47 jmp
47 # 4 0x7f7ab661ac92 - 1 stdarg
48 # 5 0x7f7ab661acba - 1 backtracegen
49 # 6 0x7f7ab661acd1 - 1 start
50 # 7 0x7f7ab61e2c53 - 1 start_thread
51 # 8 0x7f7ab5f0fdbd - 1 __clone
53 Expected non-x86_64 (i386) output; __kernel_vsyscall are skipped if found:
55 # 0 0xf779f430 __kernel_vsyscall
56 # 1 0xf7771466 - 1 raise
57 # 2 0xf77c1d07 - 1 main
58 # 3 0xf75bd963 - 1 __libc_start_main
59 # 4 0xf77c1761 - 1 _start
61 # 0 0xf779f430 __kernel_vsyscall
62 # 1 0xf7771466 - 1 raise
63 # 2 0xf77c18f4 - 1 sigusr2
64 # 3 0xf77c1a10 - 1 stdarg
65 # 4 0xf77c1a2c - 1 backtracegen
66 # 5 0xf77c1a48 - 1 start
67 # 6 0xf77699da - 1 start_thread
68 # 7 0xf769bbfe - 1 __clone
76 #include <sys/ptrace.h>
85 main (int argc __attribute__ ((unused)), char **argv)
87 fprintf (stderr, "%s: Unwinding not supported for this architecture\n",
94 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
95 #define NOINLINE_NOCLONE __attribute__ ((noinline, noclone))
97 #define NOINLINE_NOCLONE __attribute__ ((noinline))
100 #define NORETURN __attribute__ ((noreturn))
101 #define UNUSED __attribute__ ((unused))
102 #define USED __attribute__ ((used))
104 static int ptraceme, gencore;
106 /* Execution will arrive here from jmp by an artificial ptrace-spawn signal. */
108 static NOINLINE_NOCLONE void
111 assert (signo == SIGUSR2);
115 /* Do not return as stack may be invalid due to ptrace-patched PC to the
121 /* Here we dump the core for --gencore. */
123 /* Avoid tail call optimization for the raise call. */
127 static NOINLINE_NOCLONE void
134 static NOINLINE_NOCLONE USED void
137 /* Not reached, signal will get ptrace-spawn to jump into sigusr2. */
142 static NOINLINE_NOCLONE void
148 static NOINLINE_NOCLONE NORETURN void
149 stdarg (int f UNUSED, ...)
151 sighandler_t sigusr2_orig = signal (SIGUSR2, sigusr2);
152 assert (sigusr2_orig == SIG_DFL);
156 long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
157 assert_perror (errno);
163 /* Execution will get PC patched into function jmp. */
172 static NOINLINE_NOCLONE void
178 static NOINLINE_NOCLONE void
182 /* Here should be no instruction after the stdarg call as it is noreturn
183 function. It must be stdarg so that it is a call and not jump (jump as
187 static NOINLINE_NOCLONE void
194 start (void *arg UNUSED)
202 main (int argc UNUSED, char **argv)
204 setbuf (stdout, NULL);
206 ptraceme = (*argv && strcmp (*argv, "--ptraceme") == 0);
208 gencore = (*argv && strcmp (*argv, "--gencore") == 0);
211 /* These dummy* functions are there so that each of their surrounding
212 functions has some unrelated code around. The purpose of some of the
213 tests is verify unwinding the very first / after the very last instruction
214 does not inappropriately slip into the unrelated code around. */
220 printf ("%ld\n", (long) getpid ());
222 int i = pthread_create (&thread, NULL, start, NULL);
223 // pthread_* functions do not set errno.
228 long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
229 assert_perror (errno);
233 pthread_join (thread, NULL);
239 #endif /* ! __linux__ */