什么时候.so文件加载Linux?

时间:2015-03-26 17:55:26

标签: c shared-libraries dynamic-linking dlopen dynamic-loading

我有一个链接到我的可执行myexe的共享对象(a.so)。 a.so暴露了一个名为get_val()的方法,myexe正在使用它。

现在当a.so被加载到myexe的进程地址空间时?是当myexe调用get_val()API或myexe启动时。

2 个答案:

答案 0 :(得分:2)

有两种类型的库:

  • 静态库(后缀:.a / .lib),它本身就成为二进制文件的一部分。严格来说,它不是整个库,而是库中需要满足未解析链接的那些对象。
  • 共享(动态)库(后缀:.so / .dll),它有两种版本,区别于加载库的时间:
    • 动态链接库,它们是您告诉编译器和链接器的库,您调用的库类似于静态库,但它们不是库的一部分 - 它们由加载器/链接器加载(在Linux中通常通过调用__main()libc dlopen()的一部分。
    • 动态加载库,您自己调用dlopen()

(这些术语看起来有点模糊,我看到使用不同术语的不同文献;上面的术语是我记忆它以便记住概念的方式。)

因此,如果您在没有自己调用a.so的情况下使用dlopen()a.so是一个动态链接库,因此它会在程序启动时加载。在这种情况下,从系统中删除a.so将阻止您的程序启动 - 它将被加载,但在main()被调用之前它将失败。

如果您使用a.so自己致电dlopen(),则完全由您控制。

关于你的问题

Q1:如果您使用dlopen()自己致电RTLD_LAZY,则会在a.so可以解决的第一个未解决的来电时加载a.so。如果您自己致电dlopen()RTLD_NOW会立即加载a.so,即dlopen()返回之前。如果您不是自己致电dlopen()但让libc为您完成工作,则会在计划开始时加载a.so

Q2:您删除了a.so。如果您使用dlopen()致电RTLD_LAZY,并且没有运行需要a.so的代码,程序将会愉快地运行,否则会产生信号。如果您不致电dlopen()但让libc为您完成工作,程序将无法成功启动。

问题3:实际上,如果不调用a.so(或者替代它的等效内容),就无法加载dlopen()。问题是,您是自己致电dlopen(),还是让环境(即libc)为您完成工作。

免责声明:我不是这方面的专家,我的答案的某些部分可能是错误的。我将验证我的答案中我自己怀疑的那些部分,即是libc调用dlopen()还是其他内容,以及即使你是否可以进行延迟绑定不要自己使用dlopen()。我得到结果后,我会更新答案。

答案 1 :(得分:0)

我猜你是在Linux / x86-64上。它是特定于操作系统的。

通常,ELF共享库在执行开始时加载ld-linux.so(8)。实际上,共享库应该是position independent code(PIC)。

但它可能取决于dlopen(3)其旗帜RTLD_NOWRTLD_LAZY

阅读Drepper's paper: How To Write Shared Librariesx86-64 ABI specification

您可以使用strace(1)来了解您自己的Linux系统上发生的情况。

原则上,您可以使用mmap(2)动态加载foo.so并自行处理relocations。我已经(差不多)在上个世纪(对于SPARC)做了,并且相信我这是一项繁琐的工作。

BTW,dlopenGNU libcmusl-libc中实施。两者都是免费软件,你可以研究他们的源代码。

另请阅读Program Library HowTo。它简要说明了一些细节:

  • 使用

    将共享对象的源文件编译为PIC
     gcc -Wall -fPIC -O src1.c -o src1.pic.o
     gcc -Wall -fPIC -O src2.c -o src2.pic.o
    
  • 使用

    将它们链接到共享库foo.so
     gcc -shared src1.pic.o src2.pic.o -o foo.so
    
  • 使用dlopen的完整路径,例如

         void* dlh = dlopen("./foo.so", RTLD_NOW);
         if (!dlh) { fprintf(stderr, "dlopen failed: %s\n", dlerror());
                 exit(EXIT_FAILURE);
    

然后你可以有一个约定,说你的foo.so插件应该具有签名功能

  typedef int sayhello_sig_t(const char*);

名为say_hello,您可以使用以下方式获取其地址:

  sayhello_sig_t* funptr = dlsym(dlh, "say_hello");
  if (!funptr) { 
     fprintf(stderr, 
             "dlsym say_hello failure: %s\n, dlerror();
     exit(EXIT_FAILURE);
  }