I have a special audio driver that needs additional clean-up when the ALSA (Advanced Linux Sound Architecture) function, 'snd_pcm_close', is called.
This additional clean-up can't take place inside the C code for the audio driver, and so it must happen inside the ALSA library 'libasound.so' whenever a process calls 'snd_pcm_close'. To make things a little more complicated, 'libasound.so' is a pre-compiled binary for which I don't have the source code (a 3rd party has already made alterations to 'alsa-lib' and they won't give me the source code).
I could create a small shared library file with just one function 'snd_pcm_close', and then when I start any program that uses ALSA, I can use LD_PRELOAD as follows:
LD_PRELOAD=my_small_library.so some_program_that_uses_sound
This would be fine if I only had one or two programs that use sound, and if this rarely changed.
I'm looking for a solution though that will work with *any* program that links with 'libasound.so'. So here's what I did:
Step 1) Rename the original library
mv libasound.so libAsound.so
Step 2) Change the 'soname' inside the original library
sed -i 's/
libasound.so/libAsound.so/g' libAsound.so
Step 3) Write my own small library containing one function 'snd_pcm_close'
(see the source code for this library at the end of this post)
Step 4) Build my own small library to link with the original renamed library:
gcc -o libasound.so.2.0.0 my_small_library.c -ldl -L./ -l:libAsound.so.2.0.0 -Wl,-soname,libasound.so.2
So then when any process (for example 'baresip') links with 'libasound.so', here's what will happen:
a) baresip links with our small library libasound.so
b) libasound.so links with the original library named libAsound.so
c) So now both shared libraries, libasound.so and libAsound.so, have been loaded into the address space of baresip
d) Since libasound.so was loaded before libAsound.so, the function 'snd_pcm_open' is taken from my small library
e) All other functions such as 'snd_pcm_open' and 'snd_pcm_writei' are of course missing from our small library, but the Linux operating system automatically finds these functions in any other library that's linked in, and hence finds them in libAsound.so
I have tested this out and it works. What surprises me though is that I can't find a similar implementation to this on the internet -- I mean surely somebody has done this before? If anyone can suggest a better way of doing this, I'm all ears.
Source code for my small shared library:
#include <dlfcn.h> /* dlopen, dlsym, dlclose */
static void Patch_Code(void); /* Defined lower down in this file */
int snd_pcm_close(void *arg)
{
int retval = -1; /* ALSA manual says "Zero on success, or a negative value on error" */
void *const dlhandle = dlopen("libAsound.so", RTLD_NOW|RTLD_LOCAL); /* Load original lib */
if ( dlhandle )
{
int (*const snd_pcm_close_PROPER)(void*) = dlsym(dlhandle, "snd_pcm_close");
if ( snd_pcm_close_PROPER ) retval = snd_pcm_close_PROPER(arg); /* Call original func */
dlclose(dlhandle);
}
Patch_Code(); /* <--------------- Our patch is invoked here */
return retval;
}
static void Patch_Code(void)
{
/* We put our patch in here */
}