Linux 共享库详细说明与符号解析顺序

1、静态库和动态库的构建与使用

创建实验所需的源文件。

[root@localhost project]# tree
.
├── librarypathlib
│   └── say.c
├── main.c
├── runpathlib
│   └── say.c
└── staticlib
    └── say.c

文件内容如下,这里将 4 个文件中的内容放在一起展示。

//main.c
void say();

int main()
{
	say();
	return 0;
}

//staticlib/say.c
#include <stdlib.h>
#include <stdio.h>

void say()
{
	printf("hello, static lib\n");
}

//librarypathlib/say.c
#include <stdlib.h>
#include <stdio.h>

void say()
{
	printf("hello, LD_LIBRARY_PATH\n");
}

//runpathlib/say.c
#include <stdlib.h>
#include <stdio.h>

void say()
{
	printf("hello, LD_RUN_PATH\n");
}

a、静态库的构建

静态库的本质是一组目标文件的集合。编译出多个目标文件,通过 ar 命令进行归档成为一个文件。

[root@localhost project]# cd staticlib/
[root@localhost staticlib]# ls
say.c
[root@localhost staticlib]# gcc -c say.c -o say.o
[root@localhost staticlib]# ls
say.c  say.o
[root@localhost staticlib]# ar -rc libsay.a say.o
[root@localhost staticlib]# ls
libsay.a  say.c  say.o
[root@localhost staticlib]# file libsay.a 
libsay.a: current ar archive

gcc 通过 -c 选项,只编译而不进行链接。ar -rc 将目标文件进行归档,得到静态库 .a 文件。

-tv 选项列出归档文件中的所有目标文件。

b、静态库的使用

构建可执行程序时,指定使用的静态库,最终构建出可执行的 elf 文件。

[root@localhost project]# gcc main.c staticlib/libsay.a -o main_static.out
[root@localhost project]# ls
librarypathlib  main.c  main_static.out  runpathlib  staticlib
[root@localhost project]# 
[root@localhost project]# ./main_static.out 
hello, static lib
[root@localhost project]# gcc main.c -L staticlib -l say -o main_static.out
[root@localhost project]# ls
librarypathlib  main.c  main_static.out  runpathlib  staticlib
[root@localhost project]# ./main_static.out 
hello, static lib

指定带路径的库文件 staticlib/libsay.a 名字进行编译即可。

更为专业的做法是通过给gcc 指定 -L 选项指定库文件所在的路径; -l 选项指定库文件的名字(去掉开头的 lib 和结尾的后缀)进行构建。

c、动态库的构建

动态库是一种可链接的 elf 文件,而通常 Linux 上可执行的是可执行的 elf 文件。当然目标 .o 文件也是一种 elf 文件 -- 可重定位 elf 文件。

[root@localhost project]# cd runpathlib/
[root@localhost runpathlib]# ls
say.c
[root@localhost runpathlib]# gcc -c -fPIC say.c -o say.o 
[root@localhost runpathlib]# ls
say.c  say.o
[root@localhost runpathlib]# gcc -shared libsay.so say.o
gcc: error: libsay.so: No such file or directory
[root@localhost runpathlib]# gcc -shared -o libsay.so say.o
[root@localhost runpathlib]# ls
libsay.so  say.c  say.o
[root@localhost runpathlib]# file libsay.so 
libsay.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=84a4d6cf4fa7fbcd95aaf16753a806ca515d4669, not stripped

上边先编译出目标文件,然后再构建动态库。编译和构建的 2 个步骤其实可以合并成 1 个。

[root@localhost runpath]# gcc -fPIC -shared say.c -o libsay.so
[root@localhost runpath]# ls
libsay.so  say.c  say.o

注意一定要指定 -fPIC 表明编译出的目标文件是位置无关的;这样目标文件里的代码才是真正的在不同的进程间共享;否则目标文件中的代码也是各个进程间独占的。当然现在非位置无关的目标文件也不能再构建出动态库,链接器会报错:

[root@localhost runpath]# gcc -shared say.c -o libsay.so
/usr/bin/ld: /tmp/cctx4Evv.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Nonrepresentable section on output
collect2: error: ld returned 1 exit status

d、动态库的使用

有了动态链接库,现在可以构建动态链接的可执行程序。

一样的,可以通过指定带路径的库文件的名字;或者通过 -L 和 -l 选项 这种更常见的这样两种方式来构建。

[root@localhost project]# gcc -L runpathlib -l say main.c -o main_dynamic_purelib.out
[root@localhost project]# ./main_dynamic_purelib.out 
./main_dynamic_purelib.out: error while loading shared libraries: libsay.so: cannot open shared object file: No such file or directory

 当然此时是无法运行动态链接构建的可执行程序,因为程序运行时的链接器(区别于编译时的静态链接器,运行时的链接器又叫动态链接器)找不到库文件。

通过 readelf -d 选项,可以查看 elf 文件依赖的动态库。

c section at offset 0xe00 contains 25 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libsay.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

依赖的动态链接库的标签类别为 NEEDED

ldd 命令能够查看依赖的动态库的位置,由于此时运行时链接器无法定位 libsay.so 的位置,所以显示的是 not found.

[root@localhost project]# ldd main_dynamic_purelib.out
	linux-vdso.so.1 (0x00007ffeee4b2000)
	libsay.so => not found
	libc.so.6 => /lib64/libc.so.6 (0x00007fca79479000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fca7983c000)

前边提到,可以通过在构建时,指定带路径的库文件名字(区别带路径的文件名与不带路径的纯粹文件名);而不是通过 -L 和 -l 选项来使用纯粹库文件名。此 2 种构建方式,对于依赖动态链接库的可执行程序,存在差异。

root@localhost project]# gcc runpathlib/libsay.so main.c -o main_dynamic_withpathlib.out
[root@localhost project]# ./main_dynamic_withpathlib.out 
hello, LD_RUN_PATH
[root@localhost project]# ldd main_dynamic_withpathlib.out 
	linux-vdso.so.1 (0x00007ffe219bd000)
	runpathlib/libsay.so (0x00007f1ceef4d000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f1ceeb8a000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f1cef14f000)
[root@localhost project]# readelf -d main_dynamic_withpathlib.out

Dynamic section at offset 0xe00 contains 25 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [runpathlib/libsay.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

 可以看到,指定带路径的库文件来构建可执行程序,NEEDED 标签中已经包含库文件的路径,直接到此路径下去取库文件即可。

[root@localhost project]# ./main_dynamic_withpathlib.out 
hello, LD_RUN_PATH
[root@localhost project]# mv runpathlib runpathlib.bak
[root@localhost project]# ./main_dynamic_withpathlib.out 
./main_dynamic_withpathlib.out: error while loading shared libraries: runpathlib/libsay.so: cannot open shared object file: No such file or directory

 修改路径名字。运行时,一旦指定的这个路径或文件不存在,可执行程序便不能运行。

使用带路径的库文件进行构建的方式的方式也不常用;此种方式下,也不需要后文继续展开的其它知识。

2、通过 ldconfig 来添加库文件

运行时的链接器搜索的库路径可以通过 ldconfig -p 来显示。loconfig -p 其实只是解析 /etc/ld.so.cache 文件。缺省的通过将 /etc/ld.so.conf 文件中指定的路径下的所有库文件来构建 /etc/ld.so.cache.

/etc/ld.so.conf 文件的内容,其实是对 /etc/ld.so.conf.d 目录下的一系列的 .conf 配置文件进行展开。

[root@localhost project]# cat /etc/ld.so.conf
include ld.so.conf.d/*.conf

查看当前能够被动态链接器搜索到的动态库,一共有 1012 个文件。

[root@localhost project]# ldconfig -p
1012 libs found in cache `/etc/ld.so.cache'
	p11-kit-trust.so (libc6,x86-64) => /lib64/p11-kit-trust.so
	libzmf-0.0.so.0 (libc6,x86-64) => /lib64/libzmf-0.0.so.0
	libzhuyin.so.13 (libc6,x86-64) => /lib64/libzhuyin.so.13
	libz.so.1 (libc6,x86-64) => /lib64/libz.so.1
	libyelp.so.0 (libc6,x86-64) => /lib64/libyelp.so.0
	libyaml-0.so.2 (libc6,x86-64) => /lib64/libyaml-0.so.2
	libyajl.so.2 (libc6,x86-64) => /lib64/libyajl.so.2
	libxtables.so.12 (libc6,x86-64) => /lib64/libxtables.so.12
	libxslt.so.1 (libc6,x86-64) => /lib64/libxslt.so.1
	libxshmfence.so.1 (libc6,x86-64) => /lib64/libxshmfence.so.1
	libxml2.so.2 (libc6,x86-64) => /lib64/libxml2.so.2

每次执行 ldconfig 会重新构建一次  /etc/ld.so.cache.

# 将当前目录加入到 /etc/ld.so.cache 当中,并且创建 soname 软链接
ldconfig $(pwd)

# 仅仅为当前目录下的 so 文件创建 soname 软链接
ldconfig -n $(pwd)

可以指定一个路径作为 ldconfig 的参数,将该路径下的库文件也添加到 /etc/ld.so.cache 里,使之能够被动态链接器搜索到。

现在将 runpathlib 下的所有库文件,加入到动态链接器搜索路径当中。

执行 ldconfig $(pwd)/runpathlib .runpathlib.目录下只有一个 libsay.so 库文件,再次使用 ldconfig -p 可以看到,搜索的库文件从原来的 1012 变成 1013 libs found in cache `/etc/ld.so.cache',新增的就是 libsay.so。

[root@localhost project]# ldconfig $(pwd)/runpathlib
[root@localhost project]# ldconfig -p | grep runpathlib
	libsay.so (libc6,x86-64) => /root/project/runpathlib/libsay.so
[root@localhost project]# ldconfig -p | head
1013 libs found in cache `/etc/ld.so.cache'
	p11-kit-trust.so (libc6,x86-64) => /lib64/p11-kit-trust.so
	libzmf-0.0.so.0 (libc6,x86-64) => /lib64/libzmf-0.0.so.0
	libzhuyin.so.13 (libc6,x86-64) => /lib64/libzhuyin.so.13
	libz.so.1 (libc6,x86-64) => /lib64/libz.so.1
	libyelp.so.0 (libc6,x86-64) => /lib64/libyelp.so.0
	libyaml-0.so.2 (libc6,x86-64) => /lib64/libyaml-0.so.2

注意这里只能使用绝对路径。使用相对路径会报错:ldconfig: relative path `./runpathlib' used to build cache.

[root@localhost project]# ldconfig
[root@localhost project]# ldconfig -p | grep runpathlib
[root@localhost project]# 
[root@localhost project]# ldconfig $(pwd)/runpathlib
[root@localhost project]# ldconfig -p | grep runpathlib
	libsay.so (libc6,x86-64) => /root/project/runpathlib/libsay.so
[root@localhost project]# 
[root@localhost project]# gcc -L runpathlib -l say main.c -o main_dynamic.out
[root@localhost project]# ./main_dynamic.out 
hello, LD_RUN_PATH

一旦动态库能够被动态链接器搜索到,此时便能正常执行可执行程序 main_dynamic.out。

由于每次执行 ldconfig 命令后,/etc/ld.so.cache 会重建,所以此种方式只能作为一种临时方案。

3、构建可执行程序时指定运行时的动态库搜索路径

a、-rpath 参数单次构建显示作用

构建可执行程序时,可以给静态链接器传递 -rpath 参数,指定运行时的搜索路径,该信息会嵌入到可执行文件当中。通过 readelf -d 查看,运行时的搜索路径以 RPATH 标签的形式出现

[root@localhost project]# ldconfig    #重新构建 /etc/ld.so.cache
[root@localhost project]# ./main_dynamic.out 
./main_dynamic.out: error while loading shared libraries: libsay.so: cannot open shared object file: No such file or directory
[root@localhost project]# gcc -Wl,-rpath,runpathlib -L runpathlib -l say main.c -o main_dynamic_rpath.out
[root@localhost project]# ./main_dynamic_rpath.out 
hello, LD_RUN_PATH
[root@localhost project]# ldd main_dynamic_rpath.out
	linux-vdso.so.1 (0x00007ffd542e0000)
	libsay.so => runpathlib/libsay.so (0x00007f98ef503000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f98ef140000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f98ef705000)
[root@localhost project]# readelf -d main_dynamic_rpath.out 

Dynamic section at offset 0xdf0 contains 26 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libsay.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000f (RPATH)              Library rpath: [runpathlib]

传给静态链接器的参数通过 "-Wl," 开启,"-option,parameter" 选项,逗号,参数(当没有参数时,只有长选项)的形式结束。

-Wl,-rpath,runpathlib 指定将运行时的搜索路径 runpathlib(相对于运行时的当前路径的相对路径)写入到可执行 elf 文件当中。

以绝对路径指定动态库的方式也是个好主意,但是将绝对路径写死在构建参数里却不值得,问题在于需要自动识别程序运行时的路径。

通过将 $ORIGIN 字符串嵌入到 RPATH标签中即可。动态链接器将 $ORIGIN 字符串解释为程序运行时的路径。

[root@localhost project]# gcc -Wl,-rpath,'$ORIGIN'/runpathlib -L runpathlib -l say main.c -o main_dynamic_rpath.out
[root@localhost project]# 
[root@localhost project]# ldd main_dynamic_rpath.out 
	linux-vdso.so.1 (0x00007ffc6d996000)
	libsay.so => /root/project/./runpathlib/libsay.so (0x00007f7b2b1da000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f7b2ae17000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f7b2b3dc000)
[root@localhost project]# 
[root@localhost project]# readelf -d main_dynamic_rpath.out

Dynamic section at offset 0xdf0 contains 26 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libsay.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000f (RPATH)              Library rpath: [$ORIGIN/runpathlib]

可以看到 RPAHT 标签中包含的已经是 $ORIGIN/runpathlib.

ldd 查看的完整路径 /root/project/./runpathlib/libsay.so 中包含一个当前路径的 "." 符号 。

此处需要注意,构建参数中只能使用单引号 '$ORIGIN'。由于 SEHLL 的展开机制,如果使用的是双引号,在传给 gcc 之前,该参数已经被展开,如果当前 shell 环境未定义 ORIGIN 变量,意即 ORIGIN 变量为空,那么最终写入到 elf 的 RPATH 标签红的将是 /runpathlib,看到已经不是想要的那个路径了。

b、预定义变量 LD_RUN_PATH 每次构建隐式作用

除了显示的在构建时指定 -rpath 参数,还有一种相当于全局的方式:定义 LD_RUN_PATH 变量来影响每次未指定 -rpath 的构建。一旦在构建时未指定 rpath 参数;若此时有定义 LD_RUN_PATH 变量,那么 LD_RUN_PATH  变量的值将会写入到可执行程序 RPAHT 标签当中。

t@localhost project]# export LD_RUN_PATH=runpathlib
[root@localhost project]# gcc -L runpathlib -l say main.c -o main_dynamic_rpath.out
[root@localhost project]# ldd main_dynamic_rpath.out
	linux-vdso.so.1 (0x00007fff21dbe000)
	libsay.so => runpathlib/libsay.so (0x00007f3853a2c000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f3853669000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f3853c2e000)
[root@localhost project]# 
[root@localhost project]# export LD_RUN_PATH='$ORIGIN'/runpathlib
[root@localhost project]# gcc -L runpathlib -l say main.c -o main_dynamic_rpath.out
[root@localhost project]# ldd main_dynamic_rpath.out
	linux-vdso.so.1 (0x00007ffd8f5c5000)
	libsay.so => /root/project/./runpathlib/libsay.so (0x00007f59a13e6000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f59a1023000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f59a15e8000)
[root@localhost project]# readelf -d main_dynamic_rpath.out 

Dynamic section at offset 0xdf0 contains 26 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libsay.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000f (RPATH)              Library rpath: [$ORIGIN/runpathlib]

LD_RUN_PATH 变量定义完之后,一定要通过 export 导出到全局环境当中,这样构建时才能使用定义的值。LD_RUN_PATH 中一样能带入 '$ORIGIN' 字符串给动态链接器解释为运行时的当前工作目录。

 

无论是显示的还是隐式的为可执行程序指定 RPATH 标签,当前的 Linux 还支持一个新的 RUNPATH 标签。RUNPATH 与 RPATH 的区别在于查找优先级的不同。通过指定额外的 --enable-new-dtags 来创建 RUNPATH 标签。

[root@localhost project]# echo $LD_RUN_PATH
$ORIGIN/runpathlib
[root@localhost project]# gcc -Wl,--enable-new-dtags -L runpathlib -l say main.c -o main_dynamic_rpath.out
[root@localhost project]# readelf -d main_dynamic_rpath.out 

Dynamic section at offset 0xdf0 contains 26 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libsay.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000001d (RUNPATH)            Library runpath: [$ORIGIN/runpathlib]

以上的这些输出都是来自 centos8.1,GNU C Library (GNU libc) stable release version 2.28 版本。如果是一个相对旧的 Linux 版本,比如 Centos6.9,GNU C Library stable release version 2.12 版本上看到的,当指定了 --enable-new-dtags 时,是同时存在 RPATH 和 RUNPATH 两个标签的。这是因为 RUNPATH 是较新的机制,保存 RPATH 是为了向后兼容。

[root@wjsj-01 project]# readelf -d main_dynamic_rpath.out

Dynamic section at offset 0x7a8 contains 23 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libsay.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000f (RPATH)              Library rpath: [runpathlib]
 0x000000000000001d (RUNPATH)            Library runpath: [runpathlib]

4、运行时指定加载的动态库路径

可以预定义 LD_LIBRARY_PATH 变量指定运行时的动态库加载路径。先删掉定义的 LD_RUN_PATH 变量,然后进行构建,确保构建的可执行程序当中不含有 RPATH 标签。


[root@localhost project]# unset LD_RUN_PATH 
[root@localhost project]# gcc main.c -L librarypathlib -l say -o main_dynamic_ld_library_path.out
[root@localhost project]# ldd main_dynamic_ld_library_path.out 
	linux-vdso.so.1 (0x00007ffc2fdcf000)
	libsay.so => not found
	libc.so.6 => /lib64/libc.so.6 (0x00007f24cd370000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f24cd733000)
[root@localhost project]# readelf -d main_dynamic_ld_library_path.out 

Dynamic section at offset 0xe00 contains 25 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libsay.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

 此时是无法直接运行可执行程序,通过定义 LD_LIBRARY_PATH 变量并且导出到全局环境当中,动态库能够被运行时加载器搜索到,其实可执行程序能正常运行。一旦删除 LD_LIBRARY_PATH 变量,可执行程序再次无法运行。

[root@localhost project]# ./main_dynamic_ld_library_path.out 
./main_dynamic_ld_library_path.out: error while loading shared libraries: libsay.so: cannot open shared object file: No such file or directory
[root@localhost project]# export LD_LIBRARY_PATH=librarypathlib
[root@localhost project]# ./main_dynamic_ld_library_path.out 
hello, LD_LIBRARY_PATH
[root@localhost project]# unset LD_LIBRARY_PATH 
[root@localhost project]# ./main_dynamic_ld_library_path.out 
./main_dynamic_ld_library_path.out: error while loading shared libraries: libsay.so: cannot open shared object file: No such file or directory

5、动态库的搜索优先级顺序

如果可执行程序中同时包含 RPATH, RUNPATH 标签,并且 LD_LIBRARY_PATH 变量也定义,那么搜索的顺序是怎样的呢?

[root@localhost project]# gcc -Wl,-rpath,runpathlib -L librarypathlib -l say main.c -o main_dynamic_rpath.out
[root@localhost project]# echo $LD_LIBRARY_PATH
librarypathlib
[root@localhost project]# ./main_dynamic_rpath.out
hello, LD_RUN_PATH
[root@localhost project]# readelf -d main_dynamic_rpath.out 

Dynamic section at offset 0xdf0 contains 26 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libsay.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000f (RPATH)              Library rpath: [runpathlib]

上边 故意将构建时使用的动态库路径与运行时依赖的rpath 路径写得不一样,用来模拟编译和运行时依赖两个不同的路径。根据可执行文件的输出,确定运行时使用的是 RPATH 标签的下的那个库文件。

[root@localhost project]# gcc -Wl,-rpath,runpathlib,--enable-new-dtags -L librarypathlib -l say main.c -o main_dynamic_rpath.out
[root@localhost project]# echo $LD_LIBRARY_PATH 
librarypathlib
[root@localhost project]# ./main_dynamic_rpath.out
hello, LD_LIBRARY_PATH
[root@localhost project]# readelf -d main_dynamic_rpath.out 

Dynamic section at offset 0xdf0 contains 26 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libsay.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000001d (RUNPATH)            Library runpath: [runpathlib]

当拥有的是 RUNPATH 标签时,此时 LD_LIBRARY_PATH 变量的左右优先。

[root@localhost project]# unset LD_LIBRARY_PATH 
[root@localhost project]# gcc -Wl,-rpath,runpathlib,--enable-new-dtags -L librarypathlib -l say main.c -o main_dynamic_rpath.out
[root@localhost project]# ldconfig $(pwd)/librarypathlib
[root@localhost project]# ldconfig -p | grep say
	libsay.so (libc6,x86-64) => /root/project/librarypathlib/libsay.so
[root@localhost project]# ./main_dynamic_rpath.out 
hello, LD_RUN_PATH
[root@localhost project]# readelf -d main_dynamic_rpath.out 

Dynamic section at offset 0xdf0 contains 26 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libsay.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000001d (RUNPATH)            Library runpath: [runpathlib]

上边的结果说明,RUNPATH 标签优先于 /etc/ld.so.cache。

一旦取消 RUNPATH 标签,/etc/ld.so.cache 将起作用。

[root@localhost project]# gcc -L librarypathlib -l say main.c -o main_dynamic_notag.out
[root@localhost project]# 
[root@localhost project]# ldconfig -p | grep say
	libsay.so (libc6,x86-64) => /root/project/librarypathlib/libsay.so
[root@localhost project]# ./main_dynamic_notag.out 
hello, LD_LIBRARY_PATH

所以优先级顺序为:RPATH 标签 > LD_LIBRARY_PATH 变量 > RUNPATH 标签 > /etc/ld.so.cache.

6、LD_LIBRARY_PATH 环境变量的安全新问题

创建一个不包含 RPATH 和 RUNPATH 的可执行程序,将动态库通过 ldconfig 加入到 /etc/ld.so.cache 搜索路径当中,此时运行可执行程序,没有意外,使用的 ldconfig 添加的动态库。如果此时使用 LD_LIBRARY_PATH 环境变量指示优先级更高的库,运行时当然使用 LD_LIBRARY_PATH  中指定的库。如果此时设置 SUID 或者 GUID,那么对于可执行文件的拥有者来说,没有什么影响。

切换用户为非可执行文件的拥有者,可以看到一旦设置了 SUID ,那么其他人运行该可执行文件时,LD_LIBRARY_PATH 将不再起作用。这是为了安全问题,防止其他用户让可执行程序执行同名的任意的动态库。

[root@localhost project]# unset LD_LIBRARY_PATH 
[root@localhost project]# gcc -L runpathlib -l say main.c -o main_dynamic_notag.out
[root@localhost project]# ldconfig $(pwd)/runpathlib
[root@localhost project]# ldconfig -p | grep say
	libsay.so (libc6,x86-64) => /root/project/runpathlib/libsay.so
[root@localhost project]# ./main_dynamic_notag.out 
hello, LD_RUN_PATH
[root@localhost project]# export LD_LIBRARY_PATH=librarypathlib
[root@localhost project]# ./main_dynamic_notag.out #LD_LIBRARY_PATH 起作用
hello, LD_LIBRARY_PATH
[root@localhost project]# ls -lha main_dynamic_notag.out 
-rwxr-xr-x. 1 root root 11K Apr  5 06:48 main_dynamic_notag.out
[root@localhost project]# su king
[king@localhost project]$ ./main_dynamic_notag.out 
hello, LD_LIBRARY_PATH
[king@localhost project]$ exit
exit
[root@localhost project]# chmod u+s main_dynamic_notag.out  #设置 euid 
[root@localhost project]# ./main_dynamic_notag.out #当前用户不受影响
hello, LD_LIBRARY_PATH
[root@localhost project]# su king
[king@localhost project]$ ./main_dynamic_notag.out  #其他用户 LD_LIBRARY_PATH 不再其作用
hello, LD_RUN_PATH
[king@localhost project]$ ls -lha
total 20K
drwxr-xr-x.  5 root root  107 Apr  5 06:48 .
dr-xr-x---. 21 root root 4.0K Apr  5 06:32 ..
drwxr-xr-x.  2 root root   36 Apr  5 05:32 librarypathlib
-rw-r--r--.  1 root root   50 Apr  5 06:32 main.c
-rwsr-xr-x.  1 root root  11K Apr  5 06:48 main_dynamic_notag.out
drwxr-xr-x.  2 root root   49 Apr  5 05:11 runpathlib
drwxr-xr-x.  2 root root   48 Apr  5 02:06 staticlib

 

おすすめ

転載: blog.csdn.net/zhouguoqionghai/article/details/105324476