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