Brian Silverman | 8649792 | 2018-02-10 19:28:39 -0500 | [diff] [blame] | 1 | /* Get Dwarf Frame state for target PID or core file. |
| 2 | Copyright (C) 2013, 2014 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 either |
| 7 | |
| 8 | * the GNU Lesser General Public License as published by the Free |
| 9 | Software Foundation; either version 3 of the License, or (at |
| 10 | your option) any later version |
| 11 | |
| 12 | or |
| 13 | |
| 14 | * the GNU General Public License as published by the Free |
| 15 | Software Foundation; either version 2 of the License, or (at |
| 16 | your option) any later version |
| 17 | |
| 18 | or both in parallel, as here. |
| 19 | |
| 20 | elfutils is distributed in the hope that it will be useful, but |
| 21 | WITHOUT ANY WARRANTY; without even the implied warranty of |
| 22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 23 | General Public License for more details. |
| 24 | |
| 25 | You should have received copies of the GNU General Public License and |
| 26 | the GNU Lesser General Public License along with this program. If |
| 27 | not, see <http://www.gnu.org/licenses/>. */ |
| 28 | |
| 29 | #ifdef HAVE_CONFIG_H |
| 30 | # include <config.h> |
| 31 | #endif |
| 32 | |
| 33 | #include "libdwflP.h" |
| 34 | #include <unistd.h> |
| 35 | |
| 36 | /* Set STATE->pc_set from STATE->regs according to the backend. Return true on |
| 37 | success, false on error. */ |
| 38 | static bool |
| 39 | state_fetch_pc (Dwfl_Frame *state) |
| 40 | { |
| 41 | switch (state->pc_state) |
| 42 | { |
| 43 | case DWFL_FRAME_STATE_PC_SET: |
| 44 | return true; |
| 45 | case DWFL_FRAME_STATE_PC_UNDEFINED: |
| 46 | abort (); |
| 47 | case DWFL_FRAME_STATE_ERROR: |
| 48 | { |
| 49 | Ebl *ebl = state->thread->process->ebl; |
| 50 | Dwarf_CIE abi_info; |
| 51 | if (ebl_abi_cfi (ebl, &abi_info) != 0) |
| 52 | { |
| 53 | __libdwfl_seterrno (DWFL_E_LIBEBL); |
| 54 | return false; |
| 55 | } |
| 56 | unsigned ra = abi_info.return_address_register; |
| 57 | /* dwarf_frame_state_reg_is_set is not applied here. */ |
| 58 | if (ra >= ebl_frame_nregs (ebl)) |
| 59 | { |
| 60 | __libdwfl_seterrno (DWFL_E_LIBEBL_BAD); |
| 61 | return false; |
| 62 | } |
| 63 | state->pc = state->regs[ra] + ebl_ra_offset (ebl); |
| 64 | state->pc_state = DWFL_FRAME_STATE_PC_SET; |
| 65 | } |
| 66 | return true; |
| 67 | } |
| 68 | abort (); |
| 69 | } |
| 70 | |
| 71 | /* Do not call it on your own, to be used by thread_* functions only. */ |
| 72 | |
| 73 | static void |
| 74 | state_free (Dwfl_Frame *state) |
| 75 | { |
| 76 | Dwfl_Thread *thread = state->thread; |
| 77 | assert (thread->unwound == state); |
| 78 | thread->unwound = state->unwound; |
| 79 | free (state); |
| 80 | } |
| 81 | |
| 82 | static void |
| 83 | thread_free_all_states (Dwfl_Thread *thread) |
| 84 | { |
| 85 | while (thread->unwound) |
| 86 | state_free (thread->unwound); |
| 87 | } |
| 88 | |
| 89 | static Dwfl_Frame * |
| 90 | state_alloc (Dwfl_Thread *thread) |
| 91 | { |
| 92 | assert (thread->unwound == NULL); |
| 93 | Ebl *ebl = thread->process->ebl; |
| 94 | size_t nregs = ebl_frame_nregs (ebl); |
| 95 | if (nregs == 0) |
| 96 | return NULL; |
| 97 | assert (nregs < sizeof (((Dwfl_Frame *) NULL)->regs_set) * 8); |
| 98 | Dwfl_Frame *state = malloc (sizeof (*state) + sizeof (*state->regs) * nregs); |
| 99 | if (state == NULL) |
| 100 | return NULL; |
| 101 | state->thread = thread; |
| 102 | state->signal_frame = false; |
| 103 | state->initial_frame = true; |
| 104 | state->pc_state = DWFL_FRAME_STATE_ERROR; |
| 105 | memset (state->regs_set, 0, sizeof (state->regs_set)); |
| 106 | thread->unwound = state; |
| 107 | state->unwound = NULL; |
| 108 | return state; |
| 109 | } |
| 110 | |
| 111 | void |
| 112 | internal_function |
| 113 | __libdwfl_process_free (Dwfl_Process *process) |
| 114 | { |
| 115 | Dwfl *dwfl = process->dwfl; |
| 116 | if (process->callbacks->detach != NULL) |
| 117 | process->callbacks->detach (dwfl, process->callbacks_arg); |
| 118 | assert (dwfl->process == process); |
| 119 | dwfl->process = NULL; |
| 120 | if (process->ebl_close) |
| 121 | ebl_closebackend (process->ebl); |
| 122 | free (process); |
| 123 | dwfl->attacherr = DWFL_E_NOERROR; |
| 124 | } |
| 125 | |
| 126 | /* Allocate new Dwfl_Process for DWFL. */ |
| 127 | static void |
| 128 | process_alloc (Dwfl *dwfl) |
| 129 | { |
| 130 | Dwfl_Process *process = malloc (sizeof (*process)); |
| 131 | if (process == NULL) |
| 132 | return; |
| 133 | process->dwfl = dwfl; |
| 134 | dwfl->process = process; |
| 135 | } |
| 136 | |
| 137 | bool |
| 138 | dwfl_attach_state (Dwfl *dwfl, Elf *elf, pid_t pid, |
| 139 | const Dwfl_Thread_Callbacks *thread_callbacks, void *arg) |
| 140 | { |
| 141 | if (dwfl->process != NULL) |
| 142 | { |
| 143 | __libdwfl_seterrno (DWFL_E_ATTACH_STATE_CONFLICT); |
| 144 | return false; |
| 145 | } |
| 146 | |
| 147 | /* Reset any previous error, we are just going to try again. */ |
| 148 | dwfl->attacherr = DWFL_E_NOERROR; |
| 149 | /* thread_callbacks is declared NN */ |
| 150 | if (thread_callbacks->next_thread == NULL |
| 151 | || thread_callbacks->set_initial_registers == NULL) |
| 152 | { |
| 153 | dwfl->attacherr = DWFL_E_INVALID_ARGUMENT; |
| 154 | fail: |
| 155 | dwfl->attacherr = __libdwfl_canon_error (dwfl->attacherr); |
| 156 | __libdwfl_seterrno (dwfl->attacherr); |
| 157 | return false; |
| 158 | } |
| 159 | |
| 160 | Ebl *ebl; |
| 161 | bool ebl_close; |
| 162 | if (elf != NULL) |
| 163 | { |
| 164 | ebl = ebl_openbackend (elf); |
| 165 | ebl_close = true; |
| 166 | } |
| 167 | else |
| 168 | { |
| 169 | ebl = NULL; |
| 170 | for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next) |
| 171 | { |
| 172 | /* Reading of the vDSO or (deleted) modules may fail as |
| 173 | /proc/PID/mem is unreadable without PTRACE_ATTACH and |
| 174 | we may not be PTRACE_ATTACH-ed now. MOD would not be |
| 175 | re-read later to unwind it when we are already |
| 176 | PTRACE_ATTACH-ed to PID. This happens when this function |
| 177 | is called from dwfl_linux_proc_attach with elf == NULL. |
| 178 | __libdwfl_module_getebl will call __libdwfl_getelf which |
| 179 | will call the find_elf callback. */ |
| 180 | if (strncmp (mod->name, "[vdso: ", 7) == 0 |
| 181 | || strcmp (strrchr (mod->name, ' ') ?: "", |
| 182 | " (deleted)") == 0) |
| 183 | continue; |
| 184 | Dwfl_Error error = __libdwfl_module_getebl (mod); |
| 185 | if (error != DWFL_E_NOERROR) |
| 186 | continue; |
| 187 | ebl = mod->ebl; |
| 188 | break; |
| 189 | } |
| 190 | ebl_close = false; |
| 191 | } |
| 192 | if (ebl == NULL) |
| 193 | { |
| 194 | /* Not identified EBL from any of the modules. */ |
| 195 | dwfl->attacherr = DWFL_E_PROCESS_NO_ARCH; |
| 196 | goto fail; |
| 197 | } |
| 198 | process_alloc (dwfl); |
| 199 | Dwfl_Process *process = dwfl->process; |
| 200 | if (process == NULL) |
| 201 | { |
| 202 | if (ebl_close) |
| 203 | ebl_closebackend (ebl); |
| 204 | dwfl->attacherr = DWFL_E_NOMEM; |
| 205 | goto fail; |
| 206 | } |
| 207 | process->ebl = ebl; |
| 208 | process->ebl_close = ebl_close; |
| 209 | process->pid = pid; |
| 210 | process->callbacks = thread_callbacks; |
| 211 | process->callbacks_arg = arg; |
| 212 | return true; |
| 213 | } |
| 214 | INTDEF(dwfl_attach_state) |
| 215 | |
| 216 | pid_t |
| 217 | dwfl_pid (Dwfl *dwfl) |
| 218 | { |
| 219 | if (dwfl->attacherr != DWFL_E_NOERROR) |
| 220 | { |
| 221 | __libdwfl_seterrno (dwfl->attacherr); |
| 222 | return -1; |
| 223 | } |
| 224 | |
| 225 | if (dwfl->process == NULL) |
| 226 | { |
| 227 | __libdwfl_seterrno (DWFL_E_NO_ATTACH_STATE); |
| 228 | return -1; |
| 229 | } |
| 230 | return dwfl->process->pid; |
| 231 | } |
| 232 | INTDEF(dwfl_pid) |
| 233 | |
| 234 | Dwfl * |
| 235 | dwfl_thread_dwfl (Dwfl_Thread *thread) |
| 236 | { |
| 237 | return thread->process->dwfl; |
| 238 | } |
| 239 | INTDEF(dwfl_thread_dwfl) |
| 240 | |
| 241 | pid_t |
| 242 | dwfl_thread_tid (Dwfl_Thread *thread) |
| 243 | { |
| 244 | return thread->tid; |
| 245 | } |
| 246 | INTDEF(dwfl_thread_tid) |
| 247 | |
| 248 | Dwfl_Thread * |
| 249 | dwfl_frame_thread (Dwfl_Frame *state) |
| 250 | { |
| 251 | return state->thread; |
| 252 | } |
| 253 | INTDEF(dwfl_frame_thread) |
| 254 | |
| 255 | int |
| 256 | dwfl_getthreads (Dwfl *dwfl, int (*callback) (Dwfl_Thread *thread, void *arg), |
| 257 | void *arg) |
| 258 | { |
| 259 | if (dwfl->attacherr != DWFL_E_NOERROR) |
| 260 | { |
| 261 | __libdwfl_seterrno (dwfl->attacherr); |
| 262 | return -1; |
| 263 | } |
| 264 | |
| 265 | Dwfl_Process *process = dwfl->process; |
| 266 | if (process == NULL) |
| 267 | { |
| 268 | __libdwfl_seterrno (DWFL_E_NO_ATTACH_STATE); |
| 269 | return -1; |
| 270 | } |
| 271 | |
| 272 | Dwfl_Thread thread; |
| 273 | thread.process = process; |
| 274 | thread.unwound = NULL; |
| 275 | thread.callbacks_arg = NULL; |
| 276 | for (;;) |
| 277 | { |
| 278 | thread.tid = process->callbacks->next_thread (dwfl, |
| 279 | process->callbacks_arg, |
| 280 | &thread.callbacks_arg); |
| 281 | if (thread.tid < 0) |
| 282 | { |
| 283 | Dwfl_Error saved_errno = dwfl_errno (); |
| 284 | thread_free_all_states (&thread); |
| 285 | __libdwfl_seterrno (saved_errno); |
| 286 | return -1; |
| 287 | } |
| 288 | if (thread.tid == 0) |
| 289 | { |
| 290 | thread_free_all_states (&thread); |
| 291 | __libdwfl_seterrno (DWFL_E_NOERROR); |
| 292 | return 0; |
| 293 | } |
| 294 | int err = callback (&thread, arg); |
| 295 | if (err != DWARF_CB_OK) |
| 296 | { |
| 297 | thread_free_all_states (&thread); |
| 298 | return err; |
| 299 | } |
| 300 | assert (thread.unwound == NULL); |
| 301 | } |
| 302 | /* NOTREACHED */ |
| 303 | } |
| 304 | INTDEF(dwfl_getthreads) |
| 305 | |
| 306 | struct one_arg |
| 307 | { |
| 308 | pid_t tid; |
| 309 | bool seen; |
| 310 | int (*callback) (Dwfl_Thread *thread, void *arg); |
| 311 | void *arg; |
| 312 | int ret; |
| 313 | }; |
| 314 | |
| 315 | static int |
| 316 | get_one_thread_cb (Dwfl_Thread *thread, void *arg) |
| 317 | { |
| 318 | struct one_arg *oa = (struct one_arg *) arg; |
| 319 | if (! oa->seen && INTUSE(dwfl_thread_tid) (thread) == oa->tid) |
| 320 | { |
| 321 | oa->seen = true; |
| 322 | oa->ret = oa->callback (thread, oa->arg); |
| 323 | return DWARF_CB_ABORT; |
| 324 | } |
| 325 | |
| 326 | return DWARF_CB_OK; |
| 327 | } |
| 328 | |
| 329 | /* Note not currently exported, will be when there are more Dwfl_Thread |
| 330 | properties to query. Use dwfl_getthread_frames for now directly. */ |
| 331 | static int |
| 332 | getthread (Dwfl *dwfl, pid_t tid, |
| 333 | int (*callback) (Dwfl_Thread *thread, void *arg), |
| 334 | void *arg) |
| 335 | { |
| 336 | if (dwfl->attacherr != DWFL_E_NOERROR) |
| 337 | { |
| 338 | __libdwfl_seterrno (dwfl->attacherr); |
| 339 | return -1; |
| 340 | } |
| 341 | |
| 342 | Dwfl_Process *process = dwfl->process; |
| 343 | if (process == NULL) |
| 344 | { |
| 345 | __libdwfl_seterrno (DWFL_E_NO_ATTACH_STATE); |
| 346 | return -1; |
| 347 | } |
| 348 | |
| 349 | if (process->callbacks->get_thread != NULL) |
| 350 | { |
| 351 | Dwfl_Thread thread; |
| 352 | thread.process = process; |
| 353 | thread.unwound = NULL; |
| 354 | thread.callbacks_arg = NULL; |
| 355 | |
| 356 | if (process->callbacks->get_thread (dwfl, tid, process->callbacks_arg, |
| 357 | &thread.callbacks_arg)) |
| 358 | { |
| 359 | int err; |
| 360 | thread.tid = tid; |
| 361 | err = callback (&thread, arg); |
| 362 | thread_free_all_states (&thread); |
| 363 | return err; |
| 364 | } |
| 365 | |
| 366 | return -1; |
| 367 | } |
| 368 | |
| 369 | struct one_arg oa = { .tid = tid, .callback = callback, |
| 370 | .arg = arg, .seen = false }; |
| 371 | int err = INTUSE(dwfl_getthreads) (dwfl, get_one_thread_cb, &oa); |
| 372 | |
| 373 | if (err == DWARF_CB_ABORT && oa.seen) |
| 374 | return oa.ret; |
| 375 | |
| 376 | if (err == DWARF_CB_OK && ! oa.seen) |
| 377 | { |
| 378 | errno = ESRCH; |
| 379 | __libdwfl_seterrno (DWFL_E_ERRNO); |
| 380 | return -1; |
| 381 | } |
| 382 | |
| 383 | return err; |
| 384 | } |
| 385 | |
| 386 | struct one_thread |
| 387 | { |
| 388 | int (*callback) (Dwfl_Frame *frame, void *arg); |
| 389 | void *arg; |
| 390 | }; |
| 391 | |
| 392 | static int |
| 393 | get_one_thread_frames_cb (Dwfl_Thread *thread, void *arg) |
| 394 | { |
| 395 | struct one_thread *ot = (struct one_thread *) arg; |
| 396 | return INTUSE(dwfl_thread_getframes) (thread, ot->callback, ot->arg); |
| 397 | } |
| 398 | |
| 399 | int |
| 400 | dwfl_getthread_frames (Dwfl *dwfl, pid_t tid, |
| 401 | int (*callback) (Dwfl_Frame *frame, void *arg), |
| 402 | void *arg) |
| 403 | { |
| 404 | struct one_thread ot = { .callback = callback, .arg = arg }; |
| 405 | return getthread (dwfl, tid, get_one_thread_frames_cb, &ot); |
| 406 | } |
| 407 | INTDEF(dwfl_getthread_frames) |
| 408 | |
| 409 | int |
| 410 | dwfl_thread_getframes (Dwfl_Thread *thread, |
| 411 | int (*callback) (Dwfl_Frame *state, void *arg), |
| 412 | void *arg) |
| 413 | { |
| 414 | if (thread->unwound != NULL) |
| 415 | { |
| 416 | /* We had to be called from inside CALLBACK. */ |
| 417 | __libdwfl_seterrno (DWFL_E_ATTACH_STATE_CONFLICT); |
| 418 | return -1; |
| 419 | } |
| 420 | Ebl *ebl = thread->process->ebl; |
| 421 | if (ebl_frame_nregs (ebl) == 0) |
| 422 | { |
| 423 | __libdwfl_seterrno (DWFL_E_NO_UNWIND); |
| 424 | return -1; |
| 425 | } |
| 426 | if (state_alloc (thread) == NULL) |
| 427 | { |
| 428 | __libdwfl_seterrno (DWFL_E_NOMEM); |
| 429 | return -1; |
| 430 | } |
| 431 | Dwfl_Process *process = thread->process; |
| 432 | if (! process->callbacks->set_initial_registers (thread, |
| 433 | thread->callbacks_arg)) |
| 434 | { |
| 435 | thread_free_all_states (thread); |
| 436 | return -1; |
| 437 | } |
| 438 | if (! state_fetch_pc (thread->unwound)) |
| 439 | { |
| 440 | if (process->callbacks->thread_detach) |
| 441 | process->callbacks->thread_detach (thread, thread->callbacks_arg); |
| 442 | thread_free_all_states (thread); |
| 443 | return -1; |
| 444 | } |
| 445 | |
| 446 | Dwfl_Frame *state; |
| 447 | do |
| 448 | { |
| 449 | state = thread->unwound; |
| 450 | int err = callback (state, arg); |
| 451 | if (err != DWARF_CB_OK) |
| 452 | { |
| 453 | if (process->callbacks->thread_detach) |
| 454 | process->callbacks->thread_detach (thread, thread->callbacks_arg); |
| 455 | thread_free_all_states (thread); |
| 456 | return err; |
| 457 | } |
| 458 | __libdwfl_frame_unwind (state); |
| 459 | /* The old frame is no longer needed. */ |
| 460 | state_free (thread->unwound); |
| 461 | state = thread->unwound; |
| 462 | } |
| 463 | while (state && state->pc_state == DWFL_FRAME_STATE_PC_SET); |
| 464 | |
| 465 | Dwfl_Error err = dwfl_errno (); |
| 466 | if (process->callbacks->thread_detach) |
| 467 | process->callbacks->thread_detach (thread, thread->callbacks_arg); |
| 468 | if (state == NULL || state->pc_state == DWFL_FRAME_STATE_ERROR) |
| 469 | { |
| 470 | thread_free_all_states (thread); |
| 471 | __libdwfl_seterrno (err); |
| 472 | return -1; |
| 473 | } |
| 474 | assert (state->pc_state == DWFL_FRAME_STATE_PC_UNDEFINED); |
| 475 | thread_free_all_states (thread); |
| 476 | return 0; |
| 477 | } |
| 478 | INTDEF(dwfl_thread_getframes) |