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 #ifdef HAVE_CONFIG_H
30 # include <config.h>
31 #endif
32 
33 #include "libdwflP.h"
34 #include <fcntl.h>
35 #include "system.h"
36 
37 #include "../libdw/memory-access.h"
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 = (nhdr.n_namesz == 0
133 			  ? "" : note_data->d_buf + name_offset);
134       const char *desc = note_data->d_buf + desc_offset;
135       GElf_Word regs_offset;
136       size_t nregloc;
137       const Ebl_Register_Location *reglocs;
138       size_t nitems;
139       const Ebl_Core_Item *items;
140       if (! ebl_core_note (core_arg->ebl, &nhdr, name, desc,
141 			   &regs_offset, &nregloc, &reglocs, &nitems, &items))
142 	{
143 	  /* This note may be just not recognized, skip it.  */
144 	  continue;
145 	}
146       if (nhdr.n_type != NT_PRSTATUS)
147 	continue;
148       const Ebl_Core_Item *item;
149       for (item = items; item < items + nitems; item++)
150 	if (strcmp (item->name, "pid") == 0)
151 	  break;
152       if (item == items + nitems)
153 	continue;
154       uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
155       val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
156 		? be32toh (val32) : le32toh (val32));
157       pid_t tid = (int32_t) val32;
158       eu_static_assert (sizeof val32 <= sizeof tid);
159       thread_arg->note_offset = offset;
160       return tid;
161     }
162 
163   free (thread_arg);
164   return 0;
165 }
166 
167 static bool
core_set_initial_registers(Dwfl_Thread * thread,void * thread_arg_voidp)168 core_set_initial_registers (Dwfl_Thread *thread, void *thread_arg_voidp)
169 {
170   struct thread_arg *thread_arg = thread_arg_voidp;
171   struct core_arg *core_arg = thread_arg->core_arg;
172   Elf *core = core_arg->core;
173   size_t offset = thread_arg->note_offset;
174   GElf_Nhdr nhdr;
175   size_t name_offset;
176   size_t desc_offset;
177   Elf_Data *note_data = core_arg->note_data;
178   size_t nregs = ebl_frame_nregs (core_arg->ebl);
179   assert (nregs > 0);
180   assert (offset < note_data->d_size);
181   size_t getnote_err = gelf_getnote (note_data, offset, &nhdr, &name_offset,
182 				     &desc_offset);
183   /* __libdwfl_attach_state_for_core already verified the note is there.  */
184   if (getnote_err == 0)
185     return false;
186   /* Do not check NAME for now, help broken Linux kernels.  */
187   const char *name = (nhdr.n_namesz == 0
188 		      ? "" : note_data->d_buf + name_offset);
189   const char *desc = note_data->d_buf + desc_offset;
190   GElf_Word regs_offset;
191   size_t nregloc;
192   const Ebl_Register_Location *reglocs;
193   size_t nitems;
194   const Ebl_Core_Item *items;
195   int core_note_err = ebl_core_note (core_arg->ebl, &nhdr, name, desc,
196 				     &regs_offset, &nregloc, &reglocs,
197 				     &nitems, &items);
198   /* __libdwfl_attach_state_for_core already verified the note is there.  */
199   if (core_note_err == 0 || nhdr.n_type != NT_PRSTATUS)
200     return false;
201   const Ebl_Core_Item *item;
202   for (item = items; item < items + nitems; item++)
203     if (strcmp (item->name, "pid") == 0)
204       break;
205   assert (item < items + nitems);
206   pid_t tid;
207   {
208     uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
209     val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
210 	     ? be32toh (val32) : le32toh (val32));
211     tid = (int32_t) val32;
212     eu_static_assert (sizeof val32 <= sizeof tid);
213   }
214   /* core_next_thread already found this TID there.  */
215   assert (tid == INTUSE(dwfl_thread_tid) (thread));
216   for (item = items; item < items + nitems; item++)
217     if (item->pc_register)
218       break;
219   if (item < items + nitems)
220     {
221       Dwarf_Word pc;
222       switch (gelf_getclass (core) == ELFCLASS32 ? 32 : 64)
223       {
224 	case 32:;
225 	  uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
226 	  val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
227 		   ? be32toh (val32) : le32toh (val32));
228 	  /* Do a host width conversion.  */
229 	  pc = val32;
230 	  break;
231 	case 64:;
232 	  uint64_t val64 = read_8ubyte_unaligned_noncvt (desc + item->offset);
233 	  val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
234 		   ? be64toh (val64) : le64toh (val64));
235 	  pc = val64;
236 	  break;
237 	default:
238 	  abort ();
239       }
240       INTUSE(dwfl_thread_state_register_pc) (thread, pc);
241     }
242   desc += regs_offset;
243   for (size_t regloci = 0; regloci < nregloc; regloci++)
244     {
245       const Ebl_Register_Location *regloc = reglocs + regloci;
246       // Iterate even regs out of NREGS range so that we can find pc_register.
247       if (regloc->bits != 32 && regloc->bits != 64)
248 	continue;
249       const char *reg_desc = desc + regloc->offset;
250       for (unsigned regno = regloc->regno;
251 	   regno < regloc->regno + (regloc->count ?: 1U);
252 	   regno++)
253 	{
254 	  /* PPC provides DWARF register 65 irrelevant for
255 	     CFI which clashes with register 108 (LR) we need.
256 	     LR (108) is provided earlier (in NT_PRSTATUS) than the # 65.
257 	     FIXME: It depends now on their order in core notes.
258 	     FIXME: It uses private function.  */
259 	  if (regno < nregs
260 	      && __libdwfl_frame_reg_get (thread->unwound, regno, NULL))
261 	    continue;
262 	  Dwarf_Word val;
263 	  switch (regloc->bits)
264 	  {
265 	    case 32:;
266 	      uint32_t val32 = read_4ubyte_unaligned_noncvt (reg_desc);
267 	      reg_desc += sizeof val32;
268 	      val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
269 		       ? be32toh (val32) : le32toh (val32));
270 	      /* Do a host width conversion.  */
271 	      val = val32;
272 	      break;
273 	    case 64:;
274 	      uint64_t val64 = read_8ubyte_unaligned_noncvt (reg_desc);
275 	      reg_desc += sizeof val64;
276 	      val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
277 		       ? be64toh (val64) : le64toh (val64));
278 	      assert (sizeof (*thread->unwound->regs) == sizeof val64);
279 	      val = val64;
280 	      break;
281 	    default:
282 	      abort ();
283 	  }
284 	  /* Registers not valid for CFI are just ignored.  */
285 	  if (regno < nregs)
286 	    INTUSE(dwfl_thread_state_registers) (thread, regno, 1, &val);
287 	  if (regloc->pc_register)
288 	    INTUSE(dwfl_thread_state_register_pc) (thread, val);
289 	  reg_desc += regloc->pad;
290 	}
291     }
292   return true;
293 }
294 
295 static void
core_detach(Dwfl * dwfl,void * dwfl_arg)296 core_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
297 {
298   struct core_arg *core_arg = dwfl_arg;
299   ebl_closebackend (core_arg->ebl);
300   free (core_arg);
301 }
302 
303 static const Dwfl_Thread_Callbacks core_thread_callbacks =
304 {
305   core_next_thread,
306   NULL, /* get_thread */
307   core_memory_read,
308   core_set_initial_registers,
309   core_detach,
310   NULL, /* core_thread_detach */
311 };
312 
313 int
dwfl_core_file_attach(Dwfl * dwfl,Elf * core)314 dwfl_core_file_attach (Dwfl *dwfl, Elf *core)
315 {
316   Dwfl_Error err = DWFL_E_NOERROR;
317   Ebl *ebl = ebl_openbackend (core);
318   if (ebl == NULL)
319     {
320       err = DWFL_E_LIBEBL;
321     fail_err:
322       if (dwfl->process == NULL && dwfl->attacherr == DWFL_E_NOERROR)
323 	dwfl->attacherr = __libdwfl_canon_error (err);
324       __libdwfl_seterrno (err);
325       return -1;
326     }
327   size_t nregs = ebl_frame_nregs (ebl);
328   if (nregs == 0)
329     {
330       err = DWFL_E_NO_UNWIND;
331     fail:
332       ebl_closebackend (ebl);
333       goto fail_err;
334     }
335   GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (core, &ehdr_mem);
336   if (ehdr == NULL)
337     {
338       err = DWFL_E_LIBELF;
339       goto fail;
340     }
341   if (ehdr->e_type != ET_CORE)
342     {
343       err = DWFL_E_NO_CORE_FILE;
344       goto fail;
345     }
346   size_t phnum;
347   if (elf_getphdrnum (core, &phnum) < 0)
348     {
349       err = DWFL_E_LIBELF;
350       goto fail;
351     }
352   pid_t pid = -1;
353   Elf_Data *note_data = NULL;
354   for (size_t cnt = 0; cnt < phnum; ++cnt)
355     {
356       GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
357       if (phdr != NULL && phdr->p_type == PT_NOTE)
358 	{
359 	  note_data = elf_getdata_rawchunk (core, phdr->p_offset,
360 					    phdr->p_filesz, (phdr->p_align == 8
361 							     ? ELF_T_NHDR8
362 							     : ELF_T_NHDR));
363 	  break;
364 	}
365     }
366   if (note_data == NULL)
367     {
368       err = DWFL_E_LIBELF;
369       goto fail;
370     }
371   size_t offset = 0;
372   GElf_Nhdr nhdr;
373   size_t name_offset;
374   size_t desc_offset;
375   while (offset < note_data->d_size
376 	 && (offset = gelf_getnote (note_data, offset,
377 				    &nhdr, &name_offset, &desc_offset)) > 0)
378     {
379       /* Do not check NAME for now, help broken Linux kernels.  */
380       const char *name = (nhdr.n_namesz == 0
381 			  ? "" : note_data->d_buf + name_offset);
382       const char *desc = note_data->d_buf + desc_offset;
383       GElf_Word regs_offset;
384       size_t nregloc;
385       const Ebl_Register_Location *reglocs;
386       size_t nitems;
387       const Ebl_Core_Item *items;
388       if (! ebl_core_note (ebl, &nhdr, name, desc,
389 			   &regs_offset, &nregloc, &reglocs, &nitems, &items))
390 	{
391 	  /* This note may be just not recognized, skip it.  */
392 	  continue;
393 	}
394       if (nhdr.n_type != NT_PRPSINFO)
395 	continue;
396       const Ebl_Core_Item *item;
397       for (item = items; item < items + nitems; item++)
398 	if (strcmp (item->name, "pid") == 0)
399 	  break;
400       if (item == items + nitems)
401 	continue;
402       uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
403       val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
404 		? be32toh (val32) : le32toh (val32));
405       pid = (int32_t) val32;
406       eu_static_assert (sizeof val32 <= sizeof pid);
407       break;
408     }
409   if (pid == -1)
410     {
411       /* No valid NT_PRPSINFO recognized in this CORE.  */
412       err = DWFL_E_BADELF;
413       goto fail;
414     }
415   struct core_arg *core_arg = malloc (sizeof *core_arg);
416   if (core_arg == NULL)
417     {
418       err = DWFL_E_NOMEM;
419       goto fail;
420     }
421   core_arg->core = core;
422   core_arg->note_data = note_data;
423   core_arg->thread_note_offset = 0;
424   core_arg->ebl = ebl;
425   if (! INTUSE(dwfl_attach_state) (dwfl, core, pid, &core_thread_callbacks,
426 				   core_arg))
427     {
428       free (core_arg);
429       ebl_closebackend (ebl);
430       return -1;
431     }
432   return pid;
433 }
434 INTDEF (dwfl_core_file_attach)
435