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