From e3134af1d16de1ed85a94731fafcac95ab692ee2 Mon Sep 17 00:00:00 2001 From: Jan Kratochvil Date: Mon, 12 Nov 2012 18:41:21 +0100 Subject: split: libdwfl/dwfl_frame_state.c --- libdwfl/Makefile.am | 5 +- libdwfl/dwfl_frame_state.c | 567 ++------------------------------------- libdwfl/dwfl_frame_state_core.c | 305 +++++++++++++++++++++ libdwfl/dwfl_frame_state_data.c | 86 ++++++ libdwfl/dwfl_frame_state_pid.c | 220 +++++++++++++++ libdwfl/dwfl_frame_thread_next.c | 37 +++ libdwfl/dwfl_frame_tid_get.c | 36 +++ libdwfl/libdwflP.h | 26 ++ 8 files changed, 733 insertions(+), 549 deletions(-) create mode 100644 libdwfl/dwfl_frame_state_core.c create mode 100644 libdwfl/dwfl_frame_state_data.c create mode 100644 libdwfl/dwfl_frame_state_pid.c create mode 100644 libdwfl/dwfl_frame_thread_next.c create mode 100644 libdwfl/dwfl_frame_tid_get.c diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am index 399e3060..641b9ebf 100644 --- a/libdwfl/Makefile.am +++ b/libdwfl/Makefile.am @@ -69,7 +69,10 @@ libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c dwfl_version.c \ dwfl_module_register_names.c \ dwfl_segment_report_module.c \ link_map.c core-file.c open.c image-header.c \ - dwfl_frame_state.c dwfl_frame_unwind.c dwfl_frame_state_pc.c + dwfl_frame_state.c dwfl_frame_unwind.c \ + dwfl_frame_state_pc.c dwfl_frame_state_pid.c \ + dwfl_frame_state_core.c dwfl_frame_state_data.c \ + dwfl_frame_thread_next.c dwfl_frame_tid_get.c if ZLIB libdwfl_a_SOURCES += gzip.c diff --git a/libdwfl/dwfl_frame_state.c b/libdwfl/dwfl_frame_state.c index fef8cb63..54267324 100644 --- a/libdwfl/dwfl_frame_state.c +++ b/libdwfl/dwfl_frame_state.c @@ -27,52 +27,16 @@ not, see . */ #include "libdwflP.h" -#include -#include #include -#include -#include -#include -#include "system.h" +#include #ifndef MIN # define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif -/* Exact copy from libdwfl/segment.c. */ - -static GElf_Addr -segment_start (Dwfl *dwfl, GElf_Addr start) -{ - if (dwfl->segment_align > 1) - start &= -dwfl->segment_align; - return start; -} - -/* Exact copy from libdwfl/segment.c. */ - -static GElf_Addr -segment_end (Dwfl *dwfl, GElf_Addr end) -{ - if (dwfl->segment_align > 1) - end = (end + dwfl->segment_align - 1) & -dwfl->segment_align; - return end; -} - -static bool -tid_is_attached (Dwfl *dwfl, pid_t tid) -{ - for (Dwfl_Frame_State_Process *process = dwfl->framestatelist; process; - process = process->next) - for (Dwfl_Frame_State_Thread *thread = process->thread; thread; - thread = thread->next) - if (thread->tid_attached && thread->tid == tid) - return true; - return false; -} - -static bool -state_fetch_pc (Dwfl_Frame_State *state) +bool +internal_function +__libdwfl_state_fetch_pc (Dwfl_Frame_State *state) { switch (state->pc_state) { @@ -137,8 +101,9 @@ state_alloc (Dwfl_Frame_State_Thread *thread) return state; } -static void -thread_free (Dwfl_Frame_State_Thread *thread) +void +internal_function +__libdwfl_thread_free (Dwfl_Frame_State_Thread *thread) { while (thread->unwound) state_free (thread->unwound); @@ -150,10 +115,9 @@ thread_free (Dwfl_Frame_State_Thread *thread) free (thread); } -/* One state_alloc is called automatically. */ - -static Dwfl_Frame_State_Thread * -thread_alloc (Dwfl_Frame_State_Process *process, pid_t tid) +Dwfl_Frame_State_Thread * +internal_function +__libdwfl_thread_alloc (Dwfl_Frame_State_Process *process, pid_t tid) { Dwfl_Frame_State_Thread *thread = malloc (sizeof (*thread)); if (thread == NULL) @@ -166,17 +130,18 @@ thread_alloc (Dwfl_Frame_State_Process *process, pid_t tid) process->thread = thread; if (state_alloc (thread) == NULL) { - thread_free (thread); + __libdwfl_thread_free (thread); return NULL; } return thread; } -static void -process_free (Dwfl_Frame_State_Process *process) +void +internal_function +__libdwfl_process_free (Dwfl_Frame_State_Process *process) { while (process->thread) - thread_free (process->thread); + __libdwfl_thread_free (process->thread); if (process->ebl_close) ebl_closebackend (process->ebl); elf_end (process->core); @@ -188,9 +153,10 @@ process_free (Dwfl_Frame_State_Process *process) free (process); } -static Dwfl_Frame_State_Process * -process_alloc (Dwfl *dwfl, dwfl_frame_memory_read_t *memory_read, - void *memory_read_user_data) +Dwfl_Frame_State_Process * +internal_function +__libdwfl_process_alloc (Dwfl *dwfl, dwfl_frame_memory_read_t *memory_read, + void *memory_read_user_data) { Dwfl_Frame_State_Process *process = malloc (sizeof (*process)); if (process == NULL) @@ -207,498 +173,3 @@ process_alloc (Dwfl *dwfl, dwfl_frame_memory_read_t *memory_read, dwfl->framestatelist = process; return process; } - -static bool -ptrace_attach (pid_t tid) -{ - if (ptrace (PTRACE_ATTACH, tid, NULL, NULL) != 0) - return false; - /* FIXME: Handle missing SIGSTOP on old Linux kernels. */ - for (;;) - { - int status; - if (waitpid (tid, &status, __WALL) != tid || !WIFSTOPPED (status)) - { - ptrace (PTRACE_DETACH, tid, NULL, NULL); - return false; - } - if (WSTOPSIG (status) == SIGSTOP) - break; - if (ptrace (PTRACE_CONT, tid, NULL, - (void *) (uintptr_t) WSTOPSIG (status)) != 0) - { - ptrace (PTRACE_DETACH, tid, NULL, NULL); - return false; - } - } - return true; -} - -static bool -dwfl_frame_state_pid_memory_read (Dwarf_Addr addr, Dwarf_Addr *result, - void *user_data) -{ - Dwfl_Frame_State_Process *process = user_data; - assert (process->core == NULL && process->thread->tid); - if (process->ebl->class == ELFCLASS64) - { - errno = 0; - *result = ptrace (PTRACE_PEEKDATA, process->thread->tid, - (void *) (uintptr_t) addr, NULL); - if (errno != 0) - { - __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); - return false; - } - return true; - } -#if SIZEOF_LONG == 8 - /* We do not care about reads unaliged to 4 bytes boundary. - But 0x...ffc read of 8 bytes could overrun a page. */ - bool lowered = (addr & 4) != 0; - if (lowered) - addr -= 4; -#endif /* SIZEOF_LONG == 8 */ - errno = 0; - *result = ptrace (PTRACE_PEEKDATA, process->thread->tid, - (void *) (uintptr_t) addr, NULL); - if (errno != 0) - { - __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); - return false; - } -#if SIZEOF_LONG == 8 -# if BYTE_ORDER == BIG_ENDIAN - if (! lowered) - *result >>= 32; -# else - if (lowered) - *result >>= 32; -# endif -#endif /* SIZEOF_LONG == 8 */ - *result &= 0xffffffff; - return true; -} - -Dwfl_Frame_State * -dwfl_frame_state_pid (Dwfl *dwfl, pid_t pid) -{ - char dirname[64]; - int i = snprintf (dirname, sizeof (dirname), "/proc/%ld/task", (long) pid); - assert (i > 0 && i < (ssize_t) sizeof (dirname) - 1); - Dwfl_Frame_State_Process *process; - process = process_alloc (dwfl, dwfl_frame_state_pid_memory_read, NULL); - if (process == NULL) - return NULL; - process->memory_read_user_data = process; - for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next) - { - Dwfl_Error error = __libdwfl_module_getebl (mod); - if (error != DWFL_E_NOERROR) - continue; - process->ebl = mod->ebl; - } - if (process->ebl == NULL) - { - /* Not idenified EBL from any of the modules. */ - process_free (process); - __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); - return NULL; - } - DIR *dir = opendir (dirname); - if (dir == NULL) - { - process_free (process); - __libdwfl_seterrno (DWFL_E_ERRNO); - return NULL; - } - for (;;) - { - errno = 0; - struct dirent *dirent = readdir (dir); - if (dirent == NULL) - { - if (errno == 0) - break; - process_free (process); - __libdwfl_seterrno (DWFL_E_ERRNO); - return NULL; - } - if (strcmp (dirent->d_name, ".") == 0 - || strcmp (dirent->d_name, "..") == 0) - continue; - char *end; - errno = 0; - long tidl = strtol (dirent->d_name, &end, 10); - if (errno != 0) - { - process_free (process); - __libdwfl_seterrno (DWFL_E_ERRNO); - return NULL; - } - pid_t tid = tidl; - if (tidl <= 0 || (end && *end) || tid != tidl) - { - process_free (process); - __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); - return NULL; - } - Dwfl_Frame_State_Thread *thread = thread_alloc (process, tid); - if (thread == NULL) - { - process_free (process); - __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); - return NULL; - } - if (! tid_is_attached (dwfl, thread->tid)) - { - if (! ptrace_attach (thread->tid)) - { - thread_free (thread); - continue; - } - thread->tid_attached = true; - } - Dwfl_Frame_State *state = thread->unwound; - if (! ebl_frame_state (state) || ! state_fetch_pc (state)) - { - thread_free (thread); - continue; - } - } - if (closedir (dir) != 0) - { - process_free (process); - __libdwfl_seterrno (DWFL_E_ERRNO); - return NULL; - } - if (process->thread == NULL) - { - /* No valid threads recognized. */ - process_free (process); - __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); - return NULL; - } - return process->thread->unwound; -} -INTDEF (dwfl_frame_state_pid) - -static bool -dwfl_frame_state_core_memory_read (Dwarf_Addr addr, Dwarf_Addr *result, - void *user_data) -{ - Dwfl_Frame_State_Process *process = user_data; - Elf *core = process->core; - assert (core != NULL); - Dwfl *dwfl = process->dwfl; - static size_t phnum; - if (elf_getphdrnum (core, &phnum) < 0) - { - __libdwfl_seterrno (DWFL_E_LIBELF); - return false; - } - for (size_t cnt = 0; cnt < phnum; ++cnt) - { - GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem); - if (phdr == NULL || phdr->p_type != PT_LOAD) - continue; - /* Bias is zero here, a core file itself has no bias. */ - GElf_Addr start = segment_start (dwfl, phdr->p_vaddr); - GElf_Addr end = segment_end (dwfl, phdr->p_vaddr + phdr->p_memsz); - unsigned bytes = process->ebl->class == ELFCLASS64 ? 8 : 4; - if (addr < start || addr + bytes > end) - continue; - Elf_Data *data; - data = elf_getdata_rawchunk (core, phdr->p_offset + addr - start, - bytes, ELF_T_ADDR); - if (data == NULL) - { - __libdwfl_seterrno (DWFL_E_LIBELF); - return false; - } - assert (data->d_size == bytes); - /* FIXME: Currently any arch supported for unwinding supports - unaligned access. */ - if (bytes == 8) - *result = *(const uint64_t *) data->d_buf; - else - *result = *(const uint32_t *) data->d_buf; - return true; - } - __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); - return false; -} - -Dwfl_Frame_State * -dwfl_frame_state_core (Dwfl *dwfl, const char *corefile) -{ - Dwfl_Frame_State_Process *process; - process = process_alloc (dwfl, dwfl_frame_state_core_memory_read, NULL); - if (process == NULL) - return NULL; - process->memory_read_user_data = process; - int core_fd = open64 (corefile, O_RDONLY); - if (core_fd < 0) - { - process_free (process); - __libdwfl_seterrno (DWFL_E_BADELF); - return NULL; - } - process->core_fd = core_fd; - Elf *core; - Dwfl_Error err = __libdw_open_file (&core_fd, &core, true, false); - if (err != DWFL_E_NOERROR) - { - process_free (process); - __libdwfl_seterrno (err); - return NULL; - } - process->core = core; - Ebl *ebl = ebl_openbackend (core); - if (ebl == NULL) - { - process_free (process); - __libdwfl_seterrno (DWFL_E_LIBEBL); - return NULL; - } - process->ebl = ebl; - process->ebl_close = true; - size_t nregs = ebl_frame_state_nregs (ebl); - if (nregs == 0) - { - /* We do not support unwinding this CORE file EBL. */ - process_free (process); - __libdwfl_seterrno (DWFL_E_LIBEBL); - return NULL; - } - GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (core, &ehdr_mem); - if (ehdr == NULL) - { - process_free (process); - __libdwfl_seterrno (DWFL_E_LIBELF); - return NULL; - } - assert (ehdr->e_type == ET_CORE); - size_t phnum; - if (elf_getphdrnum (core, &phnum) < 0) - { - process_free (process); - __libdwfl_seterrno (DWFL_E_LIBELF); - return NULL; - } - Dwfl_Frame_State_Thread *thread = NULL; - for (size_t cnt = 0; cnt < phnum; ++cnt) - { - GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem); - if (phdr == NULL || phdr->p_type != PT_NOTE) - continue; - Elf_Data *data = elf_getdata_rawchunk (core, phdr->p_offset, - phdr->p_filesz, ELF_T_NHDR); - if (data == NULL) - { - process_free (process); - __libdwfl_seterrno (DWFL_E_LIBELF); - return NULL; - } - size_t offset = 0; - GElf_Nhdr nhdr; - size_t name_offset; - size_t desc_offset; - while (offset < data->d_size - && (offset = gelf_getnote (data, offset, - &nhdr, &name_offset, &desc_offset)) > 0) - { - /* Do not check NAME for now, help broken Linux kernels. */ - const char *name = data->d_buf + name_offset; - const char *desc = data->d_buf + desc_offset; - GElf_Word regs_offset; - size_t nregloc; - const Ebl_Register_Location *reglocs; - size_t nitems; - const Ebl_Core_Item *items; - if (! ebl_core_note (ebl, &nhdr, name, - ®s_offset, &nregloc, ®locs, &nitems, &items)) - { - /* This note may be just not recognized, skip it. */ - continue; - } - if (nhdr.n_type == NT_PRSTATUS) - { - const Ebl_Core_Item *item; - for (item = items; item < items + nitems; item++) - if (strcmp (item->name, "pid") == 0) - break; - if (item == items + nitems) - { - process_free (process); - __libdwfl_seterrno (DWFL_E_BADELF); - return NULL; - } - uint32_t val32 = *(const uint32_t *) (desc + item->offset); - val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB - ? be32toh (val32) : le32toh (val32)); - pid_t tid = (int32_t) val32; - eu_static_assert (sizeof val32 <= sizeof tid); - if (thread) - { - /* Delay initialization of THREAD till all notes for it have - been read in. */ - Dwfl_Frame_State *state = thread->unwound; - if (! ebl_frame_state (state) || ! state_fetch_pc (state)) - { - thread_free (thread); - thread = NULL; - continue; - } - } - thread = thread_alloc (process, tid); - if (thread == NULL) - { - process_free (process); - __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); - return NULL; - } - } - if (thread == NULL) - { - /* Ignore notes before first PR_NTSTATUS. */ - continue; - } - Dwfl_Frame_State *state = thread->unwound; - desc += regs_offset; - for (size_t regloci = 0; regloci < nregloc; regloci++) - { - const Ebl_Register_Location *regloc = reglocs + regloci; - if (regloc->regno >= nregs) - continue; - assert (regloc->bits == 32 || regloc->bits == 64); - const char *reg_desc = desc + regloc->offset; - for (unsigned regno = regloc->regno; - regno < MIN (regloc->regno + (regloc->count ?: 1U), nregs); - regno++) - { - /* PPC provides DWARF register 65 irrelevant for - CFI which clashes with register 108 (LR) we need. - LR (108) is provided earlier (in NT_PRSTATUS) than the # 65. - FIXME: It depends now on their order in core notes. */ - if (dwfl_frame_state_reg_get (state, regno, NULL)) - continue; - Dwarf_Addr val; - switch (regloc->bits) - { - case 32:; - uint32_t val32 = *(const uint32_t *) reg_desc; - reg_desc += sizeof val32; - val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB - ? be32toh (val32) : le32toh (val32)); - /* Do a host width conversion. */ - val = val32; - break; - case 64:; - uint64_t val64 = *(const uint64_t *) reg_desc; - reg_desc += sizeof val64; - val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB - ? be64toh (val64) : le64toh (val64)); - assert (sizeof (*state->regs) == sizeof val64); - val = val64; - break; - default: - abort (); - } - /* Registers not valid for CFI are just ignored. */ - dwfl_frame_state_reg_set (state, regno, val); - reg_desc += regloc->pad; - } - } - } - } - if (thread) - { - /* Delay initialization of THREAD till all notes for it have been read - in. */ - Dwfl_Frame_State *state = thread->unwound; - if (! ebl_frame_state (state) || ! state_fetch_pc (state)) - thread_free (thread); - } - if (process->thread == NULL) - { - /* No valid threads recognized in this CORE. */ - process_free (process); - __libdwfl_seterrno (DWFL_E_BADELF); - return NULL; - } - return process->thread->unwound; -} -INTDEF (dwfl_frame_state_core) - -Dwfl_Frame_State * -dwfl_frame_state_data (Dwfl *dwfl, bool pc_set, Dwarf_Addr pc, unsigned nregs, - const uint64_t *regs_set, const Dwarf_Addr *regs, - dwfl_frame_memory_read_t *memory_read, - void *memory_read_user_data) -{ - Ebl *ebl = NULL; - for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next) - { - Dwfl_Error error = __libdwfl_module_getebl (mod); - if (error != DWFL_E_NOERROR) - continue; - ebl = mod->ebl; - } - if (ebl == NULL || nregs > ebl_frame_state_nregs (ebl)) - { - __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); - return NULL; - } - Dwfl_Frame_State_Process *process; - process = process_alloc (dwfl, memory_read, memory_read_user_data); - if (process == NULL) - return NULL; - process->ebl = ebl; - Dwfl_Frame_State_Thread *thread = thread_alloc (process, 0); - if (thread == NULL) - { - process_free (process); - __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); - return NULL; - } - Dwfl_Frame_State *state = thread->unwound; - state->pc_state = DWFL_FRAME_STATE_ERROR; - if (pc_set) - { - state->pc = pc; - state->pc_state = DWFL_FRAME_STATE_PC_SET; - } - for (unsigned regno = 0; regno < nregs; regno++) - if ((regs_set[regno / sizeof (*regs_set) / 8] - & (1U << (regno % (sizeof (*regs_set) * 8)))) != 0 - && ! dwfl_frame_state_reg_set (state, regno, regs[regno])) - { - process_free (process); - __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); - return NULL; - } - if (! ebl_frame_state (state) || ! state_fetch_pc (state)) - { - process_free (process); - __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); - return NULL; - } - return process->thread->unwound; -} -INTDEF (dwfl_frame_state_data) - -Dwfl_Frame_State * -dwfl_frame_thread_next (Dwfl_Frame_State *state) -{ - Dwfl_Frame_State_Thread *thread_next = state->thread->next; - return thread_next ? thread_next->unwound : NULL; -} -INTDEF (dwfl_frame_thread_next) - -pid_t -dwfl_frame_tid_get (Dwfl_Frame_State *state) -{ - return state->thread->tid; -} -INTDEF (dwfl_frame_tid_get) diff --git a/libdwfl/dwfl_frame_state_core.c b/libdwfl/dwfl_frame_state_core.c new file mode 100644 index 00000000..ca4eb934 --- /dev/null +++ b/libdwfl/dwfl_frame_state_core.c @@ -0,0 +1,305 @@ +/* Get Dwarf Frame state for target core file. + Copyright (C) 2012 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#include "libdwflP.h" +#include +#include "system.h" + +#ifndef MIN +# define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +/* Exact copy from libdwfl/segment.c. */ + +static GElf_Addr +segment_start (Dwfl *dwfl, GElf_Addr start) +{ + if (dwfl->segment_align > 1) + start &= -dwfl->segment_align; + return start; +} + +/* Exact copy from libdwfl/segment.c. */ + +static GElf_Addr +segment_end (Dwfl *dwfl, GElf_Addr end) +{ + if (dwfl->segment_align > 1) + end = (end + dwfl->segment_align - 1) & -dwfl->segment_align; + return end; +} + +static bool +dwfl_frame_state_core_memory_read (Dwarf_Addr addr, Dwarf_Addr *result, + void *user_data) +{ + Dwfl_Frame_State_Process *process = user_data; + Elf *core = process->core; + assert (core != NULL); + Dwfl *dwfl = process->dwfl; + static size_t phnum; + if (elf_getphdrnum (core, &phnum) < 0) + { + __libdwfl_seterrno (DWFL_E_LIBELF); + return false; + } + for (size_t cnt = 0; cnt < phnum; ++cnt) + { + GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem); + if (phdr == NULL || phdr->p_type != PT_LOAD) + continue; + /* Bias is zero here, a core file itself has no bias. */ + GElf_Addr start = segment_start (dwfl, phdr->p_vaddr); + GElf_Addr end = segment_end (dwfl, phdr->p_vaddr + phdr->p_memsz); + unsigned bytes = process->ebl->class == ELFCLASS64 ? 8 : 4; + if (addr < start || addr + bytes > end) + continue; + Elf_Data *data; + data = elf_getdata_rawchunk (core, phdr->p_offset + addr - start, + bytes, ELF_T_ADDR); + if (data == NULL) + { + __libdwfl_seterrno (DWFL_E_LIBELF); + return false; + } + assert (data->d_size == bytes); + /* FIXME: Currently any arch supported for unwinding supports + unaligned access. */ + if (bytes == 8) + *result = *(const uint64_t *) data->d_buf; + else + *result = *(const uint32_t *) data->d_buf; + return true; + } + __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); + return false; +} + +Dwfl_Frame_State * +dwfl_frame_state_core (Dwfl *dwfl, const char *corefile) +{ + Dwfl_Frame_State_Process *process; + process = __libdwfl_process_alloc (dwfl, dwfl_frame_state_core_memory_read, + NULL); + if (process == NULL) + return NULL; + process->memory_read_user_data = process; + int core_fd = open64 (corefile, O_RDONLY); + if (core_fd < 0) + { + __libdwfl_process_free (process); + __libdwfl_seterrno (DWFL_E_BADELF); + return NULL; + } + process->core_fd = core_fd; + Elf *core; + Dwfl_Error err = __libdw_open_file (&core_fd, &core, true, false); + if (err != DWFL_E_NOERROR) + { + __libdwfl_process_free (process); + __libdwfl_seterrno (err); + return NULL; + } + process->core = core; + Ebl *ebl = ebl_openbackend (core); + if (ebl == NULL) + { + __libdwfl_process_free (process); + __libdwfl_seterrno (DWFL_E_LIBEBL); + return NULL; + } + process->ebl = ebl; + process->ebl_close = true; + size_t nregs = ebl_frame_state_nregs (ebl); + if (nregs == 0) + { + /* We do not support unwinding this CORE file EBL. */ + __libdwfl_process_free (process); + __libdwfl_seterrno (DWFL_E_LIBEBL); + return NULL; + } + GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (core, &ehdr_mem); + if (ehdr == NULL) + { + __libdwfl_process_free (process); + __libdwfl_seterrno (DWFL_E_LIBELF); + return NULL; + } + assert (ehdr->e_type == ET_CORE); + size_t phnum; + if (elf_getphdrnum (core, &phnum) < 0) + { + __libdwfl_process_free (process); + __libdwfl_seterrno (DWFL_E_LIBELF); + return NULL; + } + Dwfl_Frame_State_Thread *thread = NULL; + for (size_t cnt = 0; cnt < phnum; ++cnt) + { + GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem); + if (phdr == NULL || phdr->p_type != PT_NOTE) + continue; + Elf_Data *data = elf_getdata_rawchunk (core, phdr->p_offset, + phdr->p_filesz, ELF_T_NHDR); + if (data == NULL) + { + __libdwfl_process_free (process); + __libdwfl_seterrno (DWFL_E_LIBELF); + return NULL; + } + size_t offset = 0; + GElf_Nhdr nhdr; + size_t name_offset; + size_t desc_offset; + while (offset < data->d_size + && (offset = gelf_getnote (data, offset, + &nhdr, &name_offset, &desc_offset)) > 0) + { + /* Do not check NAME for now, help broken Linux kernels. */ + const char *name = data->d_buf + name_offset; + const char *desc = data->d_buf + desc_offset; + GElf_Word regs_offset; + size_t nregloc; + const Ebl_Register_Location *reglocs; + size_t nitems; + const Ebl_Core_Item *items; + if (! ebl_core_note (ebl, &nhdr, name, + ®s_offset, &nregloc, ®locs, &nitems, &items)) + { + /* This note may be just not recognized, skip it. */ + continue; + } + if (nhdr.n_type == NT_PRSTATUS) + { + const Ebl_Core_Item *item; + for (item = items; item < items + nitems; item++) + if (strcmp (item->name, "pid") == 0) + break; + if (item == items + nitems) + { + __libdwfl_process_free (process); + __libdwfl_seterrno (DWFL_E_BADELF); + return NULL; + } + uint32_t val32 = *(const uint32_t *) (desc + item->offset); + val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB + ? be32toh (val32) : le32toh (val32)); + pid_t tid = (int32_t) val32; + eu_static_assert (sizeof val32 <= sizeof tid); + if (thread) + { + /* Delay initialization of THREAD till all notes for it have + been read in. */ + Dwfl_Frame_State *state = thread->unwound; + if (! ebl_frame_state (state) + || ! __libdwfl_state_fetch_pc (state)) + { + __libdwfl_thread_free (thread); + thread = NULL; + continue; + } + } + thread = __libdwfl_thread_alloc (process, tid); + if (thread == NULL) + { + __libdwfl_process_free (process); + __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); + return NULL; + } + } + if (thread == NULL) + { + /* Ignore notes before first PR_NTSTATUS. */ + continue; + } + Dwfl_Frame_State *state = thread->unwound; + desc += regs_offset; + for (size_t regloci = 0; regloci < nregloc; regloci++) + { + const Ebl_Register_Location *regloc = reglocs + regloci; + if (regloc->regno >= nregs) + continue; + assert (regloc->bits == 32 || regloc->bits == 64); + const char *reg_desc = desc + regloc->offset; + for (unsigned regno = regloc->regno; + regno < MIN (regloc->regno + (regloc->count ?: 1U), nregs); + regno++) + { + /* PPC provides DWARF register 65 irrelevant for + CFI which clashes with register 108 (LR) we need. + LR (108) is provided earlier (in NT_PRSTATUS) than the # 65. + FIXME: It depends now on their order in core notes. */ + if (dwfl_frame_state_reg_get (state, regno, NULL)) + continue; + Dwarf_Addr val; + switch (regloc->bits) + { + case 32:; + uint32_t val32 = *(const uint32_t *) reg_desc; + reg_desc += sizeof val32; + val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB + ? be32toh (val32) : le32toh (val32)); + /* Do a host width conversion. */ + val = val32; + break; + case 64:; + uint64_t val64 = *(const uint64_t *) reg_desc; + reg_desc += sizeof val64; + val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB + ? be64toh (val64) : le64toh (val64)); + assert (sizeof (*state->regs) == sizeof val64); + val = val64; + break; + default: + abort (); + } + /* Registers not valid for CFI are just ignored. */ + dwfl_frame_state_reg_set (state, regno, val); + reg_desc += regloc->pad; + } + } + } + } + if (thread) + { + /* Delay initialization of THREAD till all notes for it have been read + in. */ + Dwfl_Frame_State *state = thread->unwound; + if (! ebl_frame_state (state) || ! __libdwfl_state_fetch_pc (state)) + __libdwfl_thread_free (thread); + } + if (process->thread == NULL) + { + /* No valid threads recognized in this CORE. */ + __libdwfl_process_free (process); + __libdwfl_seterrno (DWFL_E_BADELF); + return NULL; + } + return process->thread->unwound; +} +INTDEF (dwfl_frame_state_core) diff --git a/libdwfl/dwfl_frame_state_data.c b/libdwfl/dwfl_frame_state_data.c new file mode 100644 index 00000000..39accb37 --- /dev/null +++ b/libdwfl/dwfl_frame_state_data.c @@ -0,0 +1,86 @@ +/* Get Dwarf Frame state from modules present in DWFL. + Copyright (C) 2012 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#include "libdwflP.h" + +Dwfl_Frame_State * +dwfl_frame_state_data (Dwfl *dwfl, bool pc_set, Dwarf_Addr pc, unsigned nregs, + const uint64_t *regs_set, const Dwarf_Addr *regs, + dwfl_frame_memory_read_t *memory_read, + void *memory_read_user_data) +{ + Ebl *ebl = NULL; + for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next) + { + Dwfl_Error error = __libdwfl_module_getebl (mod); + if (error != DWFL_E_NOERROR) + continue; + ebl = mod->ebl; + } + if (ebl == NULL || nregs > ebl_frame_state_nregs (ebl)) + { + __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); + return NULL; + } + Dwfl_Frame_State_Process *process; + process = __libdwfl_process_alloc (dwfl, memory_read, memory_read_user_data); + if (process == NULL) + return NULL; + process->ebl = ebl; + Dwfl_Frame_State_Thread *thread = __libdwfl_thread_alloc (process, 0); + if (thread == NULL) + { + __libdwfl_process_free (process); + __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); + return NULL; + } + Dwfl_Frame_State *state = thread->unwound; + state->pc_state = DWFL_FRAME_STATE_ERROR; + if (pc_set) + { + state->pc = pc; + state->pc_state = DWFL_FRAME_STATE_PC_SET; + } + for (unsigned regno = 0; regno < nregs; regno++) + if ((regs_set[regno / sizeof (*regs_set) / 8] + & (1U << (regno % (sizeof (*regs_set) * 8)))) != 0 + && ! dwfl_frame_state_reg_set (state, regno, regs[regno])) + { + __libdwfl_process_free (process); + __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); + return NULL; + } + if (! ebl_frame_state (state) || ! __libdwfl_state_fetch_pc (state)) + { + __libdwfl_process_free (process); + __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); + return NULL; + } + return process->thread->unwound; +} +INTDEF (dwfl_frame_state_data) diff --git a/libdwfl/dwfl_frame_state_pid.c b/libdwfl/dwfl_frame_state_pid.c new file mode 100644 index 00000000..9a7e4ff2 --- /dev/null +++ b/libdwfl/dwfl_frame_state_pid.c @@ -0,0 +1,220 @@ +/* Get Dwarf Frame state for target live PID process. + Copyright (C) 2012 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#include "libdwflP.h" +#include +#include +#include + +static bool +tid_is_attached (Dwfl *dwfl, pid_t tid) +{ + for (Dwfl_Frame_State_Process *process = dwfl->framestatelist; process; + process = process->next) + for (Dwfl_Frame_State_Thread *thread = process->thread; thread; + thread = thread->next) + if (thread->tid_attached && thread->tid == tid) + return true; + return false; +} + +static bool +ptrace_attach (pid_t tid) +{ + if (ptrace (PTRACE_ATTACH, tid, NULL, NULL) != 0) + return false; + /* FIXME: Handle missing SIGSTOP on old Linux kernels. */ + for (;;) + { + int status; + if (waitpid (tid, &status, __WALL) != tid || !WIFSTOPPED (status)) + { + ptrace (PTRACE_DETACH, tid, NULL, NULL); + return false; + } + if (WSTOPSIG (status) == SIGSTOP) + break; + if (ptrace (PTRACE_CONT, tid, NULL, + (void *) (uintptr_t) WSTOPSIG (status)) != 0) + { + ptrace (PTRACE_DETACH, tid, NULL, NULL); + return false; + } + } + return true; +} + +static bool +dwfl_frame_state_pid_memory_read (Dwarf_Addr addr, Dwarf_Addr *result, + void *user_data) +{ + Dwfl_Frame_State_Process *process = user_data; + assert (process->core == NULL && process->thread->tid); + if (process->ebl->class == ELFCLASS64) + { + errno = 0; + *result = ptrace (PTRACE_PEEKDATA, process->thread->tid, + (void *) (uintptr_t) addr, NULL); + if (errno != 0) + { + __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); + return false; + } + return true; + } +#if SIZEOF_LONG == 8 + /* We do not care about reads unaliged to 4 bytes boundary. + But 0x...ffc read of 8 bytes could overrun a page. */ + bool lowered = (addr & 4) != 0; + if (lowered) + addr -= 4; +#endif /* SIZEOF_LONG == 8 */ + errno = 0; + *result = ptrace (PTRACE_PEEKDATA, process->thread->tid, + (void *) (uintptr_t) addr, NULL); + if (errno != 0) + { + __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); + return false; + } +#if SIZEOF_LONG == 8 +# if BYTE_ORDER == BIG_ENDIAN + if (! lowered) + *result >>= 32; +# else + if (lowered) + *result >>= 32; +# endif +#endif /* SIZEOF_LONG == 8 */ + *result &= 0xffffffff; + return true; +} + +Dwfl_Frame_State * +dwfl_frame_state_pid (Dwfl *dwfl, pid_t pid) +{ + char dirname[64]; + int i = snprintf (dirname, sizeof (dirname), "/proc/%ld/task", (long) pid); + assert (i > 0 && i < (ssize_t) sizeof (dirname) - 1); + Dwfl_Frame_State_Process *process; + process = __libdwfl_process_alloc (dwfl, dwfl_frame_state_pid_memory_read, + NULL); + if (process == NULL) + return NULL; + process->memory_read_user_data = process; + for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next) + { + Dwfl_Error error = __libdwfl_module_getebl (mod); + if (error != DWFL_E_NOERROR) + continue; + process->ebl = mod->ebl; + } + if (process->ebl == NULL) + { + /* Not idenified EBL from any of the modules. */ + __libdwfl_process_free (process); + __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); + return NULL; + } + DIR *dir = opendir (dirname); + if (dir == NULL) + { + __libdwfl_process_free (process); + __libdwfl_seterrno (DWFL_E_ERRNO); + return NULL; + } + for (;;) + { + errno = 0; + struct dirent *dirent = readdir (dir); + if (dirent == NULL) + { + if (errno == 0) + break; + __libdwfl_process_free (process); + __libdwfl_seterrno (DWFL_E_ERRNO); + return NULL; + } + if (strcmp (dirent->d_name, ".") == 0 + || strcmp (dirent->d_name, "..") == 0) + continue; + char *end; + errno = 0; + long tidl = strtol (dirent->d_name, &end, 10); + if (errno != 0) + { + __libdwfl_process_free (process); + __libdwfl_seterrno (DWFL_E_ERRNO); + return NULL; + } + pid_t tid = tidl; + if (tidl <= 0 || (end && *end) || tid != tidl) + { + __libdwfl_process_free (process); + __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); + return NULL; + } + Dwfl_Frame_State_Thread *thread = __libdwfl_thread_alloc (process, tid); + if (thread == NULL) + { + __libdwfl_process_free (process); + __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); + return NULL; + } + if (! tid_is_attached (dwfl, thread->tid)) + { + if (! ptrace_attach (thread->tid)) + { + __libdwfl_thread_free (thread); + continue; + } + thread->tid_attached = true; + } + Dwfl_Frame_State *state = thread->unwound; + if (! ebl_frame_state (state) || ! __libdwfl_state_fetch_pc (state)) + { + __libdwfl_thread_free (thread); + continue; + } + } + if (closedir (dir) != 0) + { + __libdwfl_process_free (process); + __libdwfl_seterrno (DWFL_E_ERRNO); + return NULL; + } + if (process->thread == NULL) + { + /* No valid threads recognized. */ + __libdwfl_process_free (process); + __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR); + return NULL; + } + return process->thread->unwound; +} +INTDEF (dwfl_frame_state_pid) diff --git a/libdwfl/dwfl_frame_thread_next.c b/libdwfl/dwfl_frame_thread_next.c new file mode 100644 index 00000000..df4aebbb --- /dev/null +++ b/libdwfl/dwfl_frame_thread_next.c @@ -0,0 +1,37 @@ +/* Get innermost frame of the next thread from state. + Copyright (C) 2012 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#include "libdwflP.h" + +Dwfl_Frame_State * +dwfl_frame_thread_next (Dwfl_Frame_State *state) +{ + Dwfl_Frame_State_Thread *thread_next = state->thread->next; + return thread_next ? thread_next->unwound : NULL; +} +INTDEF (dwfl_frame_thread_next) diff --git a/libdwfl/dwfl_frame_tid_get.c b/libdwfl/dwfl_frame_tid_get.c new file mode 100644 index 00000000..7883b5e1 --- /dev/null +++ b/libdwfl/dwfl_frame_tid_get.c @@ -0,0 +1,36 @@ +/* Get Task ID of the thread of state. + Copyright (C) 2012 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#include "libdwflP.h" + +pid_t +dwfl_frame_tid_get (Dwfl_Frame_State *state) +{ + return state->thread->tid; +} +INTDEF (dwfl_frame_tid_get) diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h index 19d5d9cd..5b243920 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -472,6 +472,32 @@ extern Dwfl_Module *__libdwfl_report_offline (Dwfl *dwfl, const char *name, const char *)) internal_function; +/* Set STATE->pc_set from STATE->regs according to the backend. Return true on + success, false on error. */ +extern bool __libdwfl_state_fetch_pc (Dwfl_Frame_State *state) + internal_function; + +/* Free and unlink THREAD from the internal lists. */ +extern void __libdwfl_thread_free (Dwfl_Frame_State_Thread *thread) + internal_function; + +/* Allocate new Dwfl_Frame_State_Thread for PID and link it to PROCESS. + Automatically create and link in also the first Dwfl_Frame_State. */ +extern Dwfl_Frame_State_Thread * + __libdwfl_thread_alloc (Dwfl_Frame_State_Process *process, pid_t tid) + internal_function; + +/* Free PROCESS. Unlink and free also any structures it references. */ +extern void __libdwfl_process_free (Dwfl_Frame_State_Process *process) + internal_function; + +/* Allocate new Dwfl_Frame_State_Process for DWFL with callback MEMORY_READ + (which is passed MEMORY_READ_USER_DATA). */ +extern Dwfl_Frame_State_Process * + __libdwfl_process_alloc (Dwfl *dwfl, dwfl_frame_memory_read_t *memory_read, + void *memory_read_user_data) + internal_function; + /* Decompression wrappers: decompress whole file into memory. */ extern Dwfl_Error __libdw_gunzip (int fd, off64_t start_offset, void *mapped, size_t mapped_size, -- cgit v1.2.1