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

x86-64汇编函数调用出现无限循环问题排查求助

问题分析:x86-64汇编程序无限循环原因

你的程序陷入无限循环的核心原因是违反了x86-64 Linux下的System V AMD64调用约定:

main函数用%ecx作为循环计数器,但%ecx属于调用者保存寄存器——这类寄存器在调用其他函数时,被调用的函数(比如puts)不需要保留它的值,会直接覆盖。每次调用f后,f里调用的puts会修改%ecx的内容,导致main的循环计数器永远无法增长到5,循环永远无法终止。

解决方案

有两种可行的修改方式:

方式一:改用被调用者保存寄存器作为计数器

System V AMD64约定中,%ebx、%esi、%edi、%r12-%r15等属于被调用者保存寄存器,被调用函数必须保证这些寄存器的值在返回时和进入时一致。我们可以把main的循环计数器换成%ebx,同时在函数开头/结尾保存、恢复它的原值:

修改后的完整代码:

.section .rodata
.LC0:
    .ascii "hello world\0"

.text
.global main
.type main, @function
main:
    pushq %rbp
    movq %rsp, %rbp
    pushq %ebx          # 保存%ebx的初始值
    movl $0, %ebx       # 用%ebx作为循环计数器

main_loop:
    cmpl $5, %ebx
    jge main_end

    call f
    incl %ebx
    jmp main_loop

main_end:
    popq %ebx           # 恢复%ebx的初始值
    movq %rbp, %rsp
    popq %rbp
    ret

.global f
.type f, @function
f:
    pushq %rbp
    movq %rsp, %rbp
    movl $0, %edx

f_loop:
    cmpl $3, %edx
    jge f_end

    leaq .LC0(%rip), %rdi
    call puts
    incl %edx
    jmp f_loop

f_end:
    movq %rbp, %rsp
    popq %rbp
    ret

方式二:手动保存/恢复%ecx的值

如果坚持使用%ecx作为计数器,需要在调用f前把它的值压入栈保存,调用完成后再从栈中恢复:

修改main函数的循环部分:

main_loop:
    cmpl $5, %ecx
    jge main_end

    pushq %rcx          # 保存%ecx的值(x86-64下要压64位寄存器%rcx)
    call f
    popq %rcx           # 恢复%ecx的值
    incl %ecx
    jmp main_loop

补充说明:调用约定的寄存器分类

System V AMD64 ABI把寄存器分为两类:

  • 调用者保存寄存器:%rax、%rcx、%rdx、%rsi、%rdi、%r8-%r11。调用其他函数前,调用者必须自行保存这些寄存器的值,否则被调用函数可能会修改它们。
  • 被调用者保存寄存器:%rbx、%rbp、%rsp、%r12-%r15。被调用函数必须保证这些寄存器的值在返回时和进入时一致,调用者可以放心使用,无需额外保存。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 03:57:35