深析静态链接库和动态链接库相同函数覆盖及库调用顺序问题

来源:互联网 发布:淘宝网唯品会 编辑:程序博客网 时间:2024/06/01 08:00
注意:编译器为gcc,若使用g++,请在库里面加上extern “C”

    两个静态库
    首先测试静态链接库,大概的代码如下:
    liba.c
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include "libA.h"
  4. void libA()
  5. {
  6.         common();
  7. }
  8.   
  9. void common()
  10. {
  11.         printf("libA common!\n");
  12. }
    liba.h
  1. #ifndef __LIBA_H__
  2. #define __LIBA_H__
  3.   
  4. void libA();
  5. void common();
  6.   
  7. #endif
    
    libb.c
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include "libB.h"
  4. void libB()
  5. {
  6.         common();
  7. }
  8.   
  9. void common()
  10. {
  11.         printf("libB common!\n");
  12. }
    libb.h
  1. #ifndef __LIBB_H__
  2. #define __LIBB_H__
  3.   
  4. void libB();
  5. void common();
  6.   
  7. #endif
    静态库的生成:
  1. gcc -c liba.c
  2. ar -rs liba.a liba.o

  3. gcc -c libb.c
  4. ar -rs libb.a libb.o
    动态库生成:
  1. gcc -fpic -shared -o liba.so liba.c

  2. gcc -fpic -shared-o libb.so libb.c
    fpic:表示为位置无关,也即在任何内存位置都可以运行。

    可以看到liba和libb都有相同的common函数,但是打印出的内容都是自家的。
    下面我们分别生成静态链接库做测试,生成好后,写个主程序进行测试。
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <dlfcn.h>
  5.   
  6. int main()
  7. {
  8.         libA();
  9.         libB();
  10.         return 0;
  11. }
    编译生成结果如下:
    
    可以得出一个结论:都为静态链接库,有同名函数参与的情况下,链接会出现符号多次定义的错误。

    两个动态库
    再来看看动态链接库,同样的liba、libb生成动态链接库
    测试主程序不修改,还是为:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <dlfcn.h>
  5.   
  6. int main()
  7. {
  8.     liba();
  9.     libb();
  10.     return 0;
  11. }
    编译结果和顺序,结果如下:
    

    
    这种编译方式叫做动态链接库的隐式调用,如果你删除一个liba.so,运行a.out会出现不能找到动态库的错误。
    这种情况也可以称为 加载时链接!静态库属于编译时链接!
     可以得出第二个结论:若都为动态库,并且进行隐式调用,输出结果和动态库的顺序有关。


    再继续看看动态加载动态库。
    修改测试程序
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <dlfcn.h>
  5.   
  6. int main()
  7. {
  8.     void* handle2 = NULL;
  9.     void (*testLibA)();
  10.     handle2 = dlopen("./libA.so", RTLD_LAZY);
  11.     if (handle2== NULL) {
  12.         perror("error!");
  13.         return ;
  14.     }
  15.     testLibA = dlsym(handle2,"libA");
  16.     if (testLibA== NULL) {
  17.         perror("error!");
  18.         return ;
  19.     }
  20.   
  21.   
  22.     void* handle = NULL;
  23.     void (*testLibB)();
  24.     handle = dlopen("./libB.so", RTLD_LAZY);
  25.     if (handle== NULL) {
  26.         perror("error!");
  27.         return ;
  28.     }
  29.     testLibB = dlsym(handle,"libB");
  30.     if (testLibB== NULL) {
  31.         perror("error!");
  32.         return ;
  33.     }
  34.     testLibA();
  35.     testLibB();
  36.   
  37.     return 0;
  38. }
    编译结果如下所示:
    
    这种情况称为运行时链接。
    如果我们再把动态库的名字加上去呢?
    
     
    

    同样可以得出结论:动态链接库如果不加库选项,函数调用是正确的,加库路径,会以库的路径顺序为主!左边覆盖右边。而且当只链接一个时也生效。如:
    

    一个静态,一个动态
    再来看看一个静态文件和一个动态文件。
    修改测试主程序
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <dlfcn.h>
  5.   
  6. int main()
  7. {
  8.     void* handle2 = NULL;
  9.     void (*testLibA)();
  10.     handle2 = dlopen("./libA.so", RTLD_LAZY);
  11.     if (handle2== NULL) {
  12.         perror("error!");
  13.         return ;
  14.     }
  15.     testLibA = dlsym(handle2,"libA");
  16.     if (testLibA== NULL) {
  17.         perror("error!");
  18.         return ;
  19.     }
  20.     testLibA();
  21.   
  22.     libB();
  23.     return 0;
  24. }
    测试结果如下:
    libb为静态链接!liba为动态加载。
    
    输出正常!
    再看下如果动态库的库名显示的加载如编译选项中:
    
    
    
    结论:在有静态库和动态库时,不把动态库名显示加入编译选项,输出是正常的,如果加进去以静态库为主,和link的顺序无关。

    补充:
    
    另外:
    这种一个共享对象里面的全局符号被另一个共享对象的全局符号覆盖的现象又被称为共享对象的全局符号介入。

    关于全局符号介入这个问题,实际上Linux下的动态链接器是这样处理的:它定义了一个规则,那就是当一个符合需要被加入到全局符号表时,如果相同的符号名已经存在,则后加入的忽略。从动态链接器的角度的装载顺序可以看到,它是按广度优先的顺序进行装载的。


原文地址:http://blog.chinaunix.net/uid-26548237-id-3837099.html

0 0