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 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 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 128 dummy1 (void) 129 { 130 asm volatile (""); 131 } 132 133 #ifdef __x86_64__ 134 static NOINLINE_NOCLONE USED 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 143 dummy2 (void) 144 { 145 asm volatile (""); 146 } 147 148 static NOINLINE_NOCLONE NORETURN void 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 173 dummy3 (void) 174 { 175 asm volatile (""); 176 } 177 178 static NOINLINE_NOCLONE 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 188 dummy4 (void) 189 { 190 asm volatile (""); 191 } 192 193 static void * 194 start (void *arg UNUSED) 195 { 196 backtracegen (); 197 /* Not reached. */ 198 abort (); 199 } 200 201 int 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