Recursion 递归函数上的程序集早期返回
这更像是一个学术练习,但我想在汇编中编写一个递归函数,如果它接收到并“中断信号”,它将返回到主函数,而不仅仅是调用它的函数(通常是同一个递归函数) 对于这个测试,我正在做一个基本的倒计时并打印一个字符的数字(8…7…6…等等)。为了模拟一个“中断”,我使用的是数字Recursion 递归函数上的程序集早期返回,recursion,assembly,x86,x86-64,setjmp,Recursion,Assembly,X86,X86 64,Setjmp,这更像是一个学术练习,但我想在汇编中编写一个递归函数,如果它接收到并“中断信号”,它将返回到主函数,而不仅仅是调用它的函数(通常是同一个递归函数) 对于这个测试,我正在做一个基本的倒计时并打印一个字符的数字(8…7…6…等等)。为了模拟一个“中断”,我使用的是数字7,因此当函数点击7时(如果它在上面启动),它将返回一个1,表示它被中断,如果它没有被中断,它将倒计时到零。以下是我迄今为止所做的: .globl _start _start: # countdown(9); mov
7
,因此当函数点击7时(如果它在上面启动),它将返回一个1
,表示它被中断,如果它没有被中断,它将倒计时到零。以下是我迄今为止所做的:
.globl _start
_start:
# countdown(9);
mov $8, %rdi
call countdown
# return 0;
mov %eax, %edi
mov $60, %eax
syscall
print:
push %rbp
mov %rsp, %rbp
# write the value to a memory location
pushq %rdi # now 16-byte aligned
add $'0', -8(%rbp)
movb $'\n', -7(%rbp)
# do a write syscall
mov $1, %rax # linux syscall write
mov $1, %rdi # file descriptor: stdout=1
lea -8(%rbp), %rsi # memory location of string goes in rsi
mov $2, %rdx # length: 1 char + newline
syscall
# restore the stack
pop %rdi
pop %rbp
ret;
countdown:
# this is the handler to call the recursive function so it can
# pass the address to jump back to in an interrupt as one of the
# function parameters
# (%rsp) currntly holds the return address, and let's pass that as the second argument
mov %rdi, %rdi # redundant, but for clarity
mov (%rsp), %rsi # return address to jump
call countdown_recursive
countdown_recursive:
# bool countdown(int n: n<10, return_address)
# ...{
push %rbp
mov %rsp, %rbp
# if (num<0) ... return
cmp $0, %rdi
jz end
# imaginary interrupt on num=7
cmp $7, %rdi
jz fast_ret
# else...printf("%d\n", num);
push %rsi
push %rdi
call print
pop %rdi
pop %rsi
# --num
dec %rdi
# countdown(num)
call countdown_recursive
end:
# ...}
mov $0, %eax
mov %rbp, %rsp
pop %rbp
ret
fast_ret:
mov $1, %eax
jmp *%rsi
.globl\u开始
_开始:
#倒计时(9);
mov$8,%rdi
呼叫倒计时
#返回0;
mov%eax,%edi
mov$60,%eax
系统调用
打印:
推送%rbp
mov%rsp,%rbp
#将该值写入内存位置
pushq%rdi#现在16字节对齐
添加$'0',-8(%rbp)
movb$'\n',-7(%rbp)
#执行写系统调用
mov$1,%rax#linux系统调用写入
mov$1,%rdi#文件描述符:stdout=1
lea-8(%rbp),%rsi#字符串的内存位置位于rsi中
mov$2,%rdx#长度:1个字符+换行符
系统调用
#恢复堆栈
pop%rdi
弹出%rbp
ret;
倒计时:
#这是调用递归函数的处理程序,因此它可以
#将中断中要跳回的地址作为
#功能参数
#(%rsp)当前保存返回地址,我们将其作为第二个参数传递
mov%rdi,%rdi冗余,但为清晰起见
mov(%rsp),%rsi#要跳转的返回地址
调用倒计时
倒计时\u递归:
#bool倒计时(int n:n除了返回此备用返回地址外,您还需要恢复呼叫者(保留呼叫)的寄存器,而不仅仅是您最近的父母的寄存器。这包括RSP
您基本上是在尝试重新发明C的setjmp
/longjmp
,它正是这样做的,包括将堆栈指针重置回调用setjmp
的范围。我认为SO标签中的一些问题是关于在asm中实现您自己的setjmp/longjmp
此外,为了提高效率,您可能希望使用一种自定义调用约定,其中返回地址指针(或实现上述操作后的jmpbuf指针)位于调用保留寄存器(如R15)中,因此您不必在递归函数体中的打印调用周围保存/恢复它