动态链接与静态链接

静态链接

静态链接是将程序需要调用到的符号(函数,变量)都拷贝到可执行文件。
链接器解析符号的时候是这么解释的:

扫描一次传入链接器的文件(文件可以是目标文件,可以是归档文件,通过命令行的方式传入,从左到右扫描一遍)。扫描的时候修改三个集合(可重定位目标文件集合E,未解析的符号集合U,已定义的符号集合D)。扫描目标文件的时候,修改集合U和集合D,扫描归档文件就会尝试匹配U中的未定义符号,匹配成功修改U和D。最后如果U非空则会出现错误。更详细的说明可以参考《深入理解计算机系统》第二版,7.6.3节。

因为这里只扫描一次,所以符号出现的顺序是有要求的。比如说A.o的符号定义在B.o,那么链接的事后A.o要在B.o之前,否则会出错。

下面是一个例子。

➜  test git:(master) ✗ ls
hello.cpp  hello.h  main.cpp

hello.h

void hello();

hello.cpp

#include "hello.h"
#include <stdio.h>
void hello()
{
        printf("hello world!\n");
}

main.cpp

#include "hello.h"
int main()
{
        hello();
}
➜  test git:(master) ✗ ls
hello.cpp  hello.h  main.cpp
➜  test git:(master) ✗ g++ -c hello.cpp  # 生成目标文件
➜  test git:(master) ✗ ls
hello.cpp  hello.h  hello.o  main.cpp
➜  test git:(master) ✗ ar rcs libhello.a hello.o  # 归档成静态链接库
➜  test git:(master) ✗ ls
hello.cpp  hello.h  hello.o  libhello.a  main.cpp
# 因为main.cpp的hello()定义在libhello.a中,main.cpp出现在libhello.a前链接可以通过,main.cpp出现在libhello.a之后链接错误。
➜  test git:(master) ✗ g++ --static -o main main.cpp ./libhello.a  
➜  test git:(master) ✗ ls
hello.cpp  hello.h  hello.o  libhello.a  main  main.cpp
➜  test git:(master) ✗ g++ --static -o main ./libhello.a main.cpp
/tmp/ccYChfu6.o: In function `main':
main.cpp:(.text+0x5): undefined reference to `hello()'
collect2: error: ld returned 1 exit status
➜  test git:(master) ✗

动态链接

相对静态链接,动态链接并不是将所有引用到的外部符号都拷贝到可执行文件,所以它生成的文件会相对较小一些。

下图是一个动态链接的流程

figure1

我是这么理解的,动态链接库的链接分两个阶段,第一个阶段是链接器(ld)的工作,另一个阶段是动态链接器(ld-linux.so)的工作,
链接器的工作就是确定能不能在给定动态链接库中找到目标文件中那些未定义的符号(函数,变量),如果能找到,就生成一个可执行文件,如果找不到就报错。动态链接库的搜索路径是

        -L …(编译命令指定的搜索路径)
        LD_LIBRARY_PATH
        /etc/ld.so.conf.d定义的搜索路径(这里定义了/usr/local/lib)
        /usr/lib

动态链接器却不会从之前编译时提供的-L路径中搜索,之后搜索

        LD_LIBRARY_PATH
        /etc/ld.so.conf.d定义的搜索路径(这里定义了/usr/local/lib)
        /usr/lib

下面我做个实验来证明我的理解。

myprint.h

void myprinter();

myprint_cwd.cpp

#include <stdio.h>
void myprinter(){
    printf("i am in the current workspace floder\n");
}

myprint_x86.cpp

#include <stdio.h>
void myprinter(){
    printf("i am in the x64 floder\n");
}

myprint_lib.cpp

#include <stdio.h>
void myprinter(){
    printf("i am in the /usr/lib floder\n");
}

main.cpp

#include "myprint.h"
int main(){
    myprinter();
}

先生成动态链接库,并考到对应的位置。

➜  ld_test git:(master) ✗ g++ -shared -fPIC myprint_cwd.cpp -o libmyprint_cwd.so
➜  ld_test git:(master) ✗ ls
libmyprint_cwd.so  main.cpp  myprint_cwd.cpp  myprint.h  myprint_lib.cpp  myprint_x64.cpp
➜  ld_test git:(master) ✗ g++ -shared -fPIC myprint_lib.cpp -o libmyprint_lib.so
➜  ld_test git:(master) ✗ g++ -shared -fPIC myprint_x64.cpp -o libmyprint_x64.so
➜  ld_test git:(master) ✗ ls
libmyprint_cwd.so  libmyprint_lib.so  libmyprint_x64.so  main.cpp  
myprint_cwd.cpp  myprint.h  myprint_lib.cpp  myprint_x64.cpp
➜  ld_test git:(master) ✗ sudo cp libmyprint_x64.so /usr/lib/x86_64-linux-gnu/libmyprint.so
➜  ld_test git:(master) ✗ sudo cp libmyprint_lib.so /usr/lib/libmyprint.so
➜  ld_test git:(master) ✗ cp libmyprint_cwd.so libmyprint.so

开始编译可执行文件。

# 我这里指定的链接路径是本地目录,它虽然能找到动态链接库,但是运行的时候却找到了/usr/lib/x86_64-linux-gnu/libmyprint.so,因为我没有定义LD_LIBRARY_PATH,它就从/etc/ld.so.conf.d定义的搜索路径去找,/etc/ld.so.conf.d定义了/usr/lib/x86_64-linux-gnu/libmyprint.so。
➜  ld_test git:(master) ✗ g++ -o main main.cpp -L . -lmyprint
➜  ld_test git:(master) ✗ ldd ./main
    linux-vdso.so.1 =>  (0x00007ffd3cf7c000)
    **libmyprint.so => /usr/lib/x86_64-linux-gnu/libmyprint.so (0x00007f6b06b5a000)
    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f6b067d8000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6b0640e000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f6b06105000)
    /lib64/ld-linux-x86-64.so.2 (0x0000563ac7b48000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f6b05eef000)
# 删除/usr/lib/x86_64-linux-gnu/libmyprint.so下的动态链接库
➜  ld_test git:(master) ✗ sudo mv /usr/lib/x86_64-linux-gnu/libmyprint.so /usr/lib/x86_64-linux-gnu/libmyprint.so.bak
➜  ld_test git:(master) ✗ ldd ./main
    linux-vdso.so.1 =>  (0x00007ffda792b000)
    **libmyprint.so => /usr/lib/libmyprint.so (0x00007f63dff7e000)
    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f63dfbfc000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f63df832000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f63df529000)
    /lib64/ld-linux-x86-64.so.2 (0x000055b871a44000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f63df313000)
# LD_LIBRARY_PATH添加当前目录
➜  ld_test git:(master) ✗ echo $LD_LIBRARY_PATH

➜  ld_test git:(master) ✗ export LD_LIBRARY_PATH=.
➜  ld_test git:(master) ✗ ldd ./main
    linux-vdso.so.1 =>  (0x00007ffe6d5ce000)
    **libmyprint.so => ./libmyprint.so (0x00007fbb88b97000)
    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fbb887e0000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fbb88416000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fbb8810d000)
    /lib64/ld-linux-x86-64.so.2 (0x0000558085857000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fbb87ef7000)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值