Line data Source code
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
54 625 : core_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result,
55 : void *dwfl_arg)
56 : {
57 625 : Dwfl_Process *process = dwfl->process;
58 625 : struct core_arg *core_arg = dwfl_arg;
59 625 : Elf *core = core_arg->core;
60 625 : assert (core != NULL);
61 625 : static size_t phnum;
62 625 : if (elf_getphdrnum (core, &phnum) < 0)
63 : {
64 0 : __libdwfl_seterrno (DWFL_E_LIBELF);
65 0 : return false;
66 : }
67 6486 : for (size_t cnt = 0; cnt < phnum; ++cnt)
68 : {
69 6474 : GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
70 6474 : if (phdr == NULL || phdr->p_type != PT_LOAD)
71 5861 : continue;
72 : /* Bias is zero here, a core file itself has no bias. */
73 5849 : GElf_Addr start = __libdwfl_segment_start (dwfl, phdr->p_vaddr);
74 17547 : GElf_Addr end = __libdwfl_segment_end (dwfl,
75 5849 : phdr->p_vaddr + phdr->p_memsz);
76 5849 : unsigned bytes = ebl_get_elfclass (process->ebl) == ELFCLASS64 ? 8 : 4;
77 5849 : if (addr < start || addr + bytes > end)
78 : continue;
79 613 : Elf_Data *data;
80 613 : data = elf_getdata_rawchunk (core, phdr->p_offset + addr - start,
81 : bytes, ELF_T_ADDR);
82 613 : if (data == NULL)
83 : {
84 0 : __libdwfl_seterrno (DWFL_E_LIBELF);
85 613 : return false;
86 : }
87 613 : assert (data->d_size == bytes);
88 613 : if (bytes == 8)
89 383 : *result = read_8ubyte_unaligned_noncvt (data->d_buf);
90 : else
91 230 : *result = read_4ubyte_unaligned_noncvt (data->d_buf);
92 : return true;
93 : }
94 12 : __libdwfl_seterrno (DWFL_E_ADDR_OUTOFRANGE);
95 12 : return false;
96 : }
97 :
98 : static pid_t
99 66 : core_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
100 : void **thread_argp)
101 : {
102 66 : struct core_arg *core_arg = dwfl_arg;
103 66 : Elf *core = core_arg->core;
104 66 : GElf_Nhdr nhdr;
105 66 : size_t name_offset;
106 66 : size_t desc_offset;
107 66 : Elf_Data *note_data = core_arg->note_data;
108 66 : size_t offset;
109 :
110 66 : struct thread_arg *thread_arg;
111 66 : if (*thread_argp == NULL)
112 : {
113 26 : core_arg->thread_note_offset = 0;
114 26 : thread_arg = malloc (sizeof (*thread_arg));
115 26 : if (thread_arg == NULL)
116 : {
117 0 : __libdwfl_seterrno (DWFL_E_NOMEM);
118 0 : return -1;
119 : }
120 26 : thread_arg->core_arg = core_arg;
121 26 : *thread_argp = thread_arg;
122 : }
123 : else
124 : thread_arg = (struct thread_arg *) *thread_argp;
125 :
126 235 : while (offset = core_arg->thread_note_offset, offset < note_data->d_size
127 235 : && (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 418 : const char *name = (nhdr.n_namesz == 0
133 209 : ? "" : note_data->d_buf + name_offset);
134 209 : const char *desc = note_data->d_buf + desc_offset;
135 209 : GElf_Word regs_offset;
136 209 : size_t nregloc;
137 209 : const Ebl_Register_Location *reglocs;
138 209 : size_t nitems;
139 209 : const Ebl_Core_Item *items;
140 209 : 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 169 : continue;
145 : }
146 125 : if (nhdr.n_type != NT_PRSTATUS)
147 : continue;
148 40 : const Ebl_Core_Item *item;
149 280 : for (item = items; item < items + nitems; item++)
150 280 : if (strcmp (item->name, "pid") == 0)
151 : break;
152 40 : if (item == items + nitems)
153 : continue;
154 40 : uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
155 40 : val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
156 40 : ? be32toh (val32) : le32toh (val32));
157 40 : pid_t tid = (int32_t) val32;
158 40 : eu_static_assert (sizeof val32 <= sizeof tid);
159 40 : thread_arg->note_offset = offset;
160 40 : return tid;
161 : }
162 :
163 26 : free (thread_arg);
164 26 : return 0;
165 : }
166 :
167 : static bool
168 40 : core_set_initial_registers (Dwfl_Thread *thread, void *thread_arg_voidp)
169 : {
170 40 : struct thread_arg *thread_arg = thread_arg_voidp;
171 40 : struct core_arg *core_arg = thread_arg->core_arg;
172 40 : Elf *core = core_arg->core;
173 40 : size_t offset = thread_arg->note_offset;
174 40 : GElf_Nhdr nhdr;
175 40 : size_t name_offset;
176 40 : size_t desc_offset;
177 40 : Elf_Data *note_data = core_arg->note_data;
178 40 : size_t nregs = ebl_frame_nregs (core_arg->ebl);
179 40 : assert (nregs > 0);
180 40 : assert (offset < note_data->d_size);
181 40 : 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 40 : if (getnote_err == 0)
185 : return false;
186 : /* Do not check NAME for now, help broken Linux kernels. */
187 80 : const char *name = (nhdr.n_namesz == 0
188 40 : ? "" : note_data->d_buf + name_offset);
189 40 : const char *desc = note_data->d_buf + desc_offset;
190 40 : GElf_Word regs_offset;
191 40 : size_t nregloc;
192 40 : const Ebl_Register_Location *reglocs;
193 40 : size_t nitems;
194 40 : const Ebl_Core_Item *items;
195 40 : 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 40 : if (core_note_err == 0 || nhdr.n_type != NT_PRSTATUS)
200 : return false;
201 40 : const Ebl_Core_Item *item;
202 280 : for (item = items; item < items + nitems; item++)
203 280 : if (strcmp (item->name, "pid") == 0)
204 : break;
205 40 : assert (item < items + nitems);
206 40 : pid_t tid;
207 : {
208 40 : uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
209 40 : val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
210 40 : ? be32toh (val32) : le32toh (val32));
211 40 : tid = (int32_t) val32;
212 40 : eu_static_assert (sizeof val32 <= sizeof tid);
213 : }
214 : /* core_next_thread already found this TID there. */
215 40 : assert (tid == INTUSE(dwfl_thread_tid) (thread));
216 660 : for (item = items; item < items + nitems; item++)
217 630 : if (item->pc_register)
218 : break;
219 40 : if (item < items + nitems)
220 : {
221 10 : Dwarf_Word pc;
222 10 : switch (gelf_getclass (core) == ELFCLASS32 ? 32 : 64)
223 : {
224 2 : case 32:;
225 2 : uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
226 2 : val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
227 2 : ? be32toh (val32) : le32toh (val32));
228 : /* Do a host width conversion. */
229 2 : pc = val32;
230 2 : break;
231 8 : case 64:;
232 8 : uint64_t val64 = read_8ubyte_unaligned_noncvt (desc + item->offset);
233 8 : val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
234 8 : ? be64toh (val64) : le64toh (val64));
235 : pc = val64;
236 : break;
237 : default:
238 : abort ();
239 : }
240 10 : INTUSE(dwfl_thread_state_register_pc) (thread, pc);
241 : }
242 40 : desc += regs_offset;
243 648 : for (size_t regloci = 0; regloci < nregloc; regloci++)
244 : {
245 608 : const Ebl_Register_Location *regloc = reglocs + regloci;
246 : // Iterate even regs out of NREGS range so that we can find pc_register.
247 608 : if (regloc->bits != 32 && regloc->bits != 64)
248 : continue;
249 472 : const char *reg_desc = desc + regloc->offset;
250 472 : for (unsigned regno = regloc->regno;
251 1428 : regno < regloc->regno + (regloc->count ?: 1U);
252 956 : 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 956 : if (regno < nregs
260 818 : && __libdwfl_frame_reg_get (thread->unwound, regno, NULL))
261 0 : continue;
262 956 : Dwarf_Word val;
263 956 : switch (regloc->bits)
264 : {
265 240 : case 32:;
266 240 : uint32_t val32 = read_4ubyte_unaligned_noncvt (reg_desc);
267 240 : reg_desc += sizeof val32;
268 240 : val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
269 240 : ? be32toh (val32) : le32toh (val32));
270 : /* Do a host width conversion. */
271 240 : val = val32;
272 240 : break;
273 716 : case 64:;
274 716 : uint64_t val64 = read_8ubyte_unaligned_noncvt (reg_desc);
275 716 : reg_desc += sizeof val64;
276 716 : val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
277 716 : ? be64toh (val64) : le64toh (val64));
278 716 : assert (sizeof (*thread->unwound->regs) == sizeof val64);
279 716 : val = val64;
280 716 : break;
281 0 : default:
282 0 : abort ();
283 : }
284 : /* Registers not valid for CFI are just ignored. */
285 956 : if (regno < nregs)
286 818 : INTUSE(dwfl_thread_state_registers) (thread, regno, 1, &val);
287 956 : if (regloc->pc_register)
288 4 : INTUSE(dwfl_thread_state_register_pc) (thread, val);
289 956 : reg_desc += regloc->pad;
290 : }
291 : }
292 : return true;
293 : }
294 :
295 : static void
296 36 : core_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
297 : {
298 36 : struct core_arg *core_arg = dwfl_arg;
299 36 : ebl_closebackend (core_arg->ebl);
300 36 : free (core_arg);
301 36 : }
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
314 36 : dwfl_core_file_attach (Dwfl *dwfl, Elf *core)
315 : {
316 36 : Dwfl_Error err = DWFL_E_NOERROR;
317 36 : Ebl *ebl = ebl_openbackend (core);
318 36 : if (ebl == NULL)
319 : {
320 : err = DWFL_E_LIBEBL;
321 0 : fail_err:
322 0 : if (dwfl->process == NULL && dwfl->attacherr == DWFL_E_NOERROR)
323 0 : dwfl->attacherr = __libdwfl_canon_error (err);
324 0 : __libdwfl_seterrno (err);
325 0 : return -1;
326 : }
327 36 : size_t nregs = ebl_frame_nregs (ebl);
328 36 : if (nregs == 0)
329 : {
330 : err = DWFL_E_NO_UNWIND;
331 0 : fail:
332 0 : ebl_closebackend (ebl);
333 0 : goto fail_err;
334 : }
335 36 : GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (core, &ehdr_mem);
336 36 : if (ehdr == NULL)
337 : {
338 : err = DWFL_E_LIBELF;
339 : goto fail;
340 : }
341 36 : if (ehdr->e_type != ET_CORE)
342 : {
343 : err = DWFL_E_NO_CORE_FILE;
344 : goto fail;
345 : }
346 36 : size_t phnum;
347 36 : 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 36 : for (size_t cnt = 0; cnt < phnum; ++cnt)
355 : {
356 36 : GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
357 36 : if (phdr != NULL && phdr->p_type == PT_NOTE)
358 : {
359 36 : note_data = elf_getdata_rawchunk (core, phdr->p_offset,
360 36 : phdr->p_filesz, (phdr->p_align == 8
361 : ? ELF_T_NHDR8
362 : : ELF_T_NHDR));
363 36 : break;
364 : }
365 : }
366 36 : 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 72 : while (offset < note_data->d_size
376 72 : && (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 144 : const char *name = (nhdr.n_namesz == 0
381 72 : ? "" : note_data->d_buf + name_offset);
382 72 : const char *desc = note_data->d_buf + desc_offset;
383 72 : GElf_Word regs_offset;
384 72 : size_t nregloc;
385 72 : const Ebl_Register_Location *reglocs;
386 72 : size_t nitems;
387 72 : const Ebl_Core_Item *items;
388 72 : 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 36 : continue;
393 : }
394 72 : if (nhdr.n_type != NT_PRPSINFO)
395 : continue;
396 36 : const Ebl_Core_Item *item;
397 288 : for (item = items; item < items + nitems; item++)
398 288 : if (strcmp (item->name, "pid") == 0)
399 : break;
400 36 : if (item == items + nitems)
401 : continue;
402 36 : uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
403 36 : val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
404 36 : ? be32toh (val32) : le32toh (val32));
405 36 : pid = (int32_t) val32;
406 36 : eu_static_assert (sizeof val32 <= sizeof pid);
407 36 : break;
408 : }
409 36 : if (pid == -1)
410 : {
411 : /* No valid NT_PRPSINFO recognized in this CORE. */
412 : err = DWFL_E_BADELF;
413 : goto fail;
414 : }
415 36 : struct core_arg *core_arg = malloc (sizeof *core_arg);
416 36 : if (core_arg == NULL)
417 : {
418 : err = DWFL_E_NOMEM;
419 : goto fail;
420 : }
421 36 : core_arg->core = core;
422 36 : core_arg->note_data = note_data;
423 36 : core_arg->thread_note_offset = 0;
424 36 : core_arg->ebl = ebl;
425 36 : if (! INTUSE(dwfl_attach_state) (dwfl, core, pid, &core_thread_callbacks,
426 : core_arg))
427 : {
428 0 : free (core_arg);
429 0 : ebl_closebackend (ebl);
430 0 : return -1;
431 : }
432 : return pid;
433 : }
434 : INTDEF (dwfl_core_file_attach)
|