From: Luke Diamand <ldiam...@roku.com> When searching the list of modules in a core file, if the core was generated on a different system to the current one, we need to look in a sysroot for the various shared objects.
For example, we might be looking at a core file from an ARM system using elfutils running on an x86 host. This change adds a new function, dwfl_set_sysroot(), which then gets used when searching for libraries and binaries. Signed-off-by: Luke Diamand <ldiam...@roku.com> Signed-off-by: Michal Sekletar <msekl...@redhat.com> --- libdw/libdw.map | 5 ++ libdwfl/Makefile.am | 1 + libdwfl/core-file.c | 2 +- libdwfl/dwfl_end.c | 1 + libdwfl/dwfl_segment_report_module.c | 20 ++++++- libdwfl/dwfl_set_sysroot.c | 80 ++++++++++++++++++++++++++++ libdwfl/libdwfl.h | 6 +++ libdwfl/libdwflP.h | 3 +- libdwfl/link_map.c | 16 +++++- 9 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 libdwfl/dwfl_set_sysroot.c diff --git a/libdw/libdw.map b/libdw/libdw.map index 3c5ce8dc..552588a9 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -378,3 +378,8 @@ ELFUTILS_0.191 { global: dwarf_cu_dwp_section_info; } ELFUTILS_0.188; + +ELFUTILS_0.192 { + global: + dwfl_set_sysroot; +} ELFUTILS_0.191; diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am index 6b26cd51..57c89604 100644 --- a/libdwfl/Makefile.am +++ b/libdwfl/Makefile.am @@ -67,6 +67,7 @@ libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c dwfl_version.c \ dwfl_module_return_value_location.c \ dwfl_module_register_names.c \ dwfl_segment_report_module.c \ + dwfl_set_sysroot.c \ link_map.c core-file.c open.c image-header.c \ dwfl_frame.c frame_unwind.c dwfl_frame_pc.c \ linux-pid-attach.c linux-core-attach.c dwfl_frame_regs.c \ diff --git a/libdwfl/core-file.c b/libdwfl/core-file.c index 89527d23..3135f884 100644 --- a/libdwfl/core-file.c +++ b/libdwfl/core-file.c @@ -559,7 +559,7 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const char *executable) ndx = 0; do { - int seg = dwfl_segment_report_module (dwfl, ndx, NULL, + int seg = dwfl_segment_report_module (dwfl, ndx, NULL, executable, &dwfl_elf_phdr_memory_callback, elf, core_file_read_eagerly, elf, elf->maximum_size, diff --git a/libdwfl/dwfl_end.c b/libdwfl/dwfl_end.c index a1812407..7b5ac8a1 100644 --- a/libdwfl/dwfl_end.c +++ b/libdwfl/dwfl_end.c @@ -48,6 +48,7 @@ dwfl_end (Dwfl *dwfl) free (dwfl->lookup_addr); free (dwfl->lookup_module); free (dwfl->lookup_segndx); + free (dwfl->sysroot); Dwfl_Module *next = dwfl->modulelist; while (next != NULL) diff --git a/libdwfl/dwfl_segment_report_module.c b/libdwfl/dwfl_segment_report_module.c index dc34e0ae..2b050d64 100644 --- a/libdwfl/dwfl_segment_report_module.c +++ b/libdwfl/dwfl_segment_report_module.c @@ -288,6 +288,7 @@ read_portion (struct read_state *read_state, int dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, + const char *executable, Dwfl_Memory_Callback *memory_callback, void *memory_callback_arg, Dwfl_Module_Callback *read_eagerly, @@ -778,7 +779,24 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, name = file_note_name; name_is_final = true; bool invalid = false; - fd = open (name, O_RDONLY); + + /* We were not handed specific executable hence try to look for it in sysroot if + it is set. */ + if (dwfl->sysroot && !executable) + { + int r; + char *n; + + r = asprintf (&n, "%s%s", dwfl->sysroot, name); + if (r > 0) + { + fd = open (n, O_RDONLY); + free (n); + } + } + else + fd = open (name, O_RDONLY); + if (fd >= 0) { Dwfl_Error error = __libdw_open_file (&fd, &elf, true, false); diff --git a/libdwfl/dwfl_set_sysroot.c b/libdwfl/dwfl_set_sysroot.c new file mode 100644 index 00000000..344d4ae5 --- /dev/null +++ b/libdwfl/dwfl_set_sysroot.c @@ -0,0 +1,80 @@ +/* Return one of the sources lines of a CU. + Copyright (C) 2024 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <sys/stat.h> + +#include "libdwflP.h" +#include "libdwP.h" + +int +dwfl_set_sysroot (Dwfl *dwfl, const char *sysroot) +{ + if (!sysroot) + { + free (dwfl->sysroot); + dwfl->sysroot = NULL; + return 0; + } + + char *r, *s; + r = realpath (sysroot, NULL); + if (!r) + return -1; + + int rc; + struct stat sb; + + rc = stat (r, &sb); + if (rc < 0 || !S_ISDIR (sb.st_mode)) + { + errno = EINVAL; + return -1; + } + + rc = asprintf (&s, "%s/", r); + if (rc < 0) + { + errno = ENOMEM; + return -1; + } + + free (dwfl->sysroot); + free (r); + + dwfl->sysroot = s; + return 0; +} + +INTDEF (dwfl_set_sysroot) \ No newline at end of file diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h index 49ad6664..4cbeab55 100644 --- a/libdwfl/libdwfl.h +++ b/libdwfl/libdwfl.h @@ -818,6 +818,12 @@ int dwfl_frame_reg (Dwfl_Frame *state, unsigned regno, Dwarf_Word *val) */ extern debuginfod_client *dwfl_get_debuginfod_client (Dwfl *dwfl); +/* Set the sysroot to use when searching for shared libraries and binaries. If not + specified, search the system root. Passing NULL clears previously set sysroot. Note + that library creates a copy of the sysroot argument. */ +int dwfl_set_sysroot (Dwfl *dwfl, const char *sysroot) + __nonnull_attribute__ (1); + #ifdef __cplusplus } #endif diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h index b3dfea1d..2dc53b81 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -134,6 +134,7 @@ struct Dwfl int next_segndx; struct Dwfl_User_Core *user_core; + char *sysroot; /* sysroot, or NULL to search standard system paths */ }; #define OFFLINE_REDZONE 0x10000 @@ -697,7 +698,7 @@ struct r_debug_info /* ... */ -extern int dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, +extern int dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, const char *executable, Dwfl_Memory_Callback *memory_callback, void *memory_callback_arg, Dwfl_Module_Callback *read_eagerly, diff --git a/libdwfl/link_map.c b/libdwfl/link_map.c index a6c66c78..8ab14862 100644 --- a/libdwfl/link_map.c +++ b/libdwfl/link_map.c @@ -416,7 +416,20 @@ report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata, if (name != NULL) { /* This code is mostly inlined dwfl_report_elf. */ - // XXX hook for sysroot + char *sysroot_name = NULL; + const char *sysroot = dwfl->sysroot; + + /* Don't use the sysroot if the path is already inside it. */ + bool name_in_sysroot = sysroot && startswith (name, sysroot); + + if (sysroot && !name_in_sysroot) + { + if (asprintf (&sysroot_name, "%s%s", sysroot, name) < 0) + return release_buffer (&memory_closure, &buffer, &buffer_available, -1); + + name = sysroot_name; + } + int fd = open (name, O_RDONLY); if (fd >= 0) { @@ -502,6 +515,7 @@ report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata, close (fd); } } + free(sysroot_name); } if (mod != NULL) -- 2.39.3 (Apple Git-146)