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