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 ®s_offset, &nregloc, ®locs, &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 ®s_offset, &nregloc, ®locs,
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 ®s_offset, &nregloc, ®locs, &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