用实例分析使用动态库DLL与使用静态库SLL的区别

本文通过实例探讨动态库DLL和静态库LIB在程序中的使用区别。动态库在内存中仅包含函数入口,而静态库则将代码直接复制到程序中,导致程序大小增加。DLL利于节省资源,而静态库确保程序独立运行,不依赖外部库。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个伟大的程序员应该知道他所写代码的每一个字节的意义,这句话说的有点夸张,但是体现了程序员应该知道其所写代码的生成的程序或软件是由什么组成的,用反汇编工具打开一个可执行文件,里面一大堆不是我们自己写的代码,那些代码是怎么来的?研究程序的PE文件,发现结构很严密,除了PE的头部(当然加载到内存中的PE文件是不包括PE的头部的,一般情况下,它的排列方式还是按照PE在磁盘中的逻辑顺序的,或者说是是按照PE被加载器生成的中间部分内存映像的逻辑结构。实际上,在内存中我们的程序是一个个节排列着的,一般情况下,代码段(.text)排在最前面,如下图是一个一般的PE结构的段:

这是使用PEID打开并查看的段的信息,是属于未加载至内存的情况下的信息,当然了,进过计算还是可以计算出加到内存中的信息的,我们看到的相关的虚拟地址就是经过计算出来的。

实际上,在内存中是没有最后的这个.reloc重定位段的,因为在内存中它是没有必要的,在把程序映射到进程的地址空间之后,重定位表已经完成了他的使命了——对磁盘中的映像到内存进行重定位。


说了这么多发现话题跑偏了,实际上这也是相关的知识,我们需要融汇贯通。

我们先来看两张图片:

(第一张是使用IDA打开的我自己编写的一个demo程序,这个程序加载了我自己编写的一个dll,这个dll中只有一个到处函数,那就是简单的add函数)。但是图片显示的是printf函数的定义,这个函数是在C静态库中的,我们的程序把这个函数所在的静态库都加载进来了,copy了一份放在了我们的程序中。

(第二张图片是我编写的加载dll的一个程序,这个程序只是简单做了调用add函数的操作)

第三章图片是我编写一个加载静态库的程序,也只是做简单的调用add函数的操作。



最后使用IDA打开两个程序:

可以看到我们把两个数1和2push之后就有了这样的语句:call ds:__imp__add,结合之前的push和现在的call很显然是调用add对1和2进行add操作,但是为什么不是add呢?那是因为这个函数是在dll中被导入的,在这个进程的地址空间中是没有这个函数的实现的。看下一幅图,这幅图是这个进程的地址空间最后的部分,在.rdata段有其函数的相关信息。

实际上,它所处的位置就是导入表,根据工具也可以看出:

导入表存储的是程序导入的DLL中函数的地址,如下是kernel32.dll中函数的地址:



接着我们再看导入静态库的程序使用IDA打开的情况:


我们接着跟进到add函数的位置:


与之前dll导入不同的是在进程的地址空间中存在add的定义,这说明,静态库被拷贝了一份到了进程的地址空间中。



在程序的最后只有kernel32.dll这一个dll。


当然了,因为我们的静态库中有一个函数,所以两个程序的大小区别不大,但是在很多的工程中的话,程序的大小将会有很大的区别了。

这也是windows程序使用dll的原因,当然了,dll还有很多的好处。


这里提出一个问题,日后再做分析:

我们的CRL(C语言运行时库)有两个版本,一个是动态的,一个是静态的。在我们上面的分析中,我们知道,我们的程序中存在printf函数的代码实现,那么显然是使用的是静态库了,那么为什么不使用的是dll?



### GCC 中动态链接库静态链接库的区别及用法 #### 一、基本概念 动态链接库(Dynamic Link Library, DLL)和静态链接库(Static Link Library, SLL)是两种不同的库文件形式。 - **静态链接库**是在编译阶段将目标代码嵌入到最终的可执行文件中的方式,生成的二进制文件独立于外部依赖项[^1]。 - **动态链接库**则是指在程序运行时加载并链接的目标代码,通常以共享对象的形式存在,在 Linux 下扩展名为 `.so`。 #### 二、主要区别 | 特性 | 静态链接库 | 动态链接库 | |---------------------|------------------------------------|------------------------------------| | 文件大小 | 较大 | 较小 | | 运行性能 | 更高 | 略低 | | 更新维护 | 不方便 | 方便 | | 加载时机 | 编译时完成 | 运行时加载 | 动态链接库虽然牺牲了一定的运行效率[^2],但由于其节省内存空间以及便于更新的特点,在现代软件开发中更为常见。 --- #### 三、使用方法对比 ##### (1)静态链接库的创建使用 以下是创建和使用静态链接库的一个简单示例: ###### 创建静态链接库 假设有一个简单的函数 `add.c`: ```c // add.c int add(int a, int b) { return a + b; } ``` 将其编译为目标文件,并打包成静态库: ```bash gcc -c add.c -o add.o ar rcs libadd.a add.o ``` ###### 使用静态链接库 编写主程序 `main.c` 并链接该静态库: ```c // main.c #include <stdio.h> extern int add(int a, int b); int main() { printf("%d\n", add(3, 5)); return 0; } ``` 编译命令如下: ```bash gcc main.c -L. -ladd -static -o static_main ``` 这里 `-static` 参数确保只使用静态库进行链接。 --- ##### (2)动态链接库的创建使用 下面是创建和使用动态链接库的过程: ###### 创建动态链接库 同样基于上述 `add.c` 文件,可以按照以下步骤构建动态库: ```bash gcc -fPIC -shared add.c -o libadd.so ``` 其中 `-fPIC` 表示生成位置无关代码,而 `-shared` 则用于指示生成共享库[^3]。 ###### 使用动态链接库 修改 `main.c` 的编译指令为: ```bash gcc main.c -L. -ladd -o dynamic_main export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./dynamic_main ``` 注意:为了使程序能够找到动态库,需设置环境变量 `LD_LIBRARY_PATH` 或者将动态库复制至标准路径如 `/usr/lib/`[^4]。 --- #### 四、总结 无论是静态还是动态链接库都有各自的适用场景。当追求更高的运行效率或希望减少对外部资源的依赖时可以选择静态链接;而在注重灵活性、节约存储空间的情况下,则更倾向于采用动态链接的方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值