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