博主心里有一个问题:能不能在动态库内部访问到外部的符号吗?比如说:在main中定义了一个 int g_num,它能否在库里面进行访问呢?
于是博主立即写了一个小程序验证了一下:
t1.c
#include <stdio.h>extern void print_num();int g_num = 0;int main(){ puts("--t1--"); g_num = 12; print_num(); return 0;}
print_num.c
#include <stdio.h>extern int g_num;void print_num(){ ++ g_num; printf("g_num=%d\n", g_num);}
编译命令为:
gcc -o libprint_num.so -shared -fPIC print_num.cgcc -o t1 t1.c -ldl -L. -lprint_num
运行示例:
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. ./t1
结果运行正常。
于是,博主兴致勃勃地进一步尝试:
t2.c
#include <dlfcn.h>#include <stdio.h>int g_num = 0;typedef void (*Func)();int main(){ puts("--t2--"); g_num = 12; void *h = dlopen("./libprint_num.so", RTLD_NOW); if (h == NULL) { puts(dlerror()); return 0; } Func f = (Func)dlsym(h, "print_num"); if (f != NULL) { (*f)(); } else { puts("dlsym fail"); } dlclose(h); return 0;}
编译与运行命令:
gcc -o t2 t2.c -ldl./t2
结果在 dlopen 时报错:
--t2--./libprint_num.so: undefined symbol: g_num
怎么会这样呢?是 dlopen 时传的参数不对吗?
细心想想,t1属于启动时链接,t2属于是运行中链接。在启动时,loader读取t1文件,得到t1中的符号表,立即进行一次链接,没有什么问题。而程序一旦运行了起来,loader就已经完成了它的使命,为了节省空间很可能将符号表释放了。而这时将进行链接,就无法再链接成功了。这样理解正确吗?
如果正如上所述。那么一定应该有什么方式能让t2的符号表驻留在内存中,比如在t2编译时加什么参数。至少能让个别符号能驻留在内存中。否则,那就不好玩了。
经过一番波折,终于还是在 http://stackoverflow.com/ 中找到答案:http://stackoverflow.com/questions/480617/receive-undefined-symbol-error-when-loading-libary-with-dlopen
原文:
Correct solution is to add
-rdynamic
to the link command of the main executable. This will add appropriate option told
(which, when usingGNU ld
, happens to be--export-dynamic
).Adding
--export-dynamic
directly is technically incorrect: it's a linker option, and so should be added as-Wl,--export-dynamic
, or-Wl,-E
. This is also less portable than-rdynamic
(other linkers have an equivalent, but the option itself is different).
果然如我上面所猜测的那样。在编译executable程序时,要加 -rdynamic 参数。如下编译 t1:
g++ -o t2 t2.c -ldl -rdynamic
运行,成功。
思考:
加了这个 -rdynamic 后,会有多大的内存开销?这个在嵌入式软件开发中是要考虑的。
另一篇文章:http://stackoverflow.com/questions/8623884/gcc-debug-symbols-g-flag-vs-linkers-rdynamic-option
联系客服