Branch data Line data Source code
1 : : /* Report modules by examining dynamic linker data structures.
2 : : Copyright (C) 2008-2016 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 <config.h>
30 : : #include "libdwflP.h"
31 : : #include "../libdw/memory-access.h"
32 : : #include "system.h"
33 : :
34 : : #include <byteswap.h>
35 : : #include <endian.h>
36 : : #include <fcntl.h>
37 : :
38 : : /* This element is always provided and always has a constant value.
39 : : This makes it an easy thing to scan for to discern the format. */
40 : : #define PROBE_TYPE AT_PHENT
41 : : #define PROBE_VAL32 sizeof (Elf32_Phdr)
42 : : #define PROBE_VAL64 sizeof (Elf64_Phdr)
43 : :
44 : :
45 : : static inline bool
46 : 214 : do_check64 (const char *a64, uint_fast8_t *elfdata)
47 : : {
48 : : /* The AUXV pointer might not even be naturally aligned for 64-bit
49 : : data, because note payloads in a core file are not aligned. */
50 : 214 : const char *typep = a64 + offsetof (Elf64_auxv_t, a_type);
51 [ + + ]: 214 : uint64_t type = read_8ubyte_unaligned_noncvt (typep);
52 : 214 : const char *valp = a64 + offsetof (Elf64_auxv_t, a_un.a_val);
53 : 214 : uint64_t val = read_8ubyte_unaligned_noncvt (valp);
54 : :
55 [ + + ]: 214 : if (type == BE64 (PROBE_TYPE)
56 [ + - ]: 4 : && val == BE64 (PROBE_VAL64))
57 : : {
58 : 4 : *elfdata = ELFDATA2MSB;
59 : 4 : return true;
60 : : }
61 : :
62 : 210 : if (type == LE64 (PROBE_TYPE)
63 [ + + ]: 210 : && val == LE64 (PROBE_VAL64))
64 : : {
65 : 24 : *elfdata = ELFDATA2LSB;
66 : 24 : return true;
67 : : }
68 : :
69 : : return false;
70 : : }
71 : :
72 : : static inline bool
73 : 367 : do_check32 (const char *a32, uint_fast8_t *elfdata)
74 : : {
75 : : /* The AUXV pointer might not even be naturally aligned for 32-bit
76 : : data, because note payloads in a core file are not aligned. */
77 : 367 : const char *typep = a32 + offsetof (Elf32_auxv_t, a_type);
78 [ + + ]: 367 : uint32_t type = read_4ubyte_unaligned_noncvt (typep);
79 : 367 : const char *valp = a32 + offsetof (Elf32_auxv_t, a_un.a_val);
80 : 367 : uint32_t val = read_4ubyte_unaligned_noncvt (valp);
81 : :
82 [ + + ]: 367 : if (type == BE32 (PROBE_TYPE)
83 [ + - ]: 3 : && val == BE32 (PROBE_VAL32))
84 : : {
85 : 3 : *elfdata = ELFDATA2MSB;
86 : 3 : return true;
87 : : }
88 : :
89 : 364 : if (type == LE32 (PROBE_TYPE)
90 [ + + ]: 364 : && val == LE32 (PROBE_VAL32))
91 : : {
92 : 4 : *elfdata = ELFDATA2LSB;
93 : 4 : return true;
94 : : }
95 : :
96 : : return false;
97 : : }
98 : :
99 : : /* Examine an auxv data block and determine its format.
100 : : Return true iff we figured it out. */
101 : : static bool
102 : 35 : auxv_format_probe (const void *auxv, size_t size,
103 : : uint_fast8_t *elfclass, uint_fast8_t *elfdata)
104 : : {
105 [ + - ]: 214 : for (size_t i = 0; i < size / sizeof (Elf64_auxv_t); ++i)
106 : : {
107 [ + + ]: 214 : if (do_check64 (auxv + i * sizeof (Elf64_auxv_t), elfdata))
108 : : {
109 : 28 : *elfclass = ELFCLASS64;
110 : 28 : return true;
111 : : }
112 : :
113 [ + + ]: 186 : if (do_check32 (auxv + (i * 2) * sizeof (Elf32_auxv_t), elfdata)
114 [ + + ]: 181 : || do_check32 (auxv + (i * 2 + 1) * sizeof (Elf32_auxv_t), elfdata))
115 : : {
116 : 7 : *elfclass = ELFCLASS32;
117 : 7 : return true;
118 : : }
119 : : }
120 : :
121 : : return false;
122 : : }
123 : :
124 : : /* This is a Dwfl_Memory_Callback that wraps another memory callback.
125 : : If the underlying callback cannot fill the data, then this will
126 : : fall back to fetching data from module files. */
127 : :
128 : : struct integrated_memory_callback
129 : : {
130 : : Dwfl_Memory_Callback *memory_callback;
131 : : void *memory_callback_arg;
132 : : void *buffer;
133 : : };
134 : :
135 : : static bool
136 : 450 : integrated_memory_callback (Dwfl *dwfl, int ndx,
137 : : void **buffer, size_t *buffer_available,
138 : : GElf_Addr vaddr,
139 : : size_t minread,
140 : : void *arg)
141 : : {
142 : 450 : struct integrated_memory_callback *info = arg;
143 : :
144 [ + + ]: 450 : if (ndx == -1)
145 : : {
146 : : /* Called for cleanup. */
147 [ + - ]: 205 : if (info->buffer != NULL)
148 : : {
149 : : /* The last probe buffer came from the underlying callback.
150 : : Let it do its cleanup. */
151 [ - + ]: 205 : assert (*buffer == info->buffer); /* XXX */
152 : 205 : *buffer = info->buffer;
153 : 205 : info->buffer = NULL;
154 : 205 : return (*info->memory_callback) (dwfl, ndx, buffer, buffer_available,
155 : : vaddr, minread,
156 : : info->memory_callback_arg);
157 : : }
158 : 0 : *buffer = NULL;
159 : 0 : *buffer_available = 0;
160 : 0 : return false;
161 : : }
162 : :
163 [ - + ]: 245 : if (*buffer != NULL)
164 : : /* For a final-read request, we only use the underlying callback. */
165 : 0 : return (*info->memory_callback) (dwfl, ndx, buffer, buffer_available,
166 : : vaddr, minread, info->memory_callback_arg);
167 : :
168 : : /* Let the underlying callback try to fill this request. */
169 [ + + ]: 245 : if ((*info->memory_callback) (dwfl, ndx, &info->buffer, buffer_available,
170 : : vaddr, minread, info->memory_callback_arg))
171 : : {
172 : 205 : *buffer = info->buffer;
173 : 205 : return true;
174 : : }
175 : :
176 : : /* Now look for module text covering this address. */
177 : :
178 : 40 : Dwfl_Module *mod;
179 : 40 : (void) INTUSE(dwfl_addrsegment) (dwfl, vaddr, &mod);
180 [ - + ]: 40 : if (mod == NULL)
181 : : return false;
182 : :
183 : 0 : Dwarf_Addr bias;
184 : 0 : Elf_Scn *scn = INTUSE(dwfl_module_address_section) (mod, &vaddr, &bias);
185 [ # # ]: 0 : if (unlikely (scn == NULL))
186 : : {
187 : : #if 0 // XXX would have to handle ndx=-1 cleanup calls passed down.
188 : : /* If we have no sections we can try to fill it from the module file
189 : : based on its phdr mappings. */
190 : : if (likely (mod->e_type != ET_REL) && mod->main.elf != NULL)
191 : : return INTUSE(dwfl_elf_phdr_memory_callback)
192 : : (dwfl, 0, buffer, buffer_available,
193 : : vaddr - mod->main.bias, minread, mod->main.elf);
194 : : #endif
195 : : return false;
196 : : }
197 : :
198 : 0 : Elf_Data *data = elf_rawdata (scn, NULL);
199 [ # # ]: 0 : if (unlikely (data == NULL))
200 : : // XXX throw error?
201 : : return false;
202 : :
203 [ # # ]: 0 : if (unlikely (data->d_size < vaddr))
204 : : return false;
205 : :
206 : : /* Provide as much data as we have. */
207 : 0 : void *contents = data->d_buf + vaddr;
208 : 0 : size_t avail = data->d_size - vaddr;
209 [ # # ]: 0 : if (unlikely (avail < minread))
210 : : return false;
211 : :
212 : : /* If probing for a string, make sure it's terminated. */
213 [ # # # # ]: 0 : if (minread == 0 && unlikely (memchr (contents, '\0', avail) == NULL))
214 : : return false;
215 : :
216 : : /* We have it! */
217 : 0 : *buffer = contents;
218 : 0 : *buffer_available = avail;
219 : 0 : return true;
220 : : }
221 : :
222 : : static size_t
223 : 158 : addrsize (uint_fast8_t elfclass)
224 : : {
225 : 158 : return elfclass * 4;
226 : : }
227 : :
228 : : struct memory_closure
229 : : {
230 : : Dwfl *dwfl;
231 : : Dwfl_Memory_Callback *callback;
232 : : void *arg;
233 : : };
234 : :
235 : : static inline int
236 : 286 : release_buffer (struct memory_closure *closure,
237 : : void **buffer, size_t *buffer_available, int result)
238 : : {
239 [ + + ]: 286 : if (*buffer != NULL)
240 : 205 : (*closure->callback) (closure->dwfl, -1, buffer, buffer_available, 0, 0,
241 : : closure->arg);
242 : :
243 : 286 : return result;
244 : : }
245 : :
246 : : static inline bool
247 : 136 : read_addrs (struct memory_closure *closure,
248 : : uint_fast8_t elfclass, uint_fast8_t elfdata,
249 : : void **buffer, size_t *buffer_available,
250 : : GElf_Addr vaddr, GElf_Addr *read_vaddr,
251 : : size_t n, GElf_Addr *addrs /* [4] */)
252 : : {
253 : 136 : size_t nb = n * addrsize (elfclass); /* Address words -> bytes to read. */
254 : 136 : Dwfl *dwfl = closure->dwfl;
255 : :
256 : : /* Read a new buffer if the old one doesn't cover these words. */
257 [ + - ]: 136 : if (buffer == NULL
258 [ + + ]: 136 : || vaddr < *read_vaddr
259 [ + + ]: 95 : || vaddr - (*read_vaddr) + nb > *buffer_available)
260 : : {
261 : 116 : release_buffer (closure, buffer, buffer_available, 0);
262 : :
263 : 116 : *read_vaddr = vaddr;
264 : 116 : int segndx = INTUSE(dwfl_addrsegment) (dwfl, vaddr, NULL);
265 [ + - ]: 116 : if (unlikely (segndx < 0)
266 [ - + ]: 116 : || unlikely (! (*closure->callback) (dwfl, segndx,
267 : : buffer, buffer_available,
268 : : vaddr, nb, closure->arg)))
269 : 0 : return true;
270 : : }
271 : :
272 : 136 : Elf32_Addr (*a32)[n] = vaddr - (*read_vaddr) + (*buffer);
273 : 136 : Elf64_Addr (*a64)[n] = (void *) a32;
274 : :
275 [ + + ]: 136 : if (elfclass == ELFCLASS32)
276 : : {
277 [ + + ]: 14 : if (elfdata == ELFDATA2MSB)
278 [ + + ]: 32 : for (size_t i = 0; i < n; ++i)
279 : 25 : addrs[i] = BE32 (read_4ubyte_unaligned_noncvt (&(*a32)[i]));
280 : : else
281 [ + + ]: 32 : for (size_t i = 0; i < n; ++i)
282 : 25 : addrs[i] = LE32 (read_4ubyte_unaligned_noncvt (&(*a32)[i]));
283 : : }
284 : : else
285 : : {
286 [ + + ]: 122 : if (elfdata == ELFDATA2MSB)
287 [ + + ]: 24 : for (size_t i = 0; i < n; ++i)
288 : 18 : addrs[i] = BE64 (read_8ubyte_unaligned_noncvt (&(*a64)[i]));
289 : : else
290 [ + + ]: 526 : for (size_t i = 0; i < n; ++i)
291 : 410 : addrs[i] = LE64 (read_8ubyte_unaligned_noncvt (&(*a64)[i]));
292 : : }
293 : :
294 : : return false;
295 : : }
296 : :
297 : : /* Report a module for each struct link_map in the linked list at r_map
298 : : in the struct r_debug at R_DEBUG_VADDR. For r_debug_info description
299 : : see dwfl_link_map_report in libdwflP.h. If R_DEBUG_INFO is not NULL then no
300 : : modules get added to DWFL, caller has to add them from filled in
301 : : R_DEBUG_INFO.
302 : :
303 : : For each link_map entry, if an existing module resides at its address,
304 : : this just modifies that module's name and suggested file name. If
305 : : no such module exists, this calls dwfl_report_elf on the l_name string.
306 : :
307 : : Returns the number of modules found, or -1 for errors. */
308 : :
309 : : static int
310 : 22 : report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata,
311 : : Dwfl *dwfl, GElf_Addr r_debug_vaddr,
312 : : Dwfl_Memory_Callback *memory_callback,
313 : : void *memory_callback_arg,
314 : : struct r_debug_info *r_debug_info)
315 : : {
316 : : /* Skip r_version, to aligned r_map field. */
317 : 22 : GElf_Addr read_vaddr = r_debug_vaddr + addrsize (elfclass);
318 : :
319 : 22 : void *buffer = NULL;
320 : 22 : size_t buffer_available = 0;
321 : 22 : GElf_Addr addrs[4];
322 : 22 : struct memory_closure memory_closure = { dwfl, memory_callback,
323 : : memory_callback_arg };
324 [ - + ]: 22 : if (unlikely (read_addrs (&memory_closure, elfclass, elfdata,
325 : : &buffer, &buffer_available, read_vaddr, &read_vaddr,
326 : : 1, addrs)))
327 : 0 : return release_buffer (&memory_closure, &buffer, &buffer_available, -1);
328 : :
329 : 22 : GElf_Addr next = addrs[0];
330 : :
331 : 22 : Dwfl_Module **lastmodp = &dwfl->modulelist;
332 : 22 : int result = 0;
333 : :
334 : : /* There can't be more elements in the link_map list than there are
335 : : segments. DWFL->lookup_elts is probably twice that number, so it
336 : : is certainly above the upper bound. If we iterate too many times,
337 : : there must be a loop in the pointers due to link_map clobberation. */
338 : 22 : size_t iterations = 0;
339 [ + + + - ]: 136 : while (next != 0 && ++iterations < dwfl->lookup_elts)
340 : : {
341 [ - + ]: 114 : if (read_addrs (&memory_closure, elfclass, elfdata,
342 : : &buffer, &buffer_available, next, &read_vaddr,
343 : : 4, addrs))
344 : 0 : return release_buffer (&memory_closure, &buffer, &buffer_available, -1);
345 : :
346 : : /* Unused: l_addr is the difference between the address in memory
347 : : and the ELF file when the core was created. We need to
348 : : recalculate the difference below because the ELF file we use
349 : : might be differently pre-linked. */
350 : : // GElf_Addr l_addr = addrs[0];
351 : 114 : GElf_Addr l_name = addrs[1];
352 : 114 : GElf_Addr l_ld = addrs[2];
353 : 114 : next = addrs[3];
354 : :
355 : : /* If a clobbered or truncated memory image has no useful pointer,
356 : : just skip this element. */
357 [ + + ]: 114 : if (l_ld == 0)
358 : 1 : continue;
359 : :
360 : : /* Fetch the string at the l_name address. */
361 : 113 : const char *name = NULL;
362 [ + - ]: 113 : if (buffer != NULL
363 [ + + ]: 113 : && read_vaddr <= l_name
364 [ + + ]: 16 : && l_name + 1 - read_vaddr < buffer_available
365 : 3 : && memchr (l_name - read_vaddr + buffer, '\0',
366 [ - + ]: 3 : buffer_available - (l_name - read_vaddr)) != NULL)
367 : : name = l_name - read_vaddr + buffer;
368 : : else
369 : : {
370 : 110 : release_buffer (&memory_closure, &buffer, &buffer_available, 0);
371 : 110 : read_vaddr = l_name;
372 : 110 : int segndx = INTUSE(dwfl_addrsegment) (dwfl, l_name, NULL);
373 [ + - ]: 110 : if (likely (segndx >= 0)
374 [ + + ]: 110 : && (*memory_callback) (dwfl, segndx,
375 : : &buffer, &buffer_available,
376 : : l_name, 0, memory_callback_arg))
377 : 70 : name = buffer;
378 : : }
379 : :
380 [ + - + + ]: 73 : if (name != NULL && name[0] == '\0')
381 : 1 : name = NULL;
382 : :
383 [ + + ]: 113 : if (iterations == 1
384 [ + + ]: 20 : && dwfl->user_core != NULL
385 [ + - ]: 17 : && dwfl->user_core->executable_for_core != NULL)
386 : 17 : name = dwfl->user_core->executable_for_core;
387 : :
388 : 113 : struct r_debug_info_module *r_debug_info_module = NULL;
389 [ + - ]: 113 : if (r_debug_info != NULL)
390 : : {
391 : : /* Save link map information about valid shared library (or
392 : : executable) which has not been found on disk. */
393 [ + + ]: 113 : const char *name1 = name == NULL ? "" : name;
394 : 113 : r_debug_info_module = malloc (sizeof (*r_debug_info_module)
395 : 113 : + strlen (name1) + 1);
396 [ - + ]: 113 : if (unlikely (r_debug_info_module == NULL))
397 : 0 : release_buffer (&memory_closure, &buffer,
398 : : &buffer_available, result);
399 : 113 : r_debug_info_module->fd = -1;
400 : 113 : r_debug_info_module->elf = NULL;
401 : 113 : r_debug_info_module->l_ld = l_ld;
402 : 113 : r_debug_info_module->start = 0;
403 : 113 : r_debug_info_module->end = 0;
404 : 113 : r_debug_info_module->disk_file_has_build_id = false;
405 : 113 : strcpy (r_debug_info_module->name, name1);
406 : 113 : r_debug_info_module->next = r_debug_info->module;
407 : 113 : r_debug_info->module = r_debug_info_module;
408 : : }
409 : :
410 : 113 : Dwfl_Module *mod = NULL;
411 [ + + ]: 113 : if (name != NULL)
412 : : {
413 : : /* This code is mostly inlined dwfl_report_elf. */
414 : : // XXX hook for sysroot
415 : 89 : int fd = open (name, O_RDONLY);
416 [ + + ]: 89 : if (fd >= 0)
417 : : {
418 : 22 : Elf *elf;
419 : 22 : Dwfl_Error error = __libdw_open_file (&fd, &elf, true, false);
420 : 22 : GElf_Addr elf_dynamic_vaddr;
421 [ - + ]: 22 : if (error == DWFL_E_NOERROR
422 [ - + ]: 22 : && __libdwfl_dynamic_vaddr_get (elf, &elf_dynamic_vaddr))
423 : : {
424 : 22 : const void *build_id_bits;
425 : 22 : GElf_Addr build_id_elfaddr;
426 : 22 : int build_id_len;
427 : 22 : bool valid = true;
428 : :
429 [ + + ]: 22 : if (__libdwfl_find_elf_build_id (NULL, elf, &build_id_bits,
430 : : &build_id_elfaddr,
431 : : &build_id_len) > 0
432 [ + - ]: 19 : && build_id_elfaddr != 0)
433 : : {
434 [ + - ]: 19 : if (r_debug_info_module != NULL)
435 : 19 : r_debug_info_module->disk_file_has_build_id = true;
436 : 19 : GElf_Addr build_id_vaddr = (build_id_elfaddr
437 : 19 : - elf_dynamic_vaddr + l_ld);
438 : :
439 : 19 : release_buffer (&memory_closure, &buffer,
440 : : &buffer_available, 0);
441 : 19 : int segndx = INTUSE(dwfl_addrsegment) (dwfl,
442 : : build_id_vaddr,
443 : : NULL);
444 [ + - ]: 19 : if (! (*memory_callback) (dwfl, segndx,
445 : : &buffer, &buffer_available,
446 : : build_id_vaddr, build_id_len,
447 : : memory_callback_arg))
448 : : {
449 : : /* File has valid build-id which cannot be read from
450 : : memory. This happens for core files without bit 4
451 : : (0x10) set in Linux /proc/PID/coredump_filter. */
452 : : }
453 : : else
454 : : {
455 [ - + ]: 19 : if (memcmp (build_id_bits, buffer, build_id_len) != 0)
456 : : /* File has valid build-id which does not match
457 : : the one in memory. */
458 : 0 : valid = false;
459 : 19 : release_buffer (&memory_closure, &buffer,
460 : : &buffer_available, 0);
461 : :
462 : : }
463 : : }
464 : :
465 [ + - ]: 19 : if (valid)
466 : : {
467 : : // It is like l_addr but it handles differently prelinked
468 : : // files at core dumping vs. core loading time.
469 : 22 : GElf_Addr base = l_ld - elf_dynamic_vaddr;
470 [ - + ]: 22 : if (r_debug_info_module == NULL)
471 : : {
472 : : // XXX hook for sysroot
473 : 0 : mod = __libdwfl_report_elf (dwfl, basename (name),
474 : : name, fd, elf, base,
475 : : true, true);
476 [ # # ]: 0 : if (mod != NULL)
477 : : {
478 : 0 : elf = NULL;
479 : 0 : fd = -1;
480 : : }
481 : : }
482 [ + - ]: 22 : else if (__libdwfl_elf_address_range (elf, base, true,
483 : : true, NULL, NULL,
484 : : &r_debug_info_module->start,
485 : : &r_debug_info_module->end,
486 : : NULL, NULL))
487 : : {
488 : 22 : r_debug_info_module->elf = elf;
489 : 22 : r_debug_info_module->fd = fd;
490 : 22 : elf = NULL;
491 : 22 : fd = -1;
492 : : }
493 : : }
494 [ - + ]: 22 : if (elf != NULL)
495 : 0 : elf_end (elf);
496 [ - + ]: 22 : if (fd != -1)
497 : 0 : close (fd);
498 : : }
499 : : }
500 : : }
501 : :
502 [ - + ]: 89 : if (mod != NULL)
503 : : {
504 : 0 : ++result;
505 : :
506 : : /* Move this module to the end of the list, so that we end
507 : : up with a list in the same order as the link_map chain. */
508 [ # # ]: 0 : if (mod->next != NULL)
509 : : {
510 [ # # ]: 0 : if (*lastmodp != mod)
511 : : {
512 : : lastmodp = &dwfl->modulelist;
513 [ # # ]: 0 : while (*lastmodp != mod)
514 : 0 : lastmodp = &(*lastmodp)->next;
515 : : }
516 : 0 : *lastmodp = mod->next;
517 : 0 : mod->next = NULL;
518 [ # # ]: 0 : while (*lastmodp != NULL)
519 : 0 : lastmodp = &(*lastmodp)->next;
520 : 0 : *lastmodp = mod;
521 : : }
522 : :
523 : 0 : lastmodp = &mod->next;
524 : : }
525 : : }
526 : :
527 : 22 : return release_buffer (&memory_closure, &buffer, &buffer_available, result);
528 : : }
529 : :
530 : : static GElf_Addr
531 : 0 : consider_executable (Dwfl_Module *mod, GElf_Addr at_phdr, GElf_Addr at_entry,
532 : : uint_fast8_t *elfclass, uint_fast8_t *elfdata,
533 : : Dwfl_Memory_Callback *memory_callback,
534 : : void *memory_callback_arg)
535 : : {
536 : 0 : GElf_Ehdr ehdr;
537 [ # # ]: 0 : if (unlikely (gelf_getehdr (mod->main.elf, &ehdr) == NULL))
538 : : return 0;
539 : :
540 [ # # ]: 0 : if (at_entry != 0)
541 : : {
542 : : /* If we have an AT_ENTRY value, reject this executable if
543 : : its entry point address could not have supplied that. */
544 : :
545 [ # # ]: 0 : if (ehdr.e_entry == 0)
546 : : return 0;
547 : :
548 [ # # ]: 0 : if (mod->e_type == ET_EXEC)
549 : : {
550 [ # # ]: 0 : if (ehdr.e_entry != at_entry)
551 : : return 0;
552 : : }
553 : : else
554 : : {
555 : : /* It could be a PIE. */
556 : 0 : }
557 : : }
558 : :
559 : : // XXX this could be saved in the file cache: phdr vaddr, DT_DEBUG d_val vaddr
560 : : /* Find the vaddr of the DT_DEBUG's d_ptr. This is the memory
561 : : address where &r_debug was written at runtime. */
562 : 0 : GElf_Xword align = mod->dwfl->segment_align;
563 : 0 : GElf_Addr d_val_vaddr = 0;
564 : 0 : size_t phnum;
565 [ # # ]: 0 : if (elf_getphdrnum (mod->main.elf, &phnum) != 0)
566 : : return 0;
567 : :
568 [ # # ]: 0 : for (size_t i = 0; i < phnum; ++i)
569 : : {
570 : 0 : GElf_Phdr phdr_mem;
571 : 0 : GElf_Phdr *phdr = gelf_getphdr (mod->main.elf, i, &phdr_mem);
572 [ # # ]: 0 : if (phdr == NULL)
573 : : break;
574 : :
575 [ # # # # : 0 : if (phdr->p_align > 1 && (align == 0 || phdr->p_align < align))
# # ]
576 : 0 : align = phdr->p_align;
577 : :
578 [ # # ]: 0 : if (at_phdr != 0
579 [ # # ]: 0 : && phdr->p_type == PT_LOAD
580 [ # # ]: 0 : && (phdr->p_offset & -align) == (ehdr.e_phoff & -align))
581 : : {
582 : : /* This is the segment that would map the phdrs.
583 : : If we have an AT_PHDR value, reject this executable
584 : : if its phdr mapping could not have supplied that. */
585 [ # # ]: 0 : if (mod->e_type == ET_EXEC)
586 : : {
587 [ # # ]: 0 : if (ehdr.e_phoff - phdr->p_offset + phdr->p_vaddr != at_phdr)
588 : 0 : return 0;
589 : : }
590 : : else
591 : : {
592 : : /* It could be a PIE. If the AT_PHDR value and our
593 : : phdr address don't match modulo ALIGN, then this
594 : : could not have been the right PIE. */
595 : 0 : if (((ehdr.e_phoff - phdr->p_offset + phdr->p_vaddr) & -align)
596 [ # # ]: 0 : != (at_phdr & -align))
597 : : return 0;
598 : :
599 : : /* Calculate the bias applied to the PIE's p_vaddr values. */
600 : 0 : GElf_Addr bias = (at_phdr - (ehdr.e_phoff - phdr->p_offset
601 : 0 : + phdr->p_vaddr));
602 : :
603 : : /* Final sanity check: if we have an AT_ENTRY value,
604 : : reject this PIE unless its biased e_entry matches. */
605 [ # # # # ]: 0 : if (at_entry != 0 && at_entry != ehdr.e_entry + bias)
606 : : return 0;
607 : :
608 : : /* If we're changing the module's address range,
609 : : we've just invalidated the module lookup table. */
610 [ # # ]: 0 : GElf_Addr mod_bias = dwfl_adjusted_address (mod, 0);
611 [ # # ]: 0 : if (bias != mod_bias)
612 : : {
613 : 0 : mod->low_addr -= mod_bias;
614 : 0 : mod->high_addr -= mod_bias;
615 : 0 : mod->low_addr += bias;
616 : 0 : mod->high_addr += bias;
617 : :
618 : 0 : free (mod->dwfl->lookup_module);
619 : 0 : mod->dwfl->lookup_module = NULL;
620 : : }
621 : : }
622 : : }
623 : :
624 [ # # ]: 0 : if (phdr->p_type == PT_DYNAMIC)
625 : : {
626 : 0 : Elf_Data *data = elf_getdata_rawchunk (mod->main.elf, phdr->p_offset,
627 : : phdr->p_filesz, ELF_T_DYN);
628 [ # # ]: 0 : if (data == NULL)
629 : 0 : continue;
630 : 0 : const size_t entsize = gelf_fsize (mod->main.elf,
631 : : ELF_T_DYN, 1, EV_CURRENT);
632 : 0 : const size_t n = data->d_size / entsize;
633 [ # # ]: 0 : for (size_t j = 0; j < n; ++j)
634 : : {
635 : 0 : GElf_Dyn dyn_mem;
636 : 0 : GElf_Dyn *dyn = gelf_getdyn (data, j, &dyn_mem);
637 [ # # # # ]: 0 : if (dyn != NULL && dyn->d_tag == DT_DEBUG)
638 : : {
639 : 0 : d_val_vaddr = phdr->p_vaddr + entsize * j + entsize / 2;
640 : 0 : break;
641 : : }
642 : : }
643 : : }
644 : : }
645 : :
646 [ # # ]: 0 : if (d_val_vaddr != 0)
647 : : {
648 : : /* Now we have the final address from which to read &r_debug. */
649 : 0 : d_val_vaddr = dwfl_adjusted_address (mod, d_val_vaddr);
650 : :
651 : 0 : void *buffer = NULL;
652 : 0 : size_t buffer_available = addrsize (ehdr.e_ident[EI_CLASS]);
653 : :
654 : 0 : int segndx = INTUSE(dwfl_addrsegment) (mod->dwfl, d_val_vaddr, NULL);
655 : :
656 [ # # ]: 0 : if ((*memory_callback) (mod->dwfl, segndx,
657 : : &buffer, &buffer_available,
658 : : d_val_vaddr, buffer_available,
659 : : memory_callback_arg))
660 : : {
661 : 0 : const union
662 : : {
663 : : Elf32_Addr a32;
664 : : Elf64_Addr a64;
665 : 0 : } *u = buffer;
666 : :
667 : 0 : GElf_Addr vaddr;
668 [ # # ]: 0 : if (ehdr.e_ident[EI_CLASS] == ELFCLASS32)
669 : 0 : vaddr = (ehdr.e_ident[EI_DATA] == ELFDATA2MSB
670 [ # # ]: 0 : ? BE32 (u->a32) : LE32 (u->a32));
671 : : else
672 : 0 : vaddr = (ehdr.e_ident[EI_DATA] == ELFDATA2MSB
673 [ # # ]: 0 : ? BE64 (u->a64) : LE64 (u->a64));
674 : :
675 : 0 : (*memory_callback) (mod->dwfl, -1, &buffer, &buffer_available, 0, 0,
676 : : memory_callback_arg);
677 : :
678 [ # # ]: 0 : if (*elfclass == ELFCLASSNONE)
679 : 0 : *elfclass = ehdr.e_ident[EI_CLASS];
680 [ # # ]: 0 : else if (*elfclass != ehdr.e_ident[EI_CLASS])
681 : 0 : return 0;
682 : :
683 [ # # ]: 0 : if (*elfdata == ELFDATANONE)
684 : 0 : *elfdata = ehdr.e_ident[EI_DATA];
685 [ # # ]: 0 : else if (*elfdata != ehdr.e_ident[EI_DATA])
686 : : return 0;
687 : :
688 : 0 : return vaddr;
689 : : }
690 : : }
691 : :
692 : : return 0;
693 : : }
694 : :
695 : : /* Try to find an existing executable module with a DT_DEBUG. */
696 : : static GElf_Addr
697 : 0 : find_executable (Dwfl *dwfl, GElf_Addr at_phdr, GElf_Addr at_entry,
698 : : uint_fast8_t *elfclass, uint_fast8_t *elfdata,
699 : : Dwfl_Memory_Callback *memory_callback,
700 : : void *memory_callback_arg)
701 : : {
702 [ # # ]: 0 : for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
703 [ # # ]: 0 : if (mod->main.elf != NULL)
704 : : {
705 : 0 : GElf_Addr r_debug_vaddr = consider_executable (mod, at_phdr, at_entry,
706 : : elfclass, elfdata,
707 : : memory_callback,
708 : : memory_callback_arg);
709 [ # # ]: 0 : if (r_debug_vaddr != 0)
710 : 0 : return r_debug_vaddr;
711 : : }
712 : :
713 : : return 0;
714 : : }
715 : :
716 : :
717 : : int
718 : 35 : dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
719 : : Dwfl_Memory_Callback *memory_callback,
720 : : void *memory_callback_arg,
721 : : struct r_debug_info *r_debug_info)
722 : : {
723 : 35 : GElf_Addr r_debug_vaddr = 0;
724 : :
725 : 35 : uint_fast8_t elfclass = ELFCLASSNONE;
726 : 35 : uint_fast8_t elfdata = ELFDATANONE;
727 [ + - ]: 35 : if (likely (auxv != NULL)
728 [ + - ]: 35 : && likely (auxv_format_probe (auxv, auxv_size, &elfclass, &elfdata)))
729 : : {
730 : 35 : GElf_Addr entry = 0;
731 : 35 : GElf_Addr phdr = 0;
732 : 35 : GElf_Xword phent = 0;
733 : 35 : GElf_Xword phnum = 0;
734 : :
735 : : #define READ_AUXV32(ptr) read_4ubyte_unaligned_noncvt (ptr)
736 : : #define READ_AUXV64(ptr) read_8ubyte_unaligned_noncvt (ptr)
737 : : #define AUXV_SCAN(NN, BL) do \
738 : : { \
739 : : const Elf##NN##_auxv_t *av = auxv; \
740 : : for (size_t i = 0; i < auxv_size / sizeof av[0]; ++i) \
741 : : { \
742 : : const char *typep = auxv + i * sizeof (Elf##NN##_auxv_t); \
743 : : typep += offsetof (Elf##NN##_auxv_t, a_type); \
744 : : uint##NN##_t type = READ_AUXV##NN (typep); \
745 : : const char *valp = auxv + i * sizeof (Elf##NN##_auxv_t); \
746 : : valp += offsetof (Elf##NN##_auxv_t, a_un.a_val); \
747 : : uint##NN##_t val = BL##NN (READ_AUXV##NN (valp)); \
748 : : if (type == BL##NN (AT_ENTRY)) \
749 : : entry = val; \
750 : : else if (type == BL##NN (AT_PHDR)) \
751 : : phdr = val; \
752 : : else if (type == BL##NN (AT_PHNUM)) \
753 : : phnum = val; \
754 : : else if (type == BL##NN (AT_PHENT)) \
755 : : phent = val; \
756 : : else if (type == BL##NN (AT_PAGESZ)) \
757 : : { \
758 : : if (val > 1 \
759 : : && (dwfl->segment_align == 0 \
760 : : || val < dwfl->segment_align)) \
761 : : dwfl->segment_align = val; \
762 : : } \
763 : : } \
764 : : } \
765 : : while (0)
766 : :
767 [ + + ]: 35 : if (elfclass == ELFCLASS32)
768 : : {
769 [ + + ]: 7 : if (elfdata == ELFDATA2MSB)
770 [ + + + + : 73 : AUXV_SCAN (32, BE);
+ + + + +
+ + - + -
- + + + ]
771 : : else
772 [ + + + + : 83 : AUXV_SCAN (32, LE);
+ + + + +
+ + - + -
- + + + ]
773 : : }
774 : : else
775 : : {
776 [ + + ]: 28 : if (elfdata == ELFDATA2MSB)
777 [ + + + + : 91 : AUXV_SCAN (64, BE);
+ + + + +
+ + - + -
- + + + ]
778 : : else
779 [ + + + + : 489 : AUXV_SCAN (64, LE);
+ + + + +
+ + - + -
- + + + ]
780 : : }
781 : :
782 : : /* If we found the phdr dimensions, search phdrs for PT_DYNAMIC. */
783 : 35 : GElf_Addr dyn_vaddr = 0;
784 : 35 : GElf_Xword dyn_filesz = 0;
785 : 35 : GElf_Addr dyn_bias = (GElf_Addr) -1;
786 : :
787 [ + - ]: 35 : if (phdr != 0 && phnum != 0)
788 : : {
789 : 35 : Dwfl_Module *phdr_mod;
790 : 35 : int phdr_segndx = INTUSE(dwfl_addrsegment) (dwfl, phdr, &phdr_mod);
791 : 35 : Elf_Data in =
792 : : {
793 : : .d_type = ELF_T_PHDR,
794 : : .d_version = EV_CURRENT,
795 : 35 : .d_size = phnum * phent,
796 : : .d_buf = NULL
797 : : };
798 : 35 : bool in_ok = (*memory_callback) (dwfl, phdr_segndx, &in.d_buf,
799 : : &in.d_size, phdr, phnum * phent,
800 : : memory_callback_arg);
801 : 35 : bool in_from_exec = false;
802 [ + + ]: 35 : if (! in_ok
803 [ + - ]: 2 : && dwfl->user_core != NULL
804 [ + - ]: 2 : && dwfl->user_core->executable_for_core != NULL)
805 : : {
806 : : /* AUXV -> PHDR -> DYNAMIC
807 : : Both AUXV and DYNAMIC should be always present in a core file.
808 : : PHDR may be missing in core file, try to read it from
809 : : EXECUTABLE_FOR_CORE to find where DYNAMIC is located in the
810 : : core file. */
811 : :
812 : 2 : int fd = open (dwfl->user_core->executable_for_core, O_RDONLY);
813 : 2 : Elf *elf;
814 : 2 : Dwfl_Error error = DWFL_E_ERRNO;
815 [ + - ]: 2 : if (fd != -1)
816 : 2 : error = __libdw_open_file (&fd, &elf, true, false);
817 [ - + ]: 2 : if (error != DWFL_E_NOERROR)
818 : : {
819 : 0 : __libdwfl_seterrno (error);
820 : 0 : return false;
821 : : }
822 : 2 : GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
823 [ - + ]: 2 : if (ehdr == NULL)
824 : : {
825 : 0 : elf_end (elf);
826 : 0 : close (fd);
827 : 0 : __libdwfl_seterrno (DWFL_E_LIBELF);
828 : 0 : return false;
829 : : }
830 : 2 : size_t e_phnum;
831 [ - + ]: 2 : if (elf_getphdrnum (elf, &e_phnum) != 0)
832 : : {
833 : 0 : elf_end (elf);
834 : 0 : close (fd);
835 : 0 : __libdwfl_seterrno (DWFL_E_LIBELF);
836 : 0 : return false;
837 : : }
838 [ + - - + ]: 2 : if (e_phnum != phnum || ehdr->e_phentsize != phent)
839 : : {
840 : 0 : elf_end (elf);
841 : 0 : close (fd);
842 : 0 : __libdwfl_seterrno (DWFL_E_BADELF);
843 : 0 : return false;
844 : : }
845 : 2 : off_t off = ehdr->e_phoff;
846 [ - + ]: 2 : assert (in.d_buf == NULL);
847 : : /* Note this in the !in_ok path. That means memory_callback
848 : : failed. But the callback might still have reset the d_size
849 : : value (to zero). So explicitly set it here again. */
850 : 2 : in.d_size = phnum * phent;
851 : 2 : in.d_buf = malloc (in.d_size);
852 [ - + ]: 2 : if (unlikely (in.d_buf == NULL))
853 : : {
854 : 0 : elf_end (elf);
855 : 0 : close (fd);
856 : 0 : __libdwfl_seterrno (DWFL_E_NOMEM);
857 : 0 : return false;
858 : : }
859 : 2 : ssize_t nread = pread_retry (fd, in.d_buf, in.d_size, off);
860 : 2 : elf_end (elf);
861 : 2 : close (fd);
862 [ - + ]: 2 : if (nread != (ssize_t) in.d_size)
863 : : {
864 : 0 : free (in.d_buf);
865 : 0 : __libdwfl_seterrno (DWFL_E_ERRNO);
866 : 0 : return false;
867 : : }
868 : 2 : in_ok = true;
869 : 2 : in_from_exec = true;
870 : : }
871 [ + - ]: 35 : if (in_ok)
872 : : {
873 [ - + ]: 35 : if (unlikely (phnum > SIZE_MAX / phent))
874 : : {
875 : 0 : __libdwfl_seterrno (DWFL_E_NOMEM);
876 : 0 : return false;
877 : : }
878 : 35 : size_t nbytes = phnum * phent;
879 : 35 : void *buf = malloc (nbytes);
880 : 35 : Elf32_Phdr (*p32)[phnum] = buf;
881 : 35 : Elf64_Phdr (*p64)[phnum] = buf;
882 [ - + ]: 35 : if (unlikely (buf == NULL))
883 : : {
884 : 0 : __libdwfl_seterrno (DWFL_E_NOMEM);
885 : 0 : return false;
886 : : }
887 : 35 : Elf_Data out =
888 : : {
889 : : .d_type = ELF_T_PHDR,
890 : : .d_version = EV_CURRENT,
891 : : .d_size = phnum * phent,
892 : : .d_buf = buf
893 : : };
894 : 35 : in.d_size = out.d_size;
895 [ + + + - ]: 63 : if (likely ((elfclass == ELFCLASS32
896 : : ? elf32_xlatetom : elf64_xlatetom)
897 : : (&out, &in, elfdata) != NULL))
898 : : {
899 : 35 : bool is32 = (elfclass == ELFCLASS32);
900 [ + + ]: 188 : for (size_t i = 0; i < phnum; ++i)
901 : : {
902 : 350 : GElf_Word type = (is32
903 : : ? (*p32)[i].p_type
904 [ + + ]: 175 : : (*p64)[i].p_type);
905 : 350 : GElf_Addr vaddr = (is32
906 : 37 : ? (*p32)[i].p_vaddr
907 [ + + ]: 175 : : (*p64)[i].p_vaddr);
908 : 350 : GElf_Xword filesz = (is32
909 : 37 : ? (*p32)[i].p_filesz
910 [ + + ]: 175 : : (*p64)[i].p_filesz);
911 : :
912 [ + + ]: 175 : if (type == PT_PHDR)
913 : : {
914 [ + - ]: 22 : if (dyn_bias == (GElf_Addr) -1
915 : : /* Do a sanity check on the putative address. */
916 : 22 : && ((vaddr & (dwfl->segment_align - 1))
917 [ + - ]: 22 : == (phdr & (dwfl->segment_align - 1))))
918 : : {
919 : 22 : dyn_bias = phdr - vaddr;
920 [ + - ]: 22 : if (dyn_vaddr != 0)
921 : : break;
922 : : }
923 : :
924 : : }
925 [ + + ]: 153 : else if (type == PT_DYNAMIC)
926 : : {
927 : 22 : dyn_vaddr = vaddr;
928 : 22 : dyn_filesz = filesz;
929 [ - + ]: 22 : if (dyn_bias != (GElf_Addr) -1)
930 : : break;
931 : : }
932 : : }
933 : : }
934 : :
935 [ + + ]: 35 : if (in_from_exec)
936 : 2 : free (in.d_buf);
937 : : else
938 : 33 : (*memory_callback) (dwfl, -1, &in.d_buf, &in.d_size, 0, 0,
939 : : memory_callback_arg);
940 : 35 : free (buf);
941 : : }
942 : : else
943 : : /* We could not read the executable's phdrs from the
944 : : memory image. If we have a presupplied executable,
945 : : we can still use the AT_PHDR and AT_ENTRY values to
946 : : verify it, and to adjust its bias if it's a PIE.
947 : :
948 : : If there was an ET_EXEC module presupplied that contains
949 : : the AT_PHDR address, then we only consider that one.
950 : : We'll either accept it if its phdr location and e_entry
951 : : make sense or reject it if they don't. If there is no
952 : : presupplied ET_EXEC, then look for a presupplied module,
953 : : which might be a PIE (ET_DYN) that needs its bias adjusted. */
954 : 0 : r_debug_vaddr = ((phdr_mod == NULL
955 [ # # ]: 0 : || phdr_mod->main.elf == NULL
956 [ # # ]: 0 : || phdr_mod->e_type != ET_EXEC)
957 : 0 : ? find_executable (dwfl, phdr, entry,
958 : : &elfclass, &elfdata,
959 : : memory_callback,
960 : : memory_callback_arg)
961 [ # # ]: 0 : : consider_executable (phdr_mod, phdr, entry,
962 : : &elfclass, &elfdata,
963 : : memory_callback,
964 : : memory_callback_arg));
965 : : }
966 : :
967 : : /* If we found PT_DYNAMIC, search it for DT_DEBUG. */
968 [ + + ]: 35 : if (dyn_filesz != 0)
969 : : {
970 [ + - ]: 22 : if (dyn_bias != (GElf_Addr) -1)
971 : 22 : dyn_vaddr += dyn_bias;
972 : :
973 : 22 : Elf_Data in =
974 : : {
975 : : .d_type = ELF_T_DYN,
976 : : .d_version = EV_CURRENT,
977 : : .d_size = dyn_filesz,
978 : : .d_buf = NULL
979 : : };
980 : 22 : int dyn_segndx = dwfl_addrsegment (dwfl, dyn_vaddr, NULL);
981 [ + - ]: 22 : if ((*memory_callback) (dwfl, dyn_segndx, &in.d_buf, &in.d_size,
982 : : dyn_vaddr, dyn_filesz, memory_callback_arg))
983 : : {
984 : 22 : void *buf = malloc (dyn_filesz);
985 : 22 : Elf32_Dyn (*d32)[dyn_filesz / sizeof (Elf32_Dyn)] = buf;
986 : 22 : Elf64_Dyn (*d64)[dyn_filesz / sizeof (Elf64_Dyn)] = buf;
987 [ - + ]: 22 : if (unlikely (buf == NULL))
988 : : {
989 : 0 : __libdwfl_seterrno (DWFL_E_NOMEM);
990 : 0 : return false;
991 : : }
992 : 22 : Elf_Data out =
993 : : {
994 : : .d_type = ELF_T_DYN,
995 : : .d_version = EV_CURRENT,
996 : : .d_size = dyn_filesz,
997 : : .d_buf = buf
998 : : };
999 : 22 : in.d_size = out.d_size;
1000 [ + + + - ]: 42 : if (likely ((elfclass == ELFCLASS32
1001 : : ? elf32_xlatetom : elf64_xlatetom)
1002 : : (&out, &in, elfdata) != NULL))
1003 : : {
1004 : : /* We are looking for DT_DEBUG. */
1005 [ + + ]: 22 : if (elfclass == ELFCLASS32)
1006 : : {
1007 : 28 : size_t n = dyn_filesz / sizeof (Elf32_Dyn);
1008 [ + - ]: 28 : for (size_t i = 0; i < n; ++i)
1009 [ + + ]: 28 : if ((*d32)[i].d_tag == DT_DEBUG)
1010 : : {
1011 : 2 : r_debug_vaddr = (*d32)[i].d_un.d_val;
1012 : 2 : break;
1013 : : }
1014 : : }
1015 : : else
1016 : : {
1017 : 272 : size_t n = dyn_filesz / sizeof (Elf64_Dyn);
1018 [ + - ]: 272 : for (size_t i = 0; i < n; ++i)
1019 [ + + ]: 272 : if ((*d64)[i].d_tag == DT_DEBUG)
1020 : : {
1021 : 20 : r_debug_vaddr = (*d64)[i].d_un.d_val;
1022 : 20 : break;
1023 : : }
1024 : : }
1025 : : }
1026 : :
1027 : 22 : (*memory_callback) (dwfl, -1, &in.d_buf, &in.d_size, 0, 0,
1028 : : memory_callback_arg);
1029 : 22 : free (buf);
1030 : : }
1031 : : }
1032 : : }
1033 : : else
1034 : : /* We have to look for a presupplied executable file to determine
1035 : : the vaddr of its dynamic section and DT_DEBUG therein. */
1036 : 0 : r_debug_vaddr = find_executable (dwfl, 0, 0, &elfclass, &elfdata,
1037 : : memory_callback, memory_callback_arg);
1038 : :
1039 [ + + ]: 35 : if (r_debug_vaddr == 0)
1040 : : return 0;
1041 : :
1042 : : /* For following pointers from struct link_map, we will use an
1043 : : integrated memory access callback that can consult module text
1044 : : elided from the core file. This is necessary when the l_name
1045 : : pointer for the dynamic linker's own entry is a pointer into the
1046 : : executable's .interp section. */
1047 : 22 : struct integrated_memory_callback mcb =
1048 : : {
1049 : : .memory_callback = memory_callback,
1050 : : .memory_callback_arg = memory_callback_arg
1051 : : };
1052 : :
1053 : : /* Now we can follow the dynamic linker's library list. */
1054 : 22 : return report_r_debug (elfclass, elfdata, dwfl, r_debug_vaddr,
1055 : : &integrated_memory_callback, &mcb, r_debug_info);
1056 : : }
1057 : : INTDEF (dwfl_link_map_report)
|