Assembly 理解递归IA32程序集调用
我正在尝试做一些练习,以便更熟悉IA32汇编,并且在将这个汇编代码的递归片段翻译成可理解的C代码方面有点困难。他们给了我们一个提示,代码中的所有函数只给出一个参数,但我对IA32堆栈的理解仍然有点差Assembly 理解递归IA32程序集调用,assembly,recursion,x86,Assembly,Recursion,X86,我正在尝试做一些练习,以便更熟悉IA32汇编,并且在将这个汇编代码的递归片段翻译成可理解的C代码方面有点困难。他们给了我们一个提示,代码中的所有函数只给出一个参数,但我对IA32堆栈的理解仍然有点差 .globl bar .type bar, @function bar: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax addl $10, %eax popl %ebp ret .globl foo .type foo, @function foo:
.globl bar
.type bar, @function
bar:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $10, %eax
popl %ebp
ret
.globl foo
.type foo, @function
foo:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl %ebx, -8(%ebp)
movl %esi, -4(%ebp)
movl 8(%ebp), %ebx
movl $1, %eax
cmpl $1, %ebx
jle .L5
movl %ebx, (%esp)
call bar
movl %eax, %esi
subl $1, %ebx
movl %ebx, (%esp)
call foo
imull %esi, %eax
.L5:
movl -8(%ebp), %ebx
movl -4(%ebp), %esi
movl %ebp, %esp
popl %ebp
ret
bar函数似乎很简单——它在参数上加10并返回。“foo”函数是我迷路的地方。我理解subl$24,esp保留了24字节的空间,但从那里我开始迷失自我。我们似乎递归地调用“foo”并每次递减参数(让我们称之为x),直到它达到1,然后将bar(x)的结果乘以bar(x-1)的结果,但其他所有发生的事情我似乎都不知道
具体来说,这两个命令的作用是什么?他们就在24美元之后,特别是我认为这是理解整个事情的关键。
移动百分比ebx,-8(%ebp)
movl%esi,-4(%ebp)在x86中,堆栈向下增长(朝向较小的地址)。在IA32中,寄存器是32位或4字节,指令指针(EIP)也是如此。堆栈指针寄存器(ESP)指向“堆栈顶部”,即推送到堆栈上的最后一个项目-由于堆栈向较低的地址增长,这是堆栈上具有有效数据的最低地址。push指令将ESP递减4,然后在该地址存储32位(4字节)。call指令有一个4字节的隐含推送(返回地址,即紧接着调用的地址) 那么,在函数入口的堆栈顶部是什么呢?这将是ESP指向的4个字节,它包含返回地址。ESP的偏移量+4是多少?这将是堆栈上第二个也是最后一个被推到堆栈上的内容,对于具有一个参数的函数,它将是该参数:
| |
+----------------+
| parameter 1 | ESP + 4
+----------------+
| return address | <===== ESP (top of stack)
+----------------+
有些函数在自动存储中没有变量。。。在这种情况下,函数前导完成。。。但是,假设每个4字节有6个变量,那么堆栈上需要6x4=24字节的自动存储。。。这是通过从ESP中减去24来实现的,然后您就有了容纳6个局部变量的空间(local-var-0…local-var-5):
堆栈现在如下所示:
| |
+----------------+
| parameter 1 | 8(ebp)
+----------------+
| return address | 4(ebp)
+----------------+
| caller's EBP | 0(ebp)
+----------------+
| local-var-0 | -4(ebp)
+----------------+
| local-var-1 | -8(ebp)
+----------------+
| local-var-2 | -12(ebp)
+----------------+
| local-var-3 | -16(ebp)
+----------------+
| local-var-4 | -20(ebp)
+----------------+
| local-var-5 | -24(ebp)
+----------------+
正如您所看到的,-8(ebp)是local-var-1的地址,-4(ebp)是local-var-0的地址,因此此代码将寄存器保存在堆栈上
movl %ebx, -8(%ebp) # save %ebx in local-var-1
movl %esi, -4(%ebp) # save %esi in local-var-0
并在返回之前进行恢复:
.L5:
movl -8(%ebp), %ebx # restore %ebx from local-var-1
movl -4(%ebp), %esi # restore %esi from local-var-0
通用寄存器%ebx和%esi(以及%edi和%ebp)由“被调用方”根据IA32调用约定保存。请参阅文档。例行程序未完成。。。“popl%e”不是指令。。。那是“popl%ebp”吗?接下来可能是“ret”?哇,是的,是popl%ebp和ret。
movl %ebx, -8(%ebp) # save %ebx in local-var-1
movl %esi, -4(%ebp) # save %esi in local-var-0
.L5:
movl -8(%ebp), %ebx # restore %ebx from local-var-1
movl -4(%ebp), %esi # restore %esi from local-var-0