From 5107c747ca24bc8d5e6ba40ba450048f06d63573 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Vivek=20Das=C2=A0Mohapatra?= <vivek@collabora.co.uk>
Date: Wed, 13 Sep 2017 23:17:06 +0100
Subject: [PATCH] Implement a capsule_shim_dlsym helper

In addition to wrapping dlopen() inside the capsule we need to
wrap dlsym() outside the capsule in order to allow libraries
that use a dlopen()/dlsym() pattern to work transparently.

capsule_shim_dlsym() bundles most of the standard logic we
expect such wrappers to use.
---
 capsule/capsule-dlmopen.c | 35 +++++++++++++++++++++++++++++++++++
 capsule/capsule.h         | 34 ++++++++++++++++++++++++++++++++++
 documentation.mk          |  1 +
 utils/utils.c             | 18 ++++++++++++++++++
 utils/utils.h             |  1 +
 5 files changed, 89 insertions(+)

diff --git a/capsule/capsule-dlmopen.c b/capsule/capsule-dlmopen.c
index eff7877a5..9c20f7847 100644
--- a/capsule/capsule-dlmopen.c
+++ b/capsule/capsule-dlmopen.c
@@ -419,3 +419,38 @@ cleanup:
     ld_libs_finish( &ldlibs );
     return res;
 }
+
+static int
+dso_is_exported (const char *dsopath, const char **exported)
+{
+    for( const char **ex = exported; ex && *ex; ex++ )
+        if( soname_matches_path( *ex, dsopath ) )
+            return 1;
+
+    return 0;
+}
+
+void *
+capsule_shim_dlsym (void *capsule,
+                    void *handle,
+                    const char *symbol,
+                    const char **exported)
+{
+    void *addr = NULL;
+
+    if( (addr = dlsym( capsule, symbol )) )
+    {
+        Dl_info dso = { 0 };
+
+        // only keep addr from the capsule if it's from an exported DSO:
+        // or if we are unable to determine where it came from (what?)
+        if( dladdr( addr, &dso ) )
+            if( !dso_is_exported( dso.dli_fname, exported ) )
+                addr = NULL;
+    }
+
+    if( addr == NULL )
+        addr = dlsym( handle, symbol );
+
+    return addr;
+}
diff --git a/capsule/capsule.h b/capsule/capsule.h
index 3fbe0202f..0de488575 100644
--- a/capsule/capsule.h
+++ b/capsule/capsule.h
@@ -170,3 +170,37 @@ void *capsule_shim_dlopen(Lmid_t ns,
                           const char **exclude,
                           const char *file,
                           int flag);
+
+/**
+ * capsule_shim_dlsym:
+ * @capsule: A dl handle as returned by capsule_dlmopen()
+ * @handle: A dl handle, as passed to dlsym()
+ * @symbol: A symbol name, as passed to dlsym()
+ * @exported: An array of DSO names considered to ba valid symbol sources
+ *
+ * Returns: a void * symbol address (cf dlsym())
+ *
+ * Some libraries have a use pattern in which their caller/user
+ * uses dlsym() to obtain symbols rather than using those symbols
+ * directly in its own code (libGL is an example of this).
+ *
+ * Since the target library may have a different symbol set than the
+ * one the libcapsule proxy shim was generated from we can't rely on
+ * dlsym() finding those symbols in the shim's symbol table.
+ *
+ * Instead we must intercept dlsym() calls made outside the capsule
+ * and attempt to look for the required symbol in the namespace defined
+ * by @capsule first - If the required symbol is found there AND is
+ * from one of the DSO names present in @exported then that symbol is
+ * returned. If either of those conditions is not met then a normal
+ * dlsym call with the passed handle is made.
+ *
+ * This function provides the functionality described above, and is
+ * intended for use in a suitable wrapper implemented in the the shim
+ * library.
+ */
+void *
+capsule_shim_dlsym (void *capsule,
+                    void *handle,
+                    const char *symbol,
+                    const char **exported);
diff --git a/documentation.mk b/documentation.mk
index ef3e21c43..9d3ab5b8f 100644
--- a/documentation.mk
+++ b/documentation.mk
@@ -29,5 +29,6 @@ man_MANS   += capsule_dlmopen.3
 man_MANS   += capsule_init.3
 man_MANS   += capsule_relocate.3
 man_MANS   += capsule_shim_dlopen.3
+man_MANS   += capsule_shim_dlsym.3
 
 CLEANFILES += $(man_MANS)
diff --git a/utils/utils.c b/utils/utils.c
index a31422421..2d57a1415 100644
--- a/utils/utils.c
+++ b/utils/utils.c
@@ -131,3 +131,21 @@ void set_debug_flags (const char *control)
             (debug_flags & DEBUG_RELOCS  ) ? 'Y' : 'n' ,
             (debug_flags & DEBUG_ELF     ) ? 'Y' : 'n' );
 }
+
+// soname: bare libfoo.so.X style name
+// path: [possibly absolute] path to DSO
+// return true if soname: libFOO.so.X matches
+// path: /path/to/libFOO.so.X.Y or /path/to/libFOO.so.X
+int soname_matches_path (const char *soname, const char *path)
+{
+    const char *path_soname = strrchr( path, '/' );
+    const char *pattern = path_soname ? path_soname + 1: path;
+    const size_t slen = strlen( soname );
+
+    if( strncmp( soname, pattern, slen ) != 0 )
+        return 0;
+
+    const char *end = pattern + slen;
+
+    return ( *end == '\0' || *end == '.' );
+}
diff --git a/utils/utils.h b/utils/utils.h
index 035a22601..87cba968b 100644
--- a/utils/utils.h
+++ b/utils/utils.h
@@ -40,3 +40,4 @@
 
 char *safe_strncpy (char *dest, const char *src, size_t n);
 int   resolve_link (const char *prefix, char *path, char *dir);
+int soname_matches_path (const char *soname, const char *path);
-- 
GitLab