记录了动态链接器的使用说明,以及 libc、glibc 和 glib 的关系
动态链接器可以通过运行一些动态链接的程序或共享对象间接运行(在这种情况下,不能传递动态链接器的命令行选项,在ELF情况下,存储在程序的.interp部分的动态链接器会被执行),或者直接运行:
/lib/ld-linux.so.* [OPTIONS] [PROGRAM [ARGUMENTS]]
说明
ld.so
和ld-linux.so*
程序找到并加载程序所需的共享对象(共享库),为程序的运行做准备,然后运行。
Linux 二进制文件需要动态链接 (运行时的链接), 除非在编译过程中给 ld 添加了 -static 选项。
ld.so 程序处理 a.out 二进制文件,这是很久以前使用的格式;ld-linux.so*
处理 ELF(/lib/ld-linux.so.1
用于 libc5
,/lib/ld-linux.so.2
用于 glibc2
),现在大家已经使用了很多年。 除此之外,两者都有相同的行为,并使用相同的支持文件和程序ldd(1)、ldconfig(8)和/etc/ld.so.conf。
在解析共享对象依赖关系时,动态链接器首先检查每个依赖关系字符串是否包含斜线(如果在链接时指定了包含斜线的共享对象路径名,则会出现这种情况)。 如果发现斜线,那么依赖字符串就会被解释为(相对或绝对)路径名,并使用该路径名加载共享对象。
如果一个共享对象的依赖关系不包含斜线,那么就会按照以下顺序进行搜索:
- (仅 ELF) 如果二进制文件的
DT_RPATH
动态部分属性存在且DT_RUNPATH
属性不存在,则使用该属性指定的目录。DT_RPATH
的使用已被废弃。 - 使用环境变量
LD_LIBRARY_PATH
(除非可执行文件是在安全执行模式下运行的),在这种情况下,它会被忽略 - (仅 ELF) 使用二进制文件
DT_RUNPATH
动态部分属性中指定的目录(如果存在)。 - 来自缓存文件
/etc/ld.so.cache
,它包含了之前在增强库路径中找到的候选共享对象的编译列表。 然而,如果二进制文件是用-z nodeflib
链接器选项链接的,那么默认路径中的共享对象将被跳过。 安装在硬件能力目录中的共享对象(见下文)比其他共享对象更优先。 - 在默认路径中
/lib
,然后是/usr/lib
。 (在某些64位架构上,64位共享对象的默认路径是/lib64
,然后是/usr/lib64
。) 如果二进制文件是用-z nodeflib
链接器选项链接的,则跳过这一步。
选项
选项 | 说明 |
---|---|
–list | 列出所有的依赖关系,以及它们是如何解析(resolved)的 |
–verify | 确认程序是动态链接的,而且这个动态链接器可以处理 |
–inhibit-cache | 不要使用/etc/ld.so.cache |
–library-path <路径>路径> | 使用<路径>代替`LD_LIBRARY_PATH`环境变量设置(见下文)路径> |
–inhibit-rpath <列表>列表> | 忽略<列表>指定的对象中的RPATH和RUNPATH信息。 在安全执行模式下运行时,这个选项会被忽略(见下文)列表> |
–audit <列表>列表> | 使用列表中命名的对象作为审核员 |
环境
各种环境变量影响着动态链接器的运行
安全执行模式
为了安全起见,如果动态链接器确定二进制文件应该在安全执行模式下运行,那么一些环境变量的效果就会失效或被修改。 这个决定是通过检查辅助向量中的AT_SECURE条目(见getauxval(3))是否有一个非零值来实现的。 这个条目可能因为各种原因而具有非零值,包括:
- 进程的实际和有效值。
- 进程的真实和有效用户ID不同,或者真实和有效组ID不同。 这通常是执行set-user-ID或set-group-ID程序的结果
- 一个非root用户ID的进程执行了一个赋予允许或有效能力的二进制程序。
- Linux安全模块可能设置了一个非零值
比较重要的环境变量
环境变量 | 说明 |
---|---|
LD_ASSUME_KERNEL | 每个共享对象都可以告知动态链接器它所需要的最小内核ABI版本。 (这个要求被编码在ELF注解部分,可以通过readelf -n 查看标有NT_GNU_ABI_TAG 的部分。) 在运行时,动态链接器确定运行中的内核的ABI版本,并将拒绝加载指定了超过该ABI版本的最小ABI版本的共享对象。LD_ASSUME_KERNEL 可以用来使动态链接器假定它是在一个具有不同的内核ABI版本的系统上运行。 例如,下面的命令行使动态链接器在加载myprog所需的共享对象时,假定它运行在Linux 2.2.5上。$ LD_ASSUME_KERNEL=2.2.5 ./myprog 在提供共享对象的多个版本(在搜索路径的不同目录中)的系统上,这些共享对象有不同的最低内核ABI版本要求, LD_ASSUME_KERNEL 可以用来选择使用的对象版本(取决于目录搜索顺序)。 从历史上看,LD_ASSUME_KERNEL 功能最常见的用法是在同时提供LinuxThreads和NPTL的系统上手动选择旧的LinuxThreads POSIX线程实现(后者通常是这类系统的默认版本);参见pthreads(7)。 |
LD_LIBRARY_PATH | 在执行时搜索ELF库的目录列表。 列表中的项目由冒号或分号分隔。 类似于PATH环境变量。 在安全执行模式下,这个变量被忽略 |
LD_PRELOAD | 在所有其他对象之前加载的额外的、用户指定的ELF共享对象列表。 列表中的项目可以用空格或冒号分隔。 这可以用来选择性地覆盖其他共享对象的功能。 使用在DESCRIPTION下给出的规则搜索对象。 在安全执行模式下,包含斜线的预加载路径名会被忽略,只有当共享对象文件上的set-user-ID模式位被启用时,才会加载标准搜索目录中的共享对象。 |
LD_TRACE_LOADED_OBJECTS | (仅 ELF) 如果设置 (为任何值),将导致程序列出其动态依赖关系,就像通过 ldd(1) 运行一样,而不是正常运行。 |
Rpath标签扩展
ld.so可以理解rpath规范中的某些字符串(DT_RPATH
或DT_RUNPATH
);这些字符串被替换为以下内容:
-
$ORIGIN
(或等价的${ORIGIN}
)这将扩展到包含程序或共享对象的目录。 因此,一个位于
somedir/app
的应用程序可以用以下方式编译gcc -Wl,-rpath,'$ORIGIN/../lib'
这样它能在
somedir/lib
中找到相关的共享对象,无论somedir在目录层次中的位置如何。 这有利于创建 “交钥匙 “应用程序(turn-key applications),这些应用程序不需要安装到特殊的目录中,而是可以解压到任何目录中,并且仍然可以找到自己的共享对象。 -
$LIB
(或者${LIB}
)根据不同的架构,它可以扩展为lib或lib64(例如,在x86-64上,它扩展为lib64,在x86-32上,它扩展为lib)。
-
$PLATFORM
(或者${PLATFORM}
)这将扩展为对应于主机系统处理器类型的字符串(例如,”x86_64”)。 在某些架构上,Linux内核并没有向动态链接器提供平台字符串。 这个字符串的值取自辅助向量中的
AT_PLATFORM
值(见getauxval(3))。
文件
-
/lib/ld.so
a.out
的动态链接器/加载器 -
/lib/ld-linux.so.{1, 2}
ELF 的动态链接器/加载器
-
/etc/ld.so.cache
文件中包含一个用于搜索共享对象的目录汇编列表,以及候选共享对象的有序列表。
-
/etc/ld.so.preload
包含一个以空格分隔的ELF共享对象列表的文件,要在程序之前加载。
-
lib*.so*
共享对象
注意
ld.so功能适用于使用libc 4.4.3或更高版本编译的可执行文件。 ELF功能从Linux 1.1.52和libc5开始提供。
libc、glibc和glib的关系
glibc 和 libc
glibc 和 libc 都是 Linux 下的 C 函数库。
libc 是 Linux 下的 ANSI C 函数库;glibc 是 Linux 下的 GUN C 函数库。
ANSI C 和 GNU C 的区别
ANSI C 函数库是基本的 C 语言函数库,包含了 C 语言最基本的库函数。这些库函数在其各种支持 C 语言的 IDE 中都是有的。
GNU C 函数库是一种类似于第三方插件的东西。由于 Linux 是用 C 语言写的,所以 Linux 的一些操作是用 C 语言实现的。因此,GUN 组织开发了一个 C 语言的库,以便让我们更好的利用 C 语言开发基于 Linux 操作系统的程序。不过现在的不同的 Linux 的发行版本对这两个函数库有不同的处理方法,有的可能已经集成在同一个库里了。
glibc是linux下面c标准库的实现,即GNU C Library。glibc本身是GNU旗下的C标准库,后来逐渐成为了Linux的标准c库,而Linux下原来的标准c库Linux libc逐渐不再被维护。Linux下面的标准c库不仅有这一个,如uclibc、klibc,以及上面被提到的Linux libc,但是glibc无疑是用得最多的。glibc在/lib目录下的.so文件为libc.so.6。
查看当前系统的 glibc 版本的两种方法:
-
/lib/libc.so.6
# /lib32/libc.so.6 GNU C Library (Ubuntu GLIBC 2.23-0ubuntu11) stable release version 2.23, by Roland McGrath et al. Copyright (C) 2016 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Compiled by GNU CC version 5.4.0 20160609. Available extensions: crypt add-on version 2.1 by Michael Glad and others GNU Libidn by Simon Josefsson Native POSIX Threads Library by Ulrich Drepper et al BIND-8.2.3-T5B libc ABIs: UNIQUE IFUNC For bug reporting instructions, please see: <https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
-
ldd
# ldd --version ldd (Ubuntu GLIBC 2.23-0ubuntu11) 2.23 Copyright (C) 2016 自由软件基金会。 这是一个自由软件;请见源代码的授权条款。本软件不含任何没有担保;甚至不保证适销性 或者适合某些特殊目的。 由 Roland McGrath 和 Ulrich Drepper 编写。
glibc 和 glib
glib 和 glibc 基本上没有太大联系,可能唯一的共同点就是,其都是 C 编程需要调用的库而已。
glib 是 Gtk+ 库和 Gnome 的基础。glib 可以在多个平台下使用,比如 Linux、Unix、Windows 等。glib 为许多标准的、常用的 C 语言结构提供了相应的替代物。
glib是GTK+的基础库,它由基础类型、对核心应用的支持、实用功能、数据类型和对象系统五个部分组成,可以在gtk网站下载其源代码。是一个综合用途的实用的轻量级的C程序库,它提供C语言的常用的数据结构的定义、相关的处理函数,有趣而实用的宏,可移植的封装和一些运行时机能,如事件循环、线程、动态调用、对象系统等的API。GTK+是可移植的,当然glib也是可移植的,你可以在linux下,也可以在windows下使用它。使用gLib2.0(glib的2.0版本)编写的应用程序,在编译时应该在编译命令中加入pkg-config --cflags --libs glib-2.0
,如:
gcc `pkg-config --cflags --libs glib-2.0` hello.c -o hello
使用glib最有名的就是GNOME了。
总结
libc, glibc在一个层次,都是C的标准实现库,是操作系统级别的基石之一。
glib是用C写的一些utilities,即C的工具库,和libc/glibc没有关系。