recursion - 递归函数的装配提前返回
问题描述
这比其他任何事情都更像是一项学术练习,但我希望在汇编中编写一个递归函数,如果它接收并“中断信号”,它将返回到主函数,而不仅仅是调用它的函数(通常是相同的递归函数)。
对于这个测试,我正在做一个基本的倒计时并打印一个字符的数字(8...7...6...等)。为了模拟一个“中断”,我使用了 number 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
以上看起来像一个有效的方法,传递我想要返回的内存地址rsi
吗?这个函数对我来说写起来非常棘手,但我认为主要是因为我对汇编很陌生/原始。
解决方案
除了返回到这个备用返回地址之外,您还需要恢复调用者的(保留调用的)寄存器,而不仅仅是您最近的父母的寄存器。这包括RSP。
您基本上是在尝试重新发明 C 的setjmp
/longjmp
正是这样做的,包括将堆栈指针重置回您调用的范围setjmp
。我认为 SO 的setjmp标签中的一些问题是关于在 asm 中实现你自己的 setjmp / longjmp。
此外,为了提高效率,您可能希望使用自定义调用约定,其中返回地址指针(或实现上述内容后的 jmpbuf 指针)位于像 R15 这样的调用保留寄存器中,因此您不必保存/围绕递归函数体内的打印调用恢复它。
推荐阅读
- jquery - 如何在jQuery中创建一个全局变量
- python - 试图用下划线替换字母的刽子手游戏错误
- python-3.x - 删除从 XML 中提取的重复数据
- c# - 购买后如何更新库存 - asp.net webapplication
- sql - 有没有办法在 SQL 中引用单元格上方的所有行?
- python - 为什么我的重置功能在 Streamlit 中不断重新运行?
- javascript - React - 使用`useEffect`时纬度和经度状态不正确
- python - 下面的python代码可以改成一行吗?#Python
- python - 将数据框两列重塑为一列和两行
- python - Python os.listdir 在主机上找不到文件夹