blob: 788801c34a4bd79bd348f8a6ec6318229ec5cf0e [file] [log] [blame]
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +01001/* Test child for parent backtrace test.
2 Copyright (C) 2013 Red Hat, Inc.
3 This file is part of elfutils.
4
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.
9
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.
14
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/>. */
17
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.
22 On x86_64 only:
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.
31 On non-x86_64:
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.
35
36 Expected x86_64 output:
37 TID 10276:
38 # 0 0x7f7ab61e9e6b raise
39 # 1 0x7f7ab661af47 - 1 main
40 # 2 0x7f7ab5e3bb45 - 1 __libc_start_main
41 # 3 0x7f7ab661aa09 - 1 _start
42 TID 10278:
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
52
53 Expected non-x86_64 (i386) output; __kernel_vsyscall are skipped if found:
54 TID 10408:
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
60 TID 10412:
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
69 */
70
71#include <config.h>
72#include <assert.h>
73#include <stdlib.h>
74#include <signal.h>
75#include <errno.h>
76#include <sys/ptrace.h>
77#include <string.h>
78#include <pthread.h>
79#include <stdio.h>
80#include <unistd.h>
81
Kurt Roeckx02cefda2014-04-22 21:46:22 +020082#ifndef __linux__
83
84int
85main (int argc __attribute__ ((unused)), char **argv)
86{
87 fprintf (stderr, "%s: Unwinding not supported for this architecture\n",
88 argv[0]);
89 return 77;
90}
91
92#else /* __linux__ */
93
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +010094#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
95#define NOINLINE_NOCLONE __attribute__ ((noinline, noclone))
96#else
97#define NOINLINE_NOCLONE __attribute__ ((noinline))
98#endif
99
100#define NORETURN __attribute__ ((noreturn))
101#define UNUSED __attribute__ ((unused))
102#define USED __attribute__ ((used))
103
104static int ptraceme, gencore;
105
106/* Execution will arrive here from jmp by an artificial ptrace-spawn signal. */
107
Jan Kratochvilf88863f2013-12-05 16:16:14 +0100108static NOINLINE_NOCLONE void
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100109sigusr2 (int signo)
110{
111 assert (signo == SIGUSR2);
112 if (! gencore)
113 {
114 raise (SIGUSR1);
Jan Kratochvil435808a2014-01-18 21:56:13 +0100115 /* Do not return as stack may be invalid due to ptrace-patched PC to the
116 jmp function. */
117 pthread_exit (NULL);
118 /* Not reached. */
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100119 abort ();
120 }
121 /* Here we dump the core for --gencore. */
122 raise (SIGABRT);
Jan Kratochvilf88863f2013-12-05 16:16:14 +0100123 /* Avoid tail call optimization for the raise call. */
124 asm volatile ("");
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100125}
126
127static NOINLINE_NOCLONE void
128dummy1 (void)
129{
130 asm volatile ("");
131}
132
133#ifdef __x86_64__
134static NOINLINE_NOCLONE USED void
135jmp (void)
136{
137 /* Not reached, signal will get ptrace-spawn to jump into sigusr2. */
138 abort ();
139}
140#endif
141
142static NOINLINE_NOCLONE void
143dummy2 (void)
144{
145 asm volatile ("");
146}
147
148static NOINLINE_NOCLONE NORETURN void
149stdarg (int f UNUSED, ...)
150{
151 sighandler_t sigusr2_orig = signal (SIGUSR2, sigusr2);
152 assert (sigusr2_orig == SIG_DFL);
153 errno = 0;
154 if (ptraceme)
155 {
156 long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
157 assert_perror (errno);
158 assert (l == 0);
159 }
160#ifdef __x86_64__
161 if (! gencore)
162 {
163 /* Execution will get PC patched into function jmp. */
164 raise (SIGUSR1);
165 }
166#endif
167 sigusr2 (SIGUSR2);
168 /* Not reached. */
169 abort ();
170}
171
172static NOINLINE_NOCLONE void
173dummy3 (void)
174{
175 asm volatile ("");
176}
177
178static NOINLINE_NOCLONE void
179backtracegen (void)
180{
181 stdarg (1);
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
184 a tail-call). */
185}
186
187static NOINLINE_NOCLONE void
188dummy4 (void)
189{
190 asm volatile ("");
191}
192
193static void *
194start (void *arg UNUSED)
195{
196 backtracegen ();
197 /* Not reached. */
198 abort ();
199}
200
201int
202main (int argc UNUSED, char **argv)
203{
204 setbuf (stdout, NULL);
205 assert (*argv++);
206 ptraceme = (*argv && strcmp (*argv, "--ptraceme") == 0);
207 argv += ptraceme;
208 gencore = (*argv && strcmp (*argv, "--gencore") == 0);
209 argv += gencore;
210 assert (!*argv);
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. */
215 dummy1 ();
216 dummy2 ();
217 dummy3 ();
218 dummy4 ();
219 if (gencore)
220 printf ("%ld\n", (long) getpid ());
221 pthread_t thread;
222 int i = pthread_create (&thread, NULL, start, NULL);
223 // pthread_* functions do not set errno.
224 assert (i == 0);
225 if (ptraceme)
226 {
227 errno = 0;
228 long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
229 assert_perror (errno);
230 assert (l == 0);
231 }
232 if (gencore)
233 pthread_join (thread, NULL);
234 else
235 raise (SIGUSR2);
Jan Kratochvil435808a2014-01-18 21:56:13 +0100236 return 0;
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100237}
Kurt Roeckx02cefda2014-04-22 21:46:22 +0200238
239#endif /* ! __linux__ */
240