[v2,1/3] libdwfl: specify optional sysroot to search for shared libraries and binaries

Message ID 20240702173100.39145-1-msekleta@redhat.com
State Committed
Headers
Series [v2,1/3] libdwfl: specify optional sysroot to search for shared libraries and binaries |

Commit Message

Michal Sekletar July 2, 2024, 5:30 p.m. UTC
  From: Luke Diamand <ldiamand@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 <ldiamand@roku.com>
Signed-off-by: Michal Sekletar <msekleta@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
  

Comments

Dmitry V. Levin July 3, 2024, 2:37 p.m. UTC | #1
On Tue, Jul 02, 2024 at 07:30:58PM +0200, Michal Sekletar wrote:
[...]
> +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);

There is a case when realpath() returns a path ending with '/':
when the path is "/".  I'm mentioning this just in case, not sure whether
sysroot == "/" makes any sense at all, and whether this needs a special
attention.
  
Michal Sekletar July 3, 2024, 3:47 p.m. UTC | #2
On Wed, Jul 3, 2024 at 4:38 PM Dmitry V. Levin <ldv@strace.io> wrote:

> On Tue, Jul 02, 2024 at 07:30:58PM +0200, Michal Sekletar wrote:
> [...]
> > +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);
>
> There is a case when realpath() returns a path ending with '/':
> when the path is "/".  I'm mentioning this just in case, not sure whether
> sysroot == "/" makes any sense at all, and whether this needs a special

attention.
>

I think that should be fine, //sysroot should be equivalent to /sysroot,
IOW, multiple slashes shouldn't cause any issues.

Michal


>
> --
> ldv
>
>
  

Patch

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)