summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Kratochvil <jan.kratochvil@redhat.com>2012-11-12 18:41:21 +0100
committerJan Kratochvil <jan.kratochvil@redhat.com>2012-11-12 18:41:21 +0100
commite3134af1d16de1ed85a94731fafcac95ab692ee2 (patch)
tree4ab70b6e8e2888516e6837a075651549e49aec81
parent267c357afd2b2015831086c5c7e6523c1db1ac68 (diff)
downloadelfutils-e3134af1d16de1ed85a94731fafcac95ab692ee2.tar.gz
split:
libdwfl/dwfl_frame_state.c
-rw-r--r--libdwfl/Makefile.am5
-rw-r--r--libdwfl/dwfl_frame_state.c567
-rw-r--r--libdwfl/dwfl_frame_state_core.c305
-rw-r--r--libdwfl/dwfl_frame_state_data.c86
-rw-r--r--libdwfl/dwfl_frame_state_pid.c220
-rw-r--r--libdwfl/dwfl_frame_thread_next.c37
-rw-r--r--libdwfl/dwfl_frame_tid_get.c36
-rw-r--r--libdwfl/libdwflP.h26
8 files changed, 733 insertions, 549 deletions
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 <http://www.gnu.org/licenses/>. */
#include "libdwflP.h"
-#include <fcntl.h>
-#include <unistd.h>
#include <sys/ptrace.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <dirent.h>
-#include "system.h"
+#include <unistd.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
-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,
- &regs_offset, &nregloc, &reglocs, &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 <http://www.gnu.org/licenses/>. */
+
+#include "libdwflP.h"
+#include <fcntl.h>
+#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,
+ &regs_offset, &nregloc, &reglocs, &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 <http://www.gnu.org/licenses/>. */
+
+#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 <http://www.gnu.org/licenses/>. */
+
+#include "libdwflP.h"
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <dirent.h>
+
+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 <http://www.gnu.org/licenses/>. */
+
+#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 <http://www.gnu.org/licenses/>. */
+
+#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,