链接、装载和库看完这个系列就够了(三)(动态库链接问题)

测试代码
前面谈到了静态库的链接顺序问题,我们看一下动态库是否也有链接问题。

动态库链接顺序问题

直接上代码:

//lib_so1.c 
#include "lib_so1.h"
int so1_fun()
{
        return 0;
}
//lib_so2.c 
#include "lib_so2.h"
#include "lib_so1.h"
int so2_fun()
{
        so1_fun();
        return 0;
}
//main.c 
#include "lib_so1.h"
#include "lib_so2.h"

int main()
{
        //so1_fun();
        so2_fun();
        return 0;
}
#Makefile 
all:main

lib_so1.so: lib_so1.o
        gcc -shared -o $@ $<
lib_so2.so: lib_so2.o
        gcc -shared -o $@ $<

main: main.o lib_so1.so lib_so2.so
        gcc -o $@ main.o -l_so1 -l_so2 -L.

%.o : %.c
        gcc -fPIC -c $< -o $@

clean:
        rm -rf *.o *.a *.so main

我们make,发现链接会报错:

# make clean;make
rm -rf *.o *.a *.so main
gcc -fPIC -c main.c -o main.o
gcc -fPIC -c lib_so1.c -o lib_so1.o
gcc -shared -o lib_so1.so lib_so1.o
gcc -fPIC -c lib_so2.c -o lib_so2.o
gcc -shared -o lib_so2.so lib_so2.o
gcc -o main main.o -l_so1 -l_so2 -L.
./lib_so2.so: undefined reference to `so1_fun'
collect2: error: ld returned 1 exit status
Makefile:9: recipe for target 'main' failed
make: *** [main] Error 1

然后我们交互Makefile中的链接顺序,就不会报错:

# make clean;make
gcc -fPIC -c main.c -o main.o
gcc -fPIC -c lib_so1.c -o lib_so1.o
gcc -shared -o lib_so1.so lib_so1.o
gcc -fPIC -c lib_so2.c -o lib_so2.o
gcc -shared -o lib_so2.so lib_so2.o
gcc -o main main.o -l_so2 -l_so1 -L.

我们发现动态库也是有链接顺序问题的,不过动态库的链接顺序问题不常见。因为这个问题只会在Makefile中链接选项中添加了动态链接,但是实际上程序中并没有调用库中任何函数才会发生。而静态库链接是以.o为单位加载,这就有可能只链接部分.o,造成其他.o没有链接进来,从而导致后续链接时发生找不到符号的问题。

动态库链接的一个问题

为例加深动态库链接的理解,我们再来看一个问题,首先我们修改一下main.c:

//main.c 
#include "lib_so1.h"
#include "lib_so2.h"

int main()
{
        so1_fun();
        //so2_fun();
        return 0;
}

然后我们make:

# make clean;make
rm -rf *.o *.a *.so main
gcc -fPIC -c main.c -o main.o
gcc -fPIC -c lib_so1.c -o lib_so1.o
gcc -shared -o lib_so1.so lib_so1.o
gcc -fPIC -c lib_so2.c -o lib_so2.o
gcc -shared -o lib_so2.so lib_so2.o
gcc -o main main.o -l_so1 -l_so2 -L.

我们看一下main最终链接的动态库:

# readelf -d main

Dynamic section at offset 0xdb8 contains 28 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [lib_so1.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000c (INIT)               0x5b8
 0x000000000000000d (FINI)               0x794
 0x0000000000000019 (INIT_ARRAY)         0x200da8
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
...

我们发现编译的时候虽然我们指定了同时链接lib_so1.so和lib_so2.so,但是最终的可执行文件中并没有链接lib_so2.so,这是什么原因?
这是因为程序中没有调用任何lib_so2.so库中的函数,所以虽然在命令行中指定了链接lib_so2.so,但是实际效果并没有链接这个库。这也解释了为什么会存在上面提到的动态库的链接问题了。

目标文件有没有顺序问题?

目前已经讨论了,静态库和动态库的链接顺序问题,那么我们是否应该看一下.o目标文件是否也存在类似的问题?

//obj1.c 
int obj1_fun()
{
        return 0;
}
//cat obj2.c 
extern int obj1_fun();
int obj2_fun()
{
        obj1_fun();
        return 0;
}
main.c 
extern int obj1_fun();
extern int obj2_fun();

int main()
{
        obj2_fun();
        return 0;
}
#Makefile 
all:main

main: main.o obj1.o obj2.o
        gcc -o $@ main.o obj1.o obj2.o

%.o : %.c
        gcc -c $< -o $@

clean:
        rm -rf *.o *.a *.so main

我们make,发现没有报错:

# make
gcc -c main.c -o main.o
gcc -c obj1.c -o obj1.o
gcc -c obj2.c -o obj2.o
gcc -o main main.o obj1.o obj2.o

我们发现.o目标文件并没有链接顺序问题,链接器会把所有.o文件中的所有符号都链接到最终的可执行文件,我们查看一下编译生成main中的符号:

# readelf -s main|grep obj
    34: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS obj1.c
    35: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS obj2.c
    49: 000000000000061a    21 FUNC    GLOBAL DEFAULT   13 obj2_fun
    58: 000000000000060f    11 FUNC    GLOBAL DEFAULT   13 obj1_fun

我们发现obj1_fun和obj2_fun两个符号都在最终的可执行文件中。
到现在,我们详细分析了静态库、动态库和目标文件链接顺序的问题,并通过代码进行了验证。
动态链接顺序不但会影响编译和链接的结果,而且还会影响程序运行输出的结果,而这个问题主要是因为符号同名导致,后面我们将详细分析一下符号同名的问题,这个问题更加常见。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值