1 /* Report modules by examining dynamic linker data structures.
2 Copyright (C) 2008-2010 Red Hat, Inc.
3 This file is part of Red Hat elfutils.
5 Red Hat elfutils is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by the
7 Free Software Foundation; version 2 of the License.
9 Red Hat elfutils is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
14 You should have received a copy of the GNU General Public License along
15 with Red Hat elfutils; if not, write to the Free Software Foundation,
16 Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
18 In addition, as a special exception, Red Hat, Inc. gives You the
19 additional right to link the code of Red Hat elfutils with code licensed
20 under any Open Source Initiative certified open source license
21 (http://www.opensource.org/licenses/index.php) which requires the
22 distribution of source code with any binary distribution and to
23 distribute linked combinations of the two. Non-GPL Code permitted under
24 this exception must only link to the code of Red Hat elfutils through
25 those well defined interfaces identified in the file named EXCEPTION
26 found in the source code files (the "Approved Interfaces"). The files
27 of Non-GPL Code may instantiate templates or use macros or inline
28 functions from the Approved Interfaces without causing the resulting
29 work to be covered by the GNU General Public License. Only Red Hat,
30 Inc. may make changes or additions to the list of Approved Interfaces.
31 Red Hat's grant of this exception is conditioned upon your not adding
32 any new exceptions. If you wish to add a new Approved Interface or
33 exception, please contact Red Hat. You must obey the GNU General Public
34 License in all respects for all of the Red Hat elfutils code and other
35 code used in conjunction with Red Hat elfutils except the Non-GPL Code
36 covered by this exception. If you modify this file, you may extend this
37 exception to your version of the file, but you are not obligated to do
38 so. If you do not wish to provide this exception without modification,
39 you must delete this exception statement from your version and license
40 this file solely under the GPL without exception.
42 Red Hat elfutils is an included package of the Open Invention Network.
43 An included package of the Open Invention Network is a package for which
44 Open Invention Network licensees cross-license their patents. No patent
45 license is granted, either expressly or impliedly, by designation as an
46 included package. Should you wish to participate in the Open Invention
47 Network licensing program, please visit www.openinventionnetwork.com
48 <http://www.openinventionnetwork.com>. */
56 /* This element is always provided and always has a constant value.
57 This makes it an easy thing to scan for to discern the format. */
58 #define PROBE_TYPE AT_PHENT
59 #define PROBE_VAL32 sizeof (Elf32_Phdr)
60 #define PROBE_VAL64 sizeof (Elf64_Phdr)
62 #if BYTE_ORDER == BIG_ENDIAN
65 # define LE32(x) bswap_32 (x)
66 # define LE64(x) bswap_64 (x)
70 # define BE32(x) bswap_32 (x)
71 # define BE64(x) bswap_64 (x)
75 /* Examine an auxv data block and determine its format.
76 Return true iff we figured it out. */
78 auxv_format_probe (const void *auxv, size_t size,
79 uint_fast8_t *elfclass, uint_fast8_t *elfdata)
84 Elf32_auxv_t a32[size / sizeof (Elf32_auxv_t)];
85 Elf64_auxv_t a64[size / sizeof (Elf64_auxv_t)];
88 inline bool check64 (size_t i)
90 if (u->a64[i].a_type == BE64 (PROBE_TYPE)
91 && u->a64[i].a_un.a_val == BE64 (PROBE_VAL64))
93 *elfdata = ELFDATA2MSB;
97 if (u->a64[i].a_type == LE64 (PROBE_TYPE)
98 && u->a64[i].a_un.a_val == LE64 (PROBE_VAL64))
100 *elfdata = ELFDATA2LSB;
107 inline bool check32 (size_t i)
109 if (u->a32[i].a_type == BE32 (PROBE_TYPE)
110 && u->a32[i].a_un.a_val == BE32 (PROBE_VAL32))
112 *elfdata = ELFDATA2MSB;
116 if (u->a32[i].a_type == LE32 (PROBE_TYPE)
117 && u->a32[i].a_un.a_val == LE32 (PROBE_VAL32))
119 *elfdata = ELFDATA2LSB;
126 for (size_t i = 0; i < size / sizeof (Elf64_auxv_t); ++i)
130 *elfclass = ELFCLASS64;
134 if (check32 (i * 2) || check32 (i * 2 + 1))
136 *elfclass = ELFCLASS32;
144 /* This is a Dwfl_Memory_Callback that wraps another memory callback.
145 If the underlying callback cannot fill the data, then this will
146 fall back to fetching data from module files. */
148 struct integrated_memory_callback
150 Dwfl_Memory_Callback *memory_callback;
151 void *memory_callback_arg;
156 integrated_memory_callback (Dwfl *dwfl, int ndx,
157 void **buffer, size_t *buffer_available,
162 struct integrated_memory_callback *info = arg;
166 /* Called for cleanup. */
167 if (info->buffer != NULL)
169 /* The last probe buffer came from the underlying callback.
170 Let it do its cleanup. */
171 assert (*buffer == info->buffer); /* XXX */
172 *buffer = info->buffer;
174 return (*info->memory_callback) (dwfl, ndx, buffer, buffer_available,
176 info->memory_callback_arg);
179 *buffer_available = 0;
184 /* For a final-read request, we only use the underlying callback. */
185 return (*info->memory_callback) (dwfl, ndx, buffer, buffer_available,
186 vaddr, minread, info->memory_callback_arg);
188 /* Let the underlying callback try to fill this request. */
189 if ((*info->memory_callback) (dwfl, ndx, &info->buffer, buffer_available,
190 vaddr, minread, info->memory_callback_arg))
192 *buffer = info->buffer;
196 /* Now look for module text covering this address. */
199 (void) INTUSE(dwfl_addrsegment) (dwfl, vaddr, &mod);
204 Elf_Scn *scn = INTUSE(dwfl_module_address_section) (mod, &vaddr, &bias);
205 if (unlikely (scn == NULL))
207 #if 0 // XXX would have to handle ndx=-1 cleanup calls passed down.
208 /* If we have no sections we can try to fill it from the module file
209 based on its phdr mappings. */
210 if (likely (mod->e_type != ET_REL) && mod->main.elf != NULL)
211 return INTUSE(dwfl_elf_phdr_memory_callback)
212 (dwfl, 0, buffer, buffer_available,
213 vaddr - mod->main.bias, minread, mod->main.elf);
218 Elf_Data *data = elf_rawdata (scn, NULL);
219 if (unlikely (data == NULL))
223 if (unlikely (data->d_size < vaddr))
226 /* Provide as much data as we have. */
227 void *contents = data->d_buf + vaddr;
228 size_t avail = data->d_size - vaddr;
229 if (unlikely (avail < minread))
232 /* If probing for a string, make sure it's terminated. */
233 if (minread == 0 && unlikely (memchr (contents, '\0', avail) == NULL))
238 *buffer_available = avail;
243 addrsize (uint_fast8_t elfclass)
248 /* Report a module for each struct link_map in the linked list at r_map
249 in the struct r_debug at R_DEBUG_VADDR.
251 For each link_map entry, if an existing module resides at its address,
252 this just modifies that module's name and suggested file name. If
253 no such module exists, this calls dwfl_report_elf on the l_name string.
255 Returns the number of modules found, or -1 for errors. */
258 report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata,
259 Dwfl *dwfl, GElf_Addr r_debug_vaddr,
260 Dwfl_Memory_Callback *memory_callback,
261 void *memory_callback_arg)
263 /* Skip r_version, to aligned r_map field. */
264 GElf_Addr read_vaddr = r_debug_vaddr + addrsize (elfclass);
267 size_t buffer_available = 0;
268 inline int release_buffer (int result)
271 (void) (*memory_callback) (dwfl, -1, &buffer, &buffer_available, 0, 0,
272 memory_callback_arg);
277 inline bool read_addrs (GElf_Addr vaddr, size_t n)
279 size_t nb = n * addrsize (elfclass); /* Address words -> bytes to read. */
281 /* Read a new buffer if the old one doesn't cover these words. */
283 || vaddr < read_vaddr
284 || vaddr - read_vaddr + nb > buffer_available)
289 int segndx = INTUSE(dwfl_addrsegment) (dwfl, vaddr, NULL);
290 if (unlikely (segndx < 0)
291 || unlikely (! (*memory_callback) (dwfl, segndx,
292 &buffer, &buffer_available,
293 vaddr, nb, memory_callback_arg)))
301 } *in = vaddr - read_vaddr + buffer;
303 if (elfclass == ELFCLASS32)
305 if (elfdata == ELFDATA2MSB)
306 for (size_t i = 0; i < n; ++i)
307 addrs[i] = BE32 (in->a32[i]);
309 for (size_t i = 0; i < n; ++i)
310 addrs[i] = LE32 (in->a32[i]);
314 if (elfdata == ELFDATA2MSB)
315 for (size_t i = 0; i < n; ++i)
316 addrs[i] = BE64 (in->a64[i]);
318 for (size_t i = 0; i < n; ++i)
319 addrs[i] = LE64 (in->a64[i]);
325 if (unlikely (read_addrs (read_vaddr, 1)))
326 return release_buffer (-1);
328 GElf_Addr next = addrs[0];
330 Dwfl_Module **lastmodp = &dwfl->modulelist;
333 /* There can't be more elements in the link_map list than there are
334 segments. DWFL->lookup_elts is probably twice that number, so it
335 is certainly above the upper bound. If we iterate too many times,
336 there must be a loop in the pointers due to link_map clobberation. */
337 size_t iterations = 0;
338 while (next != 0 && ++iterations < dwfl->lookup_elts)
340 if (read_addrs (next, 4))
341 return release_buffer (-1);
343 GElf_Addr l_addr = addrs[0];
344 GElf_Addr l_name = addrs[1];
345 GElf_Addr l_ld = addrs[2];
348 /* If a clobbered or truncated memory image has no useful pointer,
349 just skip this element. */
353 /* Fetch the string at the l_name address. */
354 const char *name = NULL;
356 && read_vaddr <= l_name
357 && l_name + 1 - read_vaddr < buffer_available
358 && memchr (l_name - read_vaddr + buffer, '\0',
359 buffer_available - (l_name - read_vaddr)) != NULL)
360 name = l_name - read_vaddr + buffer;
365 int segndx = INTUSE(dwfl_addrsegment) (dwfl, l_name, NULL);
366 if (likely (segndx >= 0)
367 && (*memory_callback) (dwfl, segndx,
368 &buffer, &buffer_available,
369 l_name, 0, memory_callback_arg))
373 if (name != NULL && name[0] == '\0')
376 /* If content-sniffing already reported a module covering
377 the same area, find that existing module to adjust.
378 The l_ld address is the only one we know for sure
379 to be within the module's own segments (its .dynamic). */
380 Dwfl_Module *mod = INTUSE(dwfl_addrmodule) (dwfl, l_ld);
383 /* We have a module. We can give it a better name from l_name. */
384 if (name != NULL && mod->name[0] == '[')
386 char *newname = strdup (basename (name));
394 if (name == NULL && mod->name[0] == '/')
397 /* If we don't have a file for it already, we can pre-install
398 the full file name from l_name. Opening the file by this
399 name will be the fallback when no build ID match is found.
400 XXX hook for sysroot */
401 if (name != NULL && mod->main.name == NULL)
402 mod->main.name = strdup (name);
404 else if (name != NULL)
406 /* We have to find the file's phdrs to compute along with l_addr
407 what its runtime address boundaries are. */
409 // XXX hook for sysroot
410 mod = INTUSE(dwfl_report_elf) (dwfl, basename (name),
418 /* Move this module to the end of the list, so that we end
419 up with a list in the same order as the link_map chain. */
420 if (mod->next != NULL)
422 if (*lastmodp != mod)
424 lastmodp = &dwfl->modulelist;
425 while (*lastmodp != mod)
426 lastmodp = &(*lastmodp)->next;
428 *lastmodp = mod->next;
430 while (*lastmodp != NULL)
431 lastmodp = &(*lastmodp)->next;
435 lastmodp = &mod->next;
439 return release_buffer (result);
443 consider_executable (Dwfl_Module *mod, GElf_Addr at_phdr, GElf_Addr at_entry,
444 uint_fast8_t *elfclass, uint_fast8_t *elfdata,
445 Dwfl_Memory_Callback *memory_callback,
446 void *memory_callback_arg)
449 if (unlikely (gelf_getehdr (mod->main.elf, &ehdr) == NULL))
454 /* If we have an AT_ENTRY value, reject this executable if
455 its entry point address could not have supplied that. */
457 if (ehdr.e_entry == 0)
460 if (mod->e_type == ET_EXEC)
462 if (ehdr.e_entry != at_entry)
467 /* It could be a PIE. */
471 // XXX this could be saved in the file cache: phdr vaddr, DT_DEBUG d_val vaddr
472 /* Find the vaddr of the DT_DEBUG's d_ptr. This is the memory
473 address where &r_debug was written at runtime. */
474 GElf_Xword align = mod->dwfl->segment_align;
475 GElf_Addr d_val_vaddr = 0;
476 for (uint_fast16_t i = 0; i < ehdr.e_phnum; ++i)
479 GElf_Phdr *phdr = gelf_getphdr (mod->main.elf, i, &phdr_mem);
483 if (phdr->p_align > 1 && (align == 0 || phdr->p_align < align))
484 align = phdr->p_align;
487 && phdr->p_type == PT_LOAD
488 && (phdr->p_offset & -align) == (ehdr.e_phoff & -align))
490 /* This is the segment that would map the phdrs.
491 If we have an AT_PHDR value, reject this executable
492 if its phdr mapping could not have supplied that. */
493 if (mod->e_type == ET_EXEC)
495 if (ehdr.e_phoff - phdr->p_offset + phdr->p_vaddr != at_phdr)
500 /* It could be a PIE. If the AT_PHDR value and our
501 phdr address don't match modulo ALIGN, then this
502 could not have been the right PIE. */
503 if (((ehdr.e_phoff - phdr->p_offset + phdr->p_vaddr) & -align)
504 != (at_phdr & -align))
507 /* Calculate the bias applied to the PIE's p_vaddr values. */
508 GElf_Addr bias = (at_phdr - (ehdr.e_phoff - phdr->p_offset
511 /* Final sanity check: if we have an AT_ENTRY value,
512 reject this PIE unless its biased e_entry matches. */
513 if (at_entry != 0 && at_entry != ehdr.e_entry + bias)
516 /* If we're changing the module's address range,
517 we've just invalidated the module lookup table. */
518 GElf_Addr mod_bias = dwfl_adjusted_address (mod, 0);
519 if (bias != mod_bias)
521 mod->low_addr -= mod_bias;
522 mod->high_addr -= mod_bias;
523 mod->low_addr += bias;
524 mod->high_addr += bias;
526 free (mod->dwfl->lookup_module);
527 mod->dwfl->lookup_module = NULL;
532 if (phdr->p_type == PT_DYNAMIC)
534 Elf_Data *data = elf_getdata_rawchunk (mod->main.elf, phdr->p_offset,
535 phdr->p_filesz, ELF_T_DYN);
538 const size_t entsize = gelf_fsize (mod->main.elf,
539 ELF_T_DYN, 1, EV_CURRENT);
540 const size_t n = data->d_size / entsize;
541 for (size_t j = 0; j < n; ++j)
544 GElf_Dyn *dyn = gelf_getdyn (data, j, &dyn_mem);
545 if (dyn != NULL && dyn->d_tag == DT_DEBUG)
547 d_val_vaddr = phdr->p_vaddr + entsize * j + entsize / 2;
554 if (d_val_vaddr != 0)
556 /* Now we have the final address from which to read &r_debug. */
557 d_val_vaddr = dwfl_adjusted_address (mod, d_val_vaddr);
560 size_t buffer_available = addrsize (ehdr.e_ident[EI_CLASS]);
562 int segndx = INTUSE(dwfl_addrsegment) (mod->dwfl, d_val_vaddr, NULL);
564 if ((*memory_callback) (mod->dwfl, segndx,
565 &buffer, &buffer_available,
566 d_val_vaddr, buffer_available,
567 memory_callback_arg))
576 if (ehdr.e_ident[EI_CLASS] == ELFCLASS32)
577 vaddr = (ehdr.e_ident[EI_DATA] == ELFDATA2MSB
578 ? BE32 (u->a32) : LE32 (u->a32));
580 vaddr = (ehdr.e_ident[EI_DATA] == ELFDATA2MSB
581 ? BE64 (u->a64) : LE64 (u->a64));
583 (*memory_callback) (mod->dwfl, -1, &buffer, &buffer_available, 0, 0,
584 memory_callback_arg);
586 if (*elfclass == ELFCLASSNONE)
587 *elfclass = ehdr.e_ident[EI_CLASS];
588 else if (*elfclass != ehdr.e_ident[EI_CLASS])
591 if (*elfdata == ELFDATANONE)
592 *elfdata = ehdr.e_ident[EI_DATA];
593 else if (*elfdata != ehdr.e_ident[EI_DATA])
603 /* Try to find an existing executable module with a DT_DEBUG. */
605 find_executable (Dwfl *dwfl, GElf_Addr at_phdr, GElf_Addr at_entry,
606 uint_fast8_t *elfclass, uint_fast8_t *elfdata,
607 Dwfl_Memory_Callback *memory_callback,
608 void *memory_callback_arg)
610 for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
611 if (mod->main.elf != NULL)
613 GElf_Addr r_debug_vaddr = consider_executable (mod, at_phdr, at_entry,
616 memory_callback_arg);
617 if (r_debug_vaddr != 0)
618 return r_debug_vaddr;
626 dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
627 Dwfl_Memory_Callback *memory_callback,
628 void *memory_callback_arg)
630 GElf_Addr r_debug_vaddr = 0;
632 uint_fast8_t elfclass = ELFCLASSNONE;
633 uint_fast8_t elfdata = ELFDATANONE;
634 if (likely (auxv != NULL)
635 && likely (auxv_format_probe (auxv, auxv_size, &elfclass, &elfdata)))
639 GElf_Xword phent = 0;
640 GElf_Xword phnum = 0;
642 #define AUXV_SCAN(NN, BL) do \
644 const Elf##NN##_auxv_t *av = auxv; \
645 for (size_t i = 0; i < auxv_size / sizeof av[0]; ++i) \
647 Elf##NN##_Addr val = BL##NN (av[i].a_un.a_val); \
648 if (av[i].a_type == BL##NN (AT_ENTRY)) \
650 else if (av[i].a_type == BL##NN (AT_PHDR)) \
652 else if (av[i].a_type == BL##NN (AT_PHNUM)) \
654 else if (av[i].a_type == BL##NN (AT_PHENT)) \
656 else if (av[i].a_type == BL##NN (AT_PAGESZ)) \
659 && (dwfl->segment_align == 0 \
660 || val < dwfl->segment_align)) \
661 dwfl->segment_align = val; \
667 if (elfclass == ELFCLASS32)
669 if (elfdata == ELFDATA2MSB)
676 if (elfdata == ELFDATA2MSB)
682 /* If we found the phdr dimensions, search phdrs for PT_DYNAMIC. */
683 GElf_Addr dyn_vaddr = 0;
684 GElf_Xword dyn_filesz = 0;
685 GElf_Addr dyn_bias = (GElf_Addr) -1;
687 inline bool consider_phdr (GElf_Word type,
688 GElf_Addr vaddr, GElf_Xword filesz)
693 if (dyn_bias == (GElf_Addr) -1
694 /* Do a sanity check on the putative address. */
695 && ((vaddr & (dwfl->segment_align - 1))
696 == (phdr & (dwfl->segment_align - 1))))
698 dyn_bias = phdr - vaddr;
699 return dyn_vaddr != 0;
706 return dyn_bias != (GElf_Addr) -1;
712 if (phdr != 0 && phnum != 0)
714 Dwfl_Module *phdr_mod;
715 int phdr_segndx = INTUSE(dwfl_addrsegment) (dwfl, phdr, &phdr_mod);
718 .d_type = ELF_T_PHDR,
719 .d_version = EV_CURRENT,
720 .d_size = phnum * phent,
723 if ((*memory_callback) (dwfl, phdr_segndx, &in.d_buf, &in.d_size,
724 phdr, phnum * phent, memory_callback_arg))
730 char data[phnum * phent];
734 .d_type = ELF_T_PHDR,
735 .d_version = EV_CURRENT,
736 .d_size = phnum * phent,
739 in.d_size = out.d_size;
740 if (likely ((elfclass == ELFCLASS32
741 ? elf32_xlatetom : elf64_xlatetom)
742 (&out, &in, elfdata) != NULL))
744 /* We are looking for PT_DYNAMIC. */
747 Elf32_Phdr p32[phnum];
748 Elf64_Phdr p64[phnum];
749 } *u = (void *) &buf;
750 if (elfclass == ELFCLASS32)
752 for (size_t i = 0; i < phnum; ++i)
753 if (consider_phdr (u->p32[i].p_type,
760 for (size_t i = 0; i < phnum; ++i)
761 if (consider_phdr (u->p64[i].p_type,
768 (*memory_callback) (dwfl, -1, &in.d_buf, &in.d_size, 0, 0,
769 memory_callback_arg);
772 /* We could not read the executable's phdrs from the
773 memory image. If we have a presupplied executable,
774 we can still use the AT_PHDR and AT_ENTRY values to
775 verify it, and to adjust its bias if it's a PIE.
777 If there was an ET_EXEC module presupplied that contains
778 the AT_PHDR address, then we only consider that one.
779 We'll either accept it if its phdr location and e_entry
780 make sense or reject it if they don't. If there is no
781 presupplied ET_EXEC, then look for a presupplied module,
782 which might be a PIE (ET_DYN) that needs its bias adjusted. */
783 r_debug_vaddr = ((phdr_mod == NULL
784 || phdr_mod->main.elf == NULL
785 || phdr_mod->e_type != ET_EXEC)
786 ? find_executable (dwfl, phdr, entry,
790 : consider_executable (phdr_mod, phdr, entry,
793 memory_callback_arg));
796 /* If we found PT_DYNAMIC, search it for DT_DEBUG. */
799 if (dyn_bias != (GElf_Addr) -1)
800 dyn_vaddr += dyn_bias;
805 .d_version = EV_CURRENT,
806 .d_size = dyn_filesz,
809 int dyn_segndx = dwfl_addrsegment (dwfl, dyn_vaddr, NULL);
810 if ((*memory_callback) (dwfl, dyn_segndx, &in.d_buf, &in.d_size,
811 dyn_vaddr, dyn_filesz, memory_callback_arg))
817 char data[dyn_filesz];
822 .d_version = EV_CURRENT,
823 .d_size = dyn_filesz,
826 in.d_size = out.d_size;
827 if (likely ((elfclass == ELFCLASS32
828 ? elf32_xlatetom : elf64_xlatetom)
829 (&out, &in, elfdata) != NULL))
831 /* We are looking for DT_DEBUG. */
834 Elf32_Dyn d32[dyn_filesz / sizeof (Elf32_Dyn)];
835 Elf64_Dyn d64[dyn_filesz / sizeof (Elf64_Dyn)];
836 } *u = (void *) &buf;
837 if (elfclass == ELFCLASS32)
839 size_t n = dyn_filesz / sizeof (Elf32_Dyn);
840 for (size_t i = 0; i < n; ++i)
841 if (u->d32[i].d_tag == DT_DEBUG)
843 r_debug_vaddr = u->d32[i].d_un.d_val;
849 size_t n = dyn_filesz / sizeof (Elf64_Dyn);
850 for (size_t i = 0; i < n; ++i)
851 if (u->d64[i].d_tag == DT_DEBUG)
853 r_debug_vaddr = u->d64[i].d_un.d_val;
859 (*memory_callback) (dwfl, -1, &in.d_buf, &in.d_size, 0, 0,
860 memory_callback_arg);
865 /* We have to look for a presupplied executable file to determine
866 the vaddr of its dynamic section and DT_DEBUG therein. */
867 r_debug_vaddr = find_executable (dwfl, 0, 0, &elfclass, &elfdata,
868 memory_callback, memory_callback_arg);
870 if (r_debug_vaddr == 0)
873 /* For following pointers from struct link_map, we will use an
874 integrated memory access callback that can consult module text
875 elided from the core file. This is necessary when the l_name
876 pointer for the dynamic linker's own entry is a pointer into the
877 executable's .interp section. */
878 struct integrated_memory_callback mcb =
880 .memory_callback = memory_callback,
881 .memory_callback_arg = memory_callback_arg
884 /* Now we can follow the dynamic linker's library list. */
885 return report_r_debug (elfclass, elfdata, dwfl, r_debug_vaddr,
886 &integrated_memory_callback, &mcb);
888 INTDEF (dwfl_link_map_report)