遇到如下情况,主程序通过dlopen来打开.so文件,但是.so用到了主程序的log函数。

编译so时,通过引用主程序头文件来编译通过,头文件有log函数声明:

extern "C" { 
   void print()
  }

在主程序的.c文件里有函数的具体实现。

但是dlopen后运行so中函数时,出现找不到相应的symbol。

这时候就需要在编译主程序ld时加上参数-rdynamic,该参数的作用是:将指示连接器把所有符号(而不仅仅只是程序已使用到的外部符号,但不包括静态符号,比如被static修饰的函数)都添加到动态符号表(即.dynsym表)里,以便那些通过dlopen()或backtrace()(这一系列函数使用.dynsym表内符号)这样的函数使用。

-rdynamic
Pass the flag ‘-export-dynamic’ to the ELF linker, on targets that support
it. This instructs the linker to add all symbols, not only used ones, to the
dynamic symbol table. This option is needed for some uses of dlopen or to
allow obtaining backtraces from within a program.

-g是编译选项,而-rdynamic是链接选项

参考:http://www.lenky.info/archives/2013/01/2190

小例子:

a.cc

  1. #include "stdio.h"
  2. #include <dlfcn.h>
  3. extern "C" {
  4. void print()
  5. {
  6. printf("I am in so file!\n");
  7. }
  8. void fun()
  9. {
  10. void * err = dlopen("./libtmp.so", RTLD_LAZY);
  11. printf("dlopen = %p\n", err);
  12. if (err == NULL) {
  13. printf("err=%s\n", dlerror());
  14. }
  15. }
  16. }

a.h

  1. extern "C" void print();
  2. extern "C" void fun();  // 函数声明和定义都要有extern “C”,或者都没有,否则调用时出现undefined symbol fun
  3. #define NODE_MODULE \
  4. extern "C" { \
  5. static void print_main() __attribute__((constructor)) // dlopen时会自动调用该contructor函数</span>
  6. static void print_main() { \
  7. print(); \
  8. } \
  9. }

so.cc

  1. #include "a.h"
  2. #include "stdio.h"
  3. NODE_MODULE

foo.h

  1. <span style="font-size:18px;">void foo();</span>

foo.cc

  1. #include "stdio.h"
  2. void foo()
  3. {
  4. printf("foo === \n");
  5. }

main.cc

  1. #include "a.h"
  2. int main(void)
  3. {
  4. fun();
  5. return 0;
  6. }

Makefile

  1. all:dynamic
  2. libtmp.so:so.cc
  3. g++ -fPIC -shared -o $@ $^
  4. a.o:
  5. g++ -c a.cc -fPIC
  6. liba.a:a.o
  7. ar -r $@  $^
  8. libso.so: foo.cc liba.a
  9. g++ -fPIC -shared -o $@ $< -L./ -la -Wl,--whole-archive -la  -Wl,--no-whole-archive -ldl
  10. dynamic:libso.so libtmp.so
  11. g++ -o $@ main.cc -Wl,--rpath=. -L./ -lso rdynamic
  12. clean:
  13. rm dynamic liba.a a.o libtmp.so

运行dynamic后输出为:

  1. I am in so file!
  2. dlopen = 0xdeb030

如果没有-rdynamic,则输出为:

  1. dlopen = (nil)
  2. err=./libtmp.so: undefined symbol: print

如果没有-Wl,--whole-archive -la  -Wl,--no-whole-archive,也会有错误:undefined symbol: print

--whole-archive 可以把 在其后面出现的静态库包含的函数和变量输出到动态库,--no-whole-archive 则关掉这个特性

使用readelf -s libso.so | grep fun来查看libso.so的符号表里是否有fun这个函数暴露出来。有--whole-archive的可以查到fun,而没有--whole-archive的,则找不到fun

先理清一下code

可执行文件dynamic依赖与libso.so,而libso.so有包含liba.a,在liba.a的函数fun调用dlopen来打开libtmp.so

主函数调用liba.a的函数来打开libtmp.so

-fvisibility=hidden

  设置默认的ELF镜像中符号的可见性为隐藏。使用这个特性可以非常充分的提高连接和加载共享库的性能,生成更加优化的代码,提供近乎完美的API输出和防止符号碰撞。我们强烈建议你在编译任何共享库的时候使用该选项。

-fvisibility-inlines-hidden

默认隐藏所有内联函数,从而减小导出符号表的大小,既能缩减文件的大小,还能提高运行性能,我们强烈建议你在编译任何共享库的时候使用该选项

所以编译的时候也不能有-fvisibility=hidden和-fvisibility-inlines-hidden。如果有,也会在dlopen时造成错误:undefined symbol

总结:

本实例虽小,但用到了不少编译选项

a: __attribute__((constructor))
主程序main函数之前被执行或dlopen时被执行

b: -rdynamic

ld时将动态库的的所有符号都输出到符号表,以便dlopen和backtrace也能调用

c: --whole-archive -la -Wl,--no-whole-archive

静态库的符号导入到动态库的符号表中,默认是hidden的

d: -fvisibility=hidden和-fvisibility-inlines-hidden

ELF镜像中符号的可见性为隐藏(在实验过程中不太好用,待研究)

在编译nodejs第三方模块时都会碰到这样的问题,第三方模块依赖与nodejs进行编译,而第三方模块又是通过dlopen来打开的,这就要求nodejs编译时将一下第三方模块需要的函数都暴露出来。

参考:

http://www.fx114.net/qa-225-106759.aspx

http://os.chinaunix.net/a2010/0112/1060/000001060902_3.shtml

rdynamic和-whole-archive的更多相关文章

  1. 记一个mvn奇怪错误: Archive for required library: 'D:/mvn/repos/junit/junit/3.8.1/junit-3.8.1.jar' in project 'xxx' cannot be read or is not a valid ZIP file

    我的maven 项目有一个红色感叹号, 而且Problems 存在 errors : Description Resource Path Location Type Archive for requi ...

  2. Flashback Data Archive ( Oracle Total Recall ) introduced in 11g

    Flashback Data Archive feature is part of Oracle Total Recall technology. Flashback Data Archive fea ...

  3. iOS 归档archive使用方法

    归档是一种很常用的文件储存方法,几乎任何类型的对象都能够被归档储存,文件将被保存成自定 义类型的文件,相对于NSUserDefault具有更好的保密性.   1.使用archiveRootObject ...

  4. 转:HAR(HTTP Archive)规范

    HAR(HTTP Archive),是一个用来储存HTTP请求/响应信息的通用文件格式,基于JSON.这个格式的出现可以使HTTP监测工具以一种通用的格式导出所收集的数据,这些数据可以被其他支持HAR ...

  5. http://www.cnblogs.com/Lawson/archive/2012/09/03/2669122.html

    http://www.cnblogs.com/Lawson/archive/2012/09/03/2669122.html

  6. alter system switch logfile与alter system archive log current的区别

    以前知道 ALTER SYSTEM SWITCH LOGFILE对单实例数据库或RAC中的当前实例执行日志切换, ALTER SYSTEM ARCHIVE LOG CURRENT会对数据库中的所有实例 ...

  7. Ubuntu W: GPG error: http://archive.ubuntukey....NO_PUBKEY 8D5A09

    在用 sudo apt-get update 时出现这样的报错: W: GPG error: http://archive.ubuntukylin.com:10006/ubuntukylin xeni ...

  8. Linux JDK 安装及卸载 http://www.cnblogs.com/benio/archive/2010/09/14/1825909.html

    参考:http://www.cnblogs.com/benio/archive/2010/09/14/1825909.html

  9. ant 介绍 http://blog.csdn.net/sunjavaduke/archive/2007/03/08/1523819.aspx

    转自: 本内容包含了Ant的历史简要介绍,Ant的功能以及Ant框架的介绍,并对下载安装使用Ant进行了示例介绍,同时通过一个Java程序讲解了Ant的基本使用方法. 1.       Ant简介:这 ...

  10. http://www.cnblogs.com/youring2/archive/2011/03/28/1997694.html

    http://www.cnblogs.com/youring2/archive/2011/03/28/1997694.html

随机推荐

  1. Phonegap开发的前后台数据交互

    在用Phonegap开发时,需要进行前后台数据交互,在网上找资料,很多东西让人一头雾水,最后借鉴了下面的博客: http://blog.sina.com.cn/s/blog_681929ae01017 ...

  2. UI学习笔记---第十四天数据持久化

    一.沙盒机制 每个应用程序位于文件系统的严格限制部分 每个应用程序只能在为该程序创建的文件系统中读取文件 每个应用程序在iOS系统内斗放在了统一的文件夹目录下 沙盘路径的位置 1. 通过Finder查 ...

  3. centos 主从复制

    1.主服务器rpm安装mysql 2.复制一台服务器叫slave(从服务器),一会儿要用 3.在主服务器,修改my.cnf文件 找到server-id,在它的下面加上 binlog-do-db = h ...

  4. 关于Ajax知识点小节

    URL:统一资源定位符 网络的七层协议:网卡 驱动  网络层(ip)  传输层(tcp udp) 会话层( )  应用层(http.) restful表征状态转移(一种表征架构) CURD 增删改查 ...

  5. spark新能优化之提高并行度

    实际上Spark集群的资源并不一定会被充分利用到,所以要尽量设置合理的并行度,来充分地利用集群的资源.才能充分提高Spark应用程序的性能. Spark会自动设置以文件作为输入源的RDD的并行度,依据 ...

  6. sql防注入的简单实现,防XSS的简单实现

    1.sql-替换'(切断字符串)符和\(转义字符)符为空, 2.xss-替换<(标签开始符)符 但用这种简单方法在sql和html中不能再使用这些字符了.

  7. google和ebay微服务经验

    摘自:http://www.infoq.com/cn/articles/ecosystems-of-microservices 多元化(polyglot)微服务是终极游戏 大规模系统和多元化微服务最终 ...

  8. 尾数为0零BigDecimal不能装成正常数

    BigDecimal b1 = rs.getBigDecimal("binary_double_column"); System.out.println( "ceshi: ...

  9. C# Timer用法及实例详解

    C# Timer用法有哪些呢?我们在使用C# Timer时都会有自己的一些总结,那么这里向你介绍3种方法,希望对你了解和学习C# Timer使用的方法有所帮助. 关于C# Timer类  在C#里关于 ...

  10. asp.net MVC 自定义@helper 和自定义函数@functions小结

    asp.net Razor 视图具有.cshtml后缀,可以轻松的实现c#代码和html标签的切换,大大提升了我们的开发效率.但是Razor语法还是有一些棉花糖值得我们了解一下,可以更加强劲的提升我们 ...