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