AArch64架构下调用printf为何需保存x8/x9/x10寄存器才能正常运行
问题原因解答
核心误区:对AArch64调用约定的理解错误
你出现这个问题的根本原因是记错了AArch64 AAPCS64调用规范中寄存器的责任划分:
- 寄存器
x0~x15全部属于调用者保存寄存器(也叫临时寄存器):被调用的函数(比如你用的printf)可以任意修改这些寄存器的值,不需要在返回前恢复原值。如果你在调用函数之后还需要使用这些寄存器里存储的数据,必须由你作为调用方,在调用函数前手动保存、调用返回后手动恢复。 - 只有
x19~x28、栈指针SP、帧指针x29、链接寄存器x30属于被调用者保存寄存器:如果被调用的函数要使用这些寄存器,才需要由函数自己完成入栈保存、出栈恢复的操作。
你用来存储数组基地址、数组最大下标、循环计数器的x8、x9、x10正好属于调用者保存的临时寄存器范围,printf内部逻辑修改这些寄存器的值是完全符合规范的,不需要提前保存。你在循环里依赖这三个寄存器的值做迭代,调用printf前如果不手动备份,调用返回后寄存器值已经被修改,后续访问数组地址就会出错,触发段错误。
优化建议
如果你不想每次调用printf都手动保存三个寄存器,可以把你的循环变量、数组基地址、数组长度放到x19~x28范围内的寄存器存储,这部分寄存器printf会负责保存恢复,你不需要手动处理。
你贴出的可正常运行的代码如下:
.text .equ ELEM, 10 .extern printf .global _start _start: .global selec_start selec_start: ldr x0, =stack mov sp, x0 sub sp, sp, #16 ldr x8, =vec // address of vec (first element) mov x9, #ELEM // index of last element in vec sub x9, x9, #1 mov x10, #0 // increment i print: cmp x10, x9 // compare i with index of last element in vec bgt end // branch to end if we hit end of loop lsl x11, x10, #3 // multiply i by 8 add x12, x8, x11 // x12 = address of vec + i*8 // x12 is address of index i in vec str x8, [sp, #-8]! str x9, [sp, #-8]! str x10, [sp, #-8]! ldr x0, =string ldr x1, [x12] bl printf ldr x10, [sp], #8 ldr x9, [sp], #8 ldr x8, [sp], #8 add x10, x10, #1 // i++ b print end: mov w8, #93 svc #0 .data string: .ascii "%d\n\0" vec: .quad 1,2,3,4,5,6,7,8,9,10 .bss .align 8 .space 4096 stack: .space 16 .end
内容的提问来源于stack exchange,提问作者gottabehidoi
相关产品推荐
相关产品推荐

