https://sourceforge.net/p/libusb/mailman/message/34281276/

From: Stanislav Brabec <sbrabec@suse.com>
Date: Thu, 09 Jul 2015 18:21:56 +0200
Subject: [libusb] [PATCH][libusb-compat-0.1][TRY3] Prevent libusb-1.0 library symbol clash
List-Id: Development discussion for libusb <libusb-devel.lists.sourceforge.net>

libusb-1.0 uses functions like libusb_open. These names are generic, and
there are applications using libusb-0.1 using these names for its own
functions (e. g. scanbuttond). If such applications uses libusb-compat,
it crashes due to symbol clash.

Such clash is easy to fix in Open Source applications, but impossible to
fix inside closed source applications.

Dynamic linker does not allow to hide symbols from dependent library.
This could be worked around by loading this library later by dlopen()
with RTLD_LOCAL flag.

Do this on platforms where it can be done.

This change makes visible only libusb-0.1 symbols but not libusb-1.0
symbols.

(It could be theoretically possible to introduce clashes with libdl, but
it is much less probable.)

To wrap symbols inside static inline functions inside libusb.h, the
Makefile generates a modified version if libusb.h.

How to reproduce:

Take testlibusb.c from libusb-0.1.12/tests and replace print_device
by libusb_open. Without this change the application crashes.

Reference:
https://bugzilla.opensuse.org/show_bug.cgi?id=596411

Signed-off-by: Stanislav Brabec <sbrabec@suse.com>
---
 configure.ac              |  23 +++++++++
 libusb/Makefile.am        |  11 +++-
 libusb/core.c             |  14 ++++++
 libusb/libusb-dload.h     | 124 ++++++++++++++++++++++++++++++++++++++++++++++
 m4/au_check_lib_soname.m4 |  43 ++++++++++++++++
 m4/au_have_gnu_errno.m4   |  17 +++++++
 6 files changed, 231 insertions(+), 1 deletion(-)
 create mode 100644 libusb/libusb-dload.h
 create mode 100644 m4/au_check_lib_soname.m4
 create mode 100644 m4/au_have_gnu_errno.m4

diff --git a/configure.ac b/configure.ac
index debfef2..18a86c2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -25,6 +25,29 @@ AC_SUBST(LIBUSB01_VERSION)
 PKG_CHECK_MODULES([LIBUSB_1_0], libusb-1.0 >= 0.9.1)
 AC_SUBST(LIBUSB_1_0_CFLAGS)
 AC_SUBST(LIBUSB_1_0_LIBS)
+# dlopen wrapper needs a location of libusb.h.
+# Setting of LIBUSB_H variable can override pkg-config.
+if test x"$LIBUSB_H" = x ; then
+	LIBUSB_H=`echo "$LIBUSB_1_0_CFLAGS" | sed 's/^.*-I//;s/ .*//'`/libusb.h
+fi
+AC_CHECK_FILE([$LIBUSB_H], [
+	AC_SUBST([LIBUSB_H])], [
+	LIBUSB_H=""])
+
+# Checks for dynamic loading of libusb-1.0
+ac_save_LIBS="$LIBS"
+AC_CHECK_HEADER([dlfcn.h], [
+	AS_IF([test x"$LIBUSB_H" != x], [
+		AC_CHECK_LIB([dl], [dlopen], [
+			AC_CHECK_LIB([dl], [dlsym], [
+				AU_CHECK_LIB_SONAME([LIBUSB_1_0], [usb-1.0], [libusb_open])])])])])
+LIBS="$ac_save_LIBS"
+if test x"$LIBUSB_1_0_SONAME" != x ; then
+	AC_DEFINE_UNQUOTED([LIBUSB_1_0_SONAME], ["$LIBUSB_1_0_SONAME"], [SONAME of libusb-1.0. If defined, libusb-1.0 can be hidden.])
+fi
+AM_CONDITIONAL([USE_LIBUSB_1_0_DLOAD], [test x"$LIBUSB_1_0_SONAME" != x])
+
+AU_HAVE_GNU_ERRNO_H
 
 # Message logging
 AC_ARG_ENABLE([log], [AS_HELP_STRING([--disable-log], [disable all logging])],
diff --git a/libusb/Makefile.am b/libusb/Makefile.am
index 33a609a..39da7c2 100644
--- a/libusb/Makefile.am
+++ b/libusb/Makefile.am
@@ -1,9 +1,18 @@
 include_HEADERS = usb.h
 lib_LTLIBRARIES = libusb.la
 
-libusb_la_SOURCES = core.c usbi.h
+libusb_la_SOURCES = core.c usbi.h libusb-dload.h
 libusb_la_CFLAGS = -fvisibility=hidden $(AM_CFLAGS) $(LIBUSB_1_0_CFLAGS)
+if USE_LIBUSB_1_0_DLOAD
+libusb_la_LIBADD = -ldl
+BUILT_SOURCES = libusb-wrapped.h
+else
 libusb_la_LIBADD = $(LIBUSB_1_0_LIBS)
+endif
 libusb_la_LDFLAGS = -version-info $(LT_MAJOR):$(LT_REVISION):$(LT_AGE) \
 	-release 0.1
 
+# Rewrite libusb.h to refer to wrapped calls.
+libusb-wrapped.h: $(LIBUSB_H) $(srcdir)/Makefile.am
+	$(SED) 's/return libusb_control_transfer/return (dl_libusb_control_transfer)/;s/\tlibusb_transfer_set_stream_id/\t(dl_libusb_transfer_set_stream_id)/;/LIBUSB_CALL libusb/{s/(/)(/;s/LIBUSB_CALL /(*dl_/;}' <$< >$@
+CONFIG_CLEAN_FILES = libusb-wrapped.h
diff --git a/libusb/core.c b/libusb/core.c
index e6e500b..b516f59 100644
--- a/libusb/core.c
+++ b/libusb/core.c
@@ -26,7 +26,11 @@
 #include <stdio.h>
 #include <string.h>
 
+#ifdef LIBUSB_1_0_SONAME
+#include "libusb-dload.h"
+#else
 #include <libusb.h>
+#endif
 
 #include "usb.h"
 #include "usbi.h"
@@ -61,12 +65,22 @@ API_EXPORTED struct usb_bus *usb_busses = NULL;
 
 #define compat_err(e) -(errno=libusb_to_errno(e))
 
+#ifdef LIBUSB_1_0_SONAME
+static void __attribute__ ((constructor)) _usb_init (void)
+{
+	libusb_dl_init ();
+}
+#endif
+
 static void __attribute__ ((destructor)) _usb_exit (void)
 {
 	if (ctx) {
 		libusb_exit (ctx);
 		ctx = NULL;
 	}
+#ifdef LIBUSB_1_0_SONAME
+	libusb_dl_exit ();
+#endif
 }
 
 static int libusb_to_errno(int result)
diff --git a/libusb/libusb-dload.h b/libusb/libusb-dload.h
new file mode 100644
index 0000000..a45a7b2
--- /dev/null
+++ b/libusb/libusb-dload.h
@@ -0,0 +1,124 @@
+/* -*- Mode: C; c-basic-offset:8 ; indent-tabs-mode:t -*- */
+/*
+ * Dynamic library wrapper hiding library symbols from libusb-1.O
+ * Copyright (C) 2015 Stanislav Brabec <sbrabec@suse.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+
+/* Include libusb.h with wrapped inlined calls and declarations in style
+ * needed for dlopen references */
+#include "libusb-wrapped.h"
+
+/* Now make defines to force use of these wrappers */
+#define libusb_bulk_transfer (dl_libusb_bulk_transfer)
+#define libusb_claim_interface (dl_libusb_claim_interface)
+#define libusb_release_interface (dl_libusb_release_interface)
+#define libusb_clear_halt (dl_libusb_clear_halt)
+#define libusb_reset_device (dl_libusb_reset_device)
+#define libusb_get_bus_number (dl_libusb_get_bus_number)
+#define libusb_open (dl_libusb_open)
+#define libusb_close (dl_libusb_close)
+#define libusb_set_configuration (dl_libusb_set_configuration)
+#define libusb_control_transfer (dl_libusb_control_transfer)
+#define libusb_detach_kernel_driver (dl_libusb_detach_kernel_driver)
+#define libusb_exit (dl_libusb_exit)
+#define libusb_set_debug (dl_libusb_set_debug)
+#define libusb_free_config_descriptor (dl_libusb_free_config_descriptor)
+#define libusb_free_device_list (dl_libusb_free_device_list)
+#define libusb_ref_device (dl_libusb_ref_device)
+#define libusb_unref_device (dl_libusb_unref_device)
+#define libusb_get_config_descriptor (dl_libusb_get_config_descriptor)
+#define libusb_free_config_descriptor (dl_libusb_free_config_descriptor)
+#define libusb_get_device_address (dl_libusb_get_device_address)
+#define libusb_get_device_descriptor (dl_libusb_get_device_descriptor)
+#define libusb_get_device_list (dl_libusb_get_device_list)
+#define libusb_free_device_list (dl_libusb_free_device_list)
+#define libusb_get_string_descriptor_ascii (dl_libusb_get_string_descriptor_ascii)
+#define libusb_init (dl_libusb_init)
+#define libusb_exit (dl_libusb_exit)
+#define libusb_set_debug (dl_libusb_set_debug)
+#define libusb_interrupt_transfer (dl_libusb_interrupt_transfer)
+#define libusb_kernel_driver_active (dl_libusb_kernel_driver_active)
+#define libusb_detach_kernel_driver (dl_libusb_detach_kernel_driver)
+#define libusb_ref_device (dl_libusb_ref_device)
+#define libusb_unref_device (dl_libusb_unref_device)
+#define libusb_release_interface (dl_libusb_release_interface)
+#define libusb_reset_device (dl_libusb_reset_device)
+#define libusb_set_configuration (dl_libusb_set_configuration)
+#define libusb_claim_interface (dl_libusb_claim_interface)
+#define libusb_set_debug (dl_libusb_set_debug)
+#define libusb_set_interface_alt_setting (dl_libusb_set_interface_alt_setting)
+#define libusb_clear_halt (dl_libusb_clear_halt)
+#define libusb_unref_device (dl_libusb_unref_device)
+
+/* And finally handle the wrapping itself */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <dlfcn.h>
+
+static void *libusb_dl_handle;
+
+#define libusb_dl_set_call(call)\
+	if (!(dl_##call = dlsym(libusb_dl_handle, #call)))\
+		goto failure;
+static inline void libusb_dl_init(void) {
+	if (!(libusb_dl_handle = dlopen(LIBUSB_1_0_SONAME, RTLD_NOW|RTLD_LOCAL)))
+		goto failure;
+	libusb_dl_set_call(libusb_bulk_transfer);
+	libusb_dl_set_call(libusb_claim_interface);
+	libusb_dl_set_call(libusb_clear_halt);
+	libusb_dl_set_call(libusb_get_bus_number);
+	libusb_dl_set_call(libusb_open);
+	libusb_dl_set_call(libusb_close);
+	libusb_dl_set_call(libusb_control_transfer);
+	libusb_dl_set_call(libusb_detach_kernel_driver);
+	libusb_dl_set_call(libusb_exit);
+	libusb_dl_set_call(libusb_free_config_descriptor);
+	libusb_dl_set_call(libusb_free_device_list);
+	libusb_dl_set_call(libusb_get_config_descriptor);
+	libusb_dl_set_call(libusb_get_device_address);
+	libusb_dl_set_call(libusb_get_device_descriptor);
+	libusb_dl_set_call(libusb_get_device_list);
+	libusb_dl_set_call(libusb_get_string_descriptor_ascii);
+	libusb_dl_set_call(libusb_init);
+	libusb_dl_set_call(libusb_interrupt_transfer);
+	libusb_dl_set_call(libusb_kernel_driver_active);
+	libusb_dl_set_call(libusb_ref_device);
+	libusb_dl_set_call(libusb_release_interface);
+	libusb_dl_set_call(libusb_reset_device);
+	libusb_dl_set_call(libusb_set_configuration);
+	libusb_dl_set_call(libusb_set_debug);
+	libusb_dl_set_call(libusb_set_interface_alt_setting);
+	libusb_dl_set_call(libusb_unref_device);
+	return;
+failure:
+#ifdef HAVE_GNU_ERRNO_H
+	fprintf(stderr, "%s: error while loading " LIBUSB_1_0_SONAME " from libusb-0.1.so.4: %s\n",
+		program_invocation_name, dlerror());
+#else
+	fprintf(stderr, "libusb-compat: error while loading " LIBUSB_1_0_SONAME " from libusb-0.1.so.4: %s\n",
+		dlerror());
+#endif
+	exit(127);
+};
+
+static inline void libusb_dl_exit(void) {
+	dlclose(libusb_dl_handle);
+};
diff --git a/m4/au_check_lib_soname.m4 b/m4/au_check_lib_soname.m4
new file mode 100644
index 0000000..d7e8774
--- /dev/null
+++ b/m4/au_check_lib_soname.m4
@@ -0,0 +1,43 @@
+m4_pattern_allow([^AU_])
+
+# AU_CHECK_LIB_SONAME(VARIABLE, LIBRARY, FUNCTION,
+#                     [ACTION-IF-FOUND], [ACTION-IF-FOUND-BUT-NO-SONAME],
+#                     [ACTION-IF-NOT-FOUND],
+#                     [OTHER-LIBRARIES])
+#------------------------------------------------------------------------
+# This is similar to AC_CHECK_LIB, but also sets LIB${VARIABLE}_SONAME
+# If SONAME is not found and ACTION-IF-FOUND-BUT-NO-SONAME is called,
+# it still does things which does AC_CHECK_LIB for ACTION-IF-FOUND.
+AC_DEFUN([AU_CHECK_LIB_SONAME], [
+  AC_REQUIRE([LT_INIT])
+  AS_VAR_PUSHDEF([ac_Lib_SONAME], [au_cv_lib_soname_$1])
+  AC_ARG_VAR([$1][_SONAME], [SONAME of lib$2, overriding ldd check])
+  AC_CHECK_LIB($2,$3,[
+    AC_PATH_PROG([PATH_LDD], [ldd])
+    AC_CACHE_CHECK([for SONAME of lib$2], [ac_Lib_SONAME],[
+      AS_IF([test x"$[$1][_SONAME]" = x""], [
+        AS_IF([test x"$PATH_LDD" != x""], [
+          AS_VAR_SET([ac_Lib_SONAME], ["unknown"])
+          AU_CHECK_LIB_SONAME_LIBS="$LIBS"
+          LIBS="$LIBS $7 -l$2"
+          shrext_regexp=`echo "$shrext_cmds" | sed 's/\./\\\\./'`
+          AC_TRY_LINK([
+void libusb_close(void *);
+], [
+libusb_close((void*)0);
+],
+              [AS_VAR_SET([ac_Lib_SONAME], [`ldd conftest$ac_exeext | grep 'lib[$2]'$shrext_regexp | sed 's/^@<:@ \t@:>@*lib[$2]'$shrext_regexp'/lib[$2]'$shrext_regexp'/;s/@<:@ \t@:>@.*$//'`])])
+          LIBS="$AU_CHECK_LIB_SONAME_LIBS"
+          AS_IF([test x"$ac_Lib_SONAME" = x ],
+            [AS_VAR_SET([ac_Lib_SONAME], [unknown])])
+          AS_IF([test x"$ac_Lib_SONAME" != x"unknown" ], [
+            AS_VAR_SET([$1][_SONAME], ["$ac_Lib_SONAME"])
+            $4], [
+            $5])], [
+          AS_VAR_SET([ac_Lib_SONAME], [unknown])
+          $5])], [
+        AS_VAR_SET([ac_Lib_SONAME], ["$[$1][_SONAME]"])
+        $4])])],
+    [$6], [$7])
+  AS_VAR_POPDEF([ac_Lib_SONAME])
+])
diff --git a/m4/au_have_gnu_errno.m4 b/m4/au_have_gnu_errno.m4
new file mode 100644
index 0000000..c06cbf1
--- /dev/null
+++ b/m4/au_have_gnu_errno.m4
@@ -0,0 +1,17 @@
+m4_pattern_allow([^AU_])
+
+# AU_HAVE_GNU_ERRNO_H
+#--------------------
+# Check for GNU extensions of errno.h.
+AC_DEFUN([AU_HAVE_GNU_ERRNO_H], [
+  AC_CACHE_CHECK([for GNU extensions of errno.h], [ac_cv_have_gnu_errno_h],[
+    AC_TRY_COMPILE([
+#define _GNU_SOURCE
+#include <errno.h>
+    ], [char *testvar = program_invocation_name;],
+    [ac_cv_have_gnu_errno_h=yes], [ac_cv_have_gnu_errno_h=no])])
+  AS_IF([test x"$ac_cv_have_gnu_errno_h" = x"yes"], [
+    AC_DEFINE([HAVE_GNU_ERRNO_H], [1], [Define to 1 if you have the <errno.h> header file with GNU extensions.])
+    AC_DEFINE([_GNU_SOURCE], [], [Force _GNU_SOURCE from AC_HAVE[]_GNU_ERRNO_H.])
+    ])
+])
-- 
2.4.5

