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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 17:24:07