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
相关产品推荐
相关产品推荐

