[COMMITTED] libebl: Try harder to find backend library in bin and lib origin paths.

Mark Wielaard mark@klomp.org
Thu May 30 17:06:00 GMT 2019


eblopenbackend tries to find libraries based on the $ORIGIN/../$LIB/
path. But depending on whether the system is multilib or multiarch
this doesn't always work. On multilib systems $LIB is always just one
directory deep (it is either .../lib or .../lib64) but on multiarch
systems it can be multiple directories deep (.../lib/x86_64-linux-gnu).
This means that on multiarch systems $ORIGIN/../$LIB only works for
binaries (where origin is .../bin/), but not for libraries.

Most of the time it still works because of RPATH which is tried afterwards.
But RPATH processing does not always work reliable.

So try multiple paths first. The first time using the $ORIGIN as if it
came from an executable (in bin/) and then using the $ORIGIN as if it
came from an library (in lib[64]/ or lib/<arch>/). So first time using
../$LIB and second time just with the elfutils EBL_SUBDIR.

The first is what we do now and always work on multilib systems. The
second try works when loading relative to a library whether on a multilib
or multiarch system.

Then we use the same fallback (not using any path) we used already
(to take advantage of any RPATH or LD_LIBRARY_PATH setting).

https://sourceware.org/bugzilla/show_bug.cgi?id=24488

Signed-off-by: Mark Wielaard <mark@klomp.org>
---
 libebl/ChangeLog        |   8 ++++
 libebl/eblopenbackend.c | 117 +++++++++++++++++++++++++++++-------------------
 2 files changed, 78 insertions(+), 47 deletions(-)

diff --git a/libebl/ChangeLog b/libebl/ChangeLog
index e050bfc..8a7d177 100644
--- a/libebl/ChangeLog
+++ b/libebl/ChangeLog
@@ -1,3 +1,11 @@
+2019-05-30  Mark Wielaard  <mark@klomp.org>
+
+	* eblopenbackend.c (try_dlopen): New function extracted from
+	openbackend.
+	(openbackend): Replace ORIGINDIR define with BINORIGINDIR and
+	LIBORIGINDIR defines. Use tryopen to open backend in bin origin
+	path, lib origin path and without an explicit path.
+
 2019-04-28  Mark Wielaard  <mark@klomp.org>
 
 	* eblsymbolbindingname.c (ebl_symbol_binding_name): Check ebl is
diff --git a/libebl/eblopenbackend.c b/libebl/eblopenbackend.c
index d54b720..36a2492 100644
--- a/libebl/eblopenbackend.c
+++ b/libebl/eblopenbackend.c
@@ -251,6 +251,50 @@ fill_defaults (Ebl *result)
   result->sysvhash_entrysize = sizeof (Elf32_Word);
 }
 
+static Ebl *
+try_dlopen (const char *dsoname, Elf *elf, GElf_Half machine, size_t cnt,
+	    Ebl *result)
+{
+  void *h = dlopen (dsoname, RTLD_LAZY);
+
+  if (h != NULL)
+    {
+      /* We managed to load the object.  Now see whether the
+	 initialization function likes our file.  */
+      static const char version[] = MODVERSION;
+      const char *modversion;
+      ebl_bhinit_t initp;
+
+      // We use a static number to help the compiler see we don't
+      // overflow the stack with an arbitrary number.
+      assert (machines[cnt].prefix_len <= MAX_PREFIX_LEN);
+      char symname[MAX_PREFIX_LEN + sizeof "_init"];
+
+      strcpy (mempcpy (symname, machines[cnt].prefix,
+		       machines[cnt].prefix_len), "_init");
+
+      initp = (ebl_bhinit_t) dlsym (h, symname);
+      if (initp != NULL
+	  && (modversion = initp (elf, machine, result, sizeof (Ebl)))
+	  && strcmp (version, modversion) == 0)
+	{
+	  /* We found a module to handle our file.  */
+	  result->dlhandle = h;
+	  result->elf = elf;
+
+	  /* A few entries are mandatory.  */
+	  assert (result->name != NULL);
+	  assert (result->destr != NULL);
+
+	  return result;
+	}
+
+      /* Not the module we need.  */
+      (void) dlclose (h);
+    }
+
+  return NULL;
+}
 
 /* Find an appropriate backend for the file associated with ELF.  */
 static Ebl *
@@ -316,60 +360,39 @@ openbackend (Elf *elf, const char *emulation, GElf_Half machine)
 #ifndef LIBEBL_SUBDIR
 # define LIBEBL_SUBDIR PACKAGE
 #endif
-#define ORIGINDIR "$ORIGIN/../$LIB/" LIBEBL_SUBDIR "/"
+
+/* This works if libebl has been staticly linked into a binary.
+   It might also work for shared libraries when installed in
+   ${prefix}/lib/ or ${prefix}/lib64/, but not for multiarch
+   library installs like ${prefix}/lib/i386-linux-gnu/  */
+#define BINORIGINDIR "$ORIGIN/../$LIB/" LIBEBL_SUBDIR "/"
+
+/* This works if libebl has been linked into a shared library,
+   just look in the subdir.  */
+#define LIBORIGINDIR "$ORIGIN/" LIBEBL_SUBDIR "/"
 
 	/* Give it a try.  At least the machine type matches.  First
-           try to load the module.  */
+           try to load the module from the (bin) origin path.  */
 	char dsoname[100];
-	strcpy (stpcpy (stpcpy (dsoname, ORIGINDIR "libebl_"),
+	strcpy (stpcpy (stpcpy (dsoname, BINORIGINDIR "libebl_"),
 			machines[cnt].dsoname),
 		".so");
+	if (try_dlopen (dsoname, elf, machine, cnt, result) != NULL)
+	  return result;
 
-	void *h = dlopen (dsoname, RTLD_LAZY);
-	if (h == NULL)
-	  {
-	    strcpy (stpcpy (stpcpy (dsoname, "libebl_"),
-			    machines[cnt].dsoname),
-		    ".so");
-	    h = dlopen (dsoname, RTLD_LAZY);
-	  }
+	/* Retry with the (lib) origin path.  */
+	strcpy (stpcpy (stpcpy (dsoname, LIBORIGINDIR "libebl_"),
+			machines[cnt].dsoname),
+		".so");
+	if (try_dlopen (dsoname, elf, machine, cnt, result) != NULL)
+	  return result;
 
-	  /* Try without an explicit path.  */
-	if (h != NULL)
-	  {
-	    /* We managed to load the object.  Now see whether the
-	       initialization function likes our file.  */
-	    static const char version[] = MODVERSION;
-	    const char *modversion;
-	    ebl_bhinit_t initp;
-
-	    // We use a static number to help the compiler see we don't
-	    // overflow the stack with an arbitrary number.
-	    assert (machines[cnt].prefix_len <= MAX_PREFIX_LEN);
-	    char symname[MAX_PREFIX_LEN + sizeof "_init"];
-
-	    strcpy (mempcpy (symname, machines[cnt].prefix,
-			     machines[cnt].prefix_len), "_init");
-
-	    initp = (ebl_bhinit_t) dlsym (h, symname);
-	    if (initp != NULL
-		&& (modversion = initp (elf, machine, result, sizeof (Ebl)))
-		&& strcmp (version, modversion) == 0)
-	      {
-		/* We found a module to handle our file.  */
-		result->dlhandle = h;
-		result->elf = elf;
-
-		/* A few entries are mandatory.  */
-		assert (result->name != NULL);
-		assert (result->destr != NULL);
-
-		return result;
-	      }
-
-	    /* Not the module we need.  */
-	    (void) dlclose (h);
-	  }
+	/* Try without an explicit path (LD_LIBRARY_PATH or RPATH).  */
+	strcpy (stpcpy (stpcpy (dsoname, "libebl_"),
+			machines[cnt].dsoname),
+		".so");
+	if (try_dlopen (dsoname, elf, machine, cnt, result) != NULL)
+	  return result;
 
 	/* We cannot find a DSO but the emulation/machine ID matches.
 	   Return that information.  */
-- 
1.8.3.1



More information about the Elfutils-devel mailing list