Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Assembly 理解递归IA32程序集调用_Assembly_Recursion_X86 - Fatal编程技术网

Assembly 理解递归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:

我正在尝试做一些练习,以便更熟悉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:
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