Squashed 'third_party/elfutils/' content from commit 555e15e

Change-Id: I61cde98949e47e5c8c09c33260de17f30921be79
git-subtree-dir: third_party/elfutils
git-subtree-split: 555e15ebe8bf1eb33d00747173cfc80cc65648a4
diff --git a/libdwfl/dwfl_frame.c b/libdwfl/dwfl_frame.c
new file mode 100644
index 0000000..881f735
--- /dev/null
+++ b/libdwfl/dwfl_frame.c
@@ -0,0 +1,478 @@
+/* Get Dwarf Frame state for target PID or core file.
+   Copyright (C) 2013, 2014 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/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include <unistd.h>
+
+/* Set STATE->pc_set from STATE->regs according to the backend.  Return true on
+   success, false on error.  */
+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;
+	/* dwarf_frame_state_reg_is_set is not applied here.  */
+	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 ();
+}
+
+/* Do not call it on your own, to be used by thread_* functions only.  */
+
+static void
+state_free (Dwfl_Frame *state)
+{
+  Dwfl_Thread *thread = state->thread;
+  assert (thread->unwound == state);
+  thread->unwound = state->unwound;
+  free (state);
+}
+
+static void
+thread_free_all_states (Dwfl_Thread *thread)
+{
+  while (thread->unwound)
+    state_free (thread->unwound);
+}
+
+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;
+}
+
+/* Allocate new Dwfl_Process for DWFL.  */
+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;
+    }
+
+  /* Reset any previous error, we are just going to try again.  */
+  dwfl->attacherr = DWFL_E_NOERROR;
+  /* thread_callbacks is declared NN */
+  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)
+	{
+	  /* Reading of the vDSO or (deleted) modules may fail as
+	     /proc/PID/mem is unreadable without PTRACE_ATTACH and
+	     we may not be PTRACE_ATTACH-ed now.  MOD would not be
+	     re-read later to unwind it when we are already
+	     PTRACE_ATTACH-ed to PID.  This happens when this function
+	     is called from dwfl_linux_proc_attach with elf == NULL.
+	     __libdwfl_module_getebl will call __libdwfl_getelf which
+	     will call the find_elf callback.  */
+	  if (strncmp (mod->name, "[vdso: ", 7) == 0
+	      || 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)
+    {
+      /* Not identified EBL from any of the modules.  */
+      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)
+	{
+	  Dwfl_Error saved_errno = dwfl_errno ();
+	  thread_free_all_states (&thread);
+	  __libdwfl_seterrno (saved_errno);
+	  return -1;
+	}
+      if (thread.tid == 0)
+	{
+	  thread_free_all_states (&thread);
+	  __libdwfl_seterrno (DWFL_E_NOERROR);
+	  return 0;
+	}
+      int err = callback (&thread, arg);
+      if (err != DWARF_CB_OK)
+	{
+	  thread_free_all_states (&thread);
+	  return err;
+	}
+      assert (thread.unwound == NULL);
+    }
+  /* NOTREACHED */
+}
+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;
+}
+
+/* Note not currently exported, will be when there are more Dwfl_Thread
+   properties to query.  Use dwfl_getthread_frames for now directly.  */
+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))
+	{
+	  int err;
+	  thread.tid = tid;
+	  err = callback (&thread, arg);
+	  thread_free_all_states (&thread);
+	  return err;
+	}
+
+      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)
+{
+  if (thread->unwound != NULL)
+    {
+      /* We had to be called from inside CALLBACK.  */
+      __libdwfl_seterrno (DWFL_E_ATTACH_STATE_CONFLICT);
+      return -1;
+    }
+  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))
+    {
+      thread_free_all_states (thread);
+      return -1;
+    }
+  if (! state_fetch_pc (thread->unwound))
+    {
+      if (process->callbacks->thread_detach)
+	process->callbacks->thread_detach (thread, thread->callbacks_arg);
+      thread_free_all_states (thread);
+      return -1;
+    }
+
+  Dwfl_Frame *state;
+  do
+    {
+      state = thread->unwound;
+      int err = callback (state, arg);
+      if (err != DWARF_CB_OK)
+	{
+	  if (process->callbacks->thread_detach)
+	    process->callbacks->thread_detach (thread, thread->callbacks_arg);
+	  thread_free_all_states (thread);
+	  return err;
+	}
+      __libdwfl_frame_unwind (state);
+      /* The old frame is no longer needed.  */
+      state_free (thread->unwound);
+      state = thread->unwound;
+    }
+  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)
+    {
+      thread_free_all_states (thread);
+      __libdwfl_seterrno (err);
+      return -1;
+    }
+  assert (state->pc_state == DWFL_FRAME_STATE_PC_UNDEFINED);
+  thread_free_all_states (thread);
+  return 0;
+}
+INTDEF(dwfl_thread_getframes)