如何在no_std环境Rust代码中访问链接脚本定义的变量
无标准库Rust访问链接脚本定义符号的方法
问题相关代码
使用的链接脚本片段:
.data : { *(.data*) } > ram . = ALIGN(4); /* Set Stack after code & data */ _stack_start = .;
待修正的无标准库Rust入口代码,直接引用_stack_start会触发编译错误:
#![no_std] #![no_main] #![allow(dead_code)] #[no_mangle] pub fn _start() { // let sp: i32 = _stack_start; -> 这行代码会触发编译错误 type FnPtr = fn() -> (); let th: FnPtr = trap_handler; unsafe { asm!("csrw mtvec, {}" , in(reg) th); } loop {} }
实现原理
链接脚本中定义的符号和普通高级语言变量逻辑不同:这类符号没有分配实际的内存存储空间,符号本身的地址就是链接阶段计算出的目标数值,因此不能按普通变量的方式直接引用。
正确实现步骤
- 在Rust代码中使用
extern "C"块声明外部链接符号,标记为static类型,类型选u8即可,不需要匹配实际存储类型 - 通过
core::ptr::addr_of!取该符号的地址,转成对应数值类型(一般是usize),得到的就是链接脚本中_stack_start对应的栈起始地址 - 所有访问外部链接符号的操作必须放在
unsafe块中,因为Rust编译器无法校验外部符号的合法性
修正后的可运行代码示例:
#![no_std] #![no_main] #![allow(dead_code)] // 声明链接脚本中定义的外部符号 extern "C" { static _stack_start: u8; } #[no_mangle] pub fn _start() { // 获取栈起始地址 let stack_top = unsafe { core::ptr::addr_of!(_stack_start) as usize }; // RISC-V架构下可直接将地址写入sp寄存器完成栈初始化 unsafe { asm!("mv sp, {}", in(reg) stack_top); } type FnPtr = fn() -> (); let th: FnPtr = trap_handler; unsafe { asm!("csrw mtvec, {}" , in(reg) th); } loop {} }
避坑提示
- 禁止直接读取
_stack_start的绑定值,该符号没有对应实际内存存储,直接读取会触发未定义行为 - 不要给
_stack_start加错命名修饰,必须通过extern "C"保证符号名在链接时和链接脚本中的定义完全匹配
内容的提问来源于stack exchange,提问作者NastyBen
相关产品推荐
相关产品推荐

