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 | 57 ++++++++++++++++++++++++++++ libdwfl/libdwfl.h | 6 +++ libdwfl/libdwflP.h | 3 +- libdwfl/link_map.c | 19 +++++++++- 9 files changed, 109 insertions(+), 5 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..bd360769 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..309ccd5d --- /dev/null +++ b/libdwfl/dwfl_set_sysroot.c @@ -0,0 +1,57 @@ +/* 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 "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; + r = realpath (sysroot, NULL); + if (!r) + { + return -1; + } + free (dwfl->sysroot); + dwfl->sysroot = r; + + 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..3dbc125a 100644 --- a/libdwfl/link_map.c +++ b/libdwfl/link_map.c @@ -416,8 +416,22 @@ 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 - int fd = open (name, O_RDONLY); + int rc; + char *path_name; + const char *sysroot = dwfl->sysroot; + + /* Don't use the sysroot if the path is already inside it. */ + bool name_in_sysroot = sysroot && (strncmp(name, sysroot, strlen(sysroot)) == 0); + + if (!name_in_sysroot && sysroot) + rc = asprintf(&path_name, "%s/%s", sysroot, name); + else + rc = asprintf(&path_name, "%s", name); + + if (unlikely(rc == -1)) + return release_buffer(&memory_closure, &buffer, &buffer_available, -1); + + int fd = open (path_name, O_RDONLY); if (fd >= 0) { Elf *elf; @@ -502,6 +516,7 @@ report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata, close (fd); } } + free(path_name); } if (mod != NULL) -- 2.39.3 (Apple Git-146)