ld.so 相关知识

1. 引子

今天在工作中用 ldd 检查 tengine 的可执行文件时,出于好奇看了一下里面涉及的 ld-linux-x86-64.so.2 动态链接库,然后查到了这样一个神奇的问答:

https://unix.stackexchange.com/questions/400621/what-is-lib64-ld-linux-x86-64-so-2-and-why-can-it-be-used-to-execute-file

我们平常习惯于用 chmod 去更改文件的权限,但是从来没有想过一个问题:如果由于一些误操作而让 chmod 本身的 X 权限没有了,那该怎么救回来?这个问答给出了一种解决方案:

/lib64/ld-linux-x86-64.so.2 /bin/chmod +x /bin/chmod

乍一看简直是魔法,在刻板印象里动态链接库文件 .so 根本不可以直接运行,这里不仅直接运行了一个动态链接库文件,还给它传入了参数。

2. 原理

2.1. ld.so 简介

实际上,我们可以无参运行 ld-linux-x86-64.so.2 (在 centos 衍生的发行版上可以无参,在 Ubuntu 上需要加 --help ),就会得到如下的输出:

You have invoked 'ld.so', the program interpreter for dynamically-linked ELF programs.  Usually, the program interpreter is invoked automatically when a dynamically-linked executable is started.

You may invoke the program interpreter program directly from the command line to load and run an ELF executable file; this is like executing that file itself, but always uses the program interpreter you invoked, instead of the program interpreter specified in the executable file you run.  Invoking the program interpreter directly provides access to additional diagnostics, and changing the dynamic linker behavior without setting environment variables (which would be inherited by subprocesses).

[省略一些参数说明]

从这些信息中我们可以得知,这个 ld.so 实际上是一个动态链接程序的“解释器”(interpreter),而当我们执行一个动态链接程序时,等效的命令实际上是把原始命令作为参数传给这个 .so。即当我们在终端里执行 chmod 时,实际上执行的命令是 ld-linux-x86-64.so.2 /bin/chmod ,而其作用是加载运行可执行文件所需的所有 .so,从而为动态链接程序的运行提供条件。

此外,实际上还有很多 .so 都是可以运行的,例如常见的 libc.so ,直接运行也可以输出一段版本和说明信息。根据网上的说明,只要库中包含 main() 函数即可。

2.2. ld.so 的详细介绍

TODO: https://www.man7.org/linux/man-pages/man8/ld.so.8.html

2.3. ld 与 ld.so

编译时,链接器 ld 是我们熟悉的老朋友,对于所有参与链接的可重定向目标文件(.o),ld会执行符号解析(将对符号的引用与符号表中的唯一确定符号关联起来)与重定向(将各个文件中的代码段、数据段分别合并,随后即可计算每个符号在虚拟内存中的地址,再把所有的符号引用修改为这个地址)。这部分可以重新读下 CSAPP

简单来说,对于动态链接程序,ld 作用于编译期,而 ld.so 作用于运行时。

3. execve()系统调用

在 stackexchange 的原回答里,答主提到了 execve() 这个 syscall,这里也稍微学习下。其系统调用的函数签名是:

int execve(const char *pathname, char *const argv[], char *const envp[]);

这个系统调用的主要作用是把 pathname 指定的程序加载到当前进程的内存空间内,将当前进程的堆、栈和所有的段数据都用新进程的相应部分代替,然后从新程序的初始化代码和 main 函数开始运行。同时,进程的ID将保持不变。

当我们在终端中输入命令时,shell 会 fork() 一个子进程出来,随后调用 execve() 来执行我们输入的命令。虽然此 syscall 会进行执行权限检查,但是由于这里检查的对象是 ld.so,因此检查通过。

4. 操作系统是如何判断一个可执行文件是静态链接还是动态链接的

这就要涉及到 ELF 文件格式了,详情请参考另一篇博客

5. 参考

https://unix.stackexchange.com/questions/400621/what-is-lib64-ld-linux-x86-64-so-2-and-why-can-it-be-used-to-execute-file
https://unix.stackexchange.com/questions/223385/why-and-how-are-some-shared-libraries-runnable-as-though-they-are-executables
https://man7.org/linux/man-pages/man2/execve.2.html
https://uclibc.org/docs/elf-64-gen.pdf

知识共享许可协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇