Linux动态库

文章介绍了静态库和动态库的概念,静态库在链接时会完全包含在可执行文件中可能导致空间冗余,而动态库在运行时加载,解决了空间问题。动态库的查找顺序包括-rpath、LD_LIBRARY_PATH和配置文件。文章还提到了动态库的管理,包括soname、链接器名称以及如何延迟加载和调试动态库。此外,还讨论了符号可见性和初始化、终止函数的使用。
摘要由CSDN通过智能技术生成

静态库

在开始讨论动态库之前,首先对静态库做一个简短的介绍。

在静态库产生之前,编译需要链接各种.o文件,随着.o文件数量的增多,管理和引用.o文件也变得困难,于是unix决定把常用的.o文件组成一个单元,这样每次只引用一个归档单元就可以了,这种方式就是静态库。

管理静态库

# 创建/更改静态库
ar r libdemo.a mod1.o mod2.o mod3.o

# 查看静态库内容
ar tv libdemo.a

# 删除动态库的一个模块
ar d libdemo.a mod3.o

使用静态库

# 全名引用
gcc main.c libdemo.a

# 简化引用
gcc main.c -ldemo

创建动态库

静态库的缺点:

  • 使用静态库链接时,会将静态库的整个副本链接到可执行文件中,如果多个静态库中都包含相同的目标文件,就会造成冗余,占用内存空间。
  • 修改静态库后,可执行文件需要重新链接动态库。

动态库也叫动态库,就是为了解决静态库的缺点而提出的,动态库在内存中只存在一个副本,并且是运行时加载,这就解决了空间占用和重新链接的问题。

# 合并创建
gcc -fPIC mod1.c mod2.c mod3.c -shared -o libfoo.so

# 分步创建:1. -fPIC会创建位置无关代码;2. -shared会生成动态库
gcc -fPIC mod1.c mod2.c mod3.c 
gcc -shared -o libfoo.so mod1.o mod2.o mod3.o

注:位置无关代码,通常是使用 相对路径+动态符号决议 来实现的。

使用动态库

gcc hello.c -L</path> -l<.so>

在使用g++命令时,一般先列举源文件,然后再列举动态库。这是因为链接器是按照输入文件的顺序来处理的,因此应该先列举需要链接的源文件,再列举需要链接的库文件。如果
库文件中有未定义的符号,链接器可以在后面的库文件中查找,直到找到为止。因此,如果先列举库文件,链接器可能会找不到所需的符号,从而导致链接错误。

由于可执行文件不再包含动态库的副本(静态库会被包含进elf中),就需要通过某种机制找出运行时所需的动态库。这是通过将动态库的名称嵌入到可执行文件中来完成的,在ELF文件中,库依赖性是记录在可执行文件DT_NEDDED标签中的,称为动态库依赖列表。

第二件事是运行时解析内嵌的库名,这个任务是动态链接器完成的/lib/ld-linux.so.2 ,它本身也是动态链接库。

配置动态库

动态库的查找顺序

动态库的函数查找顺序是:

  1. 编译时使用-rpath指定动态库路径;
  2. 使用 LD_LIBRARY_PATH 环境变量可以增加动态库的搜索目录;
  3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径,配置后要运行 ldconfig命令才能生效
  4. /lib , /usr/lib

以搜索到的第一个为准。

rpath

gcc -Wl,-rpath,/home/mtk -o prog prog.c libdemo.so

Wl用于给链接器传参数。
-Wl,-rpath,/data/workroom/libs/lib 会被解释成:ld -rpath /data/workroom/libs/lib

LD_LIBRARY_PATH

export LD_LIBRARY_PATH=<动态库路径>

ldconfig

ldconfig会在/etc/ld.so.cache中建立缓存,加速动态库的搜索过程;
liconfig -p会显示/etc/ld.so.cache的内容;
ldconfig还会检测次要版本的最新版本,并创建/更改soname链接到最新版本。
每次安装、更新、删除动态库都应该运行ldconfig,来更新缓存和soname链接。
已经运行着的程序会继续使用就版本,直到重启。
ldconfig会自动更新soname,链接器则需要手动链接。

1. 在 /etc/ld.so.conf 中添加动态库搜索路径
2. 运行 pkg-config 重建动态库缓存。

pkg-config

–cflags用于查找头文件,–libs用于查找动态库,使用dpkg-config的前提是有.pc文件存在,否则还是使用 dpkg -L 吧。

gcc -o program program.c `pkg-config --cflags --libs glib-2.0`

管理动态库

动态库有三个名称:真实名称、soname和链接器名称。

动态库会存在多个版本,而且版本号也是名字的一部分,为了提供一个统一的调用名称,需要抽象出一个兼容层soname,来代表动态库。

soname通常指代最新真实名称的次要版本,但是如果想引用旧版本,就需要在链接时使用真实名称或soname名称。

链接器名通常指向最新的soname。

# 真实名称格式,例如:libdemo.so.1.2
libname.so.major-id.minor-id

# soname格式,例如:libdemo.so.1
libname.so.major-id

# 链接器名,例如:libdemo.so
libname.so
# 嵌入soname,生成so
gcc -c -fPIC mod1.c mod2.c mod3.c -shared -Wl,-soname,libfoo.so.1 -o libfoo.so.1.0.1

# 创建soname
ln -s libfoo.so.1.0.1 libfoo.so.1

# 创建链接器
ln -s libfoo.so.1 libfoo.so
# 查看soname
objdump -p libfoo.so | grep SONAME

# 使用链接器
gcc -c main.c -lbar

延迟加载

在运行elf文件时,动态链接器会加载动态依赖列表中的所有动态库,但延迟加载比较有用,只在使用的时候加载。
使用dlopen()函数,可以即时加载动态库并搜索库中的函数。

#include<dlfcn.h>
void dlopen(const char *libfilename,int flags);

void* dlsym(void* handle, const char* symbol);

int dlclose(void* handle);

char* dlerror(void);

flags可以是RTLD_LAZY和RTLD_NOW的二者之一,前者是延迟加载,只有执行到函数代码时才加载,后者是立刻加载,会在dlopen调用后加载。

image.png

调试动态库

第一种是使用dlopen,
第二种是使用python:

import ctypes

# 加载动态链接库
mylib = ctypes.cdll.LoadLibrary('/path/to/mylib.so')

# 调用动态链接库中的函数
result = mylib.my_function(arg1, arg2)

其他动态库特性

符号可见性

gcc提供了一个特性声明,用于控制符号的可见性。

void __attribute__((visibility("hidden"))) func(){
    ...
}

这个作用是,将func限定为只对当前库可见,对库外不可见。
类似的是static关键字,将对象限定在本文件内,文件外不可见。

初始化和终止函数

在动态库加载和卸载时执行的函数。

void __attribute__ ((constructor)) foo1(void){
  ...
}

void __attribute__ ((destructor)) foo2(void){
  ...
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

多弗朗强哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值