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