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

C语言中向原始指针赋值时指向错误地址的问题排查

问题分析:AArch64裸机程序中_exit函数的汇编异常

问题背景

编写的C代码

void _exit(int status) {
   extern long long uart_addr asm ("__uart__");
   int* uart = uart_addr;
   *uart = 1;
}

编译后生成的汇编代码(使用aarch64-none-elf-gcc)

80002660: d0d7ffe0  adrp  x0, 30000000 <Image$$UART$$Base>
     80002664: 52800021  mov w1, #0x1                    // #1
     80002668: f9400000  ldr x0, [x0]
     8000266c: b9000001  str w1, [x0]

疑问

为什么会出现0x80002668处的ldr x0, [x0]指令?本意是获取__uart__的值(0x3000_0000)赋值给指针int* uart,再向该地址写入1,但这行指令会覆盖x0中的地址,导致写入操作指向错误地址。这是裸机程序,0x3000_0000是UART的物理地址。

原因分析

你用extern long long uart_addr asm ("__uart__")声明的uart_addr,在编译器看来是一个存储于__uart__符号对应地址上的变量,而非__uart__符号本身的值就是目标UART地址。

编译器生成的代码逻辑是:

  • adrp x0, __uart__:计算__uart__符号所在内存页的基地址,存入x0
  • ldr x0, [x0]:从__uart__指向的内存地址中读取数据(即你期望的UART地址),覆盖x0原有值
  • str w1, [x0]:将1写入最终x0指向的地址

但你的需求是直接将__uart__符号的值(0x30000000)当作指针使用,而非从该地址读取数据,因此这行ldr指令是多余且错误的。

修复方案

要让编译器直接将__uart__的值作为指针,需将其声明为常量表达式,而非变量,以下是两种可行方式:

方式一:直接宏定义地址

#define UART_ADDR ((volatile int *)0x30000000)

void _exit(int status) {
    *UART_ADDR = 1;
}

方式二:声明符号为绝对地址

如果需要保留汇编符号关联,可通过属性告知编译器该符号是绝对地址而非变量:

extern const volatile int* uart_addr __attribute__((alias("__uart__")));

void _exit(int status) {
    *uart_addr = 1;
}

修改后编译生成的汇编会直接使用0x30000000作为目标地址,不会出现多余的ldr指令。

内容的提问来源于stack exchange,提问作者jkang

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 22:33:23