• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Get Dwarf Frame state for target 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 #include "libdwflP.h"
30 #include <fcntl.h>
31 #include "system.h"
32 
33 #include "../libdw/memory-access.h"
34 
35 #ifndef MIN
36 # define MIN(a, b) ((a) < (b) ? (a) : (b))
37 #endif
38 
39 struct core_arg
40 {
41   Elf *core;
42   Elf_Data *note_data;
43   size_t thread_note_offset;
44   Ebl *ebl;
45 };
46 
47 struct thread_arg
48 {
49   struct core_arg *core_arg;
50   size_t note_offset;
51 };
52 
53 static bool
core_memory_read(Dwfl * dwfl,Dwarf_Addr addr,Dwarf_Word * result,void * dwfl_arg)54 core_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result,
55 		  void *dwfl_arg)
56 {
57   Dwfl_Process *process = dwfl->process;
58   struct core_arg *core_arg = dwfl_arg;
59   Elf *core = core_arg->core;
60   assert (core != NULL);
61   static size_t phnum;
62   if (elf_getphdrnum (core, &phnum) < 0)
63     {
64       __libdwfl_seterrno (DWFL_E_LIBELF);
65       return false;
66     }
67   for (size_t cnt = 0; cnt < phnum; ++cnt)
68     {
69       GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
70       if (phdr == NULL || phdr->p_type != PT_LOAD)
71 	continue;
72       /* Bias is zero here, a core file itself has no bias.  */
73       GElf_Addr start = __libdwfl_segment_start (dwfl, phdr->p_vaddr);
74       GElf_Addr end = __libdwfl_segment_end (dwfl,
75 					     phdr->p_vaddr + phdr->p_memsz);
76       unsigned bytes = ebl_get_elfclass (process->ebl) == ELFCLASS64 ? 8 : 4;
77       if (addr < start || addr + bytes > end)
78 	continue;
79       Elf_Data *data;
80       data = elf_getdata_rawchunk (core, phdr->p_offset + addr - start,
81 				   bytes, ELF_T_ADDR);
82       if (data == NULL)
83 	{
84 	  __libdwfl_seterrno (DWFL_E_LIBELF);
85 	  return false;
86 	}
87       assert (data->d_size == bytes);
88       if (bytes == 8)
89 	*result = read_8ubyte_unaligned_noncvt (data->d_buf);
90       else
91 	*result = read_4ubyte_unaligned_noncvt (data->d_buf);
92       return true;
93     }
94   __libdwfl_seterrno (DWFL_E_ADDR_OUTOFRANGE);
95   return false;
96 }
97 
98 static pid_t
core_next_thread(Dwfl * dwfl,void * dwfl_arg,void ** thread_argp)99 core_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
100 		  void **thread_argp)
101 {
102   struct core_arg *core_arg = dwfl_arg;
103   Elf *core = core_arg->core;
104   GElf_Nhdr nhdr;
105   size_t name_offset;
106   size_t desc_offset;
107   Elf_Data *note_data = core_arg->note_data;
108   size_t offset;
109 
110   struct thread_arg *thread_arg;
111   if (*thread_argp == NULL)
112     {
113       core_arg->thread_note_offset = 0;
114       thread_arg = malloc (sizeof (*thread_arg));
115       if (thread_arg == NULL)
116 	{
117 	  __libdwfl_seterrno (DWFL_E_NOMEM);
118 	  return -1;
119 	}
120       thread_arg->core_arg = core_arg;
121       *thread_argp = thread_arg;
122     }
123   else
124     thread_arg = (struct thread_arg *) *thread_argp;
125 
126   while (offset = core_arg->thread_note_offset, offset < note_data->d_size
127 	 && (core_arg->thread_note_offset = gelf_getnote (note_data, offset,
128 							  &nhdr, &name_offset,
129 							  &desc_offset)) > 0)
130     {
131       /* Do not check NAME for now, help broken Linux kernels.  */
132       const char *name = note_data->d_buf + name_offset;
133       const char *desc = note_data->d_buf + desc_offset;
134       GElf_Word regs_offset;
135       size_t nregloc;
136       const Ebl_Register_Location *reglocs;
137       size_t nitems;
138       const Ebl_Core_Item *items;
139       if (! ebl_core_note (core_arg->ebl, &nhdr, name,
140 			   &regs_offset, &nregloc, &reglocs, &nitems, &items))
141 	{
142 	  /* This note may be just not recognized, skip it.  */
143 	  continue;
144 	}
145       if (nhdr.n_type != NT_PRSTATUS)
146 	continue;
147       const Ebl_Core_Item *item;
148       for (item = items; item < items + nitems; item++)
149 	if (strcmp (item->name, "pid") == 0)
150 	  break;
151       if (item == items + nitems)
152 	continue;
153       uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
154       val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
155 		? be32toh (val32) : le32toh (val32));
156       pid_t tid = (int32_t) val32;
157       eu_static_assert (sizeof val32 <= sizeof tid);
158       thread_arg->note_offset = offset;
159       return tid;
160     }
161 
162   free (thread_arg);
163   return 0;
164 }
165 
166 static bool
core_set_initial_registers(Dwfl_Thread * thread,void * thread_arg_voidp)167 core_set_initial_registers (Dwfl_Thread *thread, void *thread_arg_voidp)
168 {
169   struct thread_arg *thread_arg = thread_arg_voidp;
170   struct core_arg *core_arg = thread_arg->core_arg;
171   Elf *core = core_arg->core;
172   size_t offset = thread_arg->note_offset;
173   GElf_Nhdr nhdr;
174   size_t name_offset;
175   size_t desc_offset;
176   Elf_Data *note_data = core_arg->note_data;
177   size_t nregs = ebl_frame_nregs (core_arg->ebl);
178   assert (nregs > 0);
179   assert (offset < note_data->d_size);
180   size_t getnote_err = gelf_getnote (note_data, offset, &nhdr, &name_offset,
181 				     &desc_offset);
182   /* __libdwfl_attach_state_for_core already verified the note is there.  */
183   assert (getnote_err != 0);
184   /* Do not check NAME for now, help broken Linux kernels.  */
185   const char *name = note_data->d_buf + name_offset;
186   const char *desc = note_data->d_buf + desc_offset;
187   GElf_Word regs_offset;
188   size_t nregloc;
189   const Ebl_Register_Location *reglocs;
190   size_t nitems;
191   const Ebl_Core_Item *items;
192   int core_note_err = ebl_core_note (core_arg->ebl, &nhdr, name, &regs_offset,
193 				     &nregloc, &reglocs, &nitems, &items);
194   /* __libdwfl_attach_state_for_core already verified the note is there.  */
195   assert (core_note_err != 0);
196   assert (nhdr.n_type == NT_PRSTATUS);
197   const Ebl_Core_Item *item;
198   for (item = items; item < items + nitems; item++)
199     if (strcmp (item->name, "pid") == 0)
200       break;
201   assert (item < items + nitems);
202   pid_t tid;
203   {
204     uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
205     val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
206 	     ? be32toh (val32) : le32toh (val32));
207     tid = (int32_t) val32;
208     eu_static_assert (sizeof val32 <= sizeof tid);
209   }
210   /* core_next_thread already found this TID there.  */
211   assert (tid == INTUSE(dwfl_thread_tid) (thread));
212   for (item = items; item < items + nitems; item++)
213     if (item->pc_register)
214       break;
215   if (item < items + nitems)
216     {
217       Dwarf_Word pc;
218       switch (gelf_getclass (core) == ELFCLASS32 ? 32 : 64)
219       {
220 	case 32:;
221 	  uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
222 	  val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
223 		   ? be32toh (val32) : le32toh (val32));
224 	  /* Do a host width conversion.  */
225 	  pc = val32;
226 	  break;
227 	case 64:;
228 	  uint64_t val64 = read_8ubyte_unaligned_noncvt (desc + item->offset);
229 	  val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
230 		   ? be64toh (val64) : le64toh (val64));
231 	  pc = val64;
232 	  break;
233 	default:
234 	  abort ();
235       }
236       INTUSE(dwfl_thread_state_register_pc) (thread, pc);
237     }
238   desc += regs_offset;
239   for (size_t regloci = 0; regloci < nregloc; regloci++)
240     {
241       const Ebl_Register_Location *regloc = reglocs + regloci;
242       // Iterate even regs out of NREGS range so that we can find pc_register.
243       if (regloc->bits != 32 && regloc->bits != 64)
244 	continue;
245       const char *reg_desc = desc + regloc->offset;
246       for (unsigned regno = regloc->regno;
247 	   regno < regloc->regno + (regloc->count ?: 1U);
248 	   regno++)
249 	{
250 	  /* PPC provides DWARF register 65 irrelevant for
251 	     CFI which clashes with register 108 (LR) we need.
252 	     LR (108) is provided earlier (in NT_PRSTATUS) than the # 65.
253 	     FIXME: It depends now on their order in core notes.
254 	     FIXME: It uses private function.  */
255 	  if (regno < nregs
256 	      && __libdwfl_frame_reg_get (thread->unwound, regno, NULL))
257 	    continue;
258 	  Dwarf_Word val;
259 	  switch (regloc->bits)
260 	  {
261 	    case 32:;
262 	      uint32_t val32 = read_4ubyte_unaligned_noncvt (reg_desc);
263 	      reg_desc += sizeof val32;
264 	      val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
265 		       ? be32toh (val32) : le32toh (val32));
266 	      /* Do a host width conversion.  */
267 	      val = val32;
268 	      break;
269 	    case 64:;
270 	      uint64_t val64 = read_8ubyte_unaligned_noncvt (reg_desc);
271 	      reg_desc += sizeof val64;
272 	      val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
273 		       ? be64toh (val64) : le64toh (val64));
274 	      assert (sizeof (*thread->unwound->regs) == sizeof val64);
275 	      val = val64;
276 	      break;
277 	    default:
278 	      abort ();
279 	  }
280 	  /* Registers not valid for CFI are just ignored.  */
281 	  if (regno < nregs)
282 	    INTUSE(dwfl_thread_state_registers) (thread, regno, 1, &val);
283 	  if (regloc->pc_register)
284 	    INTUSE(dwfl_thread_state_register_pc) (thread, val);
285 	  reg_desc += regloc->pad;
286 	}
287     }
288   return true;
289 }
290 
291 static void
core_detach(Dwfl * dwfl,void * dwfl_arg)292 core_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
293 {
294   struct core_arg *core_arg = dwfl_arg;
295   ebl_closebackend (core_arg->ebl);
296   free (core_arg);
297 }
298 
299 static const Dwfl_Thread_Callbacks core_thread_callbacks =
300 {
301   core_next_thread,
302   NULL, /* get_thread */
303   core_memory_read,
304   core_set_initial_registers,
305   core_detach,
306   NULL, /* core_thread_detach */
307 };
308 
309 int
dwfl_core_file_attach(Dwfl * dwfl,Elf * core)310 dwfl_core_file_attach (Dwfl *dwfl, Elf *core)
311 {
312   Dwfl_Error err = DWFL_E_NOERROR;
313   Ebl *ebl = ebl_openbackend (core);
314   if (ebl == NULL)
315     {
316       err = DWFL_E_LIBEBL;
317     fail_err:
318       if (dwfl->process == NULL && dwfl->attacherr == DWFL_E_NOERROR)
319 	dwfl->attacherr = __libdwfl_canon_error (err);
320       __libdwfl_seterrno (err);
321       return -1;
322     }
323   size_t nregs = ebl_frame_nregs (ebl);
324   if (nregs == 0)
325     {
326       err = DWFL_E_NO_UNWIND;
327     fail:
328       ebl_closebackend (ebl);
329       goto fail_err;
330     }
331   GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (core, &ehdr_mem);
332   if (ehdr == NULL)
333     {
334       err = DWFL_E_LIBELF;
335       goto fail;
336     }
337   if (ehdr->e_type != ET_CORE)
338     {
339       err = DWFL_E_NO_CORE_FILE;
340       goto fail;
341     }
342   size_t phnum;
343   if (elf_getphdrnum (core, &phnum) < 0)
344     {
345       err = DWFL_E_LIBELF;
346       goto fail;
347     }
348   pid_t pid = -1;
349   Elf_Data *note_data = NULL;
350   for (size_t cnt = 0; cnt < phnum; ++cnt)
351     {
352       GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
353       if (phdr != NULL && phdr->p_type == PT_NOTE)
354 	{
355 	  note_data = elf_getdata_rawchunk (core, phdr->p_offset,
356 					    phdr->p_filesz, ELF_T_NHDR);
357 	  break;
358 	}
359     }
360   if (note_data == NULL)
361     {
362       err = DWFL_E_LIBELF;
363       goto fail;
364     }
365   size_t offset = 0;
366   GElf_Nhdr nhdr;
367   size_t name_offset;
368   size_t desc_offset;
369   while (offset < note_data->d_size
370 	 && (offset = gelf_getnote (note_data, offset,
371 				    &nhdr, &name_offset, &desc_offset)) > 0)
372     {
373       /* Do not check NAME for now, help broken Linux kernels.  */
374       const char *name = note_data->d_buf + name_offset;
375       const char *desc = note_data->d_buf + desc_offset;
376       GElf_Word regs_offset;
377       size_t nregloc;
378       const Ebl_Register_Location *reglocs;
379       size_t nitems;
380       const Ebl_Core_Item *items;
381       if (! ebl_core_note (ebl, &nhdr, name,
382 			   &regs_offset, &nregloc, &reglocs, &nitems, &items))
383 	{
384 	  /* This note may be just not recognized, skip it.  */
385 	  continue;
386 	}
387       if (nhdr.n_type != NT_PRPSINFO)
388 	continue;
389       const Ebl_Core_Item *item;
390       for (item = items; item < items + nitems; item++)
391 	if (strcmp (item->name, "pid") == 0)
392 	  break;
393       if (item == items + nitems)
394 	continue;
395       uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
396       val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
397 		? be32toh (val32) : le32toh (val32));
398       pid = (int32_t) val32;
399       eu_static_assert (sizeof val32 <= sizeof pid);
400       break;
401     }
402   if (pid == -1)
403     {
404       /* No valid NT_PRPSINFO recognized in this CORE.  */
405       err = DWFL_E_BADELF;
406       goto fail;
407     }
408   struct core_arg *core_arg = malloc (sizeof *core_arg);
409   if (core_arg == NULL)
410     {
411       err = DWFL_E_NOMEM;
412       goto fail;
413     }
414   core_arg->core = core;
415   core_arg->note_data = note_data;
416   core_arg->thread_note_offset = 0;
417   core_arg->ebl = ebl;
418   if (! INTUSE(dwfl_attach_state) (dwfl, core, pid, &core_thread_callbacks,
419 				   core_arg))
420     {
421       free (core_arg);
422       ebl_closebackend (ebl);
423       return -1;
424     }
425   return pid;
426 }
427 INTDEF (dwfl_core_file_attach)
428