You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 23:10:01