库在程序编译链接及运行时的应用

本文以 Ubuntu 16.04 上的 GCC 5.4 为基础。

由操作系统中可以了解到,从源代码文件到装入内存主要包括以下几步:编译,链接,运行。

源码包在编译链接后,既可以得到相应的可执行程序(主函数源文件与其它源文件或库文件编译链接成一个可执行文件),也可以得到相应的库(其他非主函数源文件和库文件编译链接成一个库文件)。其中,最重要的部分就在于链接过程,它是 GCC 中最为核心的部分。GCC 封装了一个 ld 链接器,它专门用于实现这部分的操作。


1 静态库与动态库的区别

1.1 在内容及所占空间上

  • 静态库一般是由一系列的目标文件归档构成的。
  • 动态库一般是由某几个特殊的目标文件以及其所依赖的一些其他动态库构成的,且动态库并不直接包含它所依赖的这些动态库。正因为这个原因,在同等情况下,动态库比静态库占有的空间要小很多。

1.2 在链接过程中

静态库与动态库的另一个区别在于,依赖于它们生成可执行程序或者新的库的时候在链接阶段的处理方式的不同

  • 对于静态库。

    若所要生成的可执行文件依赖于某个静态库,就相当于是将这个静态库直接包含到可执行文件中。由于这个原因,可执行文件在运行时不需要对静态库中的内容进行外部搜索,节省了时间开销。

    但是,一旦改变这个静态库,那么依赖于它的可执行文件就势必要重新进行编译链接,所以显得不够灵活。

  • 对于动态库。

    动态库可以被多个可执行文件或者其他动态库共享,因此也叫共享库 (Shared Object)。由于在生成一个动态库或者可执行文件时,并不会直接将它所依赖的其他动态库直接包含进来。因此当链接这个动态库时或者运行运行这个可执行文件时,就需要进行外部搜索,增加了时间开销。

    不过,如果改变了这个动态库,那么依赖于它的可执行程序或动态库可以无需重新编译链接(只要这个改变不是很大),比较灵活。

当然,静态库也可以依赖于动态库或者静态库,但这种方式在某种程度上来说没有多大意义,所以这里并不对此进行讨论;
一般情况下,动态库不能依赖于静态库。

2 确定出编译时所需的头文件以及链接时所需的目标文件或库

在编写源文件时,需要根据源文件是否引用了某个头文件中的接口,来判定这个源文件所需要的所有头文件。进而才能在编译时确定出需要搜索哪些头文件。

同样,在生成动态库或者可执行文件时,需要根据源文件或目标文件所引用的接口的定义的位置,来确定出在链接时需要搜索哪些目标文件或者库。

3 直接或间接依赖的动态库

对于静态库来说,不存在间接依赖的情况,因为静态库不依赖于任何其他库。但是对于动态库来说,则存在直接依赖和间接依赖之分。

在链接生成动态库或者可执行文件时,源文件或目标文件所引用的接口的定义所在的动态库,即为链接生成的动态库或者可执行文件 直接依赖的动态库。而这些直接依赖的动态库所依赖的动态库,则称为链接生成的动态库或者可执行文件 间接依赖的动态库

  • 在链接生成动态库时,直接依赖的动态库必须要用 -l,且需要保证能够查找到直接依赖的动态库。不过并不需要保证能够查找到间接依赖的动态库(也就是说,链接生成动态库的过程与其间接依赖的动态库无关)。
  • 在链接生成可执行文件时,直接依赖的静态库或动态库必须要用 -l 参数指明,且需要保证能够查找到直接依赖的静态库或动态库。另外,还需要保证能够查找到间接依赖的动态库;
  • 在运行可执行文件时,必须要保证能够查找到其所有直接依赖的动态库以及间接依赖的动态库。

4 ELF 与 readelf 命令

ELF (Executable and Linkable Format),也即可执行连接文件格式,是一种可移植的标准目标文件格式。比如 Linux 和 Unix 中的可执行文件,静态库文件,.o文件,动态库文件等都是 ELF 格式的文件。

对于 ELF 文件,它的头部存储了这个文件的相关描述性信息。对于可执行程序或者库来说,它的头部所依赖的动态库等信息,可能还存储了 RPATH 或者 RUNPATH 字段。

这两个字段保存了搜索动态库时所需要知道的路径。当链接某个动态库生成可执行文件时,ld 链接器会根据这个动态库文件中的这两个字段去查找该动态库所依赖的其他动态库。当可执行文件运行时,也会根据其中的这两个字段去查找它所依赖的动态库。

可通过如下命令查看一个 elf 格式的文件的所有信息:
readelf -a 文件路径

如果要查看其所依赖的库的信息以及 RPATH 和 RUNPATH 字段,则可用如下命令:
readelf -d 文件路径

5 头文件的搜索顺序

  • 源文件所在目录;

    • 对于 #include “...“ 来说,搜索这个文件所在的目录(而非当前工作目录);
    • 对于 #include <...> 来说,则不会搜索这个文件所在目录。
  • 如果未找到(对于前者)或者对于后者,则按照 -I 指定的路径进行搜索;
  • 如果未指定或未找到,则按照 CPLUS_INCLUDE_PATH 环境变量(对于 C++)或者 C_INCLUDE_PATH 环境变量(对于 C)指定的的路径进行搜索;
  • 如果未指定或未找到,则按照 GCC 内置的头文件目录的路径(编译 GCC 时就已经指定好了)进行搜索;
  • 如果未找到,则查找失败。

GCC 内置的头文件目录的路径可由如下命令得到:
echo | gcc -Wp,-v -x c++ - -fsyntax-only
该命令的最后几行就是 GCC 内置的头文件目录的路径。

另外,如果要忽略掉内置的头文件目录的路径,可添加 -nostdinc 编译选项(对于 C)或者 -nostdinc++ 编译选项(对于 C++)。

6 链接时静态库的搜索顺序

首先,所需要的静态库必须要用 -l 参数指明。

  • 按照 -L 指定的路径进行搜索;
  • 如果未指定或未找到,则按照 LIBRARY_PATH 环境变量指定的路径进行搜索;
  • 如果未指定或未找到,则按照 GCC 内置的库目录的路径 进行搜索;
  • 如果未找到,则查找失败。

GCC 内置的库目录的路径可由以下命令查看:
ld --verbose | grep SEARCH_DIR

另外,如果要忽略掉内置库目录的路径,可添加 -nostdlib 编译选项。

7 链接时动态库的搜索顺序

7.1 生成的是动态库

若生成的是动态库,在查找其所依赖的动态库时,只需要找到其所 直接依赖 的其他动态库即可(必须要用 -l 参数指明)。因此链接时动态库的搜索顺序与静态库的搜索顺序相同。

7.2 生成的是可执行文件

若生成的是可执行文件,则查找其所依赖的动态库时,不仅要找到其所直接依赖的其他动态库,还要找到所有间接依赖的动态库。对于其直接依赖的动态库的搜索,则同静态库的搜索相同。

对于其间接依赖的动态库的搜索。设 main.out 依赖于其直接动态库 libbb.so,而 libbb.so 依赖于另一个动态库 libaa.so,则在链接生成 main.out 的过程中,ld 按照如下顺序查找其间接依赖的动态库 libaa.so:

  • 如果用 -l 指明了这个间接依赖的动态库,也即 -laa,则同静态库的搜索相同(忽略下面的所有步骤)。
  • 否则,如果 libbb.so 文件的头部存在 RPATH,则根据它指定的路径进行搜索;
  • 如果不存在或未找到,则按照链接参数 -Wl,-rpath 或者 -Wl,-rpath-link 指定的路径进行搜索;
  • 如果未指定或未找到,则按照 LD_RUN_PATH 环境变量指定的路径进行搜索;
  • 如果未指定或未找到,则按照 LD_LIBRARY_PATH 环境变量指定的路径进行搜索;
  • 如果未指定或未找到。如果 libbb.so 文件的头部存在 RUNPATH 字段,则根据它指定的路径进行搜索;
  • 如果不存在或未找到,则按照 /etc/ld.so.cache 中存放的路径进行搜索;
  • 如果未找到,则按照 GCC 内置的库目录的路径 进行搜索;
  • 如果未找到,则查找失败。

由于上面提到的 -rpath 参数等是 ld 链接器直接提供的,而非 gcc 或 g++(gcc 和 g++ 只是封装了 ld.so)直接提供,因此在使用这些参数时,需要分别在前面加上 -Wl, (前面的是大写字母,后面那个是小写字母),表明这些参数是由 ld 链接器提供的。

另外,

  • -LLIBRARY_PATH 环境变量只能用来查找直接依赖的库;
  • -Wl,-rpath 或者 -Wl,-rpath-linkLD_RUN_PATHLD_LIBRARY_PATH/etc/ld.so.cache 只能用来查找间接依赖的库。

    它们的更多作用详见后面小节。

  • 不可能出现既存在 RPATH 又存在 RUNPATH 的情况。

8 链接时输入文件的书写顺序

将被依赖的库文件写在依赖于它的源文件或目标文件的后面

比如,c.o 依赖于 libbb.so,则在链接生成可执行文件时不能写为:
g++ -L库所在目录的路径 -lbb c.o
而要写为:
g++ c.o -L库所在目录的路径 -lbb

在链接生成动态库时不能写为:
g++ -shared -L库所在目录的路径 -lbb c.o -o libcc.so
而要写为:
g++ -shared c.o -L库所在目录的路径 -lbb -o libcc.so

9 pkg-config

pkg-config 是程序编译链接时的一个重要工具。可用它来替代编译链接中的 -I-L-l 参数。

对于一个库或者可执行文件,如果它支持 pkg-config,那么在安装好这个库或者可执行文件时,会在 其安装目录下的 lib/pkgconfig 里产生一个 .pc文件。这个 .pc 文件中,记录了关于这个库的很多信息,比如库的版本号,库的头文件路径,库路径等等。下面是安装好 Python3 后其安装目录下的子目录中的 .pc 文件里的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
# See: man pkg-config
prefix=/home/cuckootan/Software/python3
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include

Name: Python
Description: Python library
Requires:
Version: 3.5
Libs.private: -lpthread -ldl -lutil
Libs: -L${libdir} -lpython3.5m
Cflags: -I${includedir}/python3.5m

注意到最后两行,正好是库所在目录的路径和库名,以及相应头文件所在目录的路径。因此,如果某个库支持 pkg-config,那么在编译链接生成一个依赖于这个库的可执行文件或者库时,就可以通过 pkg-config 工具来替代 -I-L-l 参数。

具体地,在链接前:

1
g++ test.cc `pkg-config --cflags 库名`

指定头文件所在目录的路径。

在链接时:

1
g++ test.o `pkg-config --libs 库名`

指定库所在目录的路径以及该目录下所有库的名字。

当然,也可以一起使用:

1
g++ test.cc `pkg-config --cflags --libs opencv`

注意:两边的符号是键盘左上角的 `,在 esc 下面,相当于是取出 pkg-config 这条命令执行的结果。

在编译链接生成某个库或可执行文件时,为了方便使用 pkg-config,可以将所需要的库相应的 pkgconfig 目录的路径加入到 PKG_CONFIG_PATH 这个环境变量中。

当然,如果所需要的库(动态库或静态库)不支持 pkg-config,那么仍然需要通过 -I 指定库对应的头文件所在目录的路径,通过 -L 指定库所在目录的路径,通过 -l 来指定所需要的库。

10 可执行文件运行时动态库的搜索顺序

  • 如果可执行文件的头部存在 RPATH,则根据它指定的路径进行搜索;
  • 如果不存在或未找到,则按照 LD_RUN_PATH 环境变量指定的路径进行搜索;
  • 如果未指定或未找到,则按照 LD_LIBRARY_PATH 环境变量指定的路径进行搜索;
  • 如果未指定或未找到。如果可执行文件的头部存在 RUNPATH 字段,则根据它指定的路径进行搜索;
  • 如果不存在或未找到,则按照 /etc/ld.so.cache 中存放的路径进行搜索;
  • 如果未找到,则按照 GCC 内置的库目录的路径 进行搜索;
  • 如果未找到,则查找失败。

不可能出现既存在 RPATH 又存在 RUNPATH 的情况。

11 ldd 命令

使用方法:
ldd 可执行文件或者动态库的路径

可以通过该命令操作 ld 链接器,查看一个可执行文件所依赖的所有动态库的信息,或者一个动态库所依赖的其他动态库(不一定是所有的,但至少会包含所有直接依赖的动态库)的信息。

假设对动态库使用 ldd 命令:

  • 如果 ld 链接器不能找到其所直接依赖的动态库,则会显示为 not found
  • 设其所直接依赖的一个动态库为 libbb.so,后者依赖于 libaa.so。如果 ld 链接器不能找到 libbb.so,则它不可能知道 libaa.so 的任何信息(甚至不知道 libbb.so 是否依赖 libaa.so)。此时,ldd 命令执行的结果不会包含 libaa.so 的相关字段。

使用方式为:
-Wl,-rpath,动态库所在目录的路径1:动态库所在目录的路径2:......
或者:
-Wl,-rpath-link,动态库所在目录的路径1:动态库所在目录的路径2:......

12.1 两者的联系与区别

正如前面所描述的那样,两者都是链接参数,都可以用来指定在链接时所生成的可执行文件所间接依赖的动态库的目录的路径。

不过,

  • 前者还可以在生成的可执行文件或者动态库文件中添加 RPATH 字段,其中的信息为 -Wl,-rpath 后面所指定的路径;
  • 而后者则不能。

12.2 \$ORIGIN 字段

对于用 -Wl,-rpath 的方式在生成的可执行文件或动态库中添加 RPATH 字段,如果生成的可执行文件依赖于自己生成的动态库,或者是生成的动态库依赖于自己之前生成的动态库,那么使用上述方法可能会存在一个问题。

就是当整个安装目录移动到其他目录下后,如果 RPATH 指定的是绝对路径,那么可能会导致可执行文件在运行时或者链接生成某个可执行文件,由于不能正确搜索到它所依赖的动态库而失败。对于此种情况,GCC 提供了一种解决方案,就是使用 \$ORIGIN 字段。

使用如下:
-Wl,-rpath,'$ORIGIN/../lib':......

由于\$为特殊字符,因此需要在这个字符串的两边添加单引号进行转义。当然,也可以是在 \$ 之前添加 \,故它的另一种写法如下:
-Wl,-rpath,\$ORIGIN/../lib:......

可执行文件在执行时或者链接生成某个可执行文件时,\$ORIGIN 会自动变换为可执行文件或动态库所在目录的绝对路径(一般来说也即安装目录下的 bin 子目录或者 lib 子目录)。

当然,如果所依赖的动态库是外部的,那么 \$ORIGIN 是无法解决安装目录移动后所带来的问题的。此时,仍然需要指定动态库的绝对路径才可以。

在使用带有\$ORIGIN关键字的路径时,也会存在一个问题,那就是 \,\$ 这些特殊字符的转义问题。

如果是直接打命令行,那就要上面两种形式之一即可。
如果是在 makefile 里,就要用添加额外的 \$ 符号对 \$ 进行转义。那么在相应的 LDFLAGS 参数里的设置如下:
-Wl,-rpath,'$$ORIGIN/../lib':......
或者
-Wl,-rpath,\$$ORIGIN/../lib:......

而现在很多从网上下载下来的源码包里,大部分均是用 autoconf 或者 cmake 生成 makefile 之类的文件的,因此这里又有一层可能需要对这些特殊字符转义的地方。事实上,即使是使用了相同的 autoconf 或者 cmake 工具的不同源码包,它内部对特殊字符的处理也不同。比如说:

Python3 使用的是 autoconf 工具,在进行配置时,在终端需要输入以下命令:
./configure --prefix=... --enable-shared LDFLAGS="-Wl,-rapth,'\$\$ORIGIN/../lib'"

ffmepg 使用的也是 autoconf 工具,在进行配置时,在终端需要输入以下命令:
./configure --prefix=... --enable-gpl --enable-version3 --enable-shared --disable-static --extra-ldflags="-Wl,-rpath,'\\\$\\\$ORIGIN/../lib'"

因此带来了很大的混乱,甚至有人在网上怒骂。为了避免这些问题,并且在设置 RPATH 更为方便,GCC 提供了 LD_RUN_PATH 环境变量。

13 LD_RUN_PATH

它是一种替代 -Wl,-rpath 的环境变量(因此也会在生成的可执行文件或者动态库文件中添加 RPATH 字段)。

使用方法为,在编译源码前设置 LD_RUN_PATH 这个环境变量。比如:
LD_RUN_PATH='$ORIGIN/../lib:动态库所在目录的路径2:......'

14 LD_LIBRARY_PATH

它也是 GCC 提供的一个环境变量。

  • 不过 LD_LIBRARY_PATH 是个全局的环境变量,一旦某个可执行文件在运行成功后,其他可执行文件在运行时可能会运行失败。
  • 而且在查找动态库时,可能会搜索一些毫不相关的库的路径,时间消耗较大。

因此这个环境变量只适合临时调试的时候使用。

15 ldconfig 命令

ld.so.cache 相当于是一个动态库及其路径的缓存文件,它由 ldconfig 命令直接管理。

当在 /etc/ld.so.conf.d 这个目录下增删改 *.conf 文件时,或者增删改这些文件中所指定路径下的动态库或 /lib/usr/lib 中的动态库时,需要运行如下命令使其生效:
sudo ldconfig
之后,会将这些配置信息更新到 ld.so.cache 中。

还可以查看 ld.so.cache 中的动态库及其路径信息:
sudo ldconfig -p

ld.so.cache 也会存在类似于 LD_LIBRARY_PATH 中出现的问题,因此一般只适合临时调试时用。

16 --enable-new-dtags

可以通过这个参数来设置 RUNPATH。使用方法如下:
-Wl,-rpath,动态库所在目录的路径1:动态库所在目录的路径2:......,--enable-new-dtags

如果是通过 LD_RUN_PATH 指定搜索目录的路径的,那么直接在链接参数 LDFALGS 中设置如下字段即可:
-Wl,--enable-new-dtags

不可能出现既存在 RPATH 又存在 RUNPATH 的情况。

RUNPATH 有一个好处,就是当存在 RUNPATH 时,就可以通过 LD_LIBRARY_PATH 设置动态库所在目录的其它路径(比如说这个库的另一个版本所在目录的路径),从而在搜寻动态库时优先从 LD_LIBRARY_PATH 中获取到动态库所在目录,达到调试的目的。

17 示例

这里通过举例来说明动态库的搜索。

17.1 代码

文件组织如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
.
├── a
│   ├── a.cc
│   └── a.h
├── b
│   ├── b.cc
│   └── b.h
├── c
│   ├── c.cc
│   └── c.h
└── test
└── main.cc

其中,b.cc 中需要调用 a.cc 中定义的函数,c.cc 中需要调用 b.cc 中定义的函数,main.cc 中需要调用 b.cc 中定义的函数。具体地:

a.h 内容为:

1
2
3
4
5
6
7
8
#ifndef _A_H_
#define _A_H_

#include "a.h"

extern void show_a();

#endif

a.cc 内容为:

1
2
3
4
5
6
7
#include "a.h"
#include <iostream>

void show_a()
{
std::cout << 'a' << std::endl;
}

b.h 内容为:

1
2
3
4
5
6
7
8
#ifndef _B_H_
#define _B_H_

#include "b.h"

extern void show_b();

#endif

b.cc 内容为:

1
2
3
4
5
6
7
8
9
10
#include "b.h"
#include "a.h"
#include <iostream>

void show_b()
{
std::cout << 'b' << std::endl;

show_a();
}

c.h 内容为:

1
2
3
4
5
6
7
8
#ifndef _C_H_
#define _C_H_

#include "c.h"

extern void show_c();

#endif

c.cc 内容为:

1
2
3
4
5
6
7
8
9
10
#include "c.h"
#include "b.h"
#include <iostream>

void show_c()
{
std::cout << 'c' << std::endl;

show_b();
}

test.cc 内容为:

1
2
3
4
5
6
7
8
9
#include "b.h"
#include <iostream>

int main(int argc, const char *argv[])
{
show_b();

return 0;
}

17.2 对 a.cc 和 b.cc 生成动态库

设当前工作目录为 a, b, c, test 所在目录。

对 a.cc 生成动态库 libaa.so:
g++ -c -fPIC -Wall ./a/a.cc -o ./a/libaa.o
g++ -shared ./a/libaa.o -o ./a/libaa.so

对 b.cc 生成动态库 libbb.so:
g++ -c -fPIC -Wall ./b/b.cc -I./a -o ./b/libbb.o
g++ -shared ./b/libbb.o -L./a -laa -o ./b/libbb.so

17.3 对 c.cc 生成动态库

设当前工作目录为 a, b, c, test 所在目录。

g++ -c -fPIC -Wall c/c.cc -I./b -o c/libcc.o
g++ -shared c/libcc.o -L./b -lbb -o c/libcc.so

由于生成的是动态库,因此在链接过程中,ld 链接器只需要查找到 libcc.so 的直接依赖 libbb.so 即可。

17.4 链接生成 main.cc 的可执行文件

设当前工作目录为 a, b, c, test 所在目录。

如果直接运行:
g++ test/main.cc -I./b -L./b -lbb -o test/main.out
则会提示 b.cc 中的 show_a() 未定义,也即在链接过程中 ld 链接器找不到 libaa.so。这个时候可以采用以下任意一种方法解决:

  • 链接时用 -l 指明 libaas.so 动态库,也即 -laa,并用 -L 指明这个动态库所在目录的路径。也即:
    g++ test/main.cc -I./b -L./b -lbb -L./a -laa -o test/main.out
  • 在生成 libbb.so 时,添加 libaa.so 所在目录的路径到 RPATH 或者 RUNPATH 中。也即:
    g++ -shared b/libbb.o -L./a -laa -Wl,-rpath,a -o b/libbb.so
    或者
    g++ -shared b/libbb.o -L./a -laa -Wl,-rpath,a,--enable-new-dtags -o b/libbb.so
  • 添加 libaa.so 所在目录的路径到 LD_RUN_PATH 之中。
  • 添加 libaa.so 所在目录的路径到 LD_LIBRARY_PATH 之中。
  • /etc/ld.so.conf.d 目录下添加包含 libaa.so 所在目录的路径的文件(后缀名为 .conf)。

17.5 运行 main.out

设当前工作目录为 a, b, c, test 所在目录。

如果仅仅只是执行上面的步骤,并不能成功运行 main.out,会提示找不到 libbb.so 的相关错误。在保证能够在运行时查找到 libaa.so 的前提下,可以采用以下任意一种方法解决:

  • 在生成 main.out 时,添加 libbb.so 所在目录的路径到 RPATH 或者 RUNPATH 中。也即:
    g++ test/main.cc -I./b -L./b -lbb -Wl,-rpath,b -o test/main.out
    或者
    g++ test/main.cc -I./b -L./b -lbb -Wl,-rpath,b,--enable-new-dtags -o test/main.out
  • 添加 libbb.so 所在目录的路径到 LD_RUN_PATH 之中。
  • 添加 libbb.so 所在目录的路径到 LD_LIBRARY_PATH 之中。
  • /etc/ld.so.conf.d 目录下添加包含 libbb.so 所在目录的路径的文件(后缀名为 .conf)。

18 configure, build 和 install

对于一般的源码包,要么是使用 autoconf 和 automake,要么是使用 cmake,当然也有使用 qmake 等其它工具,来生成 makefile 的。

对于 autoconf 和 automake 工具,执行如下命令来生成 makefile:
./configure ......

对于 cmake 工具,由于配置的参数较多且比较复杂,一般会用 cmake-gui 这个图形界面的 cmake 工具来生成 makefile。

然后执行如下命令开始 build(相当于是在源码包中编译链接生成可执行文件以及库等文件):
make

如果想删除 make 生成的文件,则可以执行:
make clean

之后,就可以进行安装了(安装目录的路径在生成 makefile 之前就已经配置好了),执行如下命令:
make install

有一个问题需要注意。一般来说,生成的 makefile 中的 -L 参数默认会被指定为源码包中的 lib 目录对应的路径,使得在链接生成动态库以及可执行文件时,对于 -l 参数所指定的这个源码包中的 lib 中的库,可以通过 -L 后面的路径找到。
当然,对于 -l 参数所指定的源码包外的其他库,在链接生成动态库以及可执行文件时对它们的搜索,则根据链接时静态库与动态库的搜索顺序进行(可通过 CMAKE_LIBRARY_PATH 指明,相当于 -L 参数)。

如果需要使用源码包里的头文件,一般来说,makefile 已经自动指明了相应的路径。
如果需要使用外部的其他头文件,则根据头文件搜索顺序进行(可通过 CMAKE_INCLUDE_PATH 指明,相当于 -I 参数)。

另外,还可以对生成的动态库或者可执行文件添加 RPATH 或 RUNPATH 字段:

  • 对于 autoconf 和 automake 来说,不仅支持 RPATH,还支持 RUNPATH。可以在执行 configure 前,通过设置 LD_RUN_PATH 进行。比如:
  • 对于 camke 来说,它只支持 RPATH。根据 CMAKE_INSTALL_RPATH 或者 LD_RUN_PATH 指定的路径来添加 RPATH。

cmake 中 RPATH 的相关选项及其常用配置为:

1
2
3
4
5
# RPATH and library search setting
SET(CMAKE_SKIP_BUILD_RPATH FALSE)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")

Reference