1da0c48c4Sopenharmony_ci/* Test custom provided Dwfl_Thread_Callbacks vector. 2da0c48c4Sopenharmony_ci Copyright (C) 2013 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/* Test custom provided Dwfl_Thread_Callbacks vector. Test mimics what 19da0c48c4Sopenharmony_ci a ptrace based vector would do. */ 20da0c48c4Sopenharmony_ci 21da0c48c4Sopenharmony_ci#include <config.h> 22da0c48c4Sopenharmony_ci#include <assert.h> 23da0c48c4Sopenharmony_ci#include <inttypes.h> 24da0c48c4Sopenharmony_ci#include <stdio.h> 25da0c48c4Sopenharmony_ci#include <stdio_ext.h> 26da0c48c4Sopenharmony_ci#include <locale.h> 27da0c48c4Sopenharmony_ci#include <dirent.h> 28da0c48c4Sopenharmony_ci#include <stdlib.h> 29da0c48c4Sopenharmony_ci#include <errno.h> 30da0c48c4Sopenharmony_ci#include <unistd.h> 31da0c48c4Sopenharmony_ci#include <dwarf.h> 32da0c48c4Sopenharmony_ci#if defined(__x86_64__) && defined(__linux__) 33da0c48c4Sopenharmony_ci#include <sys/resource.h> 34da0c48c4Sopenharmony_ci#include <sys/ptrace.h> 35da0c48c4Sopenharmony_ci#include <signal.h> 36da0c48c4Sopenharmony_ci#include <sys/types.h> 37da0c48c4Sopenharmony_ci#include <sys/wait.h> 38da0c48c4Sopenharmony_ci#include <sys/user.h> 39da0c48c4Sopenharmony_ci#include <fcntl.h> 40da0c48c4Sopenharmony_ci#include <string.h> 41da0c48c4Sopenharmony_ci#include ELFUTILS_HEADER(dwfl) 42da0c48c4Sopenharmony_ci#endif 43da0c48c4Sopenharmony_ci#include "system.h" 44da0c48c4Sopenharmony_ci 45da0c48c4Sopenharmony_ci#if !defined(__x86_64__) || !defined(__linux__) 46da0c48c4Sopenharmony_ci 47da0c48c4Sopenharmony_ciint 48da0c48c4Sopenharmony_cimain (int argc __attribute__ ((unused)), char **argv) 49da0c48c4Sopenharmony_ci{ 50da0c48c4Sopenharmony_ci fprintf (stderr, "%s: x86_64 linux only test\n", 51da0c48c4Sopenharmony_ci argv[0]); 52da0c48c4Sopenharmony_ci return 77; 53da0c48c4Sopenharmony_ci} 54da0c48c4Sopenharmony_ci 55da0c48c4Sopenharmony_ci#else /* __x86_64__ && __linux__ */ 56da0c48c4Sopenharmony_ci 57da0c48c4Sopenharmony_ci/* The only arch specific code is set_initial_registers. */ 58da0c48c4Sopenharmony_ci 59da0c48c4Sopenharmony_cistatic int 60da0c48c4Sopenharmony_cifind_elf (Dwfl_Module *mod __attribute__ ((unused)), 61da0c48c4Sopenharmony_ci void **userdata __attribute__ ((unused)), 62da0c48c4Sopenharmony_ci const char *modname __attribute__ ((unused)), 63da0c48c4Sopenharmony_ci Dwarf_Addr base __attribute__ ((unused)), 64da0c48c4Sopenharmony_ci char **file_name __attribute__ ((unused)), 65da0c48c4Sopenharmony_ci Elf **elfp __attribute__ ((unused))) 66da0c48c4Sopenharmony_ci{ 67da0c48c4Sopenharmony_ci /* Not used as modules are reported explicitly. */ 68da0c48c4Sopenharmony_ci assert (0); 69da0c48c4Sopenharmony_ci} 70da0c48c4Sopenharmony_ci 71da0c48c4Sopenharmony_cistatic bool 72da0c48c4Sopenharmony_cimemory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, 73da0c48c4Sopenharmony_ci void *dwfl_arg __attribute__ ((unused))) 74da0c48c4Sopenharmony_ci{ 75da0c48c4Sopenharmony_ci pid_t child = dwfl_pid (dwfl); 76da0c48c4Sopenharmony_ci 77da0c48c4Sopenharmony_ci errno = 0; 78da0c48c4Sopenharmony_ci long l = ptrace (PTRACE_PEEKDATA, child, (void *) (uintptr_t) addr, NULL); 79da0c48c4Sopenharmony_ci 80da0c48c4Sopenharmony_ci // The unwinder can ask for an invalid address. 81da0c48c4Sopenharmony_ci // Don't assert on that but just politely refuse. 82da0c48c4Sopenharmony_ci if (errno != 0) { 83da0c48c4Sopenharmony_ci errno = 0; 84da0c48c4Sopenharmony_ci return false; 85da0c48c4Sopenharmony_ci } 86da0c48c4Sopenharmony_ci *result = l; 87da0c48c4Sopenharmony_ci 88da0c48c4Sopenharmony_ci return true; 89da0c48c4Sopenharmony_ci} 90da0c48c4Sopenharmony_ci 91da0c48c4Sopenharmony_ci/* Return filename and VMA address *BASEP where its mapping starts which 92da0c48c4Sopenharmony_ci contains ADDR. */ 93da0c48c4Sopenharmony_ci 94da0c48c4Sopenharmony_cistatic char * 95da0c48c4Sopenharmony_cimaps_lookup (pid_t pid, Dwarf_Addr addr, GElf_Addr *basep) 96da0c48c4Sopenharmony_ci{ 97da0c48c4Sopenharmony_ci char *fname; 98da0c48c4Sopenharmony_ci int i = asprintf (&fname, "/proc/%ld/maps", (long) pid); 99da0c48c4Sopenharmony_ci assert (i > 0); 100da0c48c4Sopenharmony_ci FILE *f = fopen (fname, "r"); 101da0c48c4Sopenharmony_ci assert (f); 102da0c48c4Sopenharmony_ci free (fname); 103da0c48c4Sopenharmony_ci for (;;) 104da0c48c4Sopenharmony_ci { 105da0c48c4Sopenharmony_ci // 37e3c22000-37e3c23000 rw-p 00022000 00:11 49532 /lib64/ld-2.14.90.so */ 106da0c48c4Sopenharmony_ci unsigned long start, end, offset; 107da0c48c4Sopenharmony_ci i = fscanf (f, "%lx-%lx %*s %lx %*x:%*x %*u", &start, &end, &offset); 108da0c48c4Sopenharmony_ci if (i != 3) 109da0c48c4Sopenharmony_ci break; 110da0c48c4Sopenharmony_ci char *filename = strdup (""); 111da0c48c4Sopenharmony_ci assert (filename); 112da0c48c4Sopenharmony_ci size_t filename_len = 0; 113da0c48c4Sopenharmony_ci for (;;) 114da0c48c4Sopenharmony_ci { 115da0c48c4Sopenharmony_ci int c = fgetc (f); 116da0c48c4Sopenharmony_ci assert (c != EOF); 117da0c48c4Sopenharmony_ci if (c == '\n') 118da0c48c4Sopenharmony_ci break; 119da0c48c4Sopenharmony_ci if (c == ' ' && *filename == '\0') 120da0c48c4Sopenharmony_ci continue; 121da0c48c4Sopenharmony_ci filename = realloc (filename, filename_len + 2); 122da0c48c4Sopenharmony_ci assert (filename); 123da0c48c4Sopenharmony_ci filename[filename_len++] = c; 124da0c48c4Sopenharmony_ci filename[filename_len] = '\0'; 125da0c48c4Sopenharmony_ci } 126da0c48c4Sopenharmony_ci if (start <= addr && addr < end) 127da0c48c4Sopenharmony_ci { 128da0c48c4Sopenharmony_ci i = fclose (f); 129da0c48c4Sopenharmony_ci assert (i == 0); 130da0c48c4Sopenharmony_ci 131da0c48c4Sopenharmony_ci *basep = start - offset; 132da0c48c4Sopenharmony_ci return filename; 133da0c48c4Sopenharmony_ci } 134da0c48c4Sopenharmony_ci free (filename); 135da0c48c4Sopenharmony_ci } 136da0c48c4Sopenharmony_ci *basep = 0; 137da0c48c4Sopenharmony_ci return NULL; 138da0c48c4Sopenharmony_ci} 139da0c48c4Sopenharmony_ci 140da0c48c4Sopenharmony_ci/* Add module containing ADDR to the DWFL address space. 141da0c48c4Sopenharmony_ci 142da0c48c4Sopenharmony_ci dwfl_report_elf call here violates Dwfl manipulation as one should call 143da0c48c4Sopenharmony_ci dwfl_report only between dwfl_report_begin_add and dwfl_report_end. 144da0c48c4Sopenharmony_ci Current elfutils implementation does not mind as dwfl_report_begin_add is 145da0c48c4Sopenharmony_ci empty. */ 146da0c48c4Sopenharmony_ci 147da0c48c4Sopenharmony_cistatic Dwfl_Module * 148da0c48c4Sopenharmony_cireport_module (Dwfl *dwfl, pid_t child, Dwarf_Addr addr) 149da0c48c4Sopenharmony_ci{ 150da0c48c4Sopenharmony_ci GElf_Addr base; 151da0c48c4Sopenharmony_ci char *long_name = maps_lookup (child, addr, &base); 152da0c48c4Sopenharmony_ci if (!long_name) 153da0c48c4Sopenharmony_ci return NULL; // not found 154da0c48c4Sopenharmony_ci Dwfl_Module *mod = dwfl_report_elf (dwfl, long_name, long_name, -1, 155da0c48c4Sopenharmony_ci base, false /* add_p_vaddr */); 156da0c48c4Sopenharmony_ci assert (mod); 157da0c48c4Sopenharmony_ci free (long_name); 158da0c48c4Sopenharmony_ci assert (dwfl_addrmodule (dwfl, addr) == mod); 159da0c48c4Sopenharmony_ci return mod; 160da0c48c4Sopenharmony_ci} 161da0c48c4Sopenharmony_ci 162da0c48c4Sopenharmony_cistatic pid_t 163da0c48c4Sopenharmony_cinext_thread (Dwfl *dwfl, void *dwfl_arg __attribute__ ((unused)), 164da0c48c4Sopenharmony_ci void **thread_argp) 165da0c48c4Sopenharmony_ci{ 166da0c48c4Sopenharmony_ci if (*thread_argp != NULL) 167da0c48c4Sopenharmony_ci return 0; 168da0c48c4Sopenharmony_ci /* Put arbitrary non-NULL value into *THREAD_ARGP as a marker so that this 169da0c48c4Sopenharmony_ci function returns non-zero PID only once. */ 170da0c48c4Sopenharmony_ci *thread_argp = thread_argp; 171da0c48c4Sopenharmony_ci return dwfl_pid (dwfl); 172da0c48c4Sopenharmony_ci} 173da0c48c4Sopenharmony_ci 174da0c48c4Sopenharmony_cistatic bool 175da0c48c4Sopenharmony_ciset_initial_registers (Dwfl_Thread *thread, 176da0c48c4Sopenharmony_ci void *thread_arg __attribute__ ((unused))) 177da0c48c4Sopenharmony_ci{ 178da0c48c4Sopenharmony_ci pid_t child = dwfl_pid (dwfl_thread_dwfl (thread)); 179da0c48c4Sopenharmony_ci 180da0c48c4Sopenharmony_ci struct user_regs_struct user_regs; 181da0c48c4Sopenharmony_ci long l = ptrace (PTRACE_GETREGS, child, NULL, &user_regs); 182da0c48c4Sopenharmony_ci assert (l == 0); 183da0c48c4Sopenharmony_ci 184da0c48c4Sopenharmony_ci Dwarf_Word dwarf_regs[17]; 185da0c48c4Sopenharmony_ci dwarf_regs[0] = user_regs.rax; 186da0c48c4Sopenharmony_ci dwarf_regs[1] = user_regs.rdx; 187da0c48c4Sopenharmony_ci dwarf_regs[2] = user_regs.rcx; 188da0c48c4Sopenharmony_ci dwarf_regs[3] = user_regs.rbx; 189da0c48c4Sopenharmony_ci dwarf_regs[4] = user_regs.rsi; 190da0c48c4Sopenharmony_ci dwarf_regs[5] = user_regs.rdi; 191da0c48c4Sopenharmony_ci dwarf_regs[6] = user_regs.rbp; 192da0c48c4Sopenharmony_ci dwarf_regs[7] = user_regs.rsp; 193da0c48c4Sopenharmony_ci dwarf_regs[8] = user_regs.r8; 194da0c48c4Sopenharmony_ci dwarf_regs[9] = user_regs.r9; 195da0c48c4Sopenharmony_ci dwarf_regs[10] = user_regs.r10; 196da0c48c4Sopenharmony_ci dwarf_regs[11] = user_regs.r11; 197da0c48c4Sopenharmony_ci dwarf_regs[12] = user_regs.r12; 198da0c48c4Sopenharmony_ci dwarf_regs[13] = user_regs.r13; 199da0c48c4Sopenharmony_ci dwarf_regs[14] = user_regs.r14; 200da0c48c4Sopenharmony_ci dwarf_regs[15] = user_regs.r15; 201da0c48c4Sopenharmony_ci dwarf_regs[16] = user_regs.rip; 202da0c48c4Sopenharmony_ci bool ok = dwfl_thread_state_registers (thread, 0, 17, dwarf_regs); 203da0c48c4Sopenharmony_ci assert (ok); 204da0c48c4Sopenharmony_ci 205da0c48c4Sopenharmony_ci /* x86_64 has PC contained in its CFI subset of DWARF register set so 206da0c48c4Sopenharmony_ci elfutils will figure out the real PC value from REGS. 207da0c48c4Sopenharmony_ci So no need to explicitly call dwfl_thread_state_register_pc. */ 208da0c48c4Sopenharmony_ci 209da0c48c4Sopenharmony_ci return true; 210da0c48c4Sopenharmony_ci} 211da0c48c4Sopenharmony_ci 212da0c48c4Sopenharmony_cistatic const Dwfl_Thread_Callbacks callbacks = 213da0c48c4Sopenharmony_ci{ 214da0c48c4Sopenharmony_ci next_thread, 215da0c48c4Sopenharmony_ci NULL, /* get_thread */ 216da0c48c4Sopenharmony_ci memory_read, 217da0c48c4Sopenharmony_ci set_initial_registers, 218da0c48c4Sopenharmony_ci NULL, /* detach */ 219da0c48c4Sopenharmony_ci NULL, /* thread_detach */ 220da0c48c4Sopenharmony_ci}; 221da0c48c4Sopenharmony_ci 222da0c48c4Sopenharmony_cistatic int 223da0c48c4Sopenharmony_ciframe_callback (Dwfl_Frame *state, void *arg) 224da0c48c4Sopenharmony_ci{ 225da0c48c4Sopenharmony_ci unsigned *framenop = arg; 226da0c48c4Sopenharmony_ci Dwarf_Addr pc; 227da0c48c4Sopenharmony_ci bool isactivation; 228da0c48c4Sopenharmony_ci if (! dwfl_frame_pc (state, &pc, &isactivation)) 229da0c48c4Sopenharmony_ci { 230da0c48c4Sopenharmony_ci error (1, 0, "%s", dwfl_errmsg (-1)); 231da0c48c4Sopenharmony_ci return 1; 232da0c48c4Sopenharmony_ci } 233da0c48c4Sopenharmony_ci Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1); 234da0c48c4Sopenharmony_ci 235da0c48c4Sopenharmony_ci /* Get PC->SYMNAME. */ 236da0c48c4Sopenharmony_ci Dwfl *dwfl = dwfl_thread_dwfl (dwfl_frame_thread (state)); 237da0c48c4Sopenharmony_ci Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted); 238da0c48c4Sopenharmony_ci if (mod == NULL) 239da0c48c4Sopenharmony_ci mod = report_module (dwfl, dwfl_pid (dwfl), pc_adjusted); 240da0c48c4Sopenharmony_ci const char *symname = NULL; 241da0c48c4Sopenharmony_ci symname = dwfl_module_addrname (mod, pc_adjusted); 242da0c48c4Sopenharmony_ci 243da0c48c4Sopenharmony_ci printf ("#%2u %#" PRIx64 "%4s\t%s\n", (*framenop)++, (uint64_t) pc, 244da0c48c4Sopenharmony_ci ! isactivation ? "- 1" : "", symname); 245da0c48c4Sopenharmony_ci return DWARF_CB_OK; 246da0c48c4Sopenharmony_ci} 247da0c48c4Sopenharmony_ci 248da0c48c4Sopenharmony_cistatic int 249da0c48c4Sopenharmony_cithread_callback (Dwfl_Thread *thread, void *thread_arg __attribute__ ((unused))) 250da0c48c4Sopenharmony_ci{ 251da0c48c4Sopenharmony_ci unsigned frameno = 0; 252da0c48c4Sopenharmony_ci switch (dwfl_thread_getframes (thread, frame_callback, &frameno)) 253da0c48c4Sopenharmony_ci { 254da0c48c4Sopenharmony_ci case 0: 255da0c48c4Sopenharmony_ci break; 256da0c48c4Sopenharmony_ci case -1: 257da0c48c4Sopenharmony_ci error (1, 0, "dwfl_thread_getframes: %s", dwfl_errmsg (-1)); 258da0c48c4Sopenharmony_ci break; 259da0c48c4Sopenharmony_ci default: 260da0c48c4Sopenharmony_ci abort (); 261da0c48c4Sopenharmony_ci } 262da0c48c4Sopenharmony_ci return DWARF_CB_OK; 263da0c48c4Sopenharmony_ci} 264da0c48c4Sopenharmony_ci 265da0c48c4Sopenharmony_ciint 266da0c48c4Sopenharmony_cimain (int argc __attribute__ ((unused)), char **argv __attribute__ ((unused))) 267da0c48c4Sopenharmony_ci{ 268da0c48c4Sopenharmony_ci /* We use no threads here which can interfere with handling a stream. */ 269da0c48c4Sopenharmony_ci __fsetlocking (stdin, FSETLOCKING_BYCALLER); 270da0c48c4Sopenharmony_ci __fsetlocking (stdout, FSETLOCKING_BYCALLER); 271da0c48c4Sopenharmony_ci __fsetlocking (stderr, FSETLOCKING_BYCALLER); 272da0c48c4Sopenharmony_ci 273da0c48c4Sopenharmony_ci /* Set locale. */ 274da0c48c4Sopenharmony_ci (void) setlocale (LC_ALL, ""); 275da0c48c4Sopenharmony_ci 276da0c48c4Sopenharmony_ci elf_version (EV_CURRENT); 277da0c48c4Sopenharmony_ci 278da0c48c4Sopenharmony_ci pid_t child = fork (); 279da0c48c4Sopenharmony_ci switch (child) 280da0c48c4Sopenharmony_ci { 281da0c48c4Sopenharmony_ci case -1: 282da0c48c4Sopenharmony_ci assert (0); 283da0c48c4Sopenharmony_ci break; 284da0c48c4Sopenharmony_ci case 0:; 285da0c48c4Sopenharmony_ci long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL); 286da0c48c4Sopenharmony_ci assert (l == 0); 287da0c48c4Sopenharmony_ci raise (SIGUSR1); 288da0c48c4Sopenharmony_ci return 0; 289da0c48c4Sopenharmony_ci default: 290da0c48c4Sopenharmony_ci break; 291da0c48c4Sopenharmony_ci } 292da0c48c4Sopenharmony_ci 293da0c48c4Sopenharmony_ci int status; 294da0c48c4Sopenharmony_ci pid_t pid = waitpid (child, &status, 0); 295da0c48c4Sopenharmony_ci assert (pid == child); 296da0c48c4Sopenharmony_ci assert (WIFSTOPPED (status)); 297da0c48c4Sopenharmony_ci assert (WSTOPSIG (status) == SIGUSR1); 298da0c48c4Sopenharmony_ci 299da0c48c4Sopenharmony_ci static char *debuginfo_path; 300da0c48c4Sopenharmony_ci static const Dwfl_Callbacks offline_callbacks = 301da0c48c4Sopenharmony_ci { 302da0c48c4Sopenharmony_ci .find_debuginfo = dwfl_standard_find_debuginfo, 303da0c48c4Sopenharmony_ci .debuginfo_path = &debuginfo_path, 304da0c48c4Sopenharmony_ci .section_address = dwfl_offline_section_address, 305da0c48c4Sopenharmony_ci .find_elf = find_elf, 306da0c48c4Sopenharmony_ci }; 307da0c48c4Sopenharmony_ci Dwfl *dwfl = dwfl_begin (&offline_callbacks); 308da0c48c4Sopenharmony_ci assert (dwfl); 309da0c48c4Sopenharmony_ci 310da0c48c4Sopenharmony_ci struct user_regs_struct user_regs; 311da0c48c4Sopenharmony_ci long l = ptrace (PTRACE_GETREGS, child, NULL, &user_regs); 312da0c48c4Sopenharmony_ci assert (l == 0); 313da0c48c4Sopenharmony_ci report_module (dwfl, child, user_regs.rip); 314da0c48c4Sopenharmony_ci 315da0c48c4Sopenharmony_ci bool ok = dwfl_attach_state (dwfl, EM_NONE, child, &callbacks, NULL); 316da0c48c4Sopenharmony_ci assert (ok); 317da0c48c4Sopenharmony_ci 318da0c48c4Sopenharmony_ci /* Multiple threads are not handled here. */ 319da0c48c4Sopenharmony_ci int err = dwfl_getthreads (dwfl, thread_callback, NULL); 320da0c48c4Sopenharmony_ci assert (! err); 321da0c48c4Sopenharmony_ci 322da0c48c4Sopenharmony_ci dwfl_end (dwfl); 323da0c48c4Sopenharmony_ci kill (child, SIGKILL); 324da0c48c4Sopenharmony_ci pid = waitpid (child, &status, 0); 325da0c48c4Sopenharmony_ci assert (pid == child); 326da0c48c4Sopenharmony_ci assert (WIFSIGNALED (status)); 327da0c48c4Sopenharmony_ci assert (WTERMSIG (status) == SIGKILL); 328da0c48c4Sopenharmony_ci 329da0c48c4Sopenharmony_ci return EXIT_SUCCESS; 330da0c48c4Sopenharmony_ci} 331da0c48c4Sopenharmony_ci 332da0c48c4Sopenharmony_ci#endif /* x86_64 */ 333