Branch data 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 "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 : 544 : core_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result,
55 : : void *dwfl_arg)
56 : : {
57 : 544 : Dwfl_Process *process = dwfl->process;
58 : 544 : struct core_arg *core_arg = dwfl_arg;
59 : 544 : Elf *core = core_arg->core;
60 [ - + ]: 544 : assert (core != NULL);
61 : 544 : static size_t phnum;
62 [ - + ]: 544 : if (elf_getphdrnum (core, &phnum) < 0)
63 : : {
64 : 0 : __libdwfl_seterrno (DWFL_E_LIBELF);
65 : 0 : return false;
66 : : }
67 [ + + ]: 4784 : for (size_t cnt = 0; cnt < phnum; ++cnt)
68 : : {
69 : 4772 : GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
70 [ + - + + ]: 4772 : if (phdr == NULL || phdr->p_type != PT_LOAD)
71 : 4240 : continue;
72 : : /* Bias is zero here, a core file itself has no bias. */
73 : 4228 : GElf_Addr start = __libdwfl_segment_start (dwfl, phdr->p_vaddr);
74 : 8456 : GElf_Addr end = __libdwfl_segment_end (dwfl,
75 : 4228 : phdr->p_vaddr + phdr->p_memsz);
76 [ + + ]: 4228 : unsigned bytes = ebl_get_elfclass (process->ebl) == ELFCLASS64 ? 8 : 4;
77 [ + + + + ]: 4228 : if (addr < start || addr + bytes > end)
78 : 3696 : continue;
79 : 532 : Elf_Data *data;
80 : 532 : data = elf_getdata_rawchunk (core, phdr->p_offset + addr - start,
81 : : bytes, ELF_T_ADDR);
82 [ - + ]: 532 : if (data == NULL)
83 : : {
84 : 0 : __libdwfl_seterrno (DWFL_E_LIBELF);
85 : 532 : return false;
86 : : }
87 [ - + ]: 532 : assert (data->d_size == bytes);
88 [ + + ]: 532 : if (bytes == 8)
89 : 360 : *result = read_8ubyte_unaligned_noncvt (data->d_buf);
90 : : else
91 : 172 : *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 : 60 : core_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
100 : : void **thread_argp)
101 : : {
102 : 60 : struct core_arg *core_arg = dwfl_arg;
103 : 60 : Elf *core = core_arg->core;
104 : 60 : GElf_Nhdr nhdr;
105 : 60 : size_t name_offset;
106 : 60 : size_t desc_offset;
107 : 60 : Elf_Data *note_data = core_arg->note_data;
108 : 60 : size_t offset;
109 : :
110 : 60 : struct thread_arg *thread_arg;
111 [ + + ]: 60 : if (*thread_argp == NULL)
112 : : {
113 : 24 : core_arg->thread_note_offset = 0;
114 : 24 : thread_arg = malloc (sizeof (*thread_arg));
115 [ - + ]: 24 : if (thread_arg == NULL)
116 : : {
117 : 0 : __libdwfl_seterrno (DWFL_E_NOMEM);
118 : 0 : return -1;
119 : : }
120 : 24 : thread_arg->core_arg = core_arg;
121 : 24 : *thread_argp = thread_arg;
122 : : }
123 : : else
124 : 60 : thread_arg = (struct thread_arg *) *thread_argp;
125 : :
126 : 209 : while (offset = core_arg->thread_note_offset, offset < note_data->d_size
127 [ + + + - ]: 209 : && (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 : 370 : const char *name = (nhdr.n_namesz == 0
133 [ + - ]: 185 : ? "" : note_data->d_buf + name_offset);
134 : 185 : const char *desc = note_data->d_buf + desc_offset;
135 : 185 : GElf_Word regs_offset;
136 : 185 : size_t nregloc;
137 : 185 : const Ebl_Register_Location *reglocs;
138 : 185 : size_t nitems;
139 : 185 : const Ebl_Core_Item *items;
140 [ + + ]: 185 : 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 : 149 : continue;
145 : : }
146 [ + + ]: 111 : if (nhdr.n_type != NT_PRSTATUS)
147 : 75 : continue;
148 : 36 : const Ebl_Core_Item *item;
149 [ + - ]: 252 : for (item = items; item < items + nitems; item++)
150 [ + + ]: 252 : if (strcmp (item->name, "pid") == 0)
151 : : break;
152 [ - + ]: 36 : if (item == items + nitems)
153 : 0 : continue;
154 : 36 : uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
155 : 36 : val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
156 [ + + ]: 36 : ? be32toh (val32) : le32toh (val32));
157 : 36 : pid_t tid = (int32_t) val32;
158 : 36 : eu_static_assert (sizeof val32 <= sizeof tid);
159 : 36 : thread_arg->note_offset = offset;
160 : 36 : return tid;
161 : : }
162 : :
163 : 24 : free (thread_arg);
164 : 24 : return 0;
165 : : }
166 : :
167 : : static bool
168 : 36 : core_set_initial_registers (Dwfl_Thread *thread, void *thread_arg_voidp)
169 : : {
170 : 36 : struct thread_arg *thread_arg = thread_arg_voidp;
171 : 36 : struct core_arg *core_arg = thread_arg->core_arg;
172 : 36 : Elf *core = core_arg->core;
173 : 36 : size_t offset = thread_arg->note_offset;
174 : 36 : GElf_Nhdr nhdr;
175 : 36 : size_t name_offset;
176 : 36 : size_t desc_offset;
177 : 36 : Elf_Data *note_data = core_arg->note_data;
178 : 36 : size_t nregs = ebl_frame_nregs (core_arg->ebl);
179 [ - + ]: 36 : assert (nregs > 0);
180 [ - + ]: 36 : assert (offset < note_data->d_size);
181 : 36 : 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 [ - + ]: 36 : if (getnote_err == 0)
185 : : return false;
186 : : /* Do not check NAME for now, help broken Linux kernels. */
187 : 72 : const char *name = (nhdr.n_namesz == 0
188 [ + - ]: 36 : ? "" : note_data->d_buf + name_offset);
189 : 36 : const char *desc = note_data->d_buf + desc_offset;
190 : 36 : GElf_Word regs_offset;
191 : 36 : size_t nregloc;
192 : 36 : const Ebl_Register_Location *reglocs;
193 : 36 : size_t nitems;
194 : 36 : const Ebl_Core_Item *items;
195 : 36 : 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 [ - + - + ]: 36 : if (core_note_err == 0 || nhdr.n_type != NT_PRSTATUS)
200 : : return false;
201 : 36 : const Ebl_Core_Item *item;
202 [ + - ]: 252 : for (item = items; item < items + nitems; item++)
203 [ + + ]: 252 : if (strcmp (item->name, "pid") == 0)
204 : : break;
205 [ - + ]: 36 : assert (item < items + nitems);
206 : 36 : pid_t tid;
207 : : {
208 : 36 : uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
209 : 36 : val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
210 [ + + ]: 36 : ? be32toh (val32) : le32toh (val32));
211 : 36 : tid = (int32_t) val32;
212 : 36 : eu_static_assert (sizeof val32 <= sizeof tid);
213 : : }
214 : : /* core_next_thread already found this TID there. */
215 [ - + ]: 36 : assert (tid == INTUSE(dwfl_thread_tid) (thread));
216 [ + + ]: 592 : for (item = items; item < items + nitems; item++)
217 [ + + ]: 566 : if (item->pc_register)
218 : : break;
219 [ + + ]: 36 : 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 : 36 : desc += regs_offset;
243 [ + + ]: 570 : for (size_t regloci = 0; regloci < nregloc; regloci++)
244 : : {
245 : 534 : const Ebl_Register_Location *regloc = reglocs + regloci;
246 : : // Iterate even regs out of NREGS range so that we can find pc_register.
247 [ + + ]: 534 : if (regloc->bits != 32 && regloc->bits != 64)
248 : 114 : continue;
249 : 420 : const char *reg_desc = desc + regloc->offset;
250 : 420 : for (unsigned regno = regloc->regno;
251 [ + + ]: 1316 : regno < regloc->regno + (regloc->count ?: 1U);
252 : 896 : 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 [ + + ]: 896 : if (regno < nregs
260 [ - + ]: 766 : && __libdwfl_frame_reg_get (thread->unwound, regno, NULL) == 0)
261 : 0 : continue;
262 : 896 : Dwarf_Word val;
263 [ + + - ]: 896 : switch (regloc->bits)
264 : : {
265 : 220 : case 32:;
266 : 220 : uint32_t val32 = read_4ubyte_unaligned_noncvt (reg_desc);
267 : 220 : reg_desc += sizeof val32;
268 : 220 : val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
269 [ + + ]: 220 : ? be32toh (val32) : le32toh (val32));
270 : : /* Do a host width conversion. */
271 : 220 : val = val32;
272 : 220 : break;
273 : 676 : case 64:;
274 : 676 : uint64_t val64 = read_8ubyte_unaligned_noncvt (reg_desc);
275 : 676 : reg_desc += sizeof val64;
276 : 676 : val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
277 [ + + ]: 676 : ? be64toh (val64) : le64toh (val64));
278 : 676 : assert (sizeof (*thread->unwound->regs) == sizeof val64);
279 : 676 : val = val64;
280 : 676 : break;
281 : 0 : default:
282 : 0 : abort ();
283 : : }
284 : : /* Registers not valid for CFI are just ignored. */
285 [ + + ]: 896 : if (regno < nregs)
286 : 766 : INTUSE(dwfl_thread_state_registers) (thread, regno, 1, &val);
287 [ + + ]: 896 : if (regloc->pc_register)
288 : 4 : INTUSE(dwfl_thread_state_register_pc) (thread, val);
289 : 896 : reg_desc += regloc->pad;
290 : : }
291 : : }
292 : : return true;
293 : : }
294 : :
295 : : static void
296 : 34 : core_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
297 : : {
298 : 34 : struct core_arg *core_arg = dwfl_arg;
299 : 34 : ebl_closebackend (core_arg->ebl);
300 : 34 : free (core_arg);
301 : 34 : }
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 : 34 : dwfl_core_file_attach (Dwfl *dwfl, Elf *core)
315 : : {
316 : 34 : Dwfl_Error err = DWFL_E_NOERROR;
317 : 34 : Ebl *ebl = ebl_openbackend (core);
318 [ - + ]: 34 : 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 : 34 : size_t nregs = ebl_frame_nregs (ebl);
328 [ - + ]: 34 : 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 : 34 : GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (core, &ehdr_mem);
336 [ - + ]: 34 : if (ehdr == NULL)
337 : : {
338 : 0 : err = DWFL_E_LIBELF;
339 : 0 : goto fail;
340 : : }
341 [ - + ]: 34 : if (ehdr->e_type != ET_CORE)
342 : : {
343 : 0 : err = DWFL_E_NO_CORE_FILE;
344 : 0 : goto fail;
345 : : }
346 : 34 : size_t phnum;
347 [ - + ]: 34 : if (elf_getphdrnum (core, &phnum) < 0)
348 : : {
349 : 0 : err = DWFL_E_LIBELF;
350 : 0 : goto fail;
351 : : }
352 : 34 : pid_t pid = -1;
353 : 34 : Elf_Data *note_data = NULL;
354 [ + - ]: 34 : for (size_t cnt = 0; cnt < phnum; ++cnt)
355 : : {
356 : 34 : GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
357 [ + - + - ]: 34 : if (phdr != NULL && phdr->p_type == PT_NOTE)
358 : : {
359 : 34 : note_data = elf_getdata_rawchunk (core, phdr->p_offset,
360 [ + - ]: 34 : phdr->p_filesz, (phdr->p_align == 8
361 : : ? ELF_T_NHDR8
362 : : : ELF_T_NHDR));
363 : 34 : break;
364 : : }
365 : : }
366 [ - + ]: 34 : if (note_data == NULL)
367 : : {
368 : 0 : err = DWFL_E_LIBELF;
369 : 0 : goto fail;
370 : : }
371 : : size_t offset = 0;
372 : : GElf_Nhdr nhdr;
373 : : size_t name_offset;
374 : : size_t desc_offset;
375 : 68 : while (offset < note_data->d_size
376 [ - + + - ]: 68 : && (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 : 136 : const char *name = (nhdr.n_namesz == 0
381 [ + - ]: 68 : ? "" : note_data->d_buf + name_offset);
382 : 68 : const char *desc = note_data->d_buf + desc_offset;
383 : 68 : GElf_Word regs_offset;
384 : 68 : size_t nregloc;
385 : 68 : const Ebl_Register_Location *reglocs;
386 : 68 : size_t nitems;
387 : 68 : const Ebl_Core_Item *items;
388 [ - + ]: 68 : 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 : 34 : continue;
393 : : }
394 [ + + ]: 68 : if (nhdr.n_type != NT_PRPSINFO)
395 : 34 : continue;
396 : 34 : const Ebl_Core_Item *item;
397 [ + - ]: 272 : for (item = items; item < items + nitems; item++)
398 [ + + ]: 272 : if (strcmp (item->name, "pid") == 0)
399 : : break;
400 [ - + ]: 34 : if (item == items + nitems)
401 : 0 : continue;
402 : 34 : uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
403 : 34 : val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
404 [ + + ]: 34 : ? be32toh (val32) : le32toh (val32));
405 : 34 : pid = (int32_t) val32;
406 : 34 : eu_static_assert (sizeof val32 <= sizeof pid);
407 : 34 : break;
408 : : }
409 [ - + ]: 34 : if (pid == -1)
410 : : {
411 : : /* No valid NT_PRPSINFO recognized in this CORE. */
412 : 0 : err = DWFL_E_BADELF;
413 : 0 : goto fail;
414 : : }
415 : 34 : struct core_arg *core_arg = malloc (sizeof *core_arg);
416 [ - + ]: 34 : if (core_arg == NULL)
417 : : {
418 : 0 : err = DWFL_E_NOMEM;
419 : 0 : goto fail;
420 : : }
421 : 34 : core_arg->core = core;
422 : 34 : core_arg->note_data = note_data;
423 : 34 : core_arg->thread_note_offset = 0;
424 : 34 : core_arg->ebl = ebl;
425 [ - + ]: 34 : 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)
|