|
From: Stanislav B. <sb...@su...> - 2015-02-23 19:11:13
|
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. 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 --- configure.ac | 14 ++++ libusb/Makefile.am | 6 +- libusb/core.c | 14 ++++ libusb/libusb-dload.h | 203 ++++++++++++++++++++++++++++++++++++++++++++++ m4/au_check_lib_soname.m4 | 39 +++++++++ m4/au_have_gnu_errno.m4 | 17 ++++ 6 files changed, 292 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..8a2750b 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_FUNC([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..6b49fec --- /dev/null +++ b/m4/au_check_lib_soname.m4 @@ -0,0 +1,39 @@ +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([], [], + [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.0 -- 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 |