#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <system.h>
#include "libdwflP.h"
static bool
state_fetch_pc (Dwfl_Frame *state)
{
switch (state->pc_state)
{
case DWFL_FRAME_STATE_PC_SET:
return true;
case DWFL_FRAME_STATE_PC_UNDEFINED:
abort ();
case DWFL_FRAME_STATE_ERROR:
{
Ebl *ebl = state->thread->process->ebl;
Dwarf_CIE abi_info;
if (ebl_abi_cfi (ebl, &abi_info) != 0)
{
__libdwfl_seterrno (DWFL_E_LIBEBL);
return false;
}
unsigned ra = abi_info.return_address_register;
if (ra >= ebl_frame_nregs (ebl))
{
__libdwfl_seterrno (DWFL_E_LIBEBL_BAD);
return false;
}
state->pc = state->regs[ra] + ebl_ra_offset (ebl);
state->pc_state = DWFL_FRAME_STATE_PC_SET;
}
return true;
}
abort ();
}
static void
free_states (Dwfl_Frame *state)
{
while (state)
{
Dwfl_Frame *next = state->unwound;
free(state);
state = next;
}
}
static Dwfl_Frame *
state_alloc (Dwfl_Thread *thread)
{
assert (thread->unwound == NULL);
Ebl *ebl = thread->process->ebl;
size_t nregs = ebl_frame_nregs (ebl);
if (nregs == 0)
return NULL;
assert (nregs < sizeof (((Dwfl_Frame *) NULL)->regs_set) * 8);
Dwfl_Frame *state = malloc (sizeof (*state) + sizeof (*state->regs) * nregs);
if (state == NULL)
return NULL;
state->thread = thread;
state->signal_frame = false;
state->initial_frame = true;
state->pc_state = DWFL_FRAME_STATE_ERROR;
memset (state->regs_set, 0, sizeof (state->regs_set));
thread->unwound = state;
state->unwound = NULL;
return state;
}
void
internal_function
__libdwfl_process_free (Dwfl_Process *process)
{
Dwfl *dwfl = process->dwfl;
if (process->callbacks->detach != NULL)
process->callbacks->detach (dwfl, process->callbacks_arg);
assert (dwfl->process == process);
dwfl->process = NULL;
if (process->ebl_close)
ebl_closebackend (process->ebl);
free (process);
dwfl->attacherr = DWFL_E_NOERROR;
}
static void
process_alloc (Dwfl *dwfl)
{
Dwfl_Process *process = malloc (sizeof (*process));
if (process == NULL)
return;
process->dwfl = dwfl;
dwfl->process = process;
}
bool
dwfl_attach_state (Dwfl *dwfl, Elf *elf, pid_t pid,
const Dwfl_Thread_Callbacks *thread_callbacks, void *arg)
{
if (dwfl->process != NULL)
{
__libdwfl_seterrno (DWFL_E_ATTACH_STATE_CONFLICT);
return false;
}
dwfl->attacherr = DWFL_E_NOERROR;
if (thread_callbacks->next_thread == NULL
|| thread_callbacks->set_initial_registers == NULL)
{
dwfl->attacherr = DWFL_E_INVALID_ARGUMENT;
fail:
dwfl->attacherr = __libdwfl_canon_error (dwfl->attacherr);
__libdwfl_seterrno (dwfl->attacherr);
return false;
}
Ebl *ebl;
bool ebl_close;
if (elf != NULL)
{
ebl = ebl_openbackend (elf);
ebl_close = true;
}
else
{
ebl = NULL;
for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
{
if (startswith (mod->name, "[vdso: ")
|| strcmp (strrchr (mod->name, ' ') ?: "",
" (deleted)") == 0)
continue;
Dwfl_Error error = __libdwfl_module_getebl (mod);
if (error != DWFL_E_NOERROR)
continue;
ebl = mod->ebl;
break;
}
ebl_close = false;
}
if (ebl == NULL)
{
dwfl->attacherr = DWFL_E_PROCESS_NO_ARCH;
goto fail;
}
process_alloc (dwfl);
Dwfl_Process *process = dwfl->process;
if (process == NULL)
{
if (ebl_close)
ebl_closebackend (ebl);
dwfl->attacherr = DWFL_E_NOMEM;
goto fail;
}
process->ebl = ebl;
process->ebl_close = ebl_close;
process->pid = pid;
process->callbacks = thread_callbacks;
process->callbacks_arg = arg;
return true;
}
INTDEF(dwfl_attach_state)
pid_t
dwfl_pid (Dwfl *dwfl)
{
if (dwfl->attacherr != DWFL_E_NOERROR)
{
__libdwfl_seterrno (dwfl->attacherr);
return -1;
}
if (dwfl->process == NULL)
{
__libdwfl_seterrno (DWFL_E_NO_ATTACH_STATE);
return -1;
}
return dwfl->process->pid;
}
INTDEF(dwfl_pid)
Dwfl *
dwfl_thread_dwfl (Dwfl_Thread *thread)
{
return thread->process->dwfl;
}
INTDEF(dwfl_thread_dwfl)
pid_t
dwfl_thread_tid (Dwfl_Thread *thread)
{
return thread->tid;
}
INTDEF(dwfl_thread_tid)
Dwfl_Thread *
dwfl_frame_thread (Dwfl_Frame *state)
{
return state->thread;
}
INTDEF(dwfl_frame_thread)
int
dwfl_getthreads (Dwfl *dwfl, int (*callback) (Dwfl_Thread *thread, void *arg),
void *arg)
{
if (dwfl->attacherr != DWFL_E_NOERROR)
{
__libdwfl_seterrno (dwfl->attacherr);
return -1;
}
Dwfl_Process *process = dwfl->process;
if (process == NULL)
{
__libdwfl_seterrno (DWFL_E_NO_ATTACH_STATE);
return -1;
}
Dwfl_Thread thread;
thread.process = process;
thread.unwound = NULL;
thread.callbacks_arg = NULL;
for (;;)
{
thread.tid = process->callbacks->next_thread (dwfl,
process->callbacks_arg,
&thread.callbacks_arg);
if (thread.tid < 0)
return -1;
if (thread.tid == 0)
{
__libdwfl_seterrno (DWFL_E_NOERROR);
return 0;
}
int err = callback (&thread, arg);
if (err != DWARF_CB_OK)
return err;
assert (thread.unwound == NULL);
}
}
INTDEF(dwfl_getthreads)
struct one_arg
{
pid_t tid;
bool seen;
int (*callback) (Dwfl_Thread *thread, void *arg);
void *arg;
int ret;
};
static int
get_one_thread_cb (Dwfl_Thread *thread, void *arg)
{
struct one_arg *oa = (struct one_arg *) arg;
if (! oa->seen && INTUSE(dwfl_thread_tid) (thread) == oa->tid)
{
oa->seen = true;
oa->ret = oa->callback (thread, oa->arg);
return DWARF_CB_ABORT;
}
return DWARF_CB_OK;
}
static int
getthread (Dwfl *dwfl, pid_t tid,
int (*callback) (Dwfl_Thread *thread, void *arg),
void *arg)
{
if (dwfl->attacherr != DWFL_E_NOERROR)
{
__libdwfl_seterrno (dwfl->attacherr);
return -1;
}
Dwfl_Process *process = dwfl->process;
if (process == NULL)
{
__libdwfl_seterrno (DWFL_E_NO_ATTACH_STATE);
return -1;
}
if (process->callbacks->get_thread != NULL)
{
Dwfl_Thread thread;
thread.process = process;
thread.unwound = NULL;
thread.callbacks_arg = NULL;
if (process->callbacks->get_thread (dwfl, tid, process->callbacks_arg,
&thread.callbacks_arg))
{
thread.tid = tid;
return callback (&thread, arg);
}
return -1;
}
struct one_arg oa = { .tid = tid, .callback = callback,
.arg = arg, .seen = false };
int err = INTUSE(dwfl_getthreads) (dwfl, get_one_thread_cb, &oa);
if (err == DWARF_CB_ABORT && oa.seen)
return oa.ret;
if (err == DWARF_CB_OK && ! oa.seen)
{
errno = ESRCH;
__libdwfl_seterrno (DWFL_E_ERRNO);
return -1;
}
return err;
}
struct one_thread
{
int (*callback) (Dwfl_Frame *frame, void *arg);
void *arg;
};
static int
get_one_thread_frames_cb (Dwfl_Thread *thread, void *arg)
{
struct one_thread *ot = (struct one_thread *) arg;
return INTUSE(dwfl_thread_getframes) (thread, ot->callback, ot->arg);
}
int
dwfl_getthread_frames (Dwfl *dwfl, pid_t tid,
int (*callback) (Dwfl_Frame *frame, void *arg),
void *arg)
{
struct one_thread ot = { .callback = callback, .arg = arg };
return getthread (dwfl, tid, get_one_thread_frames_cb, &ot);
}
INTDEF(dwfl_getthread_frames)
int
dwfl_thread_getframes (Dwfl_Thread *thread,
int (*callback) (Dwfl_Frame *state, void *arg),
void *arg)
{
Ebl *ebl = thread->process->ebl;
if (ebl_frame_nregs (ebl) == 0)
{
__libdwfl_seterrno (DWFL_E_NO_UNWIND);
return -1;
}
if (state_alloc (thread) == NULL)
{
__libdwfl_seterrno (DWFL_E_NOMEM);
return -1;
}
Dwfl_Process *process = thread->process;
if (! process->callbacks->set_initial_registers (thread,
thread->callbacks_arg))
{
free_states (thread->unwound);
thread->unwound = NULL;
return -1;
}
Dwfl_Frame *state = thread->unwound;
thread->unwound = NULL;
if (! state_fetch_pc (state))
{
if (process->callbacks->thread_detach)
process->callbacks->thread_detach (thread, thread->callbacks_arg);
free_states (state);
return -1;
}
do
{
int err = callback (state, arg);
if (err != DWARF_CB_OK)
{
if (process->callbacks->thread_detach)
process->callbacks->thread_detach (thread, thread->callbacks_arg);
free_states (state);
return err;
}
__libdwfl_frame_unwind (state);
Dwfl_Frame *next = state->unwound;
free (state);
state = next;
}
while (state && state->pc_state == DWFL_FRAME_STATE_PC_SET);
Dwfl_Error err = dwfl_errno ();
if (process->callbacks->thread_detach)
process->callbacks->thread_detach (thread, thread->callbacks_arg);
if (state == NULL || state->pc_state == DWFL_FRAME_STATE_ERROR)
{
free_states (state);
__libdwfl_seterrno (err);
return -1;
}
assert (state->pc_state == DWFL_FRAME_STATE_PC_UNDEFINED);
free_states (state);
return 0;
}
INTDEF(dwfl_thread_getframes)