静态链接库顺序问题

前言

最近遇到了一个非常奇怪的问题,编译时竟因为库的链接顺序不同,就有完全不同的结果。代码非常简单如下所示:

#include "muduo/net/EventLoop.h"

int main() {
  muduo::net::EventLoop loop1;
  muduo::net::EventLoop loop2;
  return 0;
}

只是在一个线程内创建两个 EventLoop 对象,这是不被允许的,应该会报错。我使用如下命令编译:

g++ test.cc -I ../include -L ../lib -lpthread -lmuduo_base -lmuduo_net

此时却报了一堆未定义错误:

EventLoop.cc:205: undefined reference to `muduo::Logger::Logger(muduo::Logger::SourceFile, int, muduo::Logger::LogLevel)'
EventLoop.cc:205: undefined reference to `muduo::LogStream::operator<<(long)'
EventLoop.cc:205: undefined reference to `muduo::Logger::~Logger()'

这些东西都是在 muduo_base 中定义的,我明明链接的 muduo_base 库,为什么会有这些报错呢?当我改变链接库的顺序时,神奇的一幕出现了。

qgw@virtual-machine:~/build/release-install-cpp11/examples$ make
g++ echo.cc -I ../include -L ../lib -lpthread -lmuduo_net -lmuduo_base
qgw@virtual-machine:~/build/release-install-cpp11/examples$ ./a.out 
20230529 06:44:22.218800Z  5020 DEBUG EventLoop EventLoop created 0X7FFFD30BDFB0 in thread 5020 - EventLoop.cc:65
20230529 06:44:22.218835Z  5020 DEBUG EventLoop EventLoop created 0X7FFFD30BE050 in thread 5020 - EventLoop.cc:65
20230529 06:44:22.218837Z  5020 FATAL Another EventLoop 0X7FFFD30BDFB0 exists in this thread 5020 - EventLoop.cc:68
已放弃 (核心已转储)

编译成功了,运行程序也得到了我想要的结果。这究竟是为什么呢?下面就来为你解答这一迷题。

理论基础

编译过程

先来简单了解下,从我们编写的程序到形成可执行程序的过程。大致可以分为 4 个步骤,分别是预处理(Preprocessing)、编译(Compilation)、汇编(Assembly)和链接(Linking)。

compilation process

经过编译、汇编形成的目标文件并不能直接执行,它首先需要载入到链接器中。链接器确认 main 函数为初始进入点,把符号引用绑定到内存地址,把所有的目标文件集中在一起,再加上库文件,从而产生可执行文件。

静态链接

动态链接是将函数库加载到进程地址空间,程序在运行时寻找它们,而不是把函数库二进制代码作为自身可执行程序的一部分。

如果函数库的一份副本是可执行文件的物理组成部分,那么我们称之为静态链接。简单点说,就是静态链接需要将我们用到的函数代码,拷贝到可执行文件中,之后运行时就不再需要库了。

这两者在提取符号上有很大的不同:

在动态链接中,所有的库符号加载到输出文件的虚拟地址空间中,所有的符号对于链接在一起的所有文件都是可见的。

在静态链接中,在处理静态库时,它只是在静态库中查找链接器当前所知道的未定义符号。换句话说,在编译器命令行中各个静态链接库的顺序是非常重要的。

链接器会被「函数库是在哪里提到的?」「它是以什么次序出现的?」之类的问题搞的晕头转向,因为符号是通过从左到右的顺序进行解析的。在尚未出现未定义的符号时,它不会从函数库中提取任何符号。为了能从静态库中提取所需的符号,首先需要让文件包含未解析的引用。

解答

有上面的知识,开头的问题也就很好解释了。muduo 使用的是静态库,在最开始的时候,我先链接了 muduo_base 库,又链接了 muduo_net 库。

程序中只有 EventLoop 一个未定义的符号,扫描到 muduo_base 库,此时并没有能从该库提取的符号,继续扫描到 muduo_net 库,找到了 EventLoop 符号。由于 EventLoop 中用到 Logger 打印日志、Timestamp 记录时间等等。这些符号都是在 muduo_base 中定义的,但 muduo_base 已经扫描过了,向后寻找也没找到这些符号的定义,于是出现了未定义错误。

当我改变链接顺序时,先扫描 muduo_net 找到了大量定义于 muduo_base 的符号,再扫描 muduo_base 找到这些符号的定义,也就编译成功了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值