1da0c48c4Sopenharmony_ci/* Test child for parent backtrace test.
2da0c48c4Sopenharmony_ci   Copyright (C) 2013, 2016 Red Hat, Inc.
3da0c48c4Sopenharmony_ci   This file is part of elfutils.
4da0c48c4Sopenharmony_ci
5da0c48c4Sopenharmony_ci   This file is free software; you can redistribute it and/or modify
6da0c48c4Sopenharmony_ci   it under the terms of the GNU General Public License as published by
7da0c48c4Sopenharmony_ci   the Free Software Foundation; either version 3 of the License, or
8da0c48c4Sopenharmony_ci   (at your option) any later version.
9da0c48c4Sopenharmony_ci
10da0c48c4Sopenharmony_ci   elfutils is distributed in the hope that it will be useful, but
11da0c48c4Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
12da0c48c4Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13da0c48c4Sopenharmony_ci   GNU General Public License for more details.
14da0c48c4Sopenharmony_ci
15da0c48c4Sopenharmony_ci   You should have received a copy of the GNU General Public License
16da0c48c4Sopenharmony_ci   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17da0c48c4Sopenharmony_ci
18da0c48c4Sopenharmony_ci/* Command line syntax: ./backtrace-child [--ptraceme|--gencore]
19da0c48c4Sopenharmony_ci   --ptraceme will call ptrace (PTRACE_TRACEME) in the two threads.
20da0c48c4Sopenharmony_ci   --gencore will call abort () at its end.
21da0c48c4Sopenharmony_ci   Main thread will signal SIGUSR2.  Other thread will signal SIGUSR1.
22da0c48c4Sopenharmony_ci   There used to be a difference between x86_64 and other architectures.
23da0c48c4Sopenharmony_ci   To test getting a signal at the very first instruction of a function:
24da0c48c4Sopenharmony_ci     PC will get changed to function 'jmp' by backtrace.c function
25da0c48c4Sopenharmony_ci     prepare_thread.  Then SIGUSR2 will be signalled to backtrace-child
26da0c48c4Sopenharmony_ci     which will invoke function sigusr2.
27da0c48c4Sopenharmony_ci     This is all done so that signal interrupts execution of the very first
28da0c48c4Sopenharmony_ci     instruction of a function.  Properly handled unwind should not slip into
29da0c48c4Sopenharmony_ci     the previous unrelated function.
30da0c48c4Sopenharmony_ci     The tested functionality is arch-independent but the code reproducing it
31da0c48c4Sopenharmony_ci     has to be arch-specific.
32da0c48c4Sopenharmony_ci   On non-x86_64:
33da0c48c4Sopenharmony_ci     sigusr2 gets called by normal function call from function stdarg.
34da0c48c4Sopenharmony_ci   On any arch then sigusr2 calls raise (SIGUSR1) for --ptraceme.
35da0c48c4Sopenharmony_ci   abort () is called otherwise, expected for --gencore core dump.
36da0c48c4Sopenharmony_ci
37da0c48c4Sopenharmony_ci   Expected x86_64 output:
38da0c48c4Sopenharmony_ci   TID 10276:
39da0c48c4Sopenharmony_ci   # 0 0x7f7ab61e9e6b      raise
40da0c48c4Sopenharmony_ci   # 1 0x7f7ab661af47 - 1  main
41da0c48c4Sopenharmony_ci   # 2 0x7f7ab5e3bb45 - 1  __libc_start_main
42da0c48c4Sopenharmony_ci   # 3 0x7f7ab661aa09 - 1  _start
43da0c48c4Sopenharmony_ci   TID 10278:
44da0c48c4Sopenharmony_ci   # 0 0x7f7ab61e9e6b      raise
45da0c48c4Sopenharmony_ci   # 1 0x7f7ab661ab3c - 1  sigusr2
46da0c48c4Sopenharmony_ci   # 2 0x7f7ab5e4fa60      __restore_rt
47da0c48c4Sopenharmony_ci   # 3 0x7f7ab661ab47      jmp
48da0c48c4Sopenharmony_ci   # 4 0x7f7ab661ac92 - 1  stdarg
49da0c48c4Sopenharmony_ci   # 5 0x7f7ab661acba - 1  backtracegen
50da0c48c4Sopenharmony_ci   # 6 0x7f7ab661acd1 - 1  start
51da0c48c4Sopenharmony_ci   # 7 0x7f7ab61e2c53 - 1  start_thread
52da0c48c4Sopenharmony_ci   # 8 0x7f7ab5f0fdbd - 1  __clone
53da0c48c4Sopenharmony_ci
54da0c48c4Sopenharmony_ci   Expected non-x86_64 (i386) output; __kernel_vsyscall are skipped if found:
55da0c48c4Sopenharmony_ci   TID 10408:
56da0c48c4Sopenharmony_ci   # 0 0xf779f430          __kernel_vsyscall
57da0c48c4Sopenharmony_ci   # 1 0xf7771466 - 1      raise
58da0c48c4Sopenharmony_ci   # 2 0xf77c1d07 - 1      main
59da0c48c4Sopenharmony_ci   # 3 0xf75bd963 - 1      __libc_start_main
60da0c48c4Sopenharmony_ci   # 4 0xf77c1761 - 1      _start
61da0c48c4Sopenharmony_ci   TID 10412:
62da0c48c4Sopenharmony_ci   # 0 0xf779f430          __kernel_vsyscall
63da0c48c4Sopenharmony_ci   # 1 0xf7771466 - 1      raise
64da0c48c4Sopenharmony_ci   # 2 0xf77c18f4 - 1      sigusr2
65da0c48c4Sopenharmony_ci   # 3 0xf77c1a10 - 1      stdarg
66da0c48c4Sopenharmony_ci   # 4 0xf77c1a2c - 1      backtracegen
67da0c48c4Sopenharmony_ci   # 5 0xf77c1a48 - 1      start
68da0c48c4Sopenharmony_ci   # 6 0xf77699da - 1      start_thread
69da0c48c4Sopenharmony_ci   # 7 0xf769bbfe - 1      __clone
70da0c48c4Sopenharmony_ci
71da0c48c4Sopenharmony_ci   But the raise jmp patching was unreliable. It depends on the CFI for the raise()
72da0c48c4Sopenharmony_ci   function in glibc to be the same as for the jmp() function. This is not always
73da0c48c4Sopenharmony_ci   the case. Some newer glibc versions rewrote raise() and now the CFA is calculated
74da0c48c4Sopenharmony_ci   differently. So we disable raise jmp patching everywhere.
75da0c48c4Sopenharmony_ci   */
76da0c48c4Sopenharmony_ci
77da0c48c4Sopenharmony_ci#ifdef __x86_64__
78da0c48c4Sopenharmony_ci/* #define RAISE_JMP_PATCHING 1 */
79da0c48c4Sopenharmony_ci#endif
80da0c48c4Sopenharmony_ci
81da0c48c4Sopenharmony_ci#include <config.h>
82da0c48c4Sopenharmony_ci#include <assert.h>
83da0c48c4Sopenharmony_ci#include <stdlib.h>
84da0c48c4Sopenharmony_ci#include <errno.h>
85da0c48c4Sopenharmony_ci#include <string.h>
86da0c48c4Sopenharmony_ci#include <pthread.h>
87da0c48c4Sopenharmony_ci#include <stdio.h>
88da0c48c4Sopenharmony_ci#include <unistd.h>
89da0c48c4Sopenharmony_ci
90da0c48c4Sopenharmony_ci#ifndef __linux__
91da0c48c4Sopenharmony_ci
92da0c48c4Sopenharmony_ciint
93da0c48c4Sopenharmony_cimain (int argc __attribute__ ((unused)), char **argv)
94da0c48c4Sopenharmony_ci{
95da0c48c4Sopenharmony_ci  fprintf (stderr, "%s: Unwinding not supported for this architecture\n",
96da0c48c4Sopenharmony_ci           argv[0]);
97da0c48c4Sopenharmony_ci  return 77;
98da0c48c4Sopenharmony_ci}
99da0c48c4Sopenharmony_ci
100da0c48c4Sopenharmony_ci#else /* __linux__ */
101da0c48c4Sopenharmony_ci#include <sys/ptrace.h>
102da0c48c4Sopenharmony_ci#include <signal.h>
103da0c48c4Sopenharmony_ci
104da0c48c4Sopenharmony_ci#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
105da0c48c4Sopenharmony_ci#define NOINLINE_NOCLONE __attribute__ ((noinline, noclone))
106da0c48c4Sopenharmony_ci#else
107da0c48c4Sopenharmony_ci#define NOINLINE_NOCLONE __attribute__ ((noinline))
108da0c48c4Sopenharmony_ci#endif
109da0c48c4Sopenharmony_ci
110da0c48c4Sopenharmony_ci#define NORETURN __attribute__ ((noreturn))
111da0c48c4Sopenharmony_ci#define UNUSED __attribute__ ((unused))
112da0c48c4Sopenharmony_ci#define USED __attribute__ ((used))
113da0c48c4Sopenharmony_ci
114da0c48c4Sopenharmony_cistatic int ptraceme, gencore;
115da0c48c4Sopenharmony_ci
116da0c48c4Sopenharmony_ci/* Execution will arrive here from jmp by an artificial ptrace-spawn signal.  */
117da0c48c4Sopenharmony_ci
118da0c48c4Sopenharmony_cistatic NOINLINE_NOCLONE void
119da0c48c4Sopenharmony_cisigusr2 (int signo)
120da0c48c4Sopenharmony_ci{
121da0c48c4Sopenharmony_ci  assert (signo == SIGUSR2);
122da0c48c4Sopenharmony_ci  if (! gencore)
123da0c48c4Sopenharmony_ci    {
124da0c48c4Sopenharmony_ci      raise (SIGUSR1);
125da0c48c4Sopenharmony_ci      /* Do not return as stack may be invalid due to ptrace-patched PC to the
126da0c48c4Sopenharmony_ci	 jmp function.  */
127da0c48c4Sopenharmony_ci      pthread_exit (NULL);
128da0c48c4Sopenharmony_ci      /* Not reached.  */
129da0c48c4Sopenharmony_ci      abort ();
130da0c48c4Sopenharmony_ci    }
131da0c48c4Sopenharmony_ci  /* Here we dump the core for --gencore.  */
132da0c48c4Sopenharmony_ci  raise (SIGABRT);
133da0c48c4Sopenharmony_ci  /* Avoid tail call optimization for the raise call.  */
134da0c48c4Sopenharmony_ci  asm volatile ("");
135da0c48c4Sopenharmony_ci}
136da0c48c4Sopenharmony_ci
137da0c48c4Sopenharmony_cistatic NOINLINE_NOCLONE void
138da0c48c4Sopenharmony_cidummy1 (void)
139da0c48c4Sopenharmony_ci{
140da0c48c4Sopenharmony_ci  asm volatile ("");
141da0c48c4Sopenharmony_ci}
142da0c48c4Sopenharmony_ci
143da0c48c4Sopenharmony_ci#ifdef RAISE_JMP_PATCHING
144da0c48c4Sopenharmony_cistatic NOINLINE_NOCLONE USED void
145da0c48c4Sopenharmony_cijmp (void)
146da0c48c4Sopenharmony_ci{
147da0c48c4Sopenharmony_ci  /* Not reached, signal will get ptrace-spawn to jump into sigusr2.  */
148da0c48c4Sopenharmony_ci  abort ();
149da0c48c4Sopenharmony_ci}
150da0c48c4Sopenharmony_ci#endif
151da0c48c4Sopenharmony_ci
152da0c48c4Sopenharmony_cistatic NOINLINE_NOCLONE void
153da0c48c4Sopenharmony_cidummy2 (void)
154da0c48c4Sopenharmony_ci{
155da0c48c4Sopenharmony_ci  asm volatile ("");
156da0c48c4Sopenharmony_ci}
157da0c48c4Sopenharmony_ci
158da0c48c4Sopenharmony_cistatic NOINLINE_NOCLONE NORETURN void
159da0c48c4Sopenharmony_cistdarg (int f UNUSED, ...)
160da0c48c4Sopenharmony_ci{
161da0c48c4Sopenharmony_ci  sighandler_t sigusr2_orig = signal (SIGUSR2, sigusr2);
162da0c48c4Sopenharmony_ci  assert (sigusr2_orig == SIG_DFL);
163da0c48c4Sopenharmony_ci  errno = 0;
164da0c48c4Sopenharmony_ci  if (ptraceme)
165da0c48c4Sopenharmony_ci    {
166da0c48c4Sopenharmony_ci      long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
167da0c48c4Sopenharmony_ci      assert (l == 0);
168da0c48c4Sopenharmony_ci    }
169da0c48c4Sopenharmony_ci#ifdef RAISE_JMP_PATCHING
170da0c48c4Sopenharmony_ci  if (! gencore)
171da0c48c4Sopenharmony_ci    {
172da0c48c4Sopenharmony_ci      /* Execution will get PC patched into function jmp.  */
173da0c48c4Sopenharmony_ci      raise (SIGUSR1);
174da0c48c4Sopenharmony_ci    }
175da0c48c4Sopenharmony_ci#endif
176da0c48c4Sopenharmony_ci  sigusr2 (SIGUSR2);
177da0c48c4Sopenharmony_ci  /* Not reached.  */
178da0c48c4Sopenharmony_ci  abort ();
179da0c48c4Sopenharmony_ci}
180da0c48c4Sopenharmony_ci
181da0c48c4Sopenharmony_cistatic NOINLINE_NOCLONE void
182da0c48c4Sopenharmony_cidummy3 (void)
183da0c48c4Sopenharmony_ci{
184da0c48c4Sopenharmony_ci  asm volatile ("");
185da0c48c4Sopenharmony_ci}
186da0c48c4Sopenharmony_ci
187da0c48c4Sopenharmony_cistatic NOINLINE_NOCLONE void
188da0c48c4Sopenharmony_cibacktracegen (void)
189da0c48c4Sopenharmony_ci{
190da0c48c4Sopenharmony_ci  stdarg (1);
191da0c48c4Sopenharmony_ci  /* Here should be no instruction after the stdarg call as it is noreturn
192da0c48c4Sopenharmony_ci     function.  It must be stdarg so that it is a call and not jump (jump as
193da0c48c4Sopenharmony_ci     a tail-call).  */
194da0c48c4Sopenharmony_ci}
195da0c48c4Sopenharmony_ci
196da0c48c4Sopenharmony_cistatic NOINLINE_NOCLONE void
197da0c48c4Sopenharmony_cidummy4 (void)
198da0c48c4Sopenharmony_ci{
199da0c48c4Sopenharmony_ci  asm volatile ("");
200da0c48c4Sopenharmony_ci}
201da0c48c4Sopenharmony_ci
202da0c48c4Sopenharmony_cistatic void *
203da0c48c4Sopenharmony_cistart (void *arg UNUSED)
204da0c48c4Sopenharmony_ci{
205da0c48c4Sopenharmony_ci  backtracegen ();
206da0c48c4Sopenharmony_ci  /* Not reached.  */
207da0c48c4Sopenharmony_ci  abort ();
208da0c48c4Sopenharmony_ci}
209da0c48c4Sopenharmony_ci
210da0c48c4Sopenharmony_ciint
211da0c48c4Sopenharmony_cimain (int argc UNUSED, char **argv)
212da0c48c4Sopenharmony_ci{
213da0c48c4Sopenharmony_ci  setbuf (stdout, NULL);
214da0c48c4Sopenharmony_ci  assert (*argv++);
215da0c48c4Sopenharmony_ci  ptraceme = (*argv && strcmp (*argv, "--ptraceme") == 0);
216da0c48c4Sopenharmony_ci  argv += ptraceme;
217da0c48c4Sopenharmony_ci  gencore = (*argv && strcmp (*argv, "--gencore") == 0);
218da0c48c4Sopenharmony_ci  argv += gencore;
219da0c48c4Sopenharmony_ci  assert (!*argv);
220da0c48c4Sopenharmony_ci  /* These dummy* functions are there so that each of their surrounding
221da0c48c4Sopenharmony_ci     functions has some unrelated code around.  The purpose of some of the
222da0c48c4Sopenharmony_ci     tests is verify unwinding the very first / after the very last instruction
223da0c48c4Sopenharmony_ci     does not inappropriately slip into the unrelated code around.  */
224da0c48c4Sopenharmony_ci  dummy1 ();
225da0c48c4Sopenharmony_ci  dummy2 ();
226da0c48c4Sopenharmony_ci  dummy3 ();
227da0c48c4Sopenharmony_ci  dummy4 ();
228da0c48c4Sopenharmony_ci  if (gencore)
229da0c48c4Sopenharmony_ci    printf ("%ld\n", (long) getpid ());
230da0c48c4Sopenharmony_ci  pthread_t thread;
231da0c48c4Sopenharmony_ci  int i = pthread_create (&thread, NULL, start, NULL);
232da0c48c4Sopenharmony_ci  // pthread_* functions do not set errno.
233da0c48c4Sopenharmony_ci  assert (i == 0);
234da0c48c4Sopenharmony_ci  if (ptraceme)
235da0c48c4Sopenharmony_ci    {
236da0c48c4Sopenharmony_ci      errno = 0;
237da0c48c4Sopenharmony_ci      long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
238da0c48c4Sopenharmony_ci      assert (l == 0);
239da0c48c4Sopenharmony_ci    }
240da0c48c4Sopenharmony_ci  if (gencore)
241da0c48c4Sopenharmony_ci    pthread_join (thread, NULL);
242da0c48c4Sopenharmony_ci  else
243da0c48c4Sopenharmony_ci    raise (SIGUSR2);
244da0c48c4Sopenharmony_ci  return 0;
245da0c48c4Sopenharmony_ci}
246da0c48c4Sopenharmony_ci
247da0c48c4Sopenharmony_ci#endif /* ! __linux__ */
248da0c48c4Sopenharmony_ci
249