summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVivek Das Mohapatra <vivek@collabora.co.uk>2017-09-13 22:17:06 (GMT)
committerVivek Das Mohapatra <vivek@collabora.co.uk>2017-09-13 22:17:06 (GMT)
commit5107c747ca24bc8d5e6ba40ba450048f06d63573 (patch)
treecfc8ff21eef1319482400bcb83ce72a9af62ef5a
parent769efa1a3cc7f6195847dad7345e764918a04ba5 (diff)
downloadlibcapsule-5107c747ca24bc8d5e6ba40ba450048f06d63573.tar.gz
libcapsule-5107c747ca24bc8d5e6ba40ba450048f06d63573.tar.xz
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.
-rw-r--r--capsule/capsule-dlmopen.c35
-rw-r--r--capsule/capsule.h34
-rw-r--r--documentation.mk1
-rw-r--r--utils/utils.c18
-rw-r--r--utils/utils.h1
5 files changed, 89 insertions, 0 deletions
diff --git a/capsule/capsule-dlmopen.c b/capsule/capsule-dlmopen.c
index eff7877..9c20f78 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 3fbe020..0de4885 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 ef3e21c..9d3ab5b 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 a314224..2d57a14 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 035a226..87cba96 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);