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 47 int 48 main (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 59 static int 60 find_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 71 static bool 72 memory_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 94 static char * 95 maps_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 147 static Dwfl_Module * 148 report_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 162 static pid_t 163 next_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 174 static bool 175 set_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 212 static 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 222 static int 223 frame_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 248 static int 249 thread_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 265 int 266 main (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 case 0:; 284 long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL); 285 assert (l == 0); 286 raise (SIGUSR1); 287 return 0; 288 default: 289 break; 290 } 291 292 int status; 293 pid_t pid = waitpid (child, &status, 0); 294 assert (pid == child); 295 assert (WIFSTOPPED (status)); 296 assert (WSTOPSIG (status) == SIGUSR1); 297 298 static char *debuginfo_path; 299 static const Dwfl_Callbacks offline_callbacks = 300 { 301 .find_debuginfo = dwfl_standard_find_debuginfo, 302 .debuginfo_path = &debuginfo_path, 303 .section_address = dwfl_offline_section_address, 304 .find_elf = find_elf, 305 }; 306 Dwfl *dwfl = dwfl_begin (&offline_callbacks); 307 assert (dwfl); 308 309 struct user_regs_struct user_regs; 310 long l = ptrace (PTRACE_GETREGS, child, NULL, &user_regs); 311 assert (l == 0); 312 report_module (dwfl, child, user_regs.rip); 313 314 bool ok = dwfl_attach_state (dwfl, EM_NONE, child, &callbacks, NULL); 315 assert (ok); 316 317 /* Multiple threads are not handled here. */ 318 int err = dwfl_getthreads (dwfl, thread_callback, NULL); 319 assert (! err); 320 321 dwfl_end (dwfl); 322 kill (child, SIGKILL); 323 pid = waitpid (child, &status, 0); 324 assert (pid == child); 325 assert (WIFSIGNALED (status)); 326 assert (WTERMSIG (status) == SIGKILL); 327 328 return EXIT_SUCCESS; 329 } 330 331 #endif /* x86_64 */ 332