summaryrefslogtreecommitdiffstats
path: root/libdwfl/link_map.c
diff options
context:
space:
mode:
authorJan Kratochvil <[email protected]>2013-05-30 13:21:20 +0200
committerJan Kratochvil <[email protected]>2013-05-30 13:21:20 +0200
commit8ff862960efb648cdff647d7fad1be5acffe9b11 (patch)
tree436d529a28ceed160be3cbe85bf285b418d6a029 /libdwfl/link_map.c
parent63d8bfd36e8717e274be5cc3b472b87475f4cc12 (diff)
Use DT_DEBUG library search first.
libdwfl/ 2013-05-30 Jan Kratochvil <[email protected]> * argp-std.c (parse_opt) <ARGP_KEY_SUCCESS> <opt->core> <opt->e>: Set executable_for_core before calling dwfl_core_file_report. * core-file.c (clear_r_debug_info): New function. (dwfl_core_file_report): Move raw segments reporting lower. New variable r_debug_info, pass it to dwfl_segment_report_module. Call clear_r_debug_info in the end. Return sum of LISTED and SNIFFED. * dwfl_module_build_id.c (check_notes): Move into __libdwfl_find_elf_build_id. (__libdwfl_find_build_id): Rename to ... (__libdwfl_find_elf_build_id): ... here. Add parameters build_id_bits, build_id_elfaddr and build_id_len. Verify MOD vs. ELF. (__libdwfl_find_elf_build_id) (check_notes): Remove parameters mod and set, rename data_vaddr to data_elfaddr. Do not call found_build_id. (__libdwfl_find_elf_build_id): Update the check_notes caller, do not adjust its data_elfaddr parameter. (__libdwfl_find_build_id): New wrapper of __libdwfl_find_elf_build_id. * dwfl_segment_report_module.c (dwfl_segment_report_module): New parameter r_debug_info. New variable name_is_final. Adjust addresses according to R_DEBUG_INFO->MODULE. Check conflicts against DWFL. Do not overwrite NAME by SONAME if NAME_IS_FINAL. * libdwflP.h (__libdwfl_find_elf_build_id): New declaration. (struct r_debug_info_module, struct r_debug_info): New definitions. (dwfl_segment_report_module, dwfl_link_map_report): Add parameter r_debug_info. * link_map.c: Include fcntl.h. (report_r_debug): Add parameter r_debug_info, describe it in the function comment. Delete dwfl_addrmodule call and its dependent code. Verify build-id before calling dwfl_report_elf, also supply executable_for_core to it. Store r_debug_info->module info when appropriate. (dwfl_link_map_report): Add parameter r_debug_info. New variable in_ok. Try to read IN from EXECUTABLE_FOR_CORE. Update report_r_debug caller parameters. tests/ 2013-05-30 Jan Kratochvil <[email protected]> * Makefile.am (EXTRA_DIST): Add test-core-lib.so.bz2, test-core.core.bz2 and test-core.exec.bz2. * run-addrname-test.sh: New test for these files. * run-unstrip-n.sh: Update expected output. New test for these files. * test-core-lib.so.bz2: New file. * test-core.core.bz2: New file. * test-core.exec.bz2: New file. Signed-off-by: Jan Kratochvil <[email protected]>
Diffstat (limited to 'libdwfl/link_map.c')
-rw-r--r--libdwfl/link_map.c172
1 files changed, 137 insertions, 35 deletions
diff --git a/libdwfl/link_map.c b/libdwfl/link_map.c
index 9f1b867e..e752a5db 100644
--- a/libdwfl/link_map.c
+++ b/libdwfl/link_map.c
@@ -33,6 +33,7 @@
#include <byteswap.h>
#include <endian.h>
+#include <fcntl.h>
/* This element is always provided and always has a constant value.
This makes it an easy thing to scan for to discern the format. */
@@ -222,7 +223,8 @@ addrsize (uint_fast8_t elfclass)
}
/* Report a module for each struct link_map in the linked list at r_map
- in the struct r_debug at R_DEBUG_VADDR.
+ in the struct r_debug at R_DEBUG_VADDR. For r_debug_info description
+ see dwfl_link_map_report in libdwflP.h.
For each link_map entry, if an existing module resides at its address,
this just modifies that module's name and suggested file name. If
@@ -234,7 +236,8 @@ static int
report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata,
Dwfl *dwfl, GElf_Addr r_debug_vaddr,
Dwfl_Memory_Callback *memory_callback,
- void *memory_callback_arg)
+ void *memory_callback_arg,
+ struct r_debug_info *r_debug_info)
{
/* Skip r_version, to aligned r_map field. */
GElf_Addr read_vaddr = r_debug_vaddr + addrsize (elfclass);
@@ -349,42 +352,83 @@ report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata,
if (name != NULL && name[0] == '\0')
name = NULL;
- /* If content-sniffing already reported a module covering
- the same area, find that existing module to adjust.
- The l_ld address is the only one we know for sure
- to be within the module's own segments (its .dynamic). */
- Dwfl_Module *mod = INTUSE(dwfl_addrmodule) (dwfl, l_ld);
- if (mod != NULL)
+ if (iterations == 1 && dwfl->executable_for_core != NULL)
+ name = dwfl->executable_for_core;
+
+ Dwfl_Module *mod = NULL;
+ if (name != NULL)
{
- /* We have a module. We can give it a better name from l_name. */
- if (name != NULL && mod->name[0] == '[')
+ /* This code is mostly inlined dwfl_report_elf. */
+ // XXX hook for sysroot
+ int fd = open64 (name, O_RDONLY);
+ if (fd >= 0)
{
- char *newname = strdup (basename (name));
- if (newname != NULL)
+ Elf *elf;
+ Dwfl_Error error = __libdw_open_file (&fd, &elf, true, false);
+ if (error == DWFL_E_NOERROR)
{
- free (mod->name);
- mod->name = newname;
+ const void *build_id_bits;
+ GElf_Addr build_id_elfaddr;
+ int build_id_len;
+ bool valid = true;
+
+ /* FIXME: Bias L_ADDR should be computed from the prelink
+ state in memory (when the file got loaded), not against
+ the current on-disk file state as is computed below.
+
+ This verification gives false positive if in-core ELF had
+ build-id but on-disk ELF does not have any. But we cannot
+ reliably find ELF header and/or the ELF build id just from
+ the link map (and checking core segments is also not
+ reliable). */
+
+ if (__libdwfl_find_elf_build_id (NULL, elf, &build_id_bits,
+ &build_id_elfaddr,
+ &build_id_len) > 0
+ && build_id_elfaddr != 0)
+ {
+ GElf_Addr build_id_vaddr = build_id_elfaddr + l_addr;
+ release_buffer (0);
+ int segndx = INTUSE(dwfl_addrsegment) (dwfl,
+ build_id_vaddr,
+ NULL);
+ if (! (*memory_callback) (dwfl, segndx,
+ &buffer, &buffer_available,
+ build_id_vaddr, build_id_len,
+ memory_callback_arg)
+ || memcmp (build_id_bits, buffer, build_id_len) != 0)
+ {
+ /* File has valid build-id which cannot be verified
+ in memory. */
+ valid = false;
+ }
+ }
+
+ if (valid)
+ // XXX hook for sysroot
+ mod = __libdwfl_report_elf (dwfl, basename (name), name,
+ fd, elf, l_addr, true, true);
+ if (mod == NULL)
+ {
+ elf_end (elf);
+ close (fd);
+ }
}
}
-
- if (name == NULL && mod->name[0] == '/')
- name = mod->name;
-
- /* If we don't have a file for it already, we can pre-install
- the full file name from l_name. Opening the file by this
- name will be the fallback when no build ID match is found.
- XXX hook for sysroot */
- if (name != NULL && mod->main.name == NULL)
- mod->main.name = strdup (name);
}
- else if (name != NULL)
+ if (r_debug_info != NULL && mod == NULL)
{
- /* We have to find the file's phdrs to compute along with l_addr
- what its runtime address boundaries are. */
-
- // XXX hook for sysroot
- mod = INTUSE(dwfl_report_elf) (dwfl, basename (name),
- name, -1, l_addr, true);
+ /* Save link map information about valid shared library (or
+ executable) which has not been found on disk. */
+ const char *base = name == NULL ? "" : basename (name);
+ struct r_debug_info_module *module;
+ module = malloc (sizeof (*module) + strlen (base) + 1);
+ if (module == NULL)
+ return release_buffer (result);
+ module->l_ld = l_ld;
+ strcpy (module->name, base);
+ module->next = r_debug_info->module;
+ r_debug_info->module = module;
}
if (mod != NULL)
@@ -601,7 +645,8 @@ find_executable (Dwfl *dwfl, GElf_Addr at_phdr, GElf_Addr at_entry,
int
dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
Dwfl_Memory_Callback *memory_callback,
- void *memory_callback_arg)
+ void *memory_callback_arg,
+ struct r_debug_info *r_debug_info)
{
GElf_Addr r_debug_vaddr = 0;
@@ -699,8 +744,65 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
.d_size = phnum * phent,
.d_buf = NULL
};
- if ((*memory_callback) (dwfl, phdr_segndx, &in.d_buf, &in.d_size,
- phdr, phnum * phent, memory_callback_arg))
+ bool in_ok = (*memory_callback) (dwfl, phdr_segndx, &in.d_buf,
+ &in.d_size, phdr, phnum * phent,
+ memory_callback_arg);
+ if (! in_ok && dwfl->executable_for_core != NULL)
+ {
+ /* AUXV -> PHDR -> DYNAMIC
+ Both AUXV and DYNAMIC should be always present in a core file.
+ PHDR may be missing in core file, try to read it from
+ EXECUTABLE_FOR_CORE to find where DYNAMIC is located in the
+ core file. */
+
+ int fd = open (dwfl->executable_for_core, O_RDONLY);
+ Elf *elf;
+ Dwfl_Error error = DWFL_E_ERRNO;
+ if (fd != -1)
+ error = __libdw_open_file (&fd, &elf, true, false);
+ if (error != DWFL_E_NOERROR)
+ {
+ __libdwfl_seterrno (error);
+ return false;
+ }
+ GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
+ if (ehdr == NULL)
+ {
+ elf_end (elf);
+ close (fd);
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return false;
+ }
+ if (ehdr->e_phnum != phnum || ehdr->e_phentsize != phent)
+ {
+ elf_end (elf);
+ close (fd);
+ __libdwfl_seterrno (DWFL_E_BADELF);
+ return false;
+ }
+ off_t off = ehdr->e_phoff;
+ assert (in.d_buf == NULL);
+ assert (in.d_size == phnum * phent);
+ in.d_buf = malloc (in.d_size);
+ if (unlikely (in.d_buf == NULL))
+ {
+ elf_end (elf);
+ close (fd);
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return false;
+ }
+ ssize_t nread = pread_retry (fd, in.d_buf, in.d_size, off);
+ elf_end (elf);
+ close (fd);
+ if (nread != (ssize_t) in.d_size)
+ {
+ free (in.d_buf);
+ __libdwfl_seterrno (DWFL_E_ERRNO);
+ return false;
+ }
+ in_ok = true;
+ }
+ if (in_ok)
{
union
{
@@ -862,6 +964,6 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
/* Now we can follow the dynamic linker's library list. */
return report_r_debug (elfclass, elfdata, dwfl, r_debug_vaddr,
- &integrated_memory_callback, &mcb);
+ &integrated_memory_callback, &mcb, r_debug_info);
}
INTDEF (dwfl_link_map_report)