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__符号所在内存页的基地址,存入x0ldr 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
相关产品推荐
相关产品推荐

