Rust Hacking - Compound Type (Part II)

2024-05-16

这次我们来看看在 Rust 中如何表示 Compound Type (内存中),新建示例项目,并打开 src/main.rs 写入如下代码:

1#[derive(Debug)]
2struct MyStruct {
3 a: i32,
4 b: u64,
5 c: String,
6}
7
8#[derive(Debug)]
9enum MyEnum {
10 First(i32),
11 Second(usize),
12}
13
14impl MyStruct {
15 fn new(a: i32, b: u64, c: String) -> Self {
16 Self{a, b, c}
17 }
18}
19
20fn main() {
21 let my_struct = MyStruct::new(1, 2, String::from("hello"));
22 let my_first = MyEnum::First(3);
23 let my_second = MyEnum::Second(4);
24 let my_tuple = (5, 6, 7);
25 let my_array = [8, 9, 10];
26 println!("struct: {}, {}, {}", my_struct.a, my_struct.b, my_struct.c);
27 println!("tuple: {my_tuple:?}");
28 println!("array: {my_array:?}");
29 if let MyEnum::First(a) = my_first {
30 println!("enum: {a}");
31 }
32 if let MyEnum::Second(b) = my_second {
33 println!("enum: {b}");
34 }
35}

Analysis

Rust 编译器会根据 Size 和 Alignment 调整结构中各个字段在内存中布局,详情可参考 https://doc.rust-lang.org/reference/type-layout.html.

Struct

GDB 加载编译的二进制,根据上次分析的入口,我们下断点到 compound_type::main 上, run 起程序即可断到程序入口,查看 main 的反汇编结果,可得出如下分析图:

可以看到,Rust 编译器 默认 情况下并未按照结构体定义的顺序放置各个字段(此处的顺序是 c, b, a)

Tuple && Array

紧挨着 Struct 的初始化就是 Tuple 和 Array, 在初始化时并未明确指明具体类型,此处编译器默认两者类型都是 i32, 汇编中可得出这一推断:

Enum

继续往下,即是 Enum 的初始化,具体参考 Enum Memory Layout

总结

  • Compound Type 基于 Scalar Type 组合起来,其基本内存布局符合预期
  • 需要注意的是: 在未明确要求的情况下(without annotation),编译器不保证各个成员的布局与定义一致,所以在需要明确的布局时,务必使用 [repr()] 类注解