blob: 9c6ba94f1139ba5864d2cf7f752b716858f761a9 [file] [log] [blame]
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +01001/* Test child for parent backtrace test.
Mark Wielaard90084992016-08-25 17:17:23 +02002 Copyright (C) 2013, 2016 Red Hat, Inc.
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +01003 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.
Mark Wielaard90084992016-08-25 17:17:23 +020022 There used to be a difference between x86_64 and other architectures.
23 To test getting a signal at the very first instruction of a function:
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +010024 PC will get changed to function 'jmp' by backtrace.c function
25 prepare_thread. Then SIGUSR2 will be signalled to backtrace-child
26 which will invoke function sigusr2.
27 This is all done so that signal interrupts execution of the very first
28 instruction of a function. Properly handled unwind should not slip into
29 the previous unrelated function.
30 The tested functionality is arch-independent but the code reproducing it
31 has to be arch-specific.
32 On non-x86_64:
33 sigusr2 gets called by normal function call from function stdarg.
34 On any arch then sigusr2 calls raise (SIGUSR1) for --ptraceme.
35 abort () is called otherwise, expected for --gencore core dump.
36
37 Expected x86_64 output:
38 TID 10276:
39 # 0 0x7f7ab61e9e6b raise
40 # 1 0x7f7ab661af47 - 1 main
41 # 2 0x7f7ab5e3bb45 - 1 __libc_start_main
42 # 3 0x7f7ab661aa09 - 1 _start
43 TID 10278:
44 # 0 0x7f7ab61e9e6b raise
45 # 1 0x7f7ab661ab3c - 1 sigusr2
46 # 2 0x7f7ab5e4fa60 __restore_rt
47 # 3 0x7f7ab661ab47 jmp
48 # 4 0x7f7ab661ac92 - 1 stdarg
49 # 5 0x7f7ab661acba - 1 backtracegen
50 # 6 0x7f7ab661acd1 - 1 start
51 # 7 0x7f7ab61e2c53 - 1 start_thread
52 # 8 0x7f7ab5f0fdbd - 1 __clone
53
54 Expected non-x86_64 (i386) output; __kernel_vsyscall are skipped if found:
55 TID 10408:
56 # 0 0xf779f430 __kernel_vsyscall
57 # 1 0xf7771466 - 1 raise
58 # 2 0xf77c1d07 - 1 main
59 # 3 0xf75bd963 - 1 __libc_start_main
60 # 4 0xf77c1761 - 1 _start
61 TID 10412:
62 # 0 0xf779f430 __kernel_vsyscall
63 # 1 0xf7771466 - 1 raise
64 # 2 0xf77c18f4 - 1 sigusr2
65 # 3 0xf77c1a10 - 1 stdarg
66 # 4 0xf77c1a2c - 1 backtracegen
67 # 5 0xf77c1a48 - 1 start
68 # 6 0xf77699da - 1 start_thread
69 # 7 0xf769bbfe - 1 __clone
Mark Wielaard90084992016-08-25 17:17:23 +020070
71 But the raise jmp patching was unreliable. It depends on the CFI for the raise()
72 function in glibc to be the same as for the jmp() function. This is not always
73 the case. Some newer glibc versions rewrote raise() and now the CFA is calculated
74 differently. So we disable raise jmp patching everywhere.
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +010075 */
76
Mark Wielaard90084992016-08-25 17:17:23 +020077#ifdef __x86_64__
78/* #define RAISE_JMP_PATCHING 1 */
79#endif
80
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +010081#include <config.h>
82#include <assert.h>
83#include <stdlib.h>
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +010084#include <errno.h>
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +010085#include <string.h>
86#include <pthread.h>
87#include <stdio.h>
88#include <unistd.h>
89
Kurt Roeckx02cefda2014-04-22 21:46:22 +020090#ifndef __linux__
91
92int
93main (int argc __attribute__ ((unused)), char **argv)
94{
95 fprintf (stderr, "%s: Unwinding not supported for this architecture\n",
96 argv[0]);
97 return 77;
98}
99
100#else /* __linux__ */
Ulf Hermann9c1509a2017-04-20 15:37:04 +0200101#include <sys/ptrace.h>
Mark Wielaard4482d002018-02-15 14:44:18 +0100102#include <signal.h>
Kurt Roeckx02cefda2014-04-22 21:46:22 +0200103
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100104#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
105#define NOINLINE_NOCLONE __attribute__ ((noinline, noclone))
106#else
107#define NOINLINE_NOCLONE __attribute__ ((noinline))
108#endif
109
110#define NORETURN __attribute__ ((noreturn))
111#define UNUSED __attribute__ ((unused))
112#define USED __attribute__ ((used))
113
114static int ptraceme, gencore;
115
116/* Execution will arrive here from jmp by an artificial ptrace-spawn signal. */
117
Jan Kratochvilf88863f2013-12-05 16:16:14 +0100118static NOINLINE_NOCLONE void
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100119sigusr2 (int signo)
120{
121 assert (signo == SIGUSR2);
122 if (! gencore)
123 {
124 raise (SIGUSR1);
Jan Kratochvil435808a2014-01-18 21:56:13 +0100125 /* Do not return as stack may be invalid due to ptrace-patched PC to the
126 jmp function. */
127 pthread_exit (NULL);
128 /* Not reached. */
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100129 abort ();
130 }
131 /* Here we dump the core for --gencore. */
132 raise (SIGABRT);
Jan Kratochvilf88863f2013-12-05 16:16:14 +0100133 /* Avoid tail call optimization for the raise call. */
134 asm volatile ("");
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100135}
136
137static NOINLINE_NOCLONE void
138dummy1 (void)
139{
140 asm volatile ("");
141}
142
Mark Wielaard90084992016-08-25 17:17:23 +0200143#ifdef RAISE_JMP_PATCHING
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100144static NOINLINE_NOCLONE USED void
145jmp (void)
146{
147 /* Not reached, signal will get ptrace-spawn to jump into sigusr2. */
148 abort ();
149}
150#endif
151
152static NOINLINE_NOCLONE void
153dummy2 (void)
154{
155 asm volatile ("");
156}
157
158static NOINLINE_NOCLONE NORETURN void
159stdarg (int f UNUSED, ...)
160{
161 sighandler_t sigusr2_orig = signal (SIGUSR2, sigusr2);
162 assert (sigusr2_orig == SIG_DFL);
163 errno = 0;
164 if (ptraceme)
165 {
166 long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
Max Filippovc801acf2015-05-04 20:17:52 +0300167 assert (errno == 0);
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100168 assert (l == 0);
169 }
Mark Wielaard90084992016-08-25 17:17:23 +0200170#ifdef RAISE_JMP_PATCHING
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100171 if (! gencore)
172 {
173 /* Execution will get PC patched into function jmp. */
174 raise (SIGUSR1);
175 }
176#endif
177 sigusr2 (SIGUSR2);
178 /* Not reached. */
179 abort ();
180}
181
182static NOINLINE_NOCLONE void
183dummy3 (void)
184{
185 asm volatile ("");
186}
187
188static NOINLINE_NOCLONE void
189backtracegen (void)
190{
191 stdarg (1);
192 /* Here should be no instruction after the stdarg call as it is noreturn
193 function. It must be stdarg so that it is a call and not jump (jump as
194 a tail-call). */
195}
196
197static NOINLINE_NOCLONE void
198dummy4 (void)
199{
200 asm volatile ("");
201}
202
203static void *
204start (void *arg UNUSED)
205{
206 backtracegen ();
207 /* Not reached. */
208 abort ();
209}
210
211int
212main (int argc UNUSED, char **argv)
213{
214 setbuf (stdout, NULL);
215 assert (*argv++);
216 ptraceme = (*argv && strcmp (*argv, "--ptraceme") == 0);
217 argv += ptraceme;
218 gencore = (*argv && strcmp (*argv, "--gencore") == 0);
219 argv += gencore;
220 assert (!*argv);
221 /* These dummy* functions are there so that each of their surrounding
222 functions has some unrelated code around. The purpose of some of the
223 tests is verify unwinding the very first / after the very last instruction
224 does not inappropriately slip into the unrelated code around. */
225 dummy1 ();
226 dummy2 ();
227 dummy3 ();
228 dummy4 ();
229 if (gencore)
230 printf ("%ld\n", (long) getpid ());
231 pthread_t thread;
232 int i = pthread_create (&thread, NULL, start, NULL);
233 // pthread_* functions do not set errno.
234 assert (i == 0);
235 if (ptraceme)
236 {
237 errno = 0;
238 long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
Max Filippovc801acf2015-05-04 20:17:52 +0300239 assert (errno == 0);
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100240 assert (l == 0);
241 }
242 if (gencore)
243 pthread_join (thread, NULL);
244 else
245 raise (SIGUSR2);
Jan Kratochvil435808a2014-01-18 21:56:13 +0100246 return 0;
Jan Kratochvil8ae9bc92013-12-02 20:54:28 +0100247}
Kurt Roeckx02cefda2014-04-22 21:46:22 +0200248
249#endif /* ! __linux__ */
250