1 /* 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
82 #ifndef __linux__
83
84 int
main(int argc,char ** argv)85 main (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
94 #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
104 static int ptraceme, gencore;
105
106 /* Execution will arrive here from jmp by an artificial ptrace-spawn signal. */
107
108 static NOINLINE_NOCLONE void
sigusr2(int signo)109 sigusr2 (int signo)
110 {
111 assert (signo == SIGUSR2);
112 if (! gencore)
113 {
114 raise (SIGUSR1);
115 /* 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. */
119 abort ();
120 }
121 /* Here we dump the core for --gencore. */
122 raise (SIGABRT);
123 /* Avoid tail call optimization for the raise call. */
124 asm volatile ("");
125 }
126
127 static NOINLINE_NOCLONE void
dummy1(void)128 dummy1 (void)
129 {
130 asm volatile ("");
131 }
132
133 #ifdef __x86_64__
134 static NOINLINE_NOCLONE USED void
jmp(void)135 jmp (void)
136 {
137 /* Not reached, signal will get ptrace-spawn to jump into sigusr2. */
138 abort ();
139 }
140 #endif
141
142 static NOINLINE_NOCLONE void
dummy2(void)143 dummy2 (void)
144 {
145 asm volatile ("");
146 }
147
148 static NOINLINE_NOCLONE NORETURN void
stdarg(int f UNUSED,...)149 stdarg (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 (errno == 0);
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
172 static NOINLINE_NOCLONE void
dummy3(void)173 dummy3 (void)
174 {
175 asm volatile ("");
176 }
177
178 static NOINLINE_NOCLONE void
backtracegen(void)179 backtracegen (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
187 static NOINLINE_NOCLONE void
dummy4(void)188 dummy4 (void)
189 {
190 asm volatile ("");
191 }
192
193 static void *
start(void * arg UNUSED)194 start (void *arg UNUSED)
195 {
196 backtracegen ();
197 /* Not reached. */
198 abort ();
199 }
200
201 int
main(int argc UNUSED,char ** argv)202 main (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 (errno == 0);
230 assert (l == 0);
231 }
232 if (gencore)
233 pthread_join (thread, NULL);
234 else
235 raise (SIGUSR2);
236 return 0;
237 }
238
239 #endif /* ! __linux__ */
240
241