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 : #include "libdwflP.h"
30 : #include <fcntl.h>
31 : #include "system.h"
32 :
33 : #include "../libdw/memory-access.h"
34 :
35 : struct core_arg
36 : {
37 : Elf *core;
38 : Elf_Data *note_data;
39 : size_t thread_note_offset;
40 : Ebl *ebl;
41 : };
42 :
43 : struct thread_arg
44 : {
45 : struct core_arg *core_arg;
46 : size_t note_offset;
47 : };
48 :
49 : static bool
50 405 : core_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result,
51 : void *dwfl_arg)
52 : {
53 405 : Dwfl_Process *process = dwfl->process;
54 405 : struct core_arg *core_arg = dwfl_arg;
55 405 : Elf *core = core_arg->core;
56 405 : assert (core != NULL);
57 : static size_t phnum;
58 405 : if (elf_getphdrnum (core, &phnum) < 0)
59 : {
60 0 : __libdwfl_seterrno (DWFL_E_LIBELF);
61 0 : return false;
62 : }
63 6947 : for (size_t cnt = 0; cnt < phnum; ++cnt)
64 : {
65 3676 : GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
66 3676 : if (phdr == NULL || phdr->p_type != PT_LOAD)
67 3676 : continue;
68 : /* Bias is zero here, a core file itself has no bias. */
69 3271 : GElf_Addr start = __libdwfl_segment_start (dwfl, phdr->p_vaddr);
70 3271 : GElf_Addr end = __libdwfl_segment_end (dwfl,
71 3271 : phdr->p_vaddr + phdr->p_memsz);
72 3271 : unsigned bytes = ebl_get_elfclass (process->ebl) == ELFCLASS64 ? 8 : 4;
73 3271 : if (addr < start || addr + bytes > end)
74 2866 : continue;
75 : Elf_Data *data;
76 405 : data = elf_getdata_rawchunk (core, phdr->p_offset + addr - start,
77 : bytes, ELF_T_ADDR);
78 405 : if (data == NULL)
79 : {
80 0 : __libdwfl_seterrno (DWFL_E_LIBELF);
81 0 : return false;
82 : }
83 405 : assert (data->d_size == bytes);
84 405 : if (bytes == 8)
85 287 : *result = read_8ubyte_unaligned_noncvt (data->d_buf);
86 : else
87 118 : *result = read_4ubyte_unaligned_noncvt (data->d_buf);
88 : return true;
89 : }
90 0 : __libdwfl_seterrno (DWFL_E_ADDR_OUTOFRANGE);
91 0 : return false;
92 : }
93 :
94 : static pid_t
95 48 : core_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
96 : void **thread_argp)
97 : {
98 48 : struct core_arg *core_arg = dwfl_arg;
99 48 : Elf *core = core_arg->core;
100 : GElf_Nhdr nhdr;
101 : size_t name_offset;
102 : size_t desc_offset;
103 48 : Elf_Data *note_data = core_arg->note_data;
104 : size_t offset;
105 :
106 : struct thread_arg *thread_arg;
107 48 : if (*thread_argp == NULL)
108 : {
109 20 : core_arg->thread_note_offset = 0;
110 20 : thread_arg = malloc (sizeof (*thread_arg));
111 20 : if (thread_arg == NULL)
112 : {
113 0 : __libdwfl_seterrno (DWFL_E_NOMEM);
114 0 : return -1;
115 : }
116 20 : thread_arg->core_arg = core_arg;
117 20 : *thread_argp = thread_arg;
118 : }
119 : else
120 : thread_arg = (struct thread_arg *) *thread_argp;
121 :
122 314 : while (offset = core_arg->thread_note_offset, offset < note_data->d_size
123 157 : && (core_arg->thread_note_offset = gelf_getnote (note_data, offset,
124 : &nhdr, &name_offset,
125 : &desc_offset)) > 0)
126 : {
127 : /* Do not check NAME for now, help broken Linux kernels. */
128 137 : const char *name = note_data->d_buf + name_offset;
129 137 : const char *desc = note_data->d_buf + desc_offset;
130 : GElf_Word regs_offset;
131 : size_t nregloc;
132 : const Ebl_Register_Location *reglocs;
133 : size_t nitems;
134 : const Ebl_Core_Item *items;
135 137 : if (! ebl_core_note (core_arg->ebl, &nhdr, name,
136 : ®s_offset, &nregloc, ®locs, &nitems, &items))
137 : {
138 : /* This note may be just not recognized, skip it. */
139 167 : continue;
140 : }
141 79 : if (nhdr.n_type != NT_PRSTATUS)
142 51 : continue;
143 : const Ebl_Core_Item *item;
144 196 : for (item = items; item < items + nitems; item++)
145 196 : if (strcmp (item->name, "pid") == 0)
146 : break;
147 28 : if (item == items + nitems)
148 0 : continue;
149 28 : uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
150 56 : val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
151 36 : ? be32toh (val32) : le32toh (val32));
152 28 : pid_t tid = (int32_t) val32;
153 : eu_static_assert (sizeof val32 <= sizeof tid);
154 28 : thread_arg->note_offset = offset;
155 28 : return tid;
156 : }
157 :
158 20 : free (thread_arg);
159 20 : return 0;
160 : }
161 :
162 : static bool
163 28 : core_set_initial_registers (Dwfl_Thread *thread, void *thread_arg_voidp)
164 : {
165 28 : struct thread_arg *thread_arg = thread_arg_voidp;
166 28 : struct core_arg *core_arg = thread_arg->core_arg;
167 28 : Elf *core = core_arg->core;
168 28 : size_t offset = thread_arg->note_offset;
169 : GElf_Nhdr nhdr;
170 : size_t name_offset;
171 : size_t desc_offset;
172 28 : Elf_Data *note_data = core_arg->note_data;
173 28 : size_t nregs = ebl_frame_nregs (core_arg->ebl);
174 28 : assert (nregs > 0);
175 28 : assert (offset < note_data->d_size);
176 28 : size_t getnote_err = gelf_getnote (note_data, offset, &nhdr, &name_offset,
177 : &desc_offset);
178 : /* __libdwfl_attach_state_for_core already verified the note is there. */
179 28 : assert (getnote_err != 0);
180 : /* Do not check NAME for now, help broken Linux kernels. */
181 28 : const char *name = note_data->d_buf + name_offset;
182 28 : const char *desc = note_data->d_buf + desc_offset;
183 : GElf_Word regs_offset;
184 : size_t nregloc;
185 : const Ebl_Register_Location *reglocs;
186 : size_t nitems;
187 : const Ebl_Core_Item *items;
188 28 : int core_note_err = ebl_core_note (core_arg->ebl, &nhdr, name, ®s_offset,
189 : &nregloc, ®locs, &nitems, &items);
190 : /* __libdwfl_attach_state_for_core already verified the note is there. */
191 28 : assert (core_note_err != 0);
192 28 : assert (nhdr.n_type == NT_PRSTATUS);
193 : const Ebl_Core_Item *item;
194 196 : for (item = items; item < items + nitems; item++)
195 196 : if (strcmp (item->name, "pid") == 0)
196 : break;
197 28 : assert (item < items + nitems);
198 : pid_t tid;
199 : {
200 28 : uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
201 56 : val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
202 36 : ? be32toh (val32) : le32toh (val32));
203 28 : tid = (int32_t) val32;
204 : eu_static_assert (sizeof val32 <= sizeof tid);
205 : }
206 : /* core_next_thread already found this TID there. */
207 28 : assert (tid == INTUSE(dwfl_thread_tid) (thread));
208 464 : for (item = items; item < items + nitems; item++)
209 442 : if (item->pc_register)
210 : break;
211 28 : if (item < items + nitems)
212 : {
213 : Dwarf_Word pc;
214 6 : switch (gelf_getclass (core) == ELFCLASS32 ? 32 : 64)
215 : {
216 : case 32:;
217 2 : uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
218 4 : val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
219 4 : ? be32toh (val32) : le32toh (val32));
220 : /* Do a host width conversion. */
221 2 : pc = val32;
222 2 : break;
223 : case 64:;
224 4 : uint64_t val64 = read_8ubyte_unaligned_noncvt (desc + item->offset);
225 4 : val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
226 6 : ? be64toh (val64) : le64toh (val64));
227 : pc = val64;
228 : break;
229 : default:
230 0 : abort ();
231 : }
232 6 : INTUSE(dwfl_thread_state_register_pc) (thread, pc);
233 : }
234 28 : desc += regs_offset;
235 468 : for (size_t regloci = 0; regloci < nregloc; regloci++)
236 : {
237 440 : const Ebl_Register_Location *regloc = reglocs + regloci;
238 : // Iterate even regs out of NREGS range so that we can find pc_register.
239 440 : if (regloc->bits != 32 && regloc->bits != 64)
240 92 : continue;
241 348 : const char *reg_desc = desc + regloc->offset;
242 1388 : for (unsigned regno = regloc->regno;
243 1040 : regno < regloc->regno + (regloc->count ?: 1U);
244 692 : regno++)
245 : {
246 : /* PPC provides DWARF register 65 irrelevant for
247 : CFI which clashes with register 108 (LR) we need.
248 : LR (108) is provided earlier (in NT_PRSTATUS) than the # 65.
249 : FIXME: It depends now on their order in core notes.
250 : FIXME: It uses private function. */
251 692 : if (regno < nregs
252 570 : && __libdwfl_frame_reg_get (thread->unwound, regno, NULL))
253 0 : continue;
254 : Dwarf_Word val;
255 692 : switch (regloc->bits)
256 : {
257 : case 32:;
258 200 : uint32_t val32 = read_4ubyte_unaligned_noncvt (reg_desc);
259 200 : reg_desc += sizeof val32;
260 400 : val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
261 380 : ? be32toh (val32) : le32toh (val32));
262 : /* Do a host width conversion. */
263 200 : val = val32;
264 200 : break;
265 : case 64:;
266 492 : uint64_t val64 = read_8ubyte_unaligned_noncvt (reg_desc);
267 492 : reg_desc += sizeof val64;
268 984 : val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
269 600 : ? be64toh (val64) : le64toh (val64));
270 : assert (sizeof (*thread->unwound->regs) == sizeof val64);
271 492 : val = val64;
272 492 : break;
273 : default:
274 0 : abort ();
275 : }
276 : /* Registers not valid for CFI are just ignored. */
277 692 : if (regno < nregs)
278 570 : INTUSE(dwfl_thread_state_registers) (thread, regno, 1, &val);
279 692 : if (regloc->pc_register)
280 4 : INTUSE(dwfl_thread_state_register_pc) (thread, val);
281 692 : reg_desc += regloc->pad;
282 : }
283 : }
284 28 : return true;
285 : }
286 :
287 : static void
288 30 : core_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
289 : {
290 30 : struct core_arg *core_arg = dwfl_arg;
291 30 : ebl_closebackend (core_arg->ebl);
292 30 : free (core_arg);
293 30 : }
294 :
295 : static const Dwfl_Thread_Callbacks core_thread_callbacks =
296 : {
297 : core_next_thread,
298 : NULL, /* get_thread */
299 : core_memory_read,
300 : core_set_initial_registers,
301 : core_detach,
302 : NULL, /* core_thread_detach */
303 : };
304 :
305 : int
306 30 : dwfl_core_file_attach (Dwfl *dwfl, Elf *core)
307 : {
308 30 : Dwfl_Error err = DWFL_E_NOERROR;
309 30 : Ebl *ebl = ebl_openbackend (core);
310 30 : if (ebl == NULL)
311 : {
312 : err = DWFL_E_LIBEBL;
313 : fail_err:
314 0 : if (dwfl->process == NULL && dwfl->attacherr == DWFL_E_NOERROR)
315 0 : dwfl->attacherr = __libdwfl_canon_error (err);
316 0 : __libdwfl_seterrno (err);
317 0 : return -1;
318 : }
319 30 : size_t nregs = ebl_frame_nregs (ebl);
320 30 : if (nregs == 0)
321 : {
322 : err = DWFL_E_NO_UNWIND;
323 : fail:
324 0 : ebl_closebackend (ebl);
325 0 : goto fail_err;
326 : }
327 30 : GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (core, &ehdr_mem);
328 30 : if (ehdr == NULL)
329 : {
330 : err = DWFL_E_LIBELF;
331 : goto fail;
332 : }
333 30 : if (ehdr->e_type != ET_CORE)
334 : {
335 : err = DWFL_E_NO_CORE_FILE;
336 : goto fail;
337 : }
338 : size_t phnum;
339 30 : if (elf_getphdrnum (core, &phnum) < 0)
340 : {
341 : err = DWFL_E_LIBELF;
342 : goto fail;
343 : }
344 : pid_t pid = -1;
345 : Elf_Data *note_data = NULL;
346 0 : for (size_t cnt = 0; cnt < phnum; ++cnt)
347 : {
348 30 : GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
349 30 : if (phdr != NULL && phdr->p_type == PT_NOTE)
350 : {
351 30 : note_data = elf_getdata_rawchunk (core, phdr->p_offset,
352 : phdr->p_filesz, ELF_T_NHDR);
353 30 : break;
354 : }
355 : }
356 30 : if (note_data == NULL)
357 : {
358 : err = DWFL_E_LIBELF;
359 : goto fail;
360 : }
361 : size_t offset = 0;
362 : GElf_Nhdr nhdr;
363 : size_t name_offset;
364 : size_t desc_offset;
365 60 : while (offset < note_data->d_size
366 60 : && (offset = gelf_getnote (note_data, offset,
367 : &nhdr, &name_offset, &desc_offset)) > 0)
368 : {
369 : /* Do not check NAME for now, help broken Linux kernels. */
370 60 : const char *name = note_data->d_buf + name_offset;
371 60 : const char *desc = note_data->d_buf + desc_offset;
372 : GElf_Word regs_offset;
373 : size_t nregloc;
374 : const Ebl_Register_Location *reglocs;
375 : size_t nitems;
376 : const Ebl_Core_Item *items;
377 60 : if (! ebl_core_note (ebl, &nhdr, name,
378 : ®s_offset, &nregloc, ®locs, &nitems, &items))
379 : {
380 : /* This note may be just not recognized, skip it. */
381 30 : continue;
382 : }
383 60 : if (nhdr.n_type != NT_PRPSINFO)
384 30 : continue;
385 : const Ebl_Core_Item *item;
386 240 : for (item = items; item < items + nitems; item++)
387 240 : if (strcmp (item->name, "pid") == 0)
388 : break;
389 30 : if (item == items + nitems)
390 0 : continue;
391 30 : uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
392 60 : val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
393 37 : ? be32toh (val32) : le32toh (val32));
394 30 : pid = (int32_t) val32;
395 : eu_static_assert (sizeof val32 <= sizeof pid);
396 30 : break;
397 : }
398 30 : if (pid == -1)
399 : {
400 : /* No valid NT_PRPSINFO recognized in this CORE. */
401 : err = DWFL_E_BADELF;
402 : goto fail;
403 : }
404 30 : struct core_arg *core_arg = malloc (sizeof *core_arg);
405 30 : if (core_arg == NULL)
406 : {
407 : err = DWFL_E_NOMEM;
408 : goto fail;
409 : }
410 30 : core_arg->core = core;
411 30 : core_arg->note_data = note_data;
412 30 : core_arg->thread_note_offset = 0;
413 30 : core_arg->ebl = ebl;
414 30 : if (! INTUSE(dwfl_attach_state) (dwfl, core, pid, &core_thread_callbacks,
415 : core_arg))
416 : {
417 0 : free (core_arg);
418 0 : ebl_closebackend (ebl);
419 0 : return -1;
420 : }
421 : return pid;
422 : }
423 : INTDEF (dwfl_core_file_attach)
|