Jan Kratochvil | 8ae9bc9 | 2013-12-02 20:54:28 +0100 | [diff] [blame] | 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 | |
Kurt Roeckx | 02cefda | 2014-04-22 21:46:22 +0200 | [diff] [blame] | 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 | |
Jan Kratochvil | 8ae9bc9 | 2013-12-02 20:54:28 +0100 | [diff] [blame] | 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 | |
Jan Kratochvil | f88863f | 2013-12-05 16:16:14 +0100 | [diff] [blame] | 108 | static NOINLINE_NOCLONE void |
Jan Kratochvil | 8ae9bc9 | 2013-12-02 20:54:28 +0100 | [diff] [blame] | 109 | sigusr2 (int signo) |
| 110 | { |
| 111 | assert (signo == SIGUSR2); |
| 112 | if (! gencore) |
| 113 | { |
| 114 | raise (SIGUSR1); |
Jan Kratochvil | 435808a | 2014-01-18 21:56:13 +0100 | [diff] [blame] | 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. */ |
Jan Kratochvil | 8ae9bc9 | 2013-12-02 20:54:28 +0100 | [diff] [blame] | 119 | abort (); |
| 120 | } |
| 121 | /* Here we dump the core for --gencore. */ |
| 122 | raise (SIGABRT); |
Jan Kratochvil | f88863f | 2013-12-05 16:16:14 +0100 | [diff] [blame] | 123 | /* Avoid tail call optimization for the raise call. */ |
| 124 | asm volatile (""); |
Jan Kratochvil | 8ae9bc9 | 2013-12-02 20:54:28 +0100 | [diff] [blame] | 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_perror (errno); |
| 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_perror (errno); |
| 230 | assert (l == 0); |
| 231 | } |
| 232 | if (gencore) |
| 233 | pthread_join (thread, NULL); |
| 234 | else |
| 235 | raise (SIGUSR2); |
Jan Kratochvil | 435808a | 2014-01-18 21:56:13 +0100 | [diff] [blame] | 236 | return 0; |
Jan Kratochvil | 8ae9bc9 | 2013-12-02 20:54:28 +0100 | [diff] [blame] | 237 | } |
Kurt Roeckx | 02cefda | 2014-04-22 21:46:22 +0200 | [diff] [blame] | 238 | |
| 239 | #endif /* ! __linux__ */ |
| 240 | |