3 个版本
使用旧的 Rust 2015
0.1.2 | 2016年4月14日 |
---|---|
0.1.1 | 2016年1月29日 |
0.1.0 | 2015年10月23日 |
#992 在 文本处理
158 星 & 17 关注者
115KB
2K SLoC
欢迎
dryad
是 第一个 和 唯一 的 并行、64 位 ELF 动态链接器,针对 GNU/Linux 编写,完全使用 Rust 编写,并且是
- 不是并行的
- 尚未准备好投入生产
- 一个原型
- 实际上并不工作
- 处于高度动荡状态
- 并行可能是 a) 不可能的,b) 性能不佳,但尝试一下会很令人感兴趣
但所有这些都将随着时间的推移而消失!
由于多种原因,这项工作已经停滞,主要概述如下 这里,但我时不时地会玩弄它。
我有一些修复问题的想法,但还有很多事情要做!
如果您想做出贡献,请提交 PR 或建议,评论,问题,总是欢迎 :) 如果您想对其他有趣的二进制内容进行黑客攻击,goblin 或 cargo-sym 总是需要额外的手或两个。
构建
您需要安装 rustup
工具,然后切换到夜间版并添加 musl 目标
rustup default nightly
rustup target add x86_64-unknown-linux-musl
您现在真正需要的是 rustup、互联网连接和您要链接的目标的链接器
ld
(或ld.gold
)curl
- 互联网连接
- x86-64 GNU/Linux 机器
不幸的是,我目前不支持交叉编译(这无论如何都是一种不寻常的使用情况),因此您将需要一个 x86-64 GNU/Linux 机器,否则将会失败。
一旦确定,您就可以像平常一样继续操作
./gen_tests.sh
- 构建测试二进制文件(只需做一次)(很快将添加为make目标)make
- 编译dryad.so.1
并将其复制到/tmp
make run
- 运行./dryad.so.1
,这个应该能正常运行而不会段错误,如果它没有运行,请提交一个bug报告。test/test
- 运行测试二进制文件test
,其PT_INTERPRETER
是/tmp/dryad.so.1
编译和链接要求
Makefile 执行以下三项操作
- 将dryad编译成一个静态库,基本上是:
cargo build -target=x86_64-unknown-linux-musl --lib
- 将
libasm
的入口函数和运行时解析函数链接到dryad静态库,然后是rust标准库,以及pthread、libc等,并提供重要的链接器标志,如-pie
、-Bsymbolic
、-I/tmp/dryad.so.1
、-soname dryad.so.1
等。 - 将生成的二进制文件
dryad.so.1
复制到/tmp/dryad.so.1
,因为测试二进制文件中的PT_INTERPRETER
就设置为这个路径。将来我们显然会将其放置在/usr/lib/dryad.so.1
或动态链接器适当的路径上(GNU的叫做ld-linux-x86-64.so.2
)。
实际上,上面的阶段 1
和 2
是 cargo 管道中的问题,这也是为什么我仍然需要手动链接。另外,rustc 不喜欢将 musl 二进制文件编译成共享对象。
我相信,如果我将启动汇编代码转移到 Rust 源代码中的内联汇编中(从而可能消除步骤 1),一些问题就会消失,但 musl 问题可能仍然是一个问题。(我们现在在单独的 Rust crate 中使用内联汇编!)
运行
最后一步,运行 test/test
(或 test
中的其他任何测试二进制文件),将输出大量信息,然后使您的机器段错误,或者可能根本不运行,或者真的做任何其他事情——我真的无法说,因为我到目前为止只在单台机器上进行了测试。
注意:如果您使用的是 Ubuntu 或其他未将 libc
放置于 /usr/lib
的 Linux 发行版,您需要将 LD_LIBRARY_PATH=/path/to/libc
传递给您的 test/test
,即:LD_LIBRARY_PATH=/path/to/libc test/test
。此外,如果 libc
没有指向实际二进制的 soname
符号链接,或者实际二进制 作为 soname
安装,则它也无法正常工作。我们还需要为它提供 ld.so.cache
读取器和解析器 - 随意工作吧!
然而,dryad
几乎能够解释使用 libc.so.6
的(简单的)二进制(如 test/test
)。
具体来说,这意味着在高级上 dryad
执行以下操作
- 重新定位自身
- 加载并映射扁平依赖列表中的所有二进制文件
- 重新定位每个加载的二进制文件(技术上,重新定位最常见的重定位符号的子集)
- 为每个二进制文件设置 GOT,并使用其运行时符号解析函数(
_dryad_resolve_symbol
)和“rendezvous”数据结构 - 解析 GNU ifuncs,并且如果设置了
LD_BIND_NOW
,则预绑定所有函数符号。 - 传递控制权给可执行文件
- (如果未设置
LD_BIND_NOW
,则可选地延迟绑定函数调用) - 段错误
有 很多 主要任务和 许多 次要任务需要完成,才能达到“完整”的水平。首要和最重要的任务是正确设置 TLS。目前,它通过简单地调用 musl 符号 __init_tls
来处理它,这样我们就不在 fs:0
访问及其类似操作上发生段错误。
但它确实需要正确设置,因为它是一个复杂的过程。
这是我在整个动态链接过程中遇到的最不详细的文档部分,所以进展缓慢。还有一些关于它将如何工作的疑问,我将在其他时候或在一篇博客文章中详细说明。
最后,dryad
应该 能够解释自身,您可以通过调用 ./dryad.so.1
(是的,dryad 是自己的程序解释器)来验证这一点。
项目目标
1. 记录动态链接器
此项目的主要目标是完全记录
- GNU/Linux ELF x86-64 系统上的动态链接 过程
- 此类过程的一个实现
关于此主题的文档和信息目前处于 尴尬 状态,我一直在对材料的缺乏、文档等的缺失感到震惊。我曾开玩笑地说,我担心当所有老 C 程序员都死了会发生什么——但我不完全是开玩笑。
代码不是文档。如果它是,那么这个项目就会很容易,并且很久以前就完成了。
因此,我希望详细记录实现、过程,也许甚至我的经验。
我将在不久的将来更新这部分内容,请耐心等待。
2. 实现动态链接器
dryad 的当前目标实现是 ELF x86-64 GNU/Linux 系统。
请注意这一点
- ELF 加载器仅支持 64 位变体
- 汇编代码假定 x86-64 指令集
- 链接器目前针对Linux,尽管这个目标可能不会一成不变。
在开始其他架构或系统的工作之前/如果,我希望能有一个适用于ELF x86-64 GNU/Linux目标的实现。
话虽如此,特别是我对将Dryad移植到32位Linux系统上并不感兴趣,因为
- 在我看来,32位系统已经过时了
- 将大大增加源代码中的ELF目标复杂性,因为配置标志取决于我们在构建时想要切换的目标等。
- 32位ELF动态链接有更好的文档记录,我想要记录一个64位动态链接器
贡献
热情欢迎贡献!让我们为x86-64 GNU/Linux系统(以及更广泛的应用)构建一个生产级的动态链接器!或者不这么做也行。
如果你对x86-64 GNU系统上ELF的动态链接一无所知,那完全没问题,因为据我所知,没有人 anymore真的在做。如果你好奇,这里有一些随机资源
- ELF规范
- x86-64 System V应用程序二进制接口
- ELF TLS规范
- 谷歌的Bionic动态链接器源代码
- glibc动态链接器源代码
- musl dynlink.c代码
- Sco动态链接文档
- iecc动态链接文章
- ELF加载教程
- 关于GOT[0] - GOT[2]值的详细信息
man ld-so
用于动态链接基础知识man dlopen
用于运行时动态链接基础知识man 3 getauxval
关于内核传递给程序的辅助向量信息- 我还希望能添加几篇文章,讲述我在本质上已经废弃的博客上的一些_mis_adventures
待办事项
以下是我脑海中的一些主要待办事项
- 重大:正确初始化动态链接器的TLS:这是最后的冲刺。
- 重大:
dlfcn.h
实现和共享对象绑定以支持运行时动态加载 - 次要:
/etc/ld.so.cache
加载程序和解析器 - 更好的文档
- 修复代码中的任何待办事项
- 使用Rust最佳实践使不安全代码更安全;绝对需要Rust专家!
- 添加配置文件
- 添加测试
- 实现动态链接而不产生段错误
- 做所有的事情
结语
始终记住
互相之间要优秀
依赖关系
~3MB
~65K SLoC