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