Skip to main content

tg_rcore_tutorial_linker/
app.rs

1/// 应用程序元数据。
2///
3/// 链接器会把“应用起始地址表 + 应用数据”打包到镜像中,
4/// `AppMeta` 是运行期读取这些信息的入口结构。
5#[repr(C)]
6pub struct AppMeta {
7    base: u64,
8    step: u64,
9    count: u64,
10    first: u64,
11}
12
13impl AppMeta {
14    /// 定位应用程序元数据。
15    ///
16    /// 返回由链接脚本定义的应用程序元数据的静态引用。
17    #[inline]
18    pub fn locate() -> &'static Self {
19        unsafe extern "C" {
20            static apps: AppMeta;
21        }
22        // SAFETY: `apps` 是由链接脚本定义的静态符号,在程序运行期间始终有效。
23        // 它的内存布局与 AppMeta 结构体匹配(由 #[repr(C)] 保证)。
24        unsafe { &apps }
25    }
26
27    /// 遍历链接进来的应用程序。
28    #[inline]
29    pub fn iter(&'static self) -> AppIterator {
30        // 迭代器按链接顺序输出每个 app 的字节切片。
31        AppIterator { meta: self, i: 0 }
32    }
33}
34
35/// 应用程序迭代器。
36pub struct AppIterator {
37    meta: &'static AppMeta,
38    i: u64,
39}
40
41impl Iterator for AppIterator {
42    type Item = &'static [u8];
43
44    fn next(&mut self) -> Option<Self::Item> {
45        if self.i >= self.meta.count {
46            None
47        } else {
48            let i = self.i as usize;
49            self.i += 1;
50            // SAFETY: 以下操作基于链接脚本定义的应用程序布局:
51            // - `first` 之后紧跟着 count+1 个 usize,存储每个应用的位置和结尾
52            // - 如果 base != 0,则将应用拷贝到指定的基地址
53            // - 应用数据由链接器嵌入,在程序运行期间始终有效
54            unsafe {
55                let slice = core::slice::from_raw_parts(
56                    &self.meta.first as *const _ as *const usize,
57                    (self.meta.count + 1) as _,
58                );
59                let pos = slice[i];
60                let size = slice[i + 1] - pos;
61                let base = self.meta.base as usize + i * self.meta.step as usize;
62                if base != 0 {
63                    // 章节中常用该模式把 app 拷贝到固定虚拟地址(如 0x8040_0000)再执行。
64                    // SAFETY: pos 指向有效的应用数据,base 是调用者指定的目标地址,
65                    // 调用者负责确保 base 处有足够的内存空间
66                    core::ptr::copy_nonoverlapping::<u8>(pos as _, base as _, size);
67                    // 将目标区域剩余部分清零
68                    core::slice::from_raw_parts_mut(base as *mut u8, 0x20_0000)[size..].fill(0);
69                    Some(core::slice::from_raw_parts(base as _, size))
70                } else {
71                    Some(core::slice::from_raw_parts(pos as _, size))
72                }
73            }
74        }
75    }
76}