LCOV - code coverage report
Current view: top level - libdwfl - dwfl_frame.c (source / functions) Hit Total Coverage
Test: elfutils-0.176 Lines: 124 203 61.1 %
Date: 2019-02-15 17:57:54 Functions: 11 15 73.3 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13