blob: 9f05f72a0695e834191eb66beaec86e089d96118 [file] [log] [blame]
Brian Silverman86497922018-02-10 19:28:39 -05001/* 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
39struct core_arg
40{
41 Elf *core;
42 Elf_Data *note_data;
43 size_t thread_note_offset;
44 Ebl *ebl;
45};
46
47struct thread_arg
48{
49 struct core_arg *core_arg;
50 size_t note_offset;
51};
52
53static bool
54core_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
98static pid_t
99core_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,
141 &regs_offset, &nregloc, &reglocs, &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
167static bool
168core_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, &regs_offset,
195 &nregloc, &reglocs, &nitems, &items);
196 /* __libdwfl_attach_state_for_core already verified the note is there. */
197 assert (core_note_err != 0);
198 assert (nhdr.n_type == NT_PRSTATUS);
199 const Ebl_Core_Item *item;
200 for (item = items; item < items + nitems; item++)
201 if (strcmp (item->name, "pid") == 0)
202 break;
203 assert (item < items + nitems);
204 pid_t tid;
205 {
206 uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
207 val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
208 ? be32toh (val32) : le32toh (val32));
209 tid = (int32_t) val32;
210 eu_static_assert (sizeof val32 <= sizeof tid);
211 }
212 /* core_next_thread already found this TID there. */
213 assert (tid == INTUSE(dwfl_thread_tid) (thread));
214 for (item = items; item < items + nitems; item++)
215 if (item->pc_register)
216 break;
217 if (item < items + nitems)
218 {
219 Dwarf_Word pc;
220 switch (gelf_getclass (core) == ELFCLASS32 ? 32 : 64)
221 {
222 case 32:;
223 uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
224 val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
225 ? be32toh (val32) : le32toh (val32));
226 /* Do a host width conversion. */
227 pc = val32;
228 break;
229 case 64:;
230 uint64_t val64 = read_8ubyte_unaligned_noncvt (desc + item->offset);
231 val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
232 ? be64toh (val64) : le64toh (val64));
233 pc = val64;
234 break;
235 default:
236 abort ();
237 }
238 INTUSE(dwfl_thread_state_register_pc) (thread, pc);
239 }
240 desc += regs_offset;
241 for (size_t regloci = 0; regloci < nregloc; regloci++)
242 {
243 const Ebl_Register_Location *regloc = reglocs + regloci;
244 // Iterate even regs out of NREGS range so that we can find pc_register.
245 if (regloc->bits != 32 && regloc->bits != 64)
246 continue;
247 const char *reg_desc = desc + regloc->offset;
248 for (unsigned regno = regloc->regno;
249 regno < regloc->regno + (regloc->count ?: 1U);
250 regno++)
251 {
252 /* PPC provides DWARF register 65 irrelevant for
253 CFI which clashes with register 108 (LR) we need.
254 LR (108) is provided earlier (in NT_PRSTATUS) than the # 65.
255 FIXME: It depends now on their order in core notes.
256 FIXME: It uses private function. */
257 if (regno < nregs
258 && __libdwfl_frame_reg_get (thread->unwound, regno, NULL))
259 continue;
260 Dwarf_Word val;
261 switch (regloc->bits)
262 {
263 case 32:;
264 uint32_t val32 = read_4ubyte_unaligned_noncvt (reg_desc);
265 reg_desc += sizeof val32;
266 val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
267 ? be32toh (val32) : le32toh (val32));
268 /* Do a host width conversion. */
269 val = val32;
270 break;
271 case 64:;
272 uint64_t val64 = read_8ubyte_unaligned_noncvt (reg_desc);
273 reg_desc += sizeof val64;
274 val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
275 ? be64toh (val64) : le64toh (val64));
276 assert (sizeof (*thread->unwound->regs) == sizeof val64);
277 val = val64;
278 break;
279 default:
280 abort ();
281 }
282 /* Registers not valid for CFI are just ignored. */
283 if (regno < nregs)
284 INTUSE(dwfl_thread_state_registers) (thread, regno, 1, &val);
285 if (regloc->pc_register)
286 INTUSE(dwfl_thread_state_register_pc) (thread, val);
287 reg_desc += regloc->pad;
288 }
289 }
290 return true;
291}
292
293static void
294core_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
295{
296 struct core_arg *core_arg = dwfl_arg;
297 ebl_closebackend (core_arg->ebl);
298 free (core_arg);
299}
300
301static const Dwfl_Thread_Callbacks core_thread_callbacks =
302{
303 core_next_thread,
304 NULL, /* get_thread */
305 core_memory_read,
306 core_set_initial_registers,
307 core_detach,
308 NULL, /* core_thread_detach */
309};
310
311int
312dwfl_core_file_attach (Dwfl *dwfl, Elf *core)
313{
314 Dwfl_Error err = DWFL_E_NOERROR;
315 Ebl *ebl = ebl_openbackend (core);
316 if (ebl == NULL)
317 {
318 err = DWFL_E_LIBEBL;
319 fail_err:
320 if (dwfl->process == NULL && dwfl->attacherr == DWFL_E_NOERROR)
321 dwfl->attacherr = __libdwfl_canon_error (err);
322 __libdwfl_seterrno (err);
323 return -1;
324 }
325 size_t nregs = ebl_frame_nregs (ebl);
326 if (nregs == 0)
327 {
328 err = DWFL_E_NO_UNWIND;
329 fail:
330 ebl_closebackend (ebl);
331 goto fail_err;
332 }
333 GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (core, &ehdr_mem);
334 if (ehdr == NULL)
335 {
336 err = DWFL_E_LIBELF;
337 goto fail;
338 }
339 if (ehdr->e_type != ET_CORE)
340 {
341 err = DWFL_E_NO_CORE_FILE;
342 goto fail;
343 }
344 size_t phnum;
345 if (elf_getphdrnum (core, &phnum) < 0)
346 {
347 err = DWFL_E_LIBELF;
348 goto fail;
349 }
350 pid_t pid = -1;
351 Elf_Data *note_data = NULL;
352 for (size_t cnt = 0; cnt < phnum; ++cnt)
353 {
354 GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
355 if (phdr != NULL && phdr->p_type == PT_NOTE)
356 {
357 note_data = elf_getdata_rawchunk (core, phdr->p_offset,
358 phdr->p_filesz, ELF_T_NHDR);
359 break;
360 }
361 }
362 if (note_data == NULL)
363 {
364 err = DWFL_E_LIBELF;
365 goto fail;
366 }
367 size_t offset = 0;
368 GElf_Nhdr nhdr;
369 size_t name_offset;
370 size_t desc_offset;
371 while (offset < note_data->d_size
372 && (offset = gelf_getnote (note_data, offset,
373 &nhdr, &name_offset, &desc_offset)) > 0)
374 {
375 /* Do not check NAME for now, help broken Linux kernels. */
376 const char *name = (nhdr.n_namesz == 0
377 ? "" : note_data->d_buf + name_offset);
378 const char *desc = note_data->d_buf + desc_offset;
379 GElf_Word regs_offset;
380 size_t nregloc;
381 const Ebl_Register_Location *reglocs;
382 size_t nitems;
383 const Ebl_Core_Item *items;
384 if (! ebl_core_note (ebl, &nhdr, name,
385 &regs_offset, &nregloc, &reglocs, &nitems, &items))
386 {
387 /* This note may be just not recognized, skip it. */
388 continue;
389 }
390 if (nhdr.n_type != NT_PRPSINFO)
391 continue;
392 const Ebl_Core_Item *item;
393 for (item = items; item < items + nitems; item++)
394 if (strcmp (item->name, "pid") == 0)
395 break;
396 if (item == items + nitems)
397 continue;
398 uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
399 val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
400 ? be32toh (val32) : le32toh (val32));
401 pid = (int32_t) val32;
402 eu_static_assert (sizeof val32 <= sizeof pid);
403 break;
404 }
405 if (pid == -1)
406 {
407 /* No valid NT_PRPSINFO recognized in this CORE. */
408 err = DWFL_E_BADELF;
409 goto fail;
410 }
411 struct core_arg *core_arg = malloc (sizeof *core_arg);
412 if (core_arg == NULL)
413 {
414 err = DWFL_E_NOMEM;
415 goto fail;
416 }
417 core_arg->core = core;
418 core_arg->note_data = note_data;
419 core_arg->thread_note_offset = 0;
420 core_arg->ebl = ebl;
421 if (! INTUSE(dwfl_attach_state) (dwfl, core, pid, &core_thread_callbacks,
422 core_arg))
423 {
424 free (core_arg);
425 ebl_closebackend (ebl);
426 return -1;
427 }
428 return pid;
429}
430INTDEF (dwfl_core_file_attach)