静态链接动态链接的链接顺序问题和makefile示例

代码文件初始化

a.h

#ifndef A_H
#define A_H
#include <iostream>
class A
{
public:
 A(){}
    ~A(){}
    void hello();
};
#endif

a.cpp

#include "a.h"

void A::hello()
{
      std::cout << "hello world\n";
}

b.h

#ifndef B_H
#define B_H
#include "a.h"
#include <iostream>

class B
{
    public:
       void sayB();
    private:
      A a;

};
#endif

b.cpp

#include "b.h"

void B::sayB()
{
    std::cout << "B is running\n";
    a.hello();
    std::cout << "B is end\n";
}

c.cpp

#include "b.h"

int main()
{
    std::cout << "C is running\n";
    B b;
    b.sayB();
    std::cout << "C is end\n";
}

.o文件编译可执行文件

目标文件(.o文件)编译,不讲究顺序

生成各个文件的.o文件
[root@localhost test]# g++ -c *.cpp

[root@localhost test]# g++ -o main a.o b.o c.o
[root@localhost test]# g++ -o main c.o b.o a.o
[root@localhost test]# g++ -o main *.o
[root@localhost test]# ./main
C is running
B is running
hello world
B is end
C is end

直接g++ c.cpp -o main是不行的,会缺依赖,少了B::sayB()的实现
[root@localhost test]# g++ c.cpp -o main
/tmp/cc2F6BQH.o: In function `main':
c.cpp:(.text+0x2c): undefined reference to `B::sayB()'
collect2: error: ld returned 1 exit status

直接g++ c.cpp -o main b.o也是不行的,会缺依赖,少了A::hello()的实现
[root@localhost test]# g++ c.cpp -o main b.o
b.o: In function `B::sayB()':
b.cpp:(.text+0x23): undefined reference to `A::hello()'
collect2: error: ld returned 1 exit status

正确
[root@localhost test]# g++ c.cpp -o main b.o a.o
[root@localhost test]# ./main
C is running
B is running
hello world
B is end
C is end

创建静态链接文件并编译

使用 ar 将目标文件归档

ar crv libabc.a a.o b.o c.o

其中:

  • c 如果需要生成新的库文件,不要警告
  • r 代替库中现有的文件或者插入新的文件
  • v 输出详细信息

通过 ar t libabc.a 可以查看 libabc.a 中包含的目标文件,还可以通过 ar --help 查看更多帮助。

注意:我们要生成的库的文件名必须形如 libxxx.a ,这样我们在链接这个库时,就可以用 -lxxx。反过来讲,当我们告诉编译器 -lxxx时,编译器就会在指定的目录中搜索 libxxx.a 或是 libxxx.so

静态链接文件:在链接阶段,将源文件中用到的库函数与汇编生成的目标文件.o合并生成可执行文件。

静态链接文件(.a文件)链接,讲究顺序,左边的lib库依赖右边的lib库

不讲究顺序的错误方式:
[root@localhost test]# g++ -o main aaa/liba.a aaa/libb.a aaa/libc.a 
aaa/libc.a(c.o): In function `main':
c.cpp:(.text+0x2c): undefined reference to `B::sayB()'
collect2: error: ld returned 1 exit status
[root@localhost test]# g++ -o main aaa/*.a
aaa/libc.a(c.o): In function `main':
c.cpp:(.text+0x2c): undefined reference to `B::sayB()'
collect2: error: ld returned 1 exit status

1、使用3个.a静态链接文件三种方式
(1)正确顺序的依赖
libc.a依赖libb.a,libb.a依赖liba.a
[root@localhost test]# g++ -o main aaa/libc.a aaa/libb.a aaa/liba.a

(2)多次依赖
在使用一些循环依赖关系比较复杂的静态库时,也可以在链接序列中,让一个静态库出现多次,来解决一些循环依赖。
[root@localhost test]# g++ -o main aaa/liba.a aaa/libb.a aaa/libc.a aaa/libb.a aaa/liba.a

(3)使用Xlinker
Xlinker选项是将参数传给链接器,链接器在处理”-(”和”-)”之间的静态库时,是会重复查找这些静态库的,所以就解决了静态库查找顺序问题。不过,这种方式比人工提供链接顺序的方式效率会低很多。
[root@localhost test]# g++ -o main -Xlinker "-(" aaa/liba.a aaa/libb.a aaa/libc.a -Xlinker "-)"

2、使用2个.a静态链接文件
少了liba
[root@localhost test]# g++ c.cpp -o main aaa/libb.a 
aaa/libb.a(b.o): In function `B::sayB()':
b.cpp:(.text+0x23): undefined reference to `A::hello()'
collect2: error: ld returned 1 exit status
链接顺序出错
[root@localhost test]# g++ c.cpp -o main aaa/liba.a aaa/libb.a
aaa/libb.a(b.o): In function `B::sayB()':
b.cpp:(.text+0x23): undefined reference to `A::hello()'
collect2: error: ld returned 1 exit status
正确
[root@localhost test]# g++ c.cpp -o main aaa/libb.a aaa/liba.a 
[root@localhost test]# ./main 
C is running
B is running
hello world
B is end
C is end

3、将a.cpp和b.cpp制作成.a,然后编译c.cpp时进行链接
[root@localhost test]# ar crv aaa/libab.a a.o b.o
[root@localhost test]# g++ c.cpp -o main -L./aaa -lab
[root@localhost test]# ./main 
C is running
B is running
hello world
B is end
C is end

4、将a.cpp,b.cpp和c.cpp制作成.a,然后直接生成可执行文件时链接
[root@localhost test]# ar crv aaa/libabc.a a.o b.o c.o
[root@localhost test]# g++ -o main -L./aaa -labc
[root@localhost test]# ./main 
C is running
B is running
hello world
B is end
C is end

5、分别制作三个.a,分别链接,发现不行,原因未知
[root@localhost test]# g++ -o main -L./aaa/tmp -la -lb -lc
/usr/bin/ld: ./aaa/tmp/libc.a(c.o): undefined reference to symbol '__cxa_atexit@@GLIBC_2.2.5'
/usr/bin/ld: note: '__cxa_atexit@@GLIBC_2.2.5' is defined in DSO /usr/lib64/libc.so.6 so try adding it to the linker command line
/usr/lib64/libc.so.6: could not read symbols: Invalid operation
collect2: error: ld returned 1 exit status
[root@localhost test]# g++ -o main -L./aaa/tmp -lc -lb -la
/usr/bin/ld: ./aaa/tmp/libc.a(c.o): undefined reference to symbol '__cxa_atexit@@GLIBC_2.2.5'
/usr/bin/ld: note: '__cxa_atexit@@GLIBC_2.2.5' is defined in DSO /usr/lib64/libc.so.6 so try adding it to the linker command line
/usr/lib64/libc.so.6: could not read symbols: Invalid operation
collect2: error: ld returned 1 exit status

创建动态链接文件并编译

编译生成动态库

g++ -fPIC -shared -o ./soso/libab.so a.cpp b.cpp

实际上上述过程分为编译和链接两步, -fPIC是编译选项,PIC是 Position Independent Code 的缩写,表示要生成位置无关的代码,这是动态库需要的特性; -shared是链接选项,告诉g++生成动态库而不是可执行文件。

上述的一行命令等同于:

g++ -c -fPIC a.cpp b.cpp # 注意必须带-fPIC,不带的话生成的.o目标文件是没法用于生成.so文件的
g++ -shared -o libab.so a.o b.o

g++ c.cpp -o main -L./soso -lab 生成main,其中-lab表示要链接libab.so
-L./soso表示搜索要链接的库文件时包含./soso路径。

如果同一目录下同时存在同名的动态库和静态库,比如 libab.solibab.a 都在当前路径下,g++会优先链接动态库。想链接静态库需要显式指定g++ -static c.cpp -o main -L. -lab

动态链接文件:在程序运行过程中动态调用库文件,很方便,不占空间

动态链接文件(.so文件)链接,不讲究顺序

1、使用3个so动态链接文件
[root@localhost test]# g++ -o main soso/liba.so soso/libb.so soso/libc.so
[root@localhost test]# g++ -o main soso/libc.so soso/libb.so soso/liba.so
[root@localhost test]# g++ -o main soso/*.so

2、使用2个so动态链接文件
[root@localhost test]# g++ c.cpp -o main soso/libb.so 
soso/libb.so: undefined reference to `A::hello()'
collect2: error: ld returned 1 exit status
[root@localhost test]# g++ c.cpp -o main soso/libb.so soso/liba.so

3、将a.cpp和b.cpp制作成so,然后编译c.cpp时进行链接
[root@localhost test]# g++ -fPIC -shared -o ./soso/libab.so a.cpp b.cpp
[root@localhost test]# g++ c.cpp -o main -L./soso -lab
[root@localhost test]# ./main
./main: error while loading shared libraries: libab.so: cannot open shared object file: No such file or directory
找不到libab.so,因为Linux是通过 /etc/ld.so.cache 文件搜寻要链接的动态库的, /etc/ld.so.cache 是 ldconfig 程序读取 /etc/ld.so.conf 文件生成的(/etc/ld.so.conf 中并不一定需要包含 /lib 和 /usr/lib,ldconfig程序会自动搜索这两个目录)。

如果我们把 libab.so 所在的路径添加到 /etc/ld.so.conf 中,再以root权限运行 ldconfig 程序,更新 /etc/ld.so.cache ,main运行时,就可以找到 libab.so。

也可以直接用命令LD_LIBRARY_PATH=./soso的方式指定动态链接库路径。
[root@localhost test]# LD_LIBRARY_PATH=./soso ./main
C is running
B is running
hello world
B is end
C is end

4、将a.cpp,b.cpp和c.cpp制作成so,然后直接生成可执行文件时链接
[root@localhost test]# g++ -fPIC -shared -o ./soso/libabc.so a.cpp b.cpp c.cpp
[root@localhost test]# g++ -o main -L./soso -labc
[root@localhost test]# LD_LIBRARY_PATH=./soso ./main
C is running
B is running
hello world
B is end
C is end

5、分别制作三个so,分别链接,发现不行,原因未知
[root@localhost test]# g++ -fPIC -shared -o ./soso/liba.so a.cpp
[root@localhost test]# g++ -fPIC -shared -o ./soso/libb.so b.cpp
[root@localhost test]# g++ -fPIC -shared -o ./soso/libc.so c.cpp
[root@localhost test]# g++ -o main -L./soso -la -lb -lc
/usr/bin/ld: /usr/lib/gcc/x86_64-linux/4.8.5/../../../../lib64/crt1.o: undefined reference to symbol '__libc_start_main@@GLIBC_2.2.5'
/usr/bin/ld: note: '__libc_start_main@@GLIBC_2.2.5' is defined in DSO /usr/lib64/libc.so.6 so try adding it to the linker command line
/usr/lib64/libc.so.6: could not read symbols: Invalid operation
collect2: error: ld returned 1 exit status
[root@localhost test]# g++ -o main -L./soso -lc -lb -la
/usr/bin/ld: /usr/lib/gcc/x86_64-linux/4.8.5/../../../../lib64/crt1.o: undefined reference to symbol '__libc_start_main@@GLIBC_2.2.5'
/usr/bin/ld: note: '__libc_start_main@@GLIBC_2.2.5' is defined in DSO /usr/lib64/libc.so.6 so try adding it to the linker command line
/usr/lib64/libc.so.6: could not read symbols: Invalid operation
collect2: error: ld returned 1 exit status

制作makefile

将链接2个和3个动态文件及静态文件的方式做成makefile。

Makefile有三个非常有用的变量:
$@:目标文件
$^:所有的依赖文件
$<:第一个依赖文件。

.PHONY: buildabcso testabcso buildabso testabso buildabca testabca buildaba testaba clean

buildabcso: libabc.so

buildabso: libab.so

buildabca: libabc.a

buildaba: libab.a

libabc.so: a.o b.o c.o
	g++ -shared -o $@ $^

libab.so: a.o b.o
	g++ -shared -o $@ $^

libabc.a: a.o b.o c.o
	ar crv $@ $^

libab.a: a.o b.o
	ar crv $@ $^

a.o: a.cpp
	g++ -c -fPIC $<

b.o: b.cpp
	g++ -c -fPIC $<

c.o: c.cpp
	g++ -c -fPIC $<

testabcso: libabc.so
	g++ -o main -L. -labc
	LD_LIBRARY_PATH=. ./main

testabso: libab.so
	g++ c.cpp -o main -L. -lab
	LD_LIBRARY_PATH=. ./main

testabca: libabc.a
	g++ -o main -L. -labc
	./main

testaba: libab.a
	g++ c.cpp -o main -L. -lab
	./main

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

运行make buildabcso构建生成libabc.so,运行make testabcso使用该libabc.so动态链接库生成main可执行文件并执行,其他build和test同理。

makefile工作原理:
1、make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
2、如果make后有对应指令,会去.PHONY中找到对应指令;否则缺省的情况下,它会找第一条指令作为目标指令,在这里就是即buildabcso。make会把匹配到的指令作为最终的目标文件。
3、make发现buildabcso不存在,于是需要检查buildabcso该文件的依赖,在这里是libabc.so:
①、如果发现libabc.so已经存在,直接执行,由于buildabcso的行为啥都没定义,只有个依赖前缀libabc.so,所以执行buildabcso时其实啥都没做,即:

[root@localhost test]# make buildabcso
make: Nothing to be done for `buildabcso'.

②、如果发现libabc.so不存在,或是buildabcso所依赖的libabc.so文件修改时间要比buildabcso这个文件新,再检查libabc.so的依赖,也就是a.o b.o c.o,如果a.o和b.o和c.o三个文件已存在,才能执行g++ -shared -o $@ $^生成libabc.so,如果三个.o不存在,继续查对应依赖生成,一直这样迭代下去。。。
4、make会一层一层去找文件的依赖关系,直到最终编译出第一个目标文件,如果某个命令执行失败,则make会停止。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值