3个不稳定版本
0.2.1 | 2022年3月7日 |
---|---|
0.2.0 | 2021年12月7日 |
0.1.0 | 2021年12月2日 |
#372 in Unix APIs
63 monthly downloads
105KB
1.5K SLoC
linux-libc-auxv: 为不同地址空间构建和解析Linux初始堆栈布局
Linux将初始堆栈布局传递给应用程序,其中包含argc
、argv
、envp
以及位于堆栈指针正上方的auxiliary vector
。Linux程序的libc在它的_start
符号(即“crt0”)中解析此结构,并在之后将正确的指针作为参数传递给main
。此crate帮助在no_std
环境和不同的地址空间中构建和解析此数据结构。
关键词:crt0,堆栈布局,AT值,AT对,auxvec,辅助向量
我已经在一个用于微内核的自定义运行时系统中成功测试了此crate,该系统能够加载和启动未经修改的Linux二进制文件。Linux二进制文件(即libc)可以找到所有参数、环境变量以及辅助向量的数据,并将其打印到stdout。
这与https://crates.io/crates/crt0stack和https://crates.io/crates/auxv有什么不同?
此crate支持no_std
上下文,并允许构建不同地址空间的数据结构,即用户应用程序的地址空间。
当我开始创建这个库时,我只知道后者。它不支持 no_std
。因为前者支持 no_std
但不支持不同的地址空间,所以我还是不得不创建这个。对我来说,典型的用例是为不同的地址空间创建数据结构,就像Linux所做的那样。
最后但同样重要的是,我的库支持Linux的更多/所有AT变量。
功能
✅ 构建当前地址空间的数据结构
✅ 构建不同地址空间的数据结构
✅ 解析当前地址空间的数据结构 + 输出引用的数据/指针
✅ 解析不同地址空间的数据结构 + 防止内存错误 / 不解引用指针
限制
32位与64位
辅助向量包含类型为 (usize, usize)
的成对数据。因此,每个条目在32位系统上占用8字节,在64位系统上占用16字节。目前,这个库为编译的架构生成辅助向量。如果需要,可以创建一个问题或PR,这将是一个运行时设置。我从未在32位系统上测试过它,但我相信它将正常工作。
辅助向量与堆栈布局
目前,这个库只能构建和序列化整个初始堆栈布局,但不能单独构建辅助向量。
代码示例
在存储库中有多个代码示例! cargo run --example linux_parse_print_layout
展示了一个真实世界的示例。
最小:构建 + 解析
use linux_libc_auxv::{AuxVar, InitialLinuxLibcStackLayout, InitialLinuxLibcStackLayoutBuilder};
/// Minimal example that builds the initial linux libc stack layout and parses it again.
fn main() {
let builder = InitialLinuxLibcStackLayoutBuilder::new()
// can contain terminating zero; not mandatory in the builder
.add_arg_v("./first_arg\0")
.add_arg_v("./second_arg")
.add_env_v("FOO=BAR\0")
.add_env_v("PATH=/bin")
.add_aux_v(AuxVar::Clktck(100))
.add_aux_v(AuxVar::Random([
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
]))
.add_aux_v(AuxVar::ExecFn("/usr/bin/foo"))
.add_aux_v(AuxVar::Platform("x86_64"));
// memory where we serialize the data structure into
let mut buf = vec![0; builder.total_size()];
// assume user stack is at 0x7fff0000
let user_base_addr = 0x7fff0000;
unsafe {
builder.serialize_into_buf(buf.as_mut_slice(), user_base_addr);
}
// So far, this is memory safe, as long as the slice is valid memory. No pointers are
// dereferenced yet.
let parsed = InitialLinuxLibcStackLayout::from(buf.as_slice());
println!("There are {} arguments.", parsed.argc());
println!(
"There are {} environment variables.",
parsed.envv_ptr_iter().count()
);
println!(
"There are {} auxiliary vector entries/AT variables.",
parsed.aux_serialized_iter().count()
);
println!(" argv");
// ptr iter is safe for other address spaces; the other only because here user_addr == write_addr
for (i, arg) in parsed.argv_ptr_iter().enumerate() {
println!(" [{}] @ {:?}", i, arg);
}
println!(" envp");
// ptr iter is safe for other address spaces; the other only because here user_addr == write_addr
for (i, env) in parsed.envv_ptr_iter().enumerate() {
println!(" [{}] @ {:?}", i, env);
}
println!(" aux");
// ptr iter is safe for other address spaces; the other only because here user_addr == write_addr
for aux in parsed.aux_serialized_iter() {
if aux.key().value_in_data_area() {
println!(" {:?} => @ {:?}", aux.key(), aux.val() as *const u8);
} else {
println!(" {:?} => {:?}", aux.key(), aux.val() as *const u8);
}
}
}
代码示例输出
There are 2 arguments.
There are 2 environment variables.
There are 5 auxiliary vector entries/AT variables.
argv
[0] @ 0x7fff00b0
[1] @ 0x7fff00bc
envp
[0] @ 0x7fff00c9
[1] @ 0x7fff00d1
aux
Platform => @ 0x7fff0090
Clktck => 0x64
Random => @ 0x7fff0097
ExecFn => @ 0x7fff00db
Null => 0x0
术语(在代码中)
整个数据结构被我称为 InitialLinuxLibcStackLayout
。没有官方名称。它包含参数(argc
和 argv
)、环境变量(envp
或 envv
)和辅助向量(AT-variables
、auxv
、aux-pairs
、aux entries
)。
argv
数组将引用 argv data area
中的数据,envv
数组将引用 envv data area
中的数据,并且一些 auxv
值可能引用 auxv data area
中的数据。
有时(在一些文章中),辅助向量甚至描述整个数据结构。
数据结构的布局
null [HIGH ADDRESS]
filename (c string)
<env data area>
<args data area>
// round up to 16 byte
<aux vec data area>
// round up to 16 byte alignment
AT_VAR_3 = <points to aux vec data area>
AT_VAR_2 = integer
AT_VAR_1 = integer
// round up to 16 byte alignment
envv[2] = null
envv[1] = <points to env data area>
envv[0] = <points to env data area>
argv[2] = null
argv[1] = <points to args data area>
argv[0] = <points to args data area>
argc = integer <libc entry stack top> [LOW ADDRESS]
MSRV
1.56.1 稳定版 / Rust 版本 2021
背景信息 & 链接
依赖项
~1.5MB
~39K SLoC