Below is a minimal reproducible example, where the goal is to call b_util()
from a()
. My question is whether it's possible to do this without explicitly passing a b_util()
function pointer to a.c
.
I am looking for a flag like -rdynamic
, as discussed in this Stack Overflow post, which allows a shared library to call functions from the main executable. Compiling a.c
or b.c
with this flag prints the same runtime error, but maybe there is a way the flag could be utilized?
You can compile and run the below code with gcc c.c -o c.out && gcc b.c -o b.so -shared -fPIC && gcc a.c -o a.so -shared && ./c.out
, but the problem is that it prints dlopen: ./a.so: undefined symbol: b_util
:
// a.c
void b_util(void);
void a(void) {
b_util();
}
// b.c
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
void assert_dl(void *p, char *fn_name) {
if (!p) {
fprintf(stderr, "%s: %s\n", fn_name, dlerror());
exit(EXIT_FAILURE);
}
}
void b(void) {
void *dll = dlopen("./a.so", RTLD_NOW);
assert_dl(dll, "dlopen");
void (*a)(void) = dlsym(dll, "a");
assert_dl(a, "dlsym");
a();
}
void b_util(void) {
printf("b_util\n");
}
// c.c
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
void assert_dl(void *p, char *fn_name) {
if (!p) {
fprintf(stderr, "%s: %s\n", fn_name, dlerror());
exit(EXIT_FAILURE);
}
}
int main(void) {
void *dll = dlopen("./b.so", RTLD_NOW);
assert_dl(dll, "dlopen");
void (*b)(void) = dlsym(dll, "b");
assert_dl(b, "dlsym");
b();
}
Some programmer dude in the comments made me aware of the RTLD_GLOBAL
flag, and I indeed only needed to change dlopen("./b.so", RTLD_NOW)
to dlopen("./b.so", RTLD_NOW | RTLD_GLOBAL)
.
A little later I figured out that there is no need for b.so
to open a.so
. c.c
can just open b.so
with RTLD_GLOBAL
, making b_util
visible as if it had been linked directly into the program. After this, dlopen("./a.so", RTLD_NOW)
succeeds:
// a.c
void b_util(void);
void a(void) {
b_util();
}
// b.c
#include <stdio.h>
void b_util(void) {
printf("b_util\n");
}
// c.c
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
void assert_dl(void *p, char *fn_name) {
if (!p) {
fprintf(stderr, "%s: %s\n", fn_name, dlerror());
exit(EXIT_FAILURE);
}
}
int main(void) {
// Comment dlopen("./b.so") out to cause `undefined symbol: b_util`
void *dll_b = dlopen("./b.so", RTLD_NOW | RTLD_GLOBAL);
assert_dl(dll_b, "dlopen");
void *dll_a = dlopen("./a.so", RTLD_NOW);
assert_dl(dll_a, "dlopen");
void (*a)(void) = dlsym(dll_a, "a");
assert_dl(a, "dlsym");
a();
}