Fix dwfl_attach_state machine->elf
[platform/upstream/elfutils.git] / libdwfl / dwfl_frame.c
1 /* Get Dwarf Frame state for target PID or core file.
2    Copyright (C) 2013 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 #include "libdwflP.h"
30 #include <sys/ptrace.h>
31 #include <unistd.h>
32
33 /* Set STATE->pc_set from STATE->regs according to the backend.  Return true on
34    success, false on error.  */
35 static bool
36 state_fetch_pc (Dwfl_Frame *state)
37 {
38   switch (state->pc_state)
39     {
40     case DWFL_FRAME_STATE_PC_SET:
41       return true;
42     case DWFL_FRAME_STATE_PC_UNDEFINED:
43       abort ();
44     case DWFL_FRAME_STATE_ERROR:
45       {
46         Ebl *ebl = state->thread->process->ebl;
47         Dwarf_CIE abi_info;
48         if (ebl_abi_cfi (ebl, &abi_info) != 0)
49           {
50             __libdwfl_seterrno (DWFL_E_LIBEBL);
51             return false;
52           }
53         unsigned ra = abi_info.return_address_register;
54         /* dwarf_frame_state_reg_is_set is not applied here.  */
55         if (ra >= ebl_frame_nregs (ebl))
56           {
57             __libdwfl_seterrno (DWFL_E_LIBEBL_BAD);
58             return false;
59           }
60         state->pc = state->regs[ra];
61         state->pc_state = DWFL_FRAME_STATE_PC_SET;
62       }
63       return true;
64     }
65   abort ();
66 }
67
68 /* Do not call it on your own, to be used by thread_* functions only.  */
69
70 static void
71 state_free (Dwfl_Frame *state)
72 {
73   Dwfl_Thread *thread = state->thread;
74   assert (thread->unwound == state);
75   thread->unwound = state->unwound;
76   free (state);
77 }
78
79 static void
80 thread_free_all_states (Dwfl_Thread *thread)
81 {
82   while (thread->unwound)
83     state_free (thread->unwound);
84 }
85
86 static Dwfl_Frame *
87 state_alloc (Dwfl_Thread *thread)
88 {
89   assert (thread->unwound == NULL);
90   Ebl *ebl = thread->process->ebl;
91   size_t nregs = ebl_frame_nregs (ebl);
92   if (nregs == 0)
93     return NULL;
94   assert (nregs < sizeof (((Dwfl_Frame *) NULL)->regs_set) * 8);
95   Dwfl_Frame *state = malloc (sizeof (*state) + sizeof (*state->regs) * nregs);
96   if (state == NULL)
97     return NULL;
98   state->thread = thread;
99   state->signal_frame = false;
100   state->initial_frame = true;
101   state->pc_state = DWFL_FRAME_STATE_ERROR;
102   memset (state->regs_set, 0, sizeof (state->regs_set));
103   thread->unwound = state;
104   state->unwound = NULL;
105   return state;
106 }
107
108 void
109 internal_function
110 __libdwfl_process_free (Dwfl_Process *process)
111 {
112   Dwfl *dwfl = process->dwfl;
113   if (process->callbacks->detach != NULL)
114     process->callbacks->detach (dwfl, process->callbacks_arg);
115   assert (dwfl->process == process);
116   dwfl->process = NULL;
117   if (process->ebl_close)
118     ebl_closebackend (process->ebl);
119   free (process);
120 }
121
122 /* Allocate new Dwfl_Process for DWFL.  */
123 static void
124 process_alloc (Dwfl *dwfl)
125 {
126   Dwfl_Process *process = malloc (sizeof (*process));
127   if (process == NULL)
128     return;
129   process->dwfl = dwfl;
130   dwfl->process = process;
131 }
132
133 bool
134 dwfl_attach_state (Dwfl *dwfl, Elf *elf, pid_t pid,
135                    const Dwfl_Thread_Callbacks *thread_callbacks, void *arg)
136 {
137   if (thread_callbacks == NULL || thread_callbacks->next_thread == NULL
138       || thread_callbacks->set_initial_registers == NULL)
139     {
140       __libdwfl_seterrno (DWFL_E_INVALID_ARGUMENT);
141       return false;
142     }
143   if (dwfl->process != NULL)
144     {
145       __libdwfl_seterrno (DWFL_E_ATTACH_STATE_CONFLICT);
146       return false;
147     }
148   Ebl *ebl;
149   bool ebl_close;
150   if (elf != NULL)
151     {
152       ebl = ebl_openbackend (elf);
153       ebl_close = true;
154     }
155   else
156     {
157       ebl = NULL;
158       for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
159         {
160           /* Reading of the vDSO module may fail as /proc/PID/mem is unreadable
161              without PTRACE_ATTACH and we may not be PTRACE_ATTACH-ed now.
162              MOD would not be re-read later to unwind it when we are already
163              PTRACE_ATTACH-ed to PID.  */
164           if (strncmp (mod->name, "[vdso: ", 7) == 0)
165             continue;
166           Dwfl_Error error = __libdwfl_module_getebl (mod);
167           if (error != DWFL_E_NOERROR)
168             continue;
169           ebl = mod->ebl;
170           break;
171         }
172       ebl_close = false;
173     }
174   if (ebl == NULL)
175     {
176       /* Not identified EBL from any of the modules.  */
177       __libdwfl_seterrno (DWFL_E_PROCESS_NO_ARCH);
178       return false;
179     }
180   process_alloc (dwfl);
181   Dwfl_Process *process = dwfl->process;
182   if (process == NULL)
183     {
184       if (ebl_close)
185         ebl_closebackend (ebl);
186       __libdwfl_seterrno (DWFL_E_NOMEM);
187       return false;
188     }
189   process->ebl = ebl;
190   process->ebl_close = ebl_close;
191   process->pid = pid;
192   process->callbacks = thread_callbacks;
193   process->callbacks_arg = arg;
194   return true;
195 }
196 INTDEF(dwfl_attach_state)
197
198 pid_t
199 dwfl_pid (Dwfl *dwfl)
200 {
201   if (dwfl->process == NULL)
202     {
203       __libdwfl_seterrno (DWFL_E_NO_ATTACH_STATE);
204       return -1;
205     }
206   return dwfl->process->pid;
207 }
208 INTDEF(dwfl_pid)
209
210 Dwfl *
211 dwfl_thread_dwfl (Dwfl_Thread *thread)
212 {
213   return thread->process->dwfl;
214 }
215 INTDEF(dwfl_thread_dwfl)
216
217 pid_t
218 dwfl_thread_tid (Dwfl_Thread *thread)
219 {
220   return thread->tid;
221 }
222 INTDEF(dwfl_thread_tid)
223
224 Dwfl_Thread *
225 dwfl_frame_thread (Dwfl_Frame *state)
226 {
227   return state->thread;
228 }
229 INTDEF(dwfl_frame_thread)
230
231 int
232 dwfl_getthreads (Dwfl *dwfl, int (*callback) (Dwfl_Thread *thread, void *arg),
233                  void *arg)
234 {
235   Dwfl_Process *process = dwfl->process;
236   if (process == NULL)
237     {
238       __libdwfl_seterrno (DWFL_E_NO_ATTACH_STATE);
239       return -1;
240     }
241
242   Dwfl_Thread thread;
243   thread.process = process;
244   thread.unwound = NULL;
245   thread.callbacks_arg = NULL;
246   for (;;)
247     {
248       thread.tid = process->callbacks->next_thread (dwfl,
249                                                     process->callbacks_arg,
250                                                     &thread.callbacks_arg);
251       if (thread.tid < 0)
252         {
253           Dwfl_Error saved_errno = dwfl_errno ();
254           thread_free_all_states (&thread);
255           __libdwfl_seterrno (saved_errno);
256           return -1;
257         }
258       if (thread.tid == 0)
259         {
260           thread_free_all_states (&thread);
261           __libdwfl_seterrno (DWFL_E_NOERROR);
262           return 0;
263         }
264       int err = callback (&thread, arg);
265       if (err != DWARF_CB_OK)
266         {
267           thread_free_all_states (&thread);
268           return err;
269         }
270       assert (thread.unwound == NULL);
271     }
272   /* NOTREACHED */
273 }
274 INTDEF(dwfl_getthreads)
275
276 int
277 dwfl_thread_getframes (Dwfl_Thread *thread,
278                        int (*callback) (Dwfl_Frame *state, void *arg),
279                        void *arg)
280 {
281   if (thread->unwound != NULL)
282     {
283       /* We had to be called from inside CALLBACK.  */
284       __libdwfl_seterrno (DWFL_E_ATTACH_STATE_CONFLICT);
285       return -1;
286     }
287   Ebl *ebl = thread->process->ebl;
288   if (ebl_frame_nregs (ebl) == 0)
289     {
290       __libdwfl_seterrno (DWFL_E_NO_UNWIND);
291       return -1;
292     }
293   if (state_alloc (thread) == NULL)
294     {
295       __libdwfl_seterrno (DWFL_E_NOMEM);
296       return -1;
297     }
298   Dwfl_Process *process = thread->process;
299   if (! process->callbacks->set_initial_registers (thread,
300                                                    thread->callbacks_arg))
301     {
302       thread_free_all_states (thread);
303       return -1;
304     }
305   if (! state_fetch_pc (thread->unwound))
306     {
307       if (process->callbacks->thread_detach)
308         process->callbacks->thread_detach (thread, thread->callbacks_arg);
309       thread_free_all_states (thread);
310       return -1;
311     }
312
313   Dwfl_Frame *state;
314   do
315     {
316       state = thread->unwound;
317       int err = callback (state, arg);
318       if (err != DWARF_CB_OK)
319         {
320           if (process->callbacks->thread_detach)
321             process->callbacks->thread_detach (thread, thread->callbacks_arg);
322           thread_free_all_states (thread);
323           return err;
324         }
325       __libdwfl_frame_unwind (state);
326       /* The old frame is no longer needed.  */
327       state_free (thread->unwound);
328       state = thread->unwound;
329     }
330   while (state && state->pc_state == DWFL_FRAME_STATE_PC_SET);
331
332   Dwfl_Error err = dwfl_errno ();
333   if (process->callbacks->thread_detach)
334     process->callbacks->thread_detach (thread, thread->callbacks_arg);
335   if (state == NULL || state->pc_state == DWFL_FRAME_STATE_ERROR)
336     {
337       thread_free_all_states (thread);
338       __libdwfl_seterrno (err);
339       return -1;
340     }
341   assert (state->pc_state == DWFL_FRAME_STATE_PC_UNDEFINED);
342   thread_free_all_states (thread);
343   return 0;
344 }
345 INTDEF(dwfl_thread_getframes)