I am building a shared library in C that is dynamically loaded by a program that I do not have source access to. The target platform is a 64-bit Linux platform and we're using gcc to build. I was able to build a reproduction of the issue in ~100 lines, but it's still a bit to read. Hopefully it's illustrative.
The core issue is I have two non-static functions (bar
and baz
) defined in my shared library. Both need to be non-static as we expect the caller to be able to dlsym them. Additionally, baz
calls bar
. The program that is using my library also has a function named bar
, which wouldn't normally be an issue, but the calling program is compiled with -rdynamic
, as it has a function foo
that needs to be called in my shared library. The result is my shared library ends up linked to the calling program's version of bar
at runtime, producing unintuitive results.
In an ideal world I would be able to include some command line switch when compiling my shared library that would prevent this from happening.
The current solution I have is to rename my non-static functions as funname_local
and declare them static. I then define a new function:
funname() { return funname_local(); }
, and change any references to funname
in my shared library to funname_local
. This works, but it feels cumbersome, and I'd much prefer to just tell the linker to prefer symbols defined in the local compilation unit.
internal.c
#include <stdio.h>
#include "internal.h"
void
bar(void)
{
printf("I should only be callable from the main program\n");
}
internal.h
#if !defined(__INTERNAL__)
#define __INTERNAL__
void
bar(void);
#endif /* defined(__INTERNAL__) */
main.c
#include <dlfcn.h>
#include <stdio.h>
#include "internal.h"
void
foo(void)
{
printf("It's important that I am callable from both main and from any .so "
"that we dlopen, that's why we compile with -rdynamic\n");
}
int
main()
{
void *handle;
void (*fun1)(void);
void (*fun2)(void);
char *error;
if(NULL == (handle = dlopen("./shared.so", RTLD_NOW))) { /* Open library */
fprintf(stderr, "dlopen: %s\n", dlerror());
return 1;
}
dlerror(); /* Clear any existing error */
*(void **)(&fun1) = dlsym(handle, "baz"); /* Get function pointer */
if(NULL != (error = dlerror())) {
fprintf(stderr, "dlsym: %s\n", error);
dlclose(handle);
return 1;
}
*(void **)(&fun2) = dlsym(handle, "bar"); /* Get function pointer */
if(NULL != (error = dlerror())) {
fprintf(stderr, "dlsym: %s\n", error);
dlclose(handle);
return 1;
}
printf("main:\n");
foo();
bar();
fun1();
fun2();
dlclose(handle);
return 0;
}
main.h
#if !defined(__MAIN__)
#define __MAIN__
extern void
foo(void);
#endif /* defined(__MAIN__) */
shared.c
#include <stdio.h>
#include "main.h"
void
bar(void)
{
printf("bar:\n");
printf("It's important that I'm callable from a program that loads shared.so"
" as well as from other functions in shared.so\n");
}
void
baz(void)
{
printf("baz:\n");
foo();
bar();
return;
}
compile:
$ gcc -m64 -std=c89 -Wall -Wextra -Werror -pedantic -o main main.c internal.c -l dl -rdynamic
$ gcc -m64 -std=c89 -Wall -Wextra -Werror -pedantic -shared -fPIC -o shared.so shared.c
run:
$ ./main
main:
It's important that I am callable from both main and from any .so that we dlopen, that's why we compile with -rdynamic
I should only be callable from the main program
baz:
It's important that I am callable from both main and from any .so that we dlopen, that's why we compile with -rdynamic
I should only be callable from the main program
bar:
It's important that I'm callable from a program that loads shared.so as well as from other functions in shared.so
A shared object is an indivisible unit that is generated from one or more relocatable objects. Shared objects can be bound with dynamic executables to form a runable process. As their name implies, shared objects can be shared by more than one application.
Static libraries, while reusable in multiple programs, are locked into a program at compile time. Dynamic, or shared libraries, on the other hand, exist as separate files outside of the executable file.
The -shared or -dynamiclib option is required to create a shared library.
Have you tried -Bsymbolic
linker option (or -Bsymbolic-functions
)? Quoting from ld
man:
-Bsymbolic
When creating a shared library, bind references to global symbols to the definition within the shared library, if any. Normally, it is possible for a program linked against a shared library to override the definition within the shared library. This option can also be used with the --export-dynamic option, when creating a position independent executable, to bind references to global symbols to the definition within the executable. This option is only meaningful on ELF platforms which support shared libraries and position independent executables.
It seems to solve the problem:
$ gcc -m64 -std=c89 -Wall -Wextra -Werror -pedantic -shared -fPIC -o shared.so shared.c
$ gcc -m64 -std=c89 -Wall -Wextra -Werror -pedantic -o main main.c internal.c -l dl -rdynamic
$ ./main
main:
It's important that I am callable from both main and from any .so that we dlopen, that's why we compile with -rdynamic
I should only be callable from the main program
baz:
It's important that I am callable from both main and from any .so that we dlopen, that's why we compile with -rdynamic
I should only be callable from the main program
bar:
It's important that I'm callable from a program that loads shared.so as well as from other functions in shared.so
$ gcc -m64 -std=c89 -Wall -Wextra -Werror -pedantic -shared -fPIC -Wl,-Bsymbolic -o shared.so shared.c
$ ./main
main:
It's important that I am callable from both main and from any .so that we dlopen, that's why we compile with -rdynamic
I should only be callable from the main program
baz:
It's important that I am callable from both main and from any .so that we dlopen, that's why we compile with -rdynamic
bar:
It's important that I'm callable from a program that loads shared.so as well as from other functions in shared.so
bar:
It's important that I'm callable from a program that loads shared.so as well as from other functions in shared.so
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With