diff --git a/capsule/capsule-dlmopen.c b/capsule/capsule-dlmopen.c
index eff7877a59eb54d3942921f1458e1744bb43d348..9c20f7847390d3581edf0c581296e4720a120e8b 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 3fbe0202f1d018de59847fedd7551d7d84f78ffd..0de488575fe1987048ffa35eadce25cb8cfbba08 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 ef3e21c4301f02c4a0cdde082ab114a05931f3cc..9d3ab5b8fcf7b1237abda9fc1847b7f280016643 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 a31422421aae44005af3175c4a7562f832e21644..2d57a1415371daf39fd9c103f894097da756342d 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 035a2260117934df9a3049b70bd8c4c1c7b97b23..87cba968b5905bf5dc363c53f0686f81a487dae1 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);