程序员的自我修养-共享库的组织

程序员的自我修养-共享库的组织

共享库的组织方式

共享库使用 libname.so.x.y.z 这种形式
x表示主版本号,不同版本号之间可能不兼容
y表示次版本号,一般主版本号相同的次版本之间是兼容的
z是发布版本,表示bug修复或者性能调整等,同一个主版本内也是兼容的

SO-NAME机制
SO-NAME就是去掉次版本和发布版本号,只留主板不的名字
比如 libfoo.so.2.6.1,他的SO-NAME就是 libfoo.so.2
linux为每个共享库在它所在的目录创建一个跟SO-NAME相同并指向他的软链接
由于历史原因, libc 和  ld 没有遵循这个规则

ELF文件中有个 .dynamic段,其中包含了动态链接库的路径
可以使用绝对路径,也可以使用SO-NAME,然后链接器会在指定的目录下寻找这个库

 

还有一些类似 GCC_ 前缀
或者 GLIBC_PRIVATE 的版本符号,这样的富豪标记分别用于GCC编译器和GLIC内部
它提醒共享库的使用者,最好不要使用它们,因为不是对外公开的

给共享库加上版本

gcc -shared -fPIC foo.c -Xlinker --version-script libfoo.ver -o libfoo.so
 
VERS_1.2 {
    global:
        foo;
    local:
        *;
};
                                                                               

编译命令

gcc -shared -fPIC foo.c -Xlinker --version-script libfoo.ver -o libfoo.so
                                                                             

再编译一个main程序

gcc p1.c ./libfoo.so -o main

如果将这个main程序拿到其他机器上运行,找到的版本库比当前的低,就会运行报错

共享库的路径
/lib 存放系统最关键和基础的共享库
/usr/lib 存放一些非系统运行时所需要的关键性共享库
/user/local/lib 存放一些第三方应用程序的库

共享库的查找方式
先看ELF文件中定义的 .dynamic信息,如果共享库是绝对路径,就去绝对路径下找
否则就去指定的路径下找
动态链接器会在   /lib,/user/lib,/etc/ld.so.conf配置文件指定的目录中查找共享库
ldconfig程序是为共享库目录下的各个共享库创建,删除或更新相应的SO-NAME
/etc/ld.so.cache是将相应的SO-NAME收集起来,建立一个缓存
动态链接器会在这个缓存文件中查找,如果找不到还会遍历 /lib,/usr/lib这两个目录

 

调试程序

环境变量
LD_LIBRARY_PATH
也可以直接运行动态链接器来启动程序
/lib/ld-linux.so.2 -library-path /home/user /bin/ls
LD_PRELOAD,其优先级比 LD_LIBRARY_PATH还要高
可以通过这种功能,覆盖系统函数,达到库打桩的目的
LD_DEBUG,打开动态链接器的调试功能
LD_DEBUG=files ./p1  的运行结果


     25551:
     25551:     file=./libfoo.so [0];  needed by ./p1 [0]
     25551:     file=./libfoo.so [0];  generating link map
     25551:       dynamic: 0x00007f93099ebdf8  base: 0x00007f93097eb000   size: 0x0000000000201040
     25551:         entry: 0x00007f93097eb5d0  phdr: 0x00007f93097eb040  phnum:                  7
     25551:
     25551:
     25551:     file=libc.so.6 [0];  needed by ./p1 [0]
     25551:     file=libc.so.6 [0];  generating link map
     25551:       dynamic: 0x00007f93097e3b80  base: 0x00007f9309428000   size: 0x00000000003c21c0
     25551:         entry: 0x00007f9309449d10  phdr: 0x00007f9309428040  phnum:                 10
     25551:
     25551:
     25551:     calling init: /lib64/libc.so.6
     25551:
     25551:
     25551:     calling init: ./libfoo.so
     25551:
     25551:
     25551:     initialize program: ./p1
     25551:
     25551:
     25551:     transferring control: ./p1
     25551:
printf from lib.so 1
                                                                                                           

LD_DEBUG支持的其他参数

  1. bindings   显示动态链接的符号绑定过程
  2. libs       显示共享库的查找过程
  3. versions   显示符号版本依赖关系
  4. reloc      显示重定位过程
  5. symbols    显示符号表查找过程
  6. statistics 显示动态链接过程中各种统计信息
  7. all        显示以上所有信息

共享库的构造函数,析构函数
在函数前加上一些声明即可,这种是GCC对C和C++的扩展,在其他编译器上这种语法不通用

#include <stdio.h>
 
int main() {
    printf("hehe\n");
}
 
void __attribute__((constructor)) init_func() {
    printf("init...\n");
}
 
void __attribute__((destructor)) finit_func() {
    printf("destroy...\n");
}
                                                    

 

0 次阅读

发表评论

电子邮件地址不会被公开。 必填项已用*标注