|
From: Stanislav B. <sb...@su...> - 2015-05-12 15:19:18
|
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.) 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 <sb...@su...> --- configure.ac | 14 ++++ libusb/Makefile.am | 6 +- libusb/core.c | 14 ++++ libusb/libusb-dload.h | 203 ++++++++++++++++++++++++++++++++++++++++++++++ m4/au_check_lib_soname.m4 | 43 ++++++++++ m4/au_have_gnu_errno.m4 | 17 ++++ 6 files changed, 296 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..3c26903 100644 --- a/configure.ac +++ b/configure.ac @@ -26,6 +26,20 @@ PKG_CHECK_MODULES([LIBUSB_1_0], libusb-1.0 >= 0.9.1) AC_SUBST(LIBUSB_1_0_CFLAGS) AC_SUBST(LIBUSB_1_0_LIBS) +# Checks for dynamic loading of libusb-1.0 +ac_save_LIBS="$LIBS" +AC_CHECK_HEADER([dlfcn.h], [ + 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])], [log_enabled=$enableval], diff --git a/libusb/Makefile.am b/libusb/Makefile.am index 33a609a..4585025 100644 --- a/libusb/Makefile.am +++ b/libusb/Makefile.am @@ -1,9 +1,13 @@ 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 +else libusb_la_LIBADD = $(LIBUSB_1_0_LIBS) +endif libusb_la_LDFLAGS = -version-info $(LT_MAJOR):$(LT_REVISION):$(LT_AGE) \ -release 0.1 diff --git a/libusb/core.c b/libusb/core.c index e6e500b..8b5470a 100644 --- a/libusb/core.c +++ b/libusb/core.c @@ -31,6 +31,10 @@ #include "usb.h" #include "usbi.h" +#ifdef LIBUSB_1_0_SONAME +#include "libusb-dload.h" +#endif + static libusb_context *ctx = NULL; static int usb_debug = 0; @@ -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..8524645 --- /dev/null +++ b/libusb/libusb-dload.h @@ -0,0 +1,203 @@ +#include "config.h" +#include <libusb.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <dlfcn.h> + +#define libusb_bulk_transfer (dl_libusb_bulk_transfer) +static int (*dl_libusb_bulk_transfer)(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + +#define libusb_claim_interface (dl_libusb_claim_interface) +static int (*dl_libusb_claim_interface)(libusb_device_handle *dev, + int interface_number); + +#define libusb_release_interface (dl_libusb_release_interface) +static int (*dl_libusb_release_interface)(libusb_device_handle *dev, + int interface_number); + +#define libusb_clear_halt (dl_libusb_clear_halt) +static int (*dl_libusb_clear_halt)(libusb_device_handle *dev, + unsigned char endpoint); + +#define libusb_reset_device (dl_libusb_reset_device) +static int (*dl_libusb_reset_device)(libusb_device_handle *dev); + +#define libusb_get_bus_number (dl_libusb_get_bus_number) +static uint8_t (*libusb_get_bus_number)(libusb_device *dev); + +#define libusb_open (dl_libusb_open) +static int (*libusb_open)(libusb_device *dev, libusb_device_handle **handle); + +#define libusb_close (dl_libusb_close) +static void (*dl_libusb_close)(libusb_device_handle *dev_handle); + +#define libusb_set_configuration (dl_libusb_set_configuration) +static int (*dl_libusb_set_configuration)(libusb_device_handle *dev, + int configuration); + +#define libusb_control_transfer (dl_libusb_control_transfer) +static int (*dl_libusb_control_transfer)(libusb_device_handle *dev_handle, + uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + unsigned char *data, uint16_t wLength, unsigned int timeout); + +#define libusb_detach_kernel_driver (dl_libusb_detach_kernel_driver) +static int (*dl_libusb_detach_kernel_driver)(libusb_device_handle *dev, + int interface_number); + +#define libusb_exit (dl_libusb_exit) +static void (*dl_libusb_exit)(libusb_context *ctx); + +#define libusb_set_debug (dl_libusb_set_debug) +static void (*dl_libusb_set_debug)(libusb_context *ctx, int level); + +#define libusb_free_config_descriptor (dl_libusb_free_config_descriptor) +static void (*dl_libusb_free_config_descriptor)( + struct libusb_config_descriptor *config); + +#define libusb_free_device_list (dl_libusb_free_device_list) +static void (*dl_libusb_free_device_list)(libusb_device **list, + int unref_devices); + +#define libusb_ref_device (dl_libusb_ref_device) +static libusb_device * (*dl_libusb_ref_device)(libusb_device *dev); + +#define libusb_unref_device (dl_libusb_unref_device) +static void (*dl_libusb_unref_device)(libusb_device *dev); + +#define libusb_get_config_descriptor (dl_libusb_get_config_descriptor) +static int (*dl_libusb_get_config_descriptor)(libusb_device *dev, + uint8_t config_index, struct libusb_config_descriptor **config); + +#define libusb_free_config_descriptor (dl_libusb_free_config_descriptor) +static void (*dl_libusb_free_config_descriptor)( + struct libusb_config_descriptor *config); + +#define libusb_get_device_address (dl_libusb_get_device_address) +static uint8_t (*dl_libusb_get_device_address)(libusb_device *dev); + +#define libusb_get_device_descriptor (dl_libusb_get_device_descriptor) +static int (*dl_libusb_get_device_descriptor)(libusb_device *dev, + struct libusb_device_descriptor *desc); + +#define libusb_get_device_list (dl_libusb_get_device_list) +static ssize_t (*dl_libusb_get_device_list)(libusb_context *ctx, + libusb_device ***list); + +#define libusb_free_device_list (dl_libusb_free_device_list) +static void (*dl_libusb_free_device_list)(libusb_device **list, + int unref_devices); + +#define libusb_get_string_descriptor_ascii (dl_libusb_get_string_descriptor_ascii) +static int (*dl_libusb_get_string_descriptor_ascii)(libusb_device_handle *dev, + uint8_t desc_index, unsigned char *data, int length); + +#define libusb_init (dl_libusb_init) +static int (*dl_libusb_init)(libusb_context **ctx); + +#define libusb_exit (dl_libusb_exit) +static void (*dl_libusb_exit)(libusb_context *ctx); + +#define libusb_set_debug (dl_libusb_set_debug) +static void (*dl_libusb_set_debug)(libusb_context *ctx, int level); + +#define libusb_interrupt_transfer (dl_libusb_interrupt_transfer) +static int (*dl_libusb_interrupt_transfer)(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + +#define libusb_kernel_driver_active (dl_libusb_kernel_driver_active) +static int (*dl_libusb_kernel_driver_active)(libusb_device_handle *dev, + int interface_number); + +#define libusb_detach_kernel_driver (dl_libusb_detach_kernel_driver) +static int (*dl_libusb_detach_kernel_driver)(libusb_device_handle *dev, + int interface_number); + +#define libusb_ref_device (dl_libusb_ref_device) +static libusb_device * (*dl_libusb_ref_device)(libusb_device *dev); + +#define libusb_unref_device (dl_libusb_unref_device) +static void (*dl_libusb_unref_device)(libusb_device *dev); + +#define libusb_release_interface (dl_libusb_release_interface) +static int (*dl_libusb_release_interface)(libusb_device_handle *dev, + int interface_number); + +#define libusb_reset_device (dl_libusb_reset_device) +static int (*dl_libusb_reset_device)(libusb_device_handle *dev); + +#define libusb_set_configuration (dl_libusb_set_configuration) +static int (*dl_libusb_set_configuration)(libusb_device_handle *dev, + int configuration); + +#define libusb_claim_interface (dl_libusb_claim_interface) +static int (*dl_libusb_claim_interface)(libusb_device_handle *dev, + int interface_number); + +#define libusb_set_debug (dl_libusb_set_debug) +static void (*dl_libusb_set_debug)(libusb_context *ctx, int level); + +#define libusb_set_interface_alt_setting (dl_libusb_set_interface_alt_setting) +static int (*dl_libusb_set_interface_alt_setting)(libusb_device_handle *dev, + int interface_number, int alternate_setting); + +#define libusb_clear_halt (dl_libusb_clear_halt) +static int (*dl_libusb_clear_halt)(libusb_device_handle *dev, + unsigned char endpoint); + +#define libusb_unref_device (dl_libusb_unref_device) +static void (*dl_libusb_unref_device)(libusb_device *dev); + +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.3.7 -- Best Regards / S pozdravem, Stanislav Brabec software developer --------------------------------------------------------------------- SUSE LINUX, s. r. o. e-mail: sb...@su... Lihovarská 1060/12 tel: +49 911 7405384547 190 00 Praha 9 fax: +420 284 084 001 Czech Republic http://www.suse.cz/ PGP: 830B 40D5 9E05 35D8 5E27 6FA3 717C 209F A04F CD76 |