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

ARM汇编字符串反转代码触发Segmentation fault问题求助及疑问

ARM汇编字符串反转程序的段错误排查与修复

我编写了一段用于反转输入字符串的ARM汇编代码,运行时在revstr()函数的pa2.s:37行ldrb w10, [x9, 0]处触发Segmentation fault(段错误)。同时不确定打印反转字符串的字符时,是否需要每次将栈指针sp加16,求解答。

代码如下:

.section .data
input_prompt :      .asciz "Please enter a string: 
"
input_format:       .asciz "%[^\n]"
output_format:      .asciz "%c"
new_line_format:    .ascii "\n"

.section .text
.global main
main:
    # set i = 0
    add x19, xzr, xzr
    # print string
    ldr x0, = input_prompt
    bl printf

    sub sp, sp, 16

    ldr x0, = input_format
    mov x1, sp
    bl scanf

    mov x0, sp
    bl revstr

    add sp, sp, 16

#========================================

revstr:
    sub sp, sp, 16
    str lr, [sp, 0]
    # get string[i]
    # i=0 first char i = size last char
    add x9, x19, x0
    ldrb w10, [x9]
    strb w10, [sp, 8]
    # print inorder
    mov w1, w10
    ldr x0, =output_format
    bl printf

    # if w10 is '\0'(ASCII #10)
    cbz w10, ret

    add x19, x19, 1
    bl revstr

    # lr start
    ldr w1, [sp, 8]
    ldr x0, =output_format
    bl printf

#========================================

ret:
    ldr lr, [sp, 0]
    add sp, sp, 8
    br lr

# branch to this label on program completion
exit:
    mov x0, 0
    mov x8, 93
    svc 0
    ret

一、段错误的原因

  1. 递归未保存调用保存寄存器x19:x19是ARM架构中属于「被调用者保存」的寄存器,函数如果要修改它,必须先把原值存在栈里,退出时恢复。你的代码递归调用revstr时,每次直接修改x19(作为索引i)但未保存状态,导致递归深入后x19持续累加,算出的内存地址超出栈上分配的16字节字符串缓冲区范围,访问非法内存触发段错误。
  2. 栈平衡异常:revstr开头用sub sp, sp, 16分配了16字节栈空间,但ret标签处只执行add sp, sp, 8释放8字节,每次递归都会导致栈指针无法正确回退,进一步加剧内存访问异常。
  3. 注释误导:代码注释写的是判断'\0'(ASCII 10),但实际cbz w10, ret是判断w10为0(即字符串结束符'\0'),逻辑本身没问题,但注释容易造成混淆。

二、修复方案

针对上述问题,修改后的revstr函数如下:

revstr:
    sub sp, sp, 32         # 分配32字节栈空间,满足16字节对齐,用于保存lr、x19和当前字符
    str lr, [sp, 0]
    str x19, [sp, 8]       # 保存x19的原值到栈

    add x9, x19, x0
    ldrb w10, [x9]
    strb w10, [sp, 16]     # 存储当前读取的字符

    # 打印当前字符(前序遍历)
    mov w1, w10
    ldr x0, =output_format
    bl printf

    cbz w10, ret_revstr    # 遇到字符串结束符'\0'则跳转返回

    add x19, x19, 1
    bl revstr

    # 打印当前字符(后序遍历,实现反转效果)
    ldr w1, [sp, 16]
    ldr x0, =output_format
    bl printf

ret_revstr:
    ldr x19, [sp, 8]       # 恢复x19的原值
    ldr lr, [sp, 0]
    add sp, sp, 32         # 释放全部栈空间,维持栈平衡
    br lr

额外注意:ARM64调用规范要求栈指针始终保持16字节对齐,所以分配栈空间时必须使用16的倍数。


三、关于栈指针sp的疑问

打印反转字符串的字符时不需要每次将sp加16。栈指针sp仅在以下场景需要调整:

  1. 需要在栈上临时存储数据、保存寄存器时,用sub sp分配空间;
  2. 使用完栈上空间后,用add sp释放,且必须保证分配和释放的字节数一致,维持栈平衡。
    调用printf之前,只要栈已经处于16字节对齐状态(修改后的代码已保证),就无需额外调整sp。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 13:22:11