1 /* Test program for unwinding of frames. 2 Copyright (C) 2013, 2014, 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 #include <config.h> 19 #include <assert.h> 20 #include <inttypes.h> 21 #include <stdio.h> 22 #include <stdio_ext.h> 23 #include <locale.h> 24 #include <dirent.h> 25 #include <stdlib.h> 26 #include <errno.h> 27 #include <unistd.h> 28 #include <dwarf.h> 29 #ifdef __linux__ 30 #include <sys/resource.h> 31 #include <sys/ptrace.h> 32 #include <signal.h> 33 #include <sys/types.h> 34 #include <sys/wait.h> 35 #include <sys/user.h> 36 #include <fcntl.h> 37 #include <string.h> 38 #include <argp.h> 39 #include ELFUTILS_HEADER(dwfl) 40 #endif 41 #include "system.h" 42 43 #ifndef __linux__ 44 45 int 46 main (int argc __attribute__ ((unused)), char **argv) 47 { 48 fprintf (stderr, "%s: Unwinding not supported for this architecture\n", 49 argv[0]); 50 return 77; 51 } 52 53 #else /* __linux__ */ 54 55 static int 56 dump_modules (Dwfl_Module *mod, void **userdata __attribute__ ((unused)), 57 const char *name, Dwarf_Addr start, 58 void *arg __attribute__ ((unused))) 59 { 60 Dwarf_Addr end; 61 dwfl_module_info (mod, NULL, NULL, &end, NULL, NULL, NULL, NULL); 62 printf ("%#" PRIx64 "\t%#" PRIx64 "\t%s\n", (uint64_t) start, (uint64_t) end, 63 name); 64 return DWARF_CB_OK; 65 } 66 67 static bool use_raise_jmp_patching; 68 static pid_t check_tid; 69 70 static void 71 callback_verify (pid_t tid, unsigned frameno, Dwarf_Addr pc, 72 const char *symname, Dwfl *dwfl) 73 { 74 static bool seen_main = false; 75 if (symname && *symname == '.') 76 symname++; 77 if (symname && strcmp (symname, "main") == 0) 78 seen_main = true; 79 if (pc == 0) 80 { 81 assert (seen_main); 82 return; 83 } 84 if (check_tid == 0) 85 check_tid = tid; 86 if (tid != check_tid) 87 { 88 // For the main thread we are only interested if we can unwind till 89 // we see the "main" symbol. 90 return; 91 } 92 Dwfl_Module *mod; 93 /* See case 4. Special case to help out simple frame pointer unwinders. */ 94 static bool duplicate_sigusr2 = false; 95 if (duplicate_sigusr2) 96 frameno--; 97 static bool reduce_frameno = false; 98 if (reduce_frameno) 99 frameno--; 100 if (! use_raise_jmp_patching && frameno >= 2) 101 frameno += 2; 102 const char *symname2 = NULL; 103 switch (frameno) 104 { 105 case 0: 106 if (! reduce_frameno && symname 107 && (strcmp (symname, "__kernel_vsyscall") == 0 108 || strcmp (symname, "__libc_do_syscall") == 0)) 109 reduce_frameno = true; 110 else 111 assert (symname && strcmp (symname, "raise") == 0); 112 break; 113 case 1: 114 assert (symname != NULL && strcmp (symname, "sigusr2") == 0); 115 break; 116 case 2: // x86_64 only 117 /* __restore_rt - glibc maybe does not have to have this symbol. */ 118 break; 119 case 3: // use_raise_jmp_patching 120 if (use_raise_jmp_patching) 121 { 122 /* Verify we trapped on the very first instruction of jmp. */ 123 assert (symname != NULL && strcmp (symname, "jmp") == 0); 124 mod = dwfl_addrmodule (dwfl, pc - 1); 125 if (mod) 126 symname2 = dwfl_module_addrname (mod, pc - 1); 127 assert (symname2 == NULL || strcmp (symname2, "jmp") != 0); 128 break; 129 } 130 FALLTHROUGH; 131 case 4: 132 /* Some simple frame unwinders get this wrong and think sigusr2 133 is calling itself again. Allow it and just pretend there is 134 an extra sigusr2 frame. */ 135 if (symname != NULL && strcmp (symname, "sigusr2") == 0) 136 { 137 duplicate_sigusr2 = true; 138 break; 139 } 140 assert (symname != NULL && strcmp (symname, "stdarg") == 0); 141 break; 142 case 5: 143 /* Verify we trapped on the very last instruction of child. */ 144 assert (symname != NULL && strcmp (symname, "backtracegen") == 0); 145 mod = dwfl_addrmodule (dwfl, pc); 146 if (mod) 147 symname2 = dwfl_module_addrname (mod, pc); 148 149 // Note that the following assert might in theory even fail on x86_64, 150 // there is no guarantee that the compiler doesn't reorder the 151 // instructions or even inserts some padding instructions at the end 152 // (which apparently happens on ppc64). 153 if (use_raise_jmp_patching) 154 assert (symname2 == NULL || strcmp (symname2, "backtracegen") != 0); 155 break; 156 } 157 } 158 159 static int 160 frame_callback (Dwfl_Frame *state, void *frame_arg) 161 { 162 int *framenop = frame_arg; 163 Dwarf_Addr pc; 164 bool isactivation; 165 166 if (*framenop > 16) 167 { 168 error (0, 0, "Too many frames: %d\n", *framenop); 169 return DWARF_CB_ABORT; 170 } 171 172 if (! dwfl_frame_pc (state, &pc, &isactivation)) 173 { 174 error (0, 0, "%s", dwfl_errmsg (-1)); 175 return DWARF_CB_ABORT; 176 } 177 Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1); 178 179 /* Get PC->SYMNAME. */ 180 Dwfl_Thread *thread = dwfl_frame_thread (state); 181 Dwfl *dwfl = dwfl_thread_dwfl (thread); 182 Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted); 183 const char *symname = NULL; 184 if (mod) 185 symname = dwfl_module_addrname (mod, pc_adjusted); 186 187 printf ("#%2d %#" PRIx64 "%4s\t%s\n", *framenop, (uint64_t) pc, 188 ! isactivation ? "- 1" : "", symname); 189 pid_t tid = dwfl_thread_tid (thread); 190 callback_verify (tid, *framenop, pc, symname, dwfl); 191 (*framenop)++; 192 193 return DWARF_CB_OK; 194 } 195 196 static int 197 thread_callback (Dwfl_Thread *thread, void *thread_arg __attribute__((unused))) 198 { 199 printf ("TID %ld:\n", (long) dwfl_thread_tid (thread)); 200 int frameno = 0; 201 switch (dwfl_thread_getframes (thread, frame_callback, &frameno)) 202 { 203 case 0: 204 break; 205 case DWARF_CB_ABORT: 206 return DWARF_CB_ABORT; 207 case -1: 208 error (0, 0, "dwfl_thread_getframes: %s", dwfl_errmsg (-1)); 209 /* All platforms do not have yet proper unwind termination. */ 210 break; 211 default: 212 abort (); 213 } 214 return DWARF_CB_OK; 215 } 216 217 static void 218 dump (Dwfl *dwfl) 219 { 220 ptrdiff_t ptrdiff = dwfl_getmodules (dwfl, dump_modules, NULL, 0); 221 assert (ptrdiff == 0); 222 bool err = false; 223 switch (dwfl_getthreads (dwfl, thread_callback, NULL)) 224 { 225 case 0: 226 break; 227 case DWARF_CB_ABORT: 228 err = true; 229 break; 230 case -1: 231 error (0, 0, "dwfl_getthreads: %s", dwfl_errmsg (-1)); 232 err = true; 233 break; 234 default: 235 abort (); 236 } 237 callback_verify (0, 0, 0, NULL, dwfl); 238 if (err) 239 exit (EXIT_FAILURE); 240 } 241 242 struct see_exec_module 243 { 244 Dwfl_Module *mod; 245 char selfpath[PATH_MAX + 1]; 246 }; 247 248 static int 249 see_exec_module (Dwfl_Module *mod, void **userdata __attribute__ ((unused)), 250 const char *name __attribute__ ((unused)), 251 Dwarf_Addr start __attribute__ ((unused)), void *arg) 252 { 253 struct see_exec_module *data = arg; 254 if (strcmp (name, data->selfpath) != 0) 255 return DWARF_CB_OK; 256 assert (data->mod == NULL); 257 data->mod = mod; 258 return DWARF_CB_ABORT; 259 } 260 261 /* We used to do this on x86_64 only (see backtrace-child why we now don't): 262 PC will get changed to function 'jmp' by backtrace.c function 263 prepare_thread. Then SIGUSR2 will be signalled to backtrace-child 264 which will invoke function sigusr2. 265 This is all done so that signal interrupts execution of the very first 266 instruction of a function. Properly handled unwind should not slip into 267 the previous unrelated function. */ 268 269 #ifdef __x86_64__ 270 /* #define RAISE_JMP_PATCHING 1 */ 271 #endif 272 273 static void 274 prepare_thread (pid_t pid2 __attribute__ ((unused)), 275 void (*jmp) (void) __attribute__ ((unused))) 276 { 277 #ifndef RAISE_JMP_PATCHING 278 abort (); 279 #else /* RAISE_JMP_PATCHING */ 280 long l; 281 struct user_regs_struct user_regs; 282 errno = 0; 283 l = ptrace (PTRACE_GETREGS, pid2, 0, (intptr_t) &user_regs); 284 assert (l == 0); 285 user_regs.rip = (intptr_t) jmp; 286 l = ptrace (PTRACE_SETREGS, pid2, 0, (intptr_t) &user_regs); 287 assert (l == 0); 288 l = ptrace (PTRACE_CONT, pid2, NULL, (void *) (intptr_t) SIGUSR2); 289 int status; 290 pid_t got = waitpid (pid2, &status, __WALL); 291 assert (got == pid2); 292 assert (WIFSTOPPED (status)); 293 assert (WSTOPSIG (status) == SIGUSR1); 294 #endif /* RAISE_JMP_PATCHING */ 295 } 296 297 #include <asm/unistd.h> 298 #include <unistd.h> 299 300 static void 301 report_pid (Dwfl *dwfl, pid_t pid) 302 { 303 int result = dwfl_linux_proc_report (dwfl, pid); 304 if (result < 0) 305 error (2, 0, "dwfl_linux_proc_report: %s", dwfl_errmsg (-1)); 306 else if (result > 0) 307 error (2, result, "dwfl_linux_proc_report"); 308 309 if (dwfl_report_end (dwfl, NULL, NULL) != 0) 310 error (2, 0, "dwfl_report_end: %s", dwfl_errmsg (-1)); 311 312 result = dwfl_linux_proc_attach (dwfl, pid, true); 313 if (result < 0) 314 error (2, 0, "dwfl_linux_proc_attach: %s", dwfl_errmsg (-1)); 315 else if (result > 0) 316 error (2, result, "dwfl_linux_proc_attach"); 317 } 318 319 static Dwfl * 320 pid_to_dwfl (pid_t pid) 321 { 322 static char *debuginfo_path; 323 static const Dwfl_Callbacks proc_callbacks = 324 { 325 .find_debuginfo = dwfl_standard_find_debuginfo, 326 .debuginfo_path = &debuginfo_path, 327 328 .find_elf = dwfl_linux_proc_find_elf, 329 }; 330 Dwfl *dwfl = dwfl_begin (&proc_callbacks); 331 if (dwfl == NULL) 332 error (2, 0, "dwfl_begin: %s", dwfl_errmsg (-1)); 333 report_pid (dwfl, pid); 334 return dwfl; 335 } 336 337 static void 338 exec_dump (const char *exec) 339 { 340 pid_t pid = fork (); 341 switch (pid) 342 { 343 case -1: 344 abort (); 345 case 0: 346 execl (exec, exec, "--ptraceme", NULL); 347 abort (); 348 default: 349 break; 350 } 351 352 /* Catch the main thread. Catch it first otherwise the /proc evaluation of 353 PID may have caught still ourselves before executing execl above. */ 354 errno = 0; 355 int status; 356 pid_t got = waitpid (pid, &status, 0); 357 assert (got == pid); 358 assert (WIFSTOPPED (status)); 359 // Main thread will signal SIGUSR2. Other thread will signal SIGUSR1. 360 assert (WSTOPSIG (status) == SIGUSR2); 361 362 /* Catch the spawned thread. Do not use __WCLONE as we could get racy 363 __WCLONE, probably despite pthread_create already had to be called the new 364 task is not yet alive enough for waitpid. */ 365 pid_t pid2 = waitpid (-1, &status, __WALL); 366 assert (pid2 > 0); 367 assert (pid2 != pid); 368 assert (WIFSTOPPED (status)); 369 // Main thread will signal SIGUSR2. Other thread will signal SIGUSR1. 370 assert (WSTOPSIG (status) == SIGUSR1); 371 372 Dwfl *dwfl = pid_to_dwfl (pid); 373 char *selfpathname; 374 int i = asprintf (&selfpathname, "/proc/%ld/exe", (long) pid); 375 assert (i > 0); 376 struct see_exec_module data; 377 ssize_t ssize = readlink (selfpathname, data.selfpath, 378 sizeof (data.selfpath)); 379 free (selfpathname); 380 assert (ssize > 0 && ssize < (ssize_t) sizeof (data.selfpath)); 381 data.selfpath[ssize] = '\0'; 382 data.mod = NULL; 383 dwfl_getmodules (dwfl, see_exec_module, &data, 0); 384 assert (data.mod != NULL); 385 GElf_Addr loadbase; 386 Elf *elf = dwfl_module_getelf (data.mod, &loadbase); 387 GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem); 388 assert (ehdr != NULL); 389 /* It is false also on x86_64 with i386 inferior. */ 390 #ifndef RAISE_JMP_PATCHING 391 use_raise_jmp_patching = false; 392 #else /* RAISE_JMP_PATCHING_ */ 393 use_raise_jmp_patching = ehdr->e_machine == EM_X86_64; 394 #endif /* __x86_64__ */ 395 void (*jmp) (void) = 0; 396 if (use_raise_jmp_patching) 397 { 398 // Find inferior symbol named "jmp". 399 int nsym = dwfl_module_getsymtab (data.mod); 400 int symi; 401 for (symi = 1; symi < nsym; ++symi) 402 { 403 GElf_Sym symbol; 404 const char *symbol_name = dwfl_module_getsym (data.mod, symi, &symbol, NULL); 405 if (symbol_name == NULL) 406 continue; 407 switch (GELF_ST_TYPE (symbol.st_info)) 408 { 409 case STT_SECTION: 410 case STT_FILE: 411 case STT_TLS: 412 continue; 413 default: 414 if (strcmp (symbol_name, "jmp") != 0) 415 continue; 416 break; 417 } 418 /* LOADBASE is already applied here. */ 419 jmp = (void (*) (void)) (uintptr_t) symbol.st_value; 420 break; 421 } 422 assert (symi < nsym); 423 prepare_thread (pid2, jmp); 424 } 425 dwfl_end (dwfl); 426 check_tid = pid2; 427 dwfl = pid_to_dwfl (pid); 428 dump (dwfl); 429 dwfl_end (dwfl); 430 } 431 432 #define OPT_BACKTRACE_EXEC 0x100 433 434 static const struct argp_option options[] = 435 { 436 { "backtrace-exec", OPT_BACKTRACE_EXEC, "EXEC", 0, N_("Run executable"), 0 }, 437 { NULL, 0, NULL, 0, NULL, 0 } 438 }; 439 440 441 static error_t 442 parse_opt (int key, char *arg, struct argp_state *state) 443 { 444 switch (key) 445 { 446 case ARGP_KEY_INIT: 447 state->child_inputs[0] = state->input; 448 break; 449 450 case OPT_BACKTRACE_EXEC: 451 exec_dump (arg); 452 exit (0); 453 454 default: 455 return ARGP_ERR_UNKNOWN; 456 } 457 return 0; 458 } 459 460 int 461 main (int argc __attribute__ ((unused)), char **argv) 462 { 463 /* We use no threads here which can interfere with handling a stream. */ 464 __fsetlocking (stdin, FSETLOCKING_BYCALLER); 465 __fsetlocking (stdout, FSETLOCKING_BYCALLER); 466 __fsetlocking (stderr, FSETLOCKING_BYCALLER); 467 468 /* Set locale. */ 469 (void) setlocale (LC_ALL, ""); 470 471 elf_version (EV_CURRENT); 472 473 Dwfl *dwfl = NULL; 474 const struct argp_child argp_children[] = 475 { 476 { .argp = dwfl_standard_argp () }, 477 { .argp = NULL } 478 }; 479 const struct argp argp = 480 { 481 options, parse_opt, NULL, NULL, argp_children, NULL, NULL 482 }; 483 (void) argp_parse (&argp, argc, argv, 0, NULL, &dwfl); 484 assert (dwfl != NULL); 485 /* We want to make sure the dwfl was properly attached. */ 486 if (dwfl_pid (dwfl) < 0) 487 error (2, 0, "dwfl_pid: %s", dwfl_errmsg (-1)); 488 dump (dwfl); 489 dwfl_end (dwfl); 490 return 0; 491 } 492 493 #endif /* ! __linux__ */ 494 495