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 : assert (getnote_err != 0);
185 : /* Do not check NAME for now, help broken Linux kernels. */
186 80 : const char *name = (nhdr.n_namesz == 0
187 40 : ? "" : note_data->d_buf + name_offset);
188 40 : const char *desc = note_data->d_buf + desc_offset;
189 40 : GElf_Word regs_offset;
190 40 : size_t nregloc;
191 40 : const Ebl_Register_Location *reglocs;
192 40 : size_t nitems;
193 40 : const Ebl_Core_Item *items;
194 40 : 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 40 : assert (core_note_err != 0);
199 40 : assert (nhdr.n_type == NT_PRSTATUS);
200 40 : const Ebl_Core_Item *item;
201 280 : for (item = items; item < items + nitems; item++)
202 280 : if (strcmp (item->name, "pid") == 0)
203 : break;
204 40 : assert (item < items + nitems);
205 40 : pid_t tid;
206 : {
207 40 : uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
208 40 : val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
209 40 : ? be32toh (val32) : le32toh (val32));
210 40 : tid = (int32_t) val32;
211 40 : eu_static_assert (sizeof val32 <= sizeof tid);
212 : }
213 : /* core_next_thread already found this TID there. */
214 40 : assert (tid == INTUSE(dwfl_thread_tid) (thread));
215 660 : for (item = items; item < items + nitems; item++)
216 630 : if (item->pc_register)
217 : break;
218 40 : if (item < items + nitems)
219 : {
220 10 : Dwarf_Word pc;
221 10 : switch (gelf_getclass (core) == ELFCLASS32 ? 32 : 64)
222 : {
223 2 : case 32:;
224 2 : uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
225 2 : val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
226 2 : ? be32toh (val32) : le32toh (val32));
227 : /* Do a host width conversion. */
228 2 : pc = val32;
229 2 : break;
230 8 : case 64:;
231 8 : uint64_t val64 = read_8ubyte_unaligned_noncvt (desc + item->offset);
232 8 : val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
233 8 : ? be64toh (val64) : le64toh (val64));
234 : pc = val64;
235 : break;
236 : default:
237 : abort ();
238 : }
239 10 : INTUSE(dwfl_thread_state_register_pc) (thread, pc);
240 : }
241 40 : desc += regs_offset;
242 648 : for (size_t regloci = 0; regloci < nregloc; regloci++)
243 : {
244 608 : const Ebl_Register_Location *regloc = reglocs + regloci;
245 : // Iterate even regs out of NREGS range so that we can find pc_register.
246 608 : if (regloc->bits != 32 && regloc->bits != 64)
247 : continue;
248 472 : const char *reg_desc = desc + regloc->offset;
249 472 : for (unsigned regno = regloc->regno;
250 1428 : regno < regloc->regno + (regloc->count ?: 1U);
251 956 : 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 956 : if (regno < nregs
259 818 : && __libdwfl_frame_reg_get (thread->unwound, regno, NULL))
260 0 : continue;
261 956 : Dwarf_Word val;
262 956 : switch (regloc->bits)
263 : {
264 240 : case 32:;
265 240 : uint32_t val32 = read_4ubyte_unaligned_noncvt (reg_desc);
266 240 : reg_desc += sizeof val32;
267 240 : val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
268 240 : ? be32toh (val32) : le32toh (val32));
269 : /* Do a host width conversion. */
270 240 : val = val32;
271 240 : break;
272 716 : case 64:;
273 716 : uint64_t val64 = read_8ubyte_unaligned_noncvt (reg_desc);
274 716 : reg_desc += sizeof val64;
275 716 : val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
276 716 : ? be64toh (val64) : le64toh (val64));
277 716 : assert (sizeof (*thread->unwound->regs) == sizeof val64);
278 716 : val = val64;
279 716 : break;
280 0 : default:
281 0 : abort ();
282 : }
283 : /* Registers not valid for CFI are just ignored. */
284 956 : if (regno < nregs)
285 818 : INTUSE(dwfl_thread_state_registers) (thread, regno, 1, &val);
286 956 : if (regloc->pc_register)
287 4 : INTUSE(dwfl_thread_state_register_pc) (thread, val);
288 956 : reg_desc += regloc->pad;
289 : }
290 : }
291 40 : return true;
292 : }
293 :
294 : static void
295 36 : core_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
296 : {
297 36 : struct core_arg *core_arg = dwfl_arg;
298 36 : ebl_closebackend (core_arg->ebl);
299 36 : free (core_arg);
300 36 : }
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
313 36 : dwfl_core_file_attach (Dwfl *dwfl, Elf *core)
314 : {
315 36 : Dwfl_Error err = DWFL_E_NOERROR;
316 36 : Ebl *ebl = ebl_openbackend (core);
317 36 : if (ebl == NULL)
318 : {
319 : err = DWFL_E_LIBEBL;
320 0 : fail_err:
321 0 : if (dwfl->process == NULL && dwfl->attacherr == DWFL_E_NOERROR)
322 0 : dwfl->attacherr = __libdwfl_canon_error (err);
323 0 : __libdwfl_seterrno (err);
324 0 : return -1;
325 : }
326 36 : size_t nregs = ebl_frame_nregs (ebl);
327 36 : if (nregs == 0)
328 : {
329 : err = DWFL_E_NO_UNWIND;
330 0 : fail:
331 0 : ebl_closebackend (ebl);
332 0 : goto fail_err;
333 : }
334 36 : GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (core, &ehdr_mem);
335 36 : if (ehdr == NULL)
336 : {
337 : err = DWFL_E_LIBELF;
338 : goto fail;
339 : }
340 36 : if (ehdr->e_type != ET_CORE)
341 : {
342 : err = DWFL_E_NO_CORE_FILE;
343 : goto fail;
344 : }
345 36 : size_t phnum;
346 36 : 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 36 : for (size_t cnt = 0; cnt < phnum; ++cnt)
354 : {
355 36 : GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
356 36 : if (phdr != NULL && phdr->p_type == PT_NOTE)
357 : {
358 36 : note_data = elf_getdata_rawchunk (core, phdr->p_offset,
359 36 : phdr->p_filesz, (phdr->p_align == 8
360 : ? ELF_T_NHDR8
361 : : ELF_T_NHDR));
362 36 : break;
363 : }
364 : }
365 36 : 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 72 : while (offset < note_data->d_size
375 72 : && (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 144 : const char *name = (nhdr.n_namesz == 0
380 72 : ? "" : note_data->d_buf + name_offset);
381 72 : const char *desc = note_data->d_buf + desc_offset;
382 72 : GElf_Word regs_offset;
383 72 : size_t nregloc;
384 72 : const Ebl_Register_Location *reglocs;
385 72 : size_t nitems;
386 72 : const Ebl_Core_Item *items;
387 72 : 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 36 : continue;
392 : }
393 72 : if (nhdr.n_type != NT_PRPSINFO)
394 : continue;
395 36 : const Ebl_Core_Item *item;
396 288 : for (item = items; item < items + nitems; item++)
397 288 : if (strcmp (item->name, "pid") == 0)
398 : break;
399 36 : if (item == items + nitems)
400 : continue;
401 36 : uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
402 36 : val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
403 36 : ? be32toh (val32) : le32toh (val32));
404 36 : pid = (int32_t) val32;
405 36 : eu_static_assert (sizeof val32 <= sizeof pid);
406 36 : break;
407 : }
408 36 : if (pid == -1)
409 : {
410 : /* No valid NT_PRPSINFO recognized in this CORE. */
411 : err = DWFL_E_BADELF;
412 : goto fail;
413 : }
414 36 : struct core_arg *core_arg = malloc (sizeof *core_arg);
415 36 : if (core_arg == NULL)
416 : {
417 : err = DWFL_E_NOMEM;
418 : goto fail;
419 : }
420 36 : core_arg->core = core;
421 36 : core_arg->note_data = note_data;
422 36 : core_arg->thread_note_offset = 0;
423 36 : core_arg->ebl = ebl;
424 36 : if (! INTUSE(dwfl_attach_state) (dwfl, core, pid, &core_thread_callbacks,
425 : core_arg))
426 : {
427 0 : free (core_arg);
428 0 : ebl_closebackend (ebl);
429 0 : return -1;
430 : }
431 : return pid;
432 : }
433 : INTDEF (dwfl_core_file_attach)
|