diff mbox

["PATCH,alsa-lib"] Change snd_dlopen() function to return the error string

Message ID 20171127085016.11339-1-perex@perex.cz (mailing list archive)
State New, archived
Headers show

Commit Message

Jaroslav Kysela Nov. 27, 2017, 8:50 a.m. UTC
The dlopen() function might fail also for another reason than
a missing file, thus return the error string from dlerror().

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
 include/global.h        |  2 +-
 src/Versions.in         |  5 +++++
 src/conf.c              | 13 ++++++-----
 src/dlmisc.c            | 60 +++++++++++++++++++++++++++++++++++++++----------
 src/hwdep/hwdep.c       |  6 ++---
 src/mixer/simple_abst.c | 10 ++++-----
 src/pcm/pcm_hooks.c     |  8 +++----
 src/pcm/pcm_meter.c     |  6 ++---
 src/rawmidi/rawmidi.c   |  6 ++---
 src/seq/seq.c           |  6 ++---
 src/timer/timer.c       |  6 ++---
 src/timer/timer_query.c |  6 ++---
 12 files changed, 88 insertions(+), 46 deletions(-)

Comments

Takashi Sakamoto Nov. 27, 2017, 6:19 p.m. UTC | #1
Hi Jaroslav,

On Nov 27 2017 17:50, Jaroslav Kysela wrote:
> The dlopen() function might fail also for another reason than
> a missing file, thus return the error string from dlerror().
> 
> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
> ---
>   include/global.h        |  2 +-
>   src/Versions.in         |  5 +++++
>   src/conf.c              | 13 ++++++-----
>   src/dlmisc.c            | 60 +++++++++++++++++++++++++++++++++++++++----------
>   src/hwdep/hwdep.c       |  6 ++---
>   src/mixer/simple_abst.c | 10 ++++-----
>   src/pcm/pcm_hooks.c     |  8 +++----
>   src/pcm/pcm_meter.c     |  6 ++---
>   src/rawmidi/rawmidi.c   |  6 ++---
>   src/seq/seq.c           |  6 ++---
>   src/timer/timer.c       |  6 ++---
>   src/timer/timer_query.c |  6 ++---
>   12 files changed, 88 insertions(+), 46 deletions(-)

Unfortunately, this patch brings build error in my environment (Ubuntu 
17.10 amd64, gcc7.2.0 from gcc-7 7.2.0-8ubuntu3).

$ ./gitcompile
$ make
make[2]: Entering directory '/tmp/alsa-lib/src'
   CCLD     libasound.la
/usr/bin/ld: unable to find version dependency `ALSA_1.1.6'
pcm/.libs/libpcm.a(pcm_generic.o): In function `snd1_pcm_generic_hwsync':
/tmp/alsa-lib/src/pcm/pcm_generic.c:142: warning:
collect2: error: ld returned 1 exit status
Makefile:491: recipe for target 'libasound.la' failed
make[2]: *** [libasound.la] Error 1

At present, I don't know exactly the reason, but I'll start 
investigating it in next morning (I'd like to have sleep...).

I found some nitpicking.

1. 'modules/mixer/simple/sbasedl.c' calls this function but missing 
changes in this patch.

(As I suggested to you in a previous Audiominiconf, it's good to us to 
investigate simplification of the unused abstraction of 'simple' mixer 
functionality.)

2. A line includes a tab only.

> diff --git a/src/dlmisc.c b/src/dlmisc.c
> index f154ebd0..fca843e1 100644
> --- a/src/dlmisc.c
> +++ b/src/dlmisc.c
> @@ -73,24 +79,49 @@ void *snd_dlopen(const char *name, int mode)
> ...
> +errpath:
> +	if (errbuf)
> +		snprintf(errbuf, errbuflen, "%s: %s", filename, dlerror());
> +	return NULL;
> +	
>   #else
>   	return NULL;
>   #endif
>   }


Regards

Takashi Sakamoto
Takashi Iwai Nov. 27, 2017, 8:24 p.m. UTC | #2
On Mon, 27 Nov 2017 19:19:51 +0100,
Takashi Sakamoto wrote:
> 
> Hi Jaroslav,
> 
> On Nov 27 2017 17:50, Jaroslav Kysela wrote:
> > The dlopen() function might fail also for another reason than
> > a missing file, thus return the error string from dlerror().
> >
> > Signed-off-by: Jaroslav Kysela <perex@perex.cz>
> > ---
> >   include/global.h        |  2 +-
> >   src/Versions.in         |  5 +++++
> >   src/conf.c              | 13 ++++++-----
> >   src/dlmisc.c            | 60 +++++++++++++++++++++++++++++++++++++++----------
> >   src/hwdep/hwdep.c       |  6 ++---
> >   src/mixer/simple_abst.c | 10 ++++-----
> >   src/pcm/pcm_hooks.c     |  8 +++----
> >   src/pcm/pcm_meter.c     |  6 ++---
> >   src/rawmidi/rawmidi.c   |  6 ++---
> >   src/seq/seq.c           |  6 ++---
> >   src/timer/timer.c       |  6 ++---
> >   src/timer/timer_query.c |  6 ++---
> >   12 files changed, 88 insertions(+), 46 deletions(-)
> 
> Unfortunately, this patch brings build error in my environment (Ubuntu
> 17.10 amd64, gcc7.2.0 from gcc-7 7.2.0-8ubuntu3).
> 
> $ ./gitcompile
> $ make
> make[2]: Entering directory '/tmp/alsa-lib/src'
>   CCLD     libasound.la
> /usr/bin/ld: unable to find version dependency `ALSA_1.1.6'
> pcm/.libs/libpcm.a(pcm_generic.o): In function `snd1_pcm_generic_hwsync':
> /tmp/alsa-lib/src/pcm/pcm_generic.c:142: warning:
> collect2: error: ld returned 1 exit status
> Makefile:491: recipe for target 'libasound.la' failed
> make[2]: *** [libasound.la] Error 1
> 
> At present, I don't know exactly the reason, but I'll start
> investigating it in next morning (I'd like to have sleep...).

It's likely a bogus definition in version symbols.
It has to be a form like:

  ALSA_1.1.6 {
    global:
      @SYMBOL_PREFIX@snd_dlopen;
  } ALSA_1.1.5;

while the patch put "ALSA_1.1.6" to both places.

Besides that, I'm scared by the versioned symbols, of which I have
only a bad memory.

Can we simply introduce another function (e.g. snd_dlopen_new() or
whatever) and make the existing snd_dlopen() a wrapper for that?


thanks,

Takashi
Jaroslav Kysela Nov. 27, 2017, 8:49 p.m. UTC | #3
Dne 27.11.2017 v 21:24 Takashi Iwai napsal(a):
> On Mon, 27 Nov 2017 19:19:51 +0100,
> Takashi Sakamoto wrote:
>>
>> Hi Jaroslav,
>>
>> On Nov 27 2017 17:50, Jaroslav Kysela wrote:
>>> The dlopen() function might fail also for another reason than
>>> a missing file, thus return the error string from dlerror().
>>>
>>> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
>>> ---
>>>   include/global.h        |  2 +-
>>>   src/Versions.in         |  5 +++++
>>>   src/conf.c              | 13 ++++++-----
>>>   src/dlmisc.c            | 60 +++++++++++++++++++++++++++++++++++++++----------
>>>   src/hwdep/hwdep.c       |  6 ++---
>>>   src/mixer/simple_abst.c | 10 ++++-----
>>>   src/pcm/pcm_hooks.c     |  8 +++----
>>>   src/pcm/pcm_meter.c     |  6 ++---
>>>   src/rawmidi/rawmidi.c   |  6 ++---
>>>   src/seq/seq.c           |  6 ++---
>>>   src/timer/timer.c       |  6 ++---
>>>   src/timer/timer_query.c |  6 ++---
>>>   12 files changed, 88 insertions(+), 46 deletions(-)
>>
>> Unfortunately, this patch brings build error in my environment (Ubuntu
>> 17.10 amd64, gcc7.2.0 from gcc-7 7.2.0-8ubuntu3).
>>
>> $ ./gitcompile
>> $ make
>> make[2]: Entering directory '/tmp/alsa-lib/src'
>>   CCLD     libasound.la
>> /usr/bin/ld: unable to find version dependency `ALSA_1.1.6'
>> pcm/.libs/libpcm.a(pcm_generic.o): In function `snd1_pcm_generic_hwsync':
>> /tmp/alsa-lib/src/pcm/pcm_generic.c:142: warning:
>> collect2: error: ld returned 1 exit status
>> Makefile:491: recipe for target 'libasound.la' failed
>> make[2]: *** [libasound.la] Error 1
>>
>> At present, I don't know exactly the reason, but I'll start
>> investigating it in next morning (I'd like to have sleep...).
> 
> It's likely a bogus definition in version symbols.
> It has to be a form like:
> 
>   ALSA_1.1.6 {
>     global:
>       @SYMBOL_PREFIX@snd_dlopen;
>   } ALSA_1.1.5;
> 
> while the patch put "ALSA_1.1.6" to both places.

Yes, I forgot to modify src/Versions.in (I modified only src/Versions
for my local build).

> Besides that, I'm scared by the versioned symbols, of which I have
> only a bad memory.

I know, but the major issue that we forgot to apply old symbol versions
to some functions in the past, right ? I don't see any drawback.

I applied this patchset with other configure.ac cleanups. I need to fix
build for python3, but that's a different story.

				Jaroslav
Takashi Iwai Nov. 27, 2017, 9:24 p.m. UTC | #4
On Mon, 27 Nov 2017 21:49:06 +0100,
Jaroslav Kysela wrote:
> 
> Dne 27.11.2017 v 21:24 Takashi Iwai napsal(a):
> > On Mon, 27 Nov 2017 19:19:51 +0100,
> > Takashi Sakamoto wrote:
> >>
> >> Hi Jaroslav,
> >>
> >> On Nov 27 2017 17:50, Jaroslav Kysela wrote:
> >>> The dlopen() function might fail also for another reason than
> >>> a missing file, thus return the error string from dlerror().
> >>>
> >>> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
> >>> ---
> >>>   include/global.h        |  2 +-
> >>>   src/Versions.in         |  5 +++++
> >>>   src/conf.c              | 13 ++++++-----
> >>>   src/dlmisc.c            | 60 +++++++++++++++++++++++++++++++++++++++----------
> >>>   src/hwdep/hwdep.c       |  6 ++---
> >>>   src/mixer/simple_abst.c | 10 ++++-----
> >>>   src/pcm/pcm_hooks.c     |  8 +++----
> >>>   src/pcm/pcm_meter.c     |  6 ++---
> >>>   src/rawmidi/rawmidi.c   |  6 ++---
> >>>   src/seq/seq.c           |  6 ++---
> >>>   src/timer/timer.c       |  6 ++---
> >>>   src/timer/timer_query.c |  6 ++---
> >>>   12 files changed, 88 insertions(+), 46 deletions(-)
> >>
> >> Unfortunately, this patch brings build error in my environment (Ubuntu
> >> 17.10 amd64, gcc7.2.0 from gcc-7 7.2.0-8ubuntu3).
> >>
> >> $ ./gitcompile
> >> $ make
> >> make[2]: Entering directory '/tmp/alsa-lib/src'
> >>   CCLD     libasound.la
> >> /usr/bin/ld: unable to find version dependency `ALSA_1.1.6'
> >> pcm/.libs/libpcm.a(pcm_generic.o): In function `snd1_pcm_generic_hwsync':
> >> /tmp/alsa-lib/src/pcm/pcm_generic.c:142: warning:
> >> collect2: error: ld returned 1 exit status
> >> Makefile:491: recipe for target 'libasound.la' failed
> >> make[2]: *** [libasound.la] Error 1
> >>
> >> At present, I don't know exactly the reason, but I'll start
> >> investigating it in next morning (I'd like to have sleep...).
> > 
> > It's likely a bogus definition in version symbols.
> > It has to be a form like:
> > 
> >   ALSA_1.1.6 {
> >     global:
> >       @SYMBOL_PREFIX@snd_dlopen;
> >   } ALSA_1.1.5;
> > 
> > while the patch put "ALSA_1.1.6" to both places.
> 
> Yes, I forgot to modify src/Versions.in (I modified only src/Versions
> for my local build).
> 
> > Besides that, I'm scared by the versioned symbols, of which I have
> > only a bad memory.
> 
> I know, but the major issue that we forgot to apply old symbol versions
> to some functions in the past, right ? I don't see any drawback.

IIRC, some apps didn't compile the stuff properly with the versioned
symbols, thus fall back to bind with the old symbols without error,
which leads to a crash at runtime.

Another case is when an app or a library does symbol resolution by
itself via dlopen and dlsym.  libao does it, for example.

The snd_dlopen() isn't supposed to be used widely in external apps, so
maybe the influence is limited.  But I still feel bad with the usage
of versioned symbols unless really needed.  In this case, we can avoid
the issue just by adding another better function.  And, the purpose is
only for a better debuggability, so the change in caller side isn't
mandatory.


thanks,

Takashi
Jaroslav Kysela Nov. 28, 2017, 7:26 a.m. UTC | #5
Dne 27.11.2017 v 22:24 Takashi Iwai napsal(a):
> On Mon, 27 Nov 2017 21:49:06 +0100,
> Jaroslav Kysela wrote:
>>
>> Dne 27.11.2017 v 21:24 Takashi Iwai napsal(a):
>>> On Mon, 27 Nov 2017 19:19:51 +0100,
>>> Takashi Sakamoto wrote:
>>>>
>>>> Hi Jaroslav,
>>>>
>>>> On Nov 27 2017 17:50, Jaroslav Kysela wrote:
>>>>> The dlopen() function might fail also for another reason than
>>>>> a missing file, thus return the error string from dlerror().
>>>>>
>>>>> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
>>>>> ---
>>>>>   include/global.h        |  2 +-
>>>>>   src/Versions.in         |  5 +++++
>>>>>   src/conf.c              | 13 ++++++-----
>>>>>   src/dlmisc.c            | 60 +++++++++++++++++++++++++++++++++++++++----------
>>>>>   src/hwdep/hwdep.c       |  6 ++---
>>>>>   src/mixer/simple_abst.c | 10 ++++-----
>>>>>   src/pcm/pcm_hooks.c     |  8 +++----
>>>>>   src/pcm/pcm_meter.c     |  6 ++---
>>>>>   src/rawmidi/rawmidi.c   |  6 ++---
>>>>>   src/seq/seq.c           |  6 ++---
>>>>>   src/timer/timer.c       |  6 ++---
>>>>>   src/timer/timer_query.c |  6 ++---
>>>>>   12 files changed, 88 insertions(+), 46 deletions(-)
>>>>
>>>> Unfortunately, this patch brings build error in my environment (Ubuntu
>>>> 17.10 amd64, gcc7.2.0 from gcc-7 7.2.0-8ubuntu3).
>>>>
>>>> $ ./gitcompile
>>>> $ make
>>>> make[2]: Entering directory '/tmp/alsa-lib/src'
>>>>   CCLD     libasound.la
>>>> /usr/bin/ld: unable to find version dependency `ALSA_1.1.6'
>>>> pcm/.libs/libpcm.a(pcm_generic.o): In function `snd1_pcm_generic_hwsync':
>>>> /tmp/alsa-lib/src/pcm/pcm_generic.c:142: warning:
>>>> collect2: error: ld returned 1 exit status
>>>> Makefile:491: recipe for target 'libasound.la' failed
>>>> make[2]: *** [libasound.la] Error 1
>>>>
>>>> At present, I don't know exactly the reason, but I'll start
>>>> investigating it in next morning (I'd like to have sleep...).
>>>
>>> It's likely a bogus definition in version symbols.
>>> It has to be a form like:
>>>
>>>   ALSA_1.1.6 {
>>>     global:
>>>       @SYMBOL_PREFIX@snd_dlopen;
>>>   } ALSA_1.1.5;
>>>
>>> while the patch put "ALSA_1.1.6" to both places.
>>
>> Yes, I forgot to modify src/Versions.in (I modified only src/Versions
>> for my local build).
>>
>>> Besides that, I'm scared by the versioned symbols, of which I have
>>> only a bad memory.
>>
>> I know, but the major issue that we forgot to apply old symbol versions
>> to some functions in the past, right ? I don't see any drawback.
> 
> IIRC, some apps didn't compile the stuff properly with the versioned
> symbols, thus fall back to bind with the old symbols without error,
> which leads to a crash at runtime.

It's probably a linking issue which should be resolved by the developer.

> Another case is when an app or a library does symbol resolution by
> itself via dlopen and dlsym.  libao does it, for example.

The developers might use dlvsym() or recompile and fix compilation
errors for new library.

> The snd_dlopen() isn't supposed to be used widely in external apps, so
> maybe the influence is limited.  But I still feel bad with the usage
> of versioned symbols unless really needed.  In this case, we can avoid
> the issue just by adding another better function.  And, the purpose is
> only for a better debuggability, so the change in caller side isn't
> mandatory.

Yep, snd_dlopen() is probably not used outside alsa-lib at all.

The glibc is full of versioned symbols and it seems that it plays good
with them.

					Jaroslav
Takashi Iwai Nov. 28, 2017, 8:02 a.m. UTC | #6
On Tue, 28 Nov 2017 08:26:09 +0100,
Jaroslav Kysela wrote:
> 
> Dne 27.11.2017 v 22:24 Takashi Iwai napsal(a):
> > On Mon, 27 Nov 2017 21:49:06 +0100,
> > Jaroslav Kysela wrote:
> >>
> >> Dne 27.11.2017 v 21:24 Takashi Iwai napsal(a):
> >>> On Mon, 27 Nov 2017 19:19:51 +0100,
> >>> Takashi Sakamoto wrote:
> >>>>
> >>>> Hi Jaroslav,
> >>>>
> >>>> On Nov 27 2017 17:50, Jaroslav Kysela wrote:
> >>>>> The dlopen() function might fail also for another reason than
> >>>>> a missing file, thus return the error string from dlerror().
> >>>>>
> >>>>> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
> >>>>> ---
> >>>>>   include/global.h        |  2 +-
> >>>>>   src/Versions.in         |  5 +++++
> >>>>>   src/conf.c              | 13 ++++++-----
> >>>>>   src/dlmisc.c            | 60 +++++++++++++++++++++++++++++++++++++++----------
> >>>>>   src/hwdep/hwdep.c       |  6 ++---
> >>>>>   src/mixer/simple_abst.c | 10 ++++-----
> >>>>>   src/pcm/pcm_hooks.c     |  8 +++----
> >>>>>   src/pcm/pcm_meter.c     |  6 ++---
> >>>>>   src/rawmidi/rawmidi.c   |  6 ++---
> >>>>>   src/seq/seq.c           |  6 ++---
> >>>>>   src/timer/timer.c       |  6 ++---
> >>>>>   src/timer/timer_query.c |  6 ++---
> >>>>>   12 files changed, 88 insertions(+), 46 deletions(-)
> >>>>
> >>>> Unfortunately, this patch brings build error in my environment (Ubuntu
> >>>> 17.10 amd64, gcc7.2.0 from gcc-7 7.2.0-8ubuntu3).
> >>>>
> >>>> $ ./gitcompile
> >>>> $ make
> >>>> make[2]: Entering directory '/tmp/alsa-lib/src'
> >>>>   CCLD     libasound.la
> >>>> /usr/bin/ld: unable to find version dependency `ALSA_1.1.6'
> >>>> pcm/.libs/libpcm.a(pcm_generic.o): In function `snd1_pcm_generic_hwsync':
> >>>> /tmp/alsa-lib/src/pcm/pcm_generic.c:142: warning:
> >>>> collect2: error: ld returned 1 exit status
> >>>> Makefile:491: recipe for target 'libasound.la' failed
> >>>> make[2]: *** [libasound.la] Error 1
> >>>>
> >>>> At present, I don't know exactly the reason, but I'll start
> >>>> investigating it in next morning (I'd like to have sleep...).
> >>>
> >>> It's likely a bogus definition in version symbols.
> >>> It has to be a form like:
> >>>
> >>>   ALSA_1.1.6 {
> >>>     global:
> >>>       @SYMBOL_PREFIX@snd_dlopen;
> >>>   } ALSA_1.1.5;
> >>>
> >>> while the patch put "ALSA_1.1.6" to both places.
> >>
> >> Yes, I forgot to modify src/Versions.in (I modified only src/Versions
> >> for my local build).
> >>
> >>> Besides that, I'm scared by the versioned symbols, of which I have
> >>> only a bad memory.
> >>
> >> I know, but the major issue that we forgot to apply old symbol versions
> >> to some functions in the past, right ? I don't see any drawback.
> > 
> > IIRC, some apps didn't compile the stuff properly with the versioned
> > symbols, thus fall back to bind with the old symbols without error,
> > which leads to a crash at runtime.
> 
> It's probably a linking issue which should be resolved by the developer.
>
> > Another case is when an app or a library does symbol resolution by
> > itself via dlopen and dlsym.  libao does it, for example.
> 
> The developers might use dlvsym() or recompile and fix compilation
> errors for new library.

Both cases can be addressed in the application side, yes.
But the problem is that it *requires* the fix in application side.

We have to provide both old and new APIs in anyway, no matter whether
versioned symbol is used or not.  Then what's the merit of the
versioned symbol, after all...?


> > The snd_dlopen() isn't supposed to be used widely in external apps, so
> > maybe the influence is limited.  But I still feel bad with the usage
> > of versioned symbols unless really needed.  In this case, we can avoid
> > the issue just by adding another better function.  And, the purpose is
> > only for a better debuggability, so the change in caller side isn't
> > mandatory.
> 
> Yep, snd_dlopen() is probably not used outside alsa-lib at all.
> 
> The glibc is full of versioned symbols and it seems that it plays good
> with them.

glibc functions are not referred with dlopen/dlsym, so the issue above
doesn't happen.  But we're in a different situation, and the versioned
symbols make thing more complicated than needed, IMO.


thanks,

Takashi
diff mbox

Patch

diff --git a/include/global.h b/include/global.h
index 41680829..d73d333a 100644
--- a/include/global.h
+++ b/include/global.h
@@ -97,7 +97,7 @@  extern struct snd_dlsym_link *snd_dlsym_start;
 /** \brief Returns the version of a dynamic symbol as a string. */
 #define SND_DLSYM_VERSION(version) __STRING(version)
 
-void *snd_dlopen(const char *file, int mode);
+void *snd_dlopen(const char *file, int mode, char *errbuf, size_t errbuflen);
 void *snd_dlsym(void *handle, const char *name, const char *version);
 int snd_dlclose(void *handle);
 
diff --git a/src/Versions.in b/src/Versions.in
index 8d2dd116..2f9e1697 100644
--- a/src/Versions.in
+++ b/src/Versions.in
@@ -129,3 +129,8 @@  ALSA_0.9.7 {
     @SYMBOL_PREFIX@alsa_lisp_*;
 } ALSA_0.9.5;
 
+ALSA_1.1.6 {
+  global:
+
+    @SYMBOL_PREFIX@snd_dlopen;
+} ALSA_1.1.6;
diff --git a/src/conf.c b/src/conf.c
index c410bfb8..e71bb972 100644
--- a/src/conf.c
+++ b/src/conf.c
@@ -982,6 +982,7 @@  static int get_freestring(char **string, int id, input_t *input)
 		case '.':
 			if (!id)
 				break;
+			/* fall through */
 		case ' ':
 		case '\f':
 		case '\t':
@@ -3477,7 +3478,7 @@  static int snd_config_hooks_call(snd_config_t *root, snd_config_t *config, snd_c
 {
 	void *h = NULL;
 	snd_config_t *c, *func_conf = NULL;
-	char *buf = NULL;
+	char *buf = NULL, errbuf[256];
 	const char *lib = NULL, *func_name = NULL;
 	const char *str;
 	int (*func)(snd_config_t *root, snd_config_t *config, snd_config_t **dst, snd_config_t *private_data) = NULL;
@@ -3537,11 +3538,11 @@  static int snd_config_hooks_call(snd_config_t *root, snd_config_t *config, snd_c
 		buf[len-1] = '\0';
 		func_name = buf;
 	}
-	h = snd_dlopen(lib, RTLD_NOW);
+	h = snd_dlopen(lib, RTLD_NOW, errbuf, sizeof(errbuf));
 	func = h ? snd_dlsym(h, func_name, SND_DLSYM_VERSION(SND_CONFIG_DLSYM_VERSION_HOOK)) : NULL;
 	err = 0;
 	if (!h) {
-		SNDERR("Cannot open shared library %s", lib);
+		SNDERR("Cannot open shared library %s (%s)", lib, errbuf);
 		err = -ENOENT;
 	} else if (!func) {
 		SNDERR("symbol %s is not defined inside %s", func_name, lib);
@@ -4471,7 +4472,7 @@  static int _snd_config_evaluate(snd_config_t *src,
 {
 	int err;
 	if (pass == SND_CONFIG_WALK_PASS_PRE) {
-		char *buf = NULL;
+		char *buf = NULL, errbuf[256];
 		const char *lib = NULL, *func_name = NULL;
 		const char *str;
 		int (*func)(snd_config_t **dst, snd_config_t *root,
@@ -4530,12 +4531,12 @@  static int _snd_config_evaluate(snd_config_t *src,
 			buf[len-1] = '\0';
 			func_name = buf;
 		}
-		h = snd_dlopen(lib, RTLD_NOW);
+		h = snd_dlopen(lib, RTLD_NOW, errbuf, sizeof(errbuf));
 		if (h)
 			func = snd_dlsym(h, func_name, SND_DLSYM_VERSION(SND_CONFIG_DLSYM_VERSION_EVALUATE));
 		err = 0;
 		if (!h) {
-			SNDERR("Cannot open shared library %s", lib);
+			SNDERR("Cannot open shared library %s (%s)", lib, errbuf);
 			err = -ENOENT;
 			goto _errbuf;
 		} else if (!func) {
diff --git a/src/dlmisc.c b/src/dlmisc.c
index f154ebd0..fca843e1 100644
--- a/src/dlmisc.c
+++ b/src/dlmisc.c
@@ -43,12 +43,18 @@  struct snd_dlsym_link *snd_dlsym_start = NULL;
  * \brief Opens a dynamic library - ALSA wrapper for \c dlopen.
  * \param name name of the library, similar to \c dlopen.
  * \param mode mode flags, similar to \c dlopen.
+ * \param errbuf a string buffer for the error message \c dlerror.
+ * \param errbuflen a length of the string buffer for the error message.
  * \return Library handle if successful, otherwise \c NULL.
  *
  * This function can emulate dynamic linking for the static build of
  * the alsa-lib library. In that case, \p name is set to \c NULL.
  */
-void *snd_dlopen(const char *name, int mode)
+#ifndef DOXYGEN
+void *INTERNAL(snd_dlopen)(const char *name, int mode, char *errbuf, size_t errbuflen)
+#else
+void *snd_dlopen(const char *name, int mode, char *errbuf, size_t errbuflen)
+#endif
 {
 #ifndef PIC
 	if (name == NULL)
@@ -73,24 +79,49 @@  void *snd_dlopen(const char *name, int mode)
 	 * via ld.so.conf.
 	 */
 	void *handle = NULL;
-	char *filename;
+	char *filename = NULL;
 
 	if (name && name[0] != '/') {
-		filename = malloc(sizeof(ALSA_PLUGIN_DIR) + 1 + strlen(name) + 1);
-		strcpy(filename, ALSA_PLUGIN_DIR);
-		strcat(filename, "/");
-		strcat(filename, name);
-		handle = dlopen(filename, mode);
-		free(filename);
+		filename = alloca(sizeof(ALSA_PLUGIN_DIR) + 1 + strlen(name) + 1);
+		if (filename) {
+			strcpy(filename, ALSA_PLUGIN_DIR);
+			strcat(filename, "/");
+			strcat(filename, name);
+			handle = dlopen(filename, mode);
+			if (!handle) {
+				/* if the filename exists and cannot be opened */
+				/* return immediately */
+				if (access(filename, X_OK) == 0)
+					goto errpath;
+			}
+		}
 	}
-	if (!handle)
+	if (!handle) {
 		handle = dlopen(name, mode);
+		if (!handle)
+			goto errpath;
+	}
 	return handle;
+errpath:
+	if (errbuf)
+		snprintf(errbuf, errbuflen, "%s: %s", filename, dlerror());
+	return NULL;
+	
 #else
 	return NULL;
 #endif
 }
 
+#ifndef DOXYGEN
+void *INTERNAL(snd_dlopen_old)(const char *name, int mode)
+{
+  return INTERNAL(snd_dlopen)(name, mode, NULL, 0);
+}
+#endif
+
+use_symbol_version(__snd_dlopen_old, snd_dlopen, ALSA_0.9);
+use_default_symbol_version(__snd_dlopen, snd_dlopen, ALSA_1.1.6);
+
 /**
  * \brief Closes a dynamic library - ALSA wrapper for \c dlclose.
  * \param handle Library handle, similar to \c dlclose.
@@ -229,6 +260,7 @@  void *snd_dlobj_cache_get(const char *lib, const char *name,
 	struct list_head *p;
 	struct dlobj_cache *c;
 	void *func, *dlobj;
+	char errbuf[256];
 
 	snd_dlobj_lock();
 	list_for_each(p, &pcm_dlobj_list) {
@@ -247,11 +279,15 @@  void *snd_dlobj_cache_get(const char *lib, const char *name,
 		}
 	}
 
-	dlobj = snd_dlopen(lib, RTLD_NOW);
+	errbuf[0] = '\0';
+	dlobj = snd_dlopen(lib, RTLD_NOW,
+	                   verbose ? errbuf : 0,
+	                   verbose ? sizeof(errbuf) : 0);
 	if (dlobj == NULL) {
 		if (verbose)
-			SNDERR("Cannot open shared library %s",
-						lib ? lib : "[builtin]");
+			SNDERR("Cannot open shared library %s (%s)",
+						lib ? lib : "[builtin]",
+						errbuf);
 		snd_dlobj_unlock();
 		return NULL;
 	}
diff --git a/src/hwdep/hwdep.c b/src/hwdep/hwdep.c
index 29350936..7aa475c0 100644
--- a/src/hwdep/hwdep.c
+++ b/src/hwdep/hwdep.c
@@ -41,7 +41,7 @@  static int snd_hwdep_open_conf(snd_hwdep_t **hwdep,
 			       snd_config_t *hwdep_conf, int mode)
 {
 	const char *str;
-	char buf[256];
+	char buf[256], errbuf[256];
 	int err;
 	snd_config_t *conf, *type_conf = NULL;
 	snd_config_iterator_t i, next;
@@ -116,12 +116,12 @@  static int snd_hwdep_open_conf(snd_hwdep_t **hwdep,
 #ifndef PIC
 	snd_hwdep_open_symbols();
 #endif
-	h = snd_dlopen(lib, RTLD_NOW);
+	h = snd_dlopen(lib, RTLD_NOW, errbuf, sizeof(errbuf));
 	if (h)
 		open_func = snd_dlsym(h, open_name, SND_DLSYM_VERSION(SND_HWDEP_DLSYM_VERSION));
 	err = 0;
 	if (!h) {
-		SNDERR("Cannot open shared library %s", lib);
+		SNDERR("Cannot open shared library %s (%s)", lib, errbuf);
 		err = -ENOENT;
 	} else if (!open_func) {
 		SNDERR("symbol %s is not defined inside %s", open_name, lib);
diff --git a/src/mixer/simple_abst.c b/src/mixer/simple_abst.c
index 73528393..32a3f621 100644
--- a/src/mixer/simple_abst.c
+++ b/src/mixer/simple_abst.c
@@ -66,7 +66,7 @@  static int try_open(snd_mixer_class_t *class, const char *lib)
 	class_priv_t *priv = snd_mixer_class_get_private(class);
 	snd_mixer_event_t event_func;
 	snd_mixer_sbasic_init_t init_func = NULL;
-	char *xlib, *path;
+	char *xlib, *path, errbuf[256];
 	void *h;
 	int err = 0;
 
@@ -81,9 +81,9 @@  static int try_open(snd_mixer_class_t *class, const char *lib)
 	strcpy(xlib, path);
 	strcat(xlib, "/");
 	strcat(xlib, lib);
-	h = snd_dlopen(xlib, RTLD_NOW);
+	h = snd_dlopen(xlib, RTLD_NOW, errbuf, sizeof(errbuf));
 	if (h == NULL) {
-		SNDERR("Unable to open library '%s'", xlib);
+		SNDERR("Unable to open library '%s' (%s)", xlib, errbuf);
 		free(xlib);
 		return -ENXIO;
 	}
@@ -114,7 +114,7 @@  static int try_open_full(snd_mixer_class_t *class, snd_mixer_t *mixer,
 	class_priv_t *priv = snd_mixer_class_get_private(class);
 	snd_mixer_event_t event_func;
 	snd_mixer_sfbasic_init_t init_func = NULL;
-	char *xlib, *path;
+	char *xlib, *path, errbuf[256];
 	void *h;
 	int err = 0;
 
@@ -128,7 +128,7 @@  static int try_open_full(snd_mixer_class_t *class, snd_mixer_t *mixer,
 	strcat(xlib, "/");
 	strcat(xlib, lib);
 	/* note python modules requires RTLD_GLOBAL */
-	h = snd_dlopen(xlib, RTLD_NOW|RTLD_GLOBAL);
+	h = snd_dlopen(xlib, RTLD_NOW|RTLD_GLOBAL, errbuf, sizeof(errbuf));
 	if (h == NULL) {
 		SNDERR("Unable to open library '%s'", xlib);
 		free(xlib);
diff --git a/src/pcm/pcm_hooks.c b/src/pcm/pcm_hooks.c
index adb7230b..1f08bd78 100644
--- a/src/pcm/pcm_hooks.c
+++ b/src/pcm/pcm_hooks.c
@@ -341,7 +341,7 @@  even if the specified control doesn't exist.
 static int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *root, snd_config_t *conf)
 {
 	int err;
-	char buf[256];
+	char buf[256], errbuf[256];
 	const char *str, *id;
 	const char *lib = NULL, *install = NULL;
 	snd_config_t *type = NULL, *args = NULL;
@@ -424,12 +424,12 @@  static int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *root, snd_config_
 		install = buf;
 		snprintf(buf, sizeof(buf), "_snd_pcm_hook_%s_install", str);
 	}
-	h = snd_dlopen(lib, RTLD_NOW);
+	h = snd_dlopen(lib, RTLD_NOW, errbuf, sizeof(errbuf));
 	install_func = h ? snd_dlsym(h, install, SND_DLSYM_VERSION(SND_PCM_DLSYM_VERSION)) : NULL;
 	err = 0;
 	if (!h) {
-		SNDERR("Cannot open shared library %s",
-		       lib ? lib : "[builtin]");
+		SNDERR("Cannot open shared library %s (%s)",
+		       lib ? lib : "[builtin]", errbuf);
 		err = -ENOENT;
 	} else if (!install_func) {
 		SNDERR("symbol %s is not defined inside %s", install,
diff --git a/src/pcm/pcm_meter.c b/src/pcm/pcm_meter.c
index 53685094..75df09ca 100644
--- a/src/pcm/pcm_meter.c
+++ b/src/pcm/pcm_meter.c
@@ -605,7 +605,7 @@  int snd_pcm_meter_open(snd_pcm_t **pcmp, const char *name, unsigned int frequenc
 static int snd_pcm_meter_add_scope_conf(snd_pcm_t *pcm, const char *name,
 					snd_config_t *root, snd_config_t *conf)
 {
-	char buf[256];
+	char buf[256], errbuf[256];
 	snd_config_iterator_t i, next;
 	const char *id;
 	const char *lib = NULL, *open_name = NULL, *str = NULL;
@@ -670,11 +670,11 @@  static int snd_pcm_meter_add_scope_conf(snd_pcm_t *pcm, const char *name,
 		open_name = buf;
 		snprintf(buf, sizeof(buf), "_snd_pcm_scope_%s_open", str);
 	}
-	h = snd_dlopen(lib, RTLD_NOW);
+	h = snd_dlopen(lib, RTLD_NOW, errbuf, sizeof(errbuf));
 	open_func = h ? dlsym(h, open_name) : NULL;
 	err = 0;
 	if (!h) {
-		SNDERR("Cannot open shared library %s", lib);
+		SNDERR("Cannot open shared library %s (%s)", lib, errbuf);
 		err = -ENOENT;
 	} else if (!open_func) {
 		SNDERR("symbol %s is not defined inside %s", open_name, lib);
diff --git a/src/rawmidi/rawmidi.c b/src/rawmidi/rawmidi.c
index 7180becd..88d43017 100644
--- a/src/rawmidi/rawmidi.c
+++ b/src/rawmidi/rawmidi.c
@@ -162,7 +162,7 @@  static int snd_rawmidi_open_conf(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp
 				 snd_config_t *rawmidi_conf, int mode)
 {
 	const char *str;
-	char buf[256];
+	char buf[256], errbuf[256];
 	int err;
 	snd_config_t *conf, *type_conf = NULL;
 	snd_config_iterator_t i, next;
@@ -239,12 +239,12 @@  static int snd_rawmidi_open_conf(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp
 #ifndef PIC
 	snd_rawmidi_open_symbols();
 #endif
-	h = snd_dlopen(lib, RTLD_NOW);
+	h = snd_dlopen(lib, RTLD_NOW, errbuf, sizeof(errbuf));
 	if (h)
 		open_func = snd_dlsym(h, open_name, SND_DLSYM_VERSION(SND_RAWMIDI_DLSYM_VERSION));
 	err = 0;
 	if (!h) {
-		SNDERR("Cannot open shared library %s", lib);
+		SNDERR("Cannot open shared library %s (%s)", lib, errbuf);
 		err = -ENOENT;
 	} else if (!open_func) {
 		SNDERR("symbol %s is not defined inside %s", open_name, lib);
diff --git a/src/seq/seq.c b/src/seq/seq.c
index 630f52c3..983c4fab 100644
--- a/src/seq/seq.c
+++ b/src/seq/seq.c
@@ -823,7 +823,7 @@  static int snd_seq_open_conf(snd_seq_t **seqp, const char *name,
 			     int streams, int mode)
 {
 	const char *str;
-	char buf[256];
+	char buf[256], errbuf[256];
 	int err;
 	snd_config_t *conf, *type_conf = NULL;
 	snd_config_iterator_t i, next;
@@ -899,12 +899,12 @@  static int snd_seq_open_conf(snd_seq_t **seqp, const char *name,
 #ifndef PIC
 	snd_seq_open_symbols();
 #endif
-	h = snd_dlopen(lib, RTLD_NOW);
+	h = snd_dlopen(lib, RTLD_NOW, errbuf, sizeof(errbuf));
 	if (h)
 		open_func = snd_dlsym(h, open_name, SND_DLSYM_VERSION(SND_SEQ_DLSYM_VERSION));
 	err = 0;
 	if (!h) {
-		SNDERR("Cannot open shared library %s", lib);
+		SNDERR("Cannot open shared library %s (%s)", lib, errbuf);
 		err = -ENOENT;
 	} else if (!open_func) {
 		SNDERR("symbol %s is not defined inside %s", open_name, lib);
diff --git a/src/timer/timer.c b/src/timer/timer.c
index e027c903..8c49737e 100644
--- a/src/timer/timer.c
+++ b/src/timer/timer.c
@@ -76,7 +76,7 @@  static int snd_timer_open_conf(snd_timer_t **timer,
 			       snd_config_t *timer_conf, int mode)
 {
 	const char *str;
-	char buf[256];
+	char buf[256], errbuf[256];
 	int err;
 	snd_config_t *conf, *type_conf = NULL;
 	snd_config_iterator_t i, next;
@@ -150,12 +150,12 @@  static int snd_timer_open_conf(snd_timer_t **timer,
 #ifndef PIC
 	snd_timer_open_symbols();
 #endif
-	h = snd_dlopen(lib, RTLD_NOW);
+	h = snd_dlopen(lib, RTLD_NOW, errbuf, sizeof(errbuf));
 	if (h)
 		open_func = snd_dlsym(h, open_name, SND_DLSYM_VERSION(SND_TIMER_DLSYM_VERSION));
 	err = 0;
 	if (!h) {
-		SNDERR("Cannot open shared library %s", lib);
+		SNDERR("Cannot open shared library %s (%s)", lib, errbuf);
 		err = -ENOENT;
 	} else if (!open_func) {
 		SNDERR("symbol %s is not defined inside %s", open_name, lib);
diff --git a/src/timer/timer_query.c b/src/timer/timer_query.c
index d999d21b..6739f3c4 100644
--- a/src/timer/timer_query.c
+++ b/src/timer/timer_query.c
@@ -33,7 +33,7 @@  static int snd_timer_query_open_conf(snd_timer_query_t **timer,
 				     snd_config_t *timer_conf, int mode)
 {
 	const char *str;
-	char buf[256];
+	char buf[256], errbuf[256];
 	int err;
 	snd_config_t *conf, *type_conf = NULL;
 	snd_config_iterator_t i, next;
@@ -108,12 +108,12 @@  static int snd_timer_query_open_conf(snd_timer_query_t **timer,
 #ifndef PIC
 	snd_timer_query_open_symbols();
 #endif
-	h = snd_dlopen(lib, RTLD_NOW);
+	h = snd_dlopen(lib, RTLD_NOW, errbuf, sizeof(errbuf));
 	if (h)
 		open_func = snd_dlsym(h, open_name, SND_DLSYM_VERSION(SND_TIMER_QUERY_DLSYM_VERSION));
 	err = 0;
 	if (!h) {
-		SNDERR("Cannot open shared library %s", lib);
+		SNDERR("Cannot open shared library %s (%s)", lib, errbuf);
 		err = -ENOENT;
 	} else if (!open_func) {
 		SNDERR("symbol %s is not defined inside %s", open_name, lib);