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