C/C++链接问题:命令行参数顺序

背景-链接器

静态链接库时程序员迷惑的源头之一。原因是Linux 链接器使用它们解析外部引用的方式。
在符号解析阶段,链接器从左到右按照它们在编译器驱动程序命令行上出现的顺序来扫描可重定位目标文件和存档文件。
(驱动程序自动将命令行中所有的.c文件翻译为.o文件。在这次扫描中,链接器维护一个可重定位目标文件的集合E(这个集合中的文件会被合并起来形成可执行文件),一个未解析的符号(即引用了但是尚未定义的符号)集合U,以及一个在前面输入文件中已定义的符号集合 D。初始时,EUD均为空。

  • 对于命令行上的每个输入文件 f,链接器会判断是一个目标文件还是一个存档文件(静态库)。如果f是一个目标文件,那么链接器把广添加到E,修改UD来反映f中的符号定义和引用,并继续下一个输人文件。
  • 如果f是一个存档文件,那么链接器就尝试匹配U 中未解析的符号和由存档文件成员定
    义的符号。如果某个存档文件成员 m,定义了一个符号来解析U 中的一个引用,那么就
    m加到E中,并且链接器修改UD来反映m 中的符号定义和引用。对存档文件中
    所有的成员目标文件都依次进行这个过程,直到UD都不再发生变化。此时,任何不
    包含在 E中的成员目标文件都简单地被丢弃,而链接器将继续处理下一个输入文件。
  • 如果当链接器完成对命令行上输人文件的扫描后,U是非空的,那么链接器就会输出一
    个错误并终止。否则,它会合并和重定位E中的目标文件,构建输出的可执行文件。

不幸的是,这种算法会导致一些令人困扰的链接时错误,因为命令行上的库和目标文件的顺序非常重要。在命令行中,如果定义一个符号的库出现在引用这个符号的目标文件之前,那么引用就不能被解析,链接会失败。

问题描述

1.1 示例

源程序如下:

#include <iostream>
#include <mpi.h>
using namespace std;
int main(){
    int provided = -1;
    MPI_Init_thread(NULL,NULL,MPI_THREAD_SINGLE,&provided);
}

执行指令如下:

g++ -I/usr/include/x86_64-linux-gnu/openmpi -L/lib/x86_64-linux-gnu -lmpi -lmpi_cxx mpi_init_thread.cpp

1.2 问题

在这里插入图片描述

1.3 描述

在处理mpi_init_thread.cpp文件时,U是空的,所以没有mpi中的成员目标文件添加到E
。因此,对于MPI_Init_thread的引用是绝不会被解析的,所以链接器产生一条错误信息并终止。

应对方式

2.1 解决方式

关于库的一般准则是将它们放在命令行的结尾。

  • 如果各个库的成员是相互独立的(也就是说没有成员引用另一个成员定义的符号),那么这些库就可以以任何顺序放置在命令行的结尾处。
  • 另一方面,如果库不是相互独立的,那么必须对它们排序,使得对于每个被存档文件的成员外部引用的符号 s,在命令行中至少有一个 s的定义是在对s的引用之后的。比如,假设 foo.c调用libx.alibz.a 中的函数,而这两个库又调用 liby.a中的函数。那么,在命令行中 libx.alibz.a 必须处在 liby.a之前:
gcc foo.c libx.a libz.a liby.a

2.2 解决案例

将源程序mpi_init_thread.cpp放到命令行靠前位置。

g++ mpi_init_thread.cpp -I/usr/include/x86_64-linux-gnu/openmpi -L/lib/x86_64-linux-gnu -lmpi -lmpi_cxx

2.3 拓展

2.3.1 依赖问题

如果需要满足依赖需求,可以在命令行上重复库。比如,假设·foo.c 调用 libx.a中的函数,该库又调用 liby.a中的函数,而 liby.a 又调用 libx.a中的函数。

2.3.2 解决方案

  • libx.a 必须在命令行上重复出现:
gcc foo.c libx.a liby.a libx.a
  • 另一种方法是:我们可以将 libx.aliby.a 合并成一个单独的存档文件。

[1] RANDALE.BRYANT,DAVIDR.O‘HALLARON.深入理解计算机系统[M].机械工业出版社,2011.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值