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
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
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
104static int ptraceme, gencore;
105
106/* Execution will arrive here from jmp by an artificial ptrace-spawn signal.  */
107
108static NOINLINE_NOCLONE void
109sigusr2 (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
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 (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
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 (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