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_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 
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_perror (errno);
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