x64汇编中的递归阶乘子例程会导致堆栈溢出

时间:2015-10-04 15:16:03

标签: assembly recursion 64-bit x86-64 att

我正在实现一个递归算法来计算x64汇编中给定数字的阶乘。 (我正在使用AT& T语法。)

伪代码如下所示:

int factorial(int x){
    if(x == 1){
        return 1;
    }else{
        return x*factorial(x-1);
    }
 }

现在,我在x64程序集中的实现:

factorial:  
    #Start of subroutine
    pushq %rbp
    movq %rsp, %rbp 

    cmpq $1, %rdi   #If rdi == 1, return 1, otherwise return x*factorial(x-1)
    je if           #je : %rdi == 1

    jmp else

if:                 #return 1
    movq $1, %rax
    jmp factend

else:               #return x*factorial(x-1)
    pushq %rdi      #Save x
    subq $1,%rdi    #Calculate x-1

    call factorial  #Calculate factorial from x-1
    popq %rdi       #Get rdi value before decrement
    mulq %rdi       #Multiply with rax, rax is either 1, or result of previous multiplication

    jmp factend     #End this subroutine, continue with previous 

factend:
    #End of subroutine
    movq %rbp, %rsp
    popq %rbp
    ret

然而,这种实现并没有停止。我得到一个分段错误,这是由堆栈溢出引起的。永远不会执行if块,并且子例程卡在一个带有else代码的循环中。如果我一步一步地遵循我的实现并写下寄存器和堆栈的值,我似乎没有遇到问题。可能导致这种情况的原因是什么?

修改 我如何检索输入值:

formatinput: .asciz "%d"

#Get input from terminal
subq $8, %rsp
leaq -8(%rbp), %rsi
movq $0, %rax
movq $formatinput, %rdi
call scanf

#Calculate factorial of input value
movq -8(%rbp), %rdi
movq $1, %rax
call factorial

另一个编辑

我的完整代码:

#Define main
.global main

.global inout

#Define string to be printed
formatinput: .asciz "%d"
formatoutput: .asciz "%d\n"

str:    .asciz "Assignment %d"


main:


#Start of program   
    movq %rsp, %rbp

    #Print statement
    movq $0, %rax
    movq $4, %rsi
    movq $str, %rdi
    call printf

    call inout

end:
    #Exit program with code 0, no errors
    movq $0, %rdi
    call exit

#inout subroutine
inout:  
    #Start of subroutine
    pushq %rbp
    movq %rsp, %rbp

    #Get input from terminal
    subq $8, %rsp
    leaq -8(%rbp), %rsi
    movq $0, %rax
    movq $formatinput, %rdi
    call scanf

    #Calculate factorial of input value
    movq -8(%rbp), %rdi
    movq $1, %rax
    call factorial
    movq %rax, -8(%rbp)

    #Print result
    movq $0, %rax
    movq -8(%rbp), %rsi
    movq $formatoutput, %rdi
    call printf

    movq %rbp, %rsp
    popq %rbp
    ret

#factorial subroutine
# int factorial(int x){
#   if(x == 1){
#       return 1;
#   }else{
#       return x*factorial(x-1);
#   }
# }

factorial:  
    #Start of subroutine
    pushq %rbp
    movq %rsp, %rbp 

    cmpq $1, %rdi       #If rdi == 1, return 1, otherwise return x*factorial(x-1)
    jg if           #jg : %rdi > $1

    jmp else

if:             #return 1
    movq $1, %rax
    jmp factend

else:               #return x*factorial(x-1)
    pushq %rdi      #Save x
    subq $1,%rdi        #Calculate x-1

    call factorial      #Calculate factorial from x-1
    popq %rdi       #Get rdi value before decrement
    mulq %rdi       #Multiply with rax, rax is either 1, or result of previous multiplication

    jmp factend     #End this subroutine, continue with previous 

factend:
    #End of subroutine
    movq %rbp, %rsp
    popq %rbp
    ret

#print test subroutine
print:
    #Start of subroutine
    pushq %rbp
    movq %rsp, %rbp

    pushq %rdi
    pushq %rsi
    pushq %rax

    movq $0, %rax
    movq %rdi, %rsi
    movq $formatoutput, %rdi
    call printf

    popq %rsi
    popq %rdi
    popq %rax

    movq %rbp, %rsp
    popq %rbp
    ret

1 个答案:

答案 0 :(得分:4)

您使用64位整数,而您的c代码使用int,通常为32位。因此,scanf("%d")无法触及您加载到%rdi的值的高32位以传递给factorial()
无论在scanf()之前的那些高位中是什么{1}}现在被解释为您传递的数字的一部分,因此1不是像factorial()这样的输入,而是将其解释为18612532834992129,这会导致堆栈溢出。 />

您可以在scanf()movq -8(%rbp), %rdi之后替换with movl -8(%rbp), %edi,或将scanf() - 格式说明符从%d更改为%ld

movl - 变体显示了一个关于x86-64的有趣消息:使用32位操作隐式清除64位寄存器的高32位(异常为xchg %eax, %eax,因为这个是规范nop)。