Recursion 这个函数中LEA指令的用途是什么?整个递归是做什么的?

Recursion 这个函数中LEA指令的用途是什么?整个递归是做什么的?,recursion,assembly,x86,Recursion,Assembly,X86,我一直在尝试解决以下递归函数的功能: func4: 0x08048cfa <+0>: push edi 0x08048cfb <+1>: push esi 0x08048cfc <+2>: push ebx 0x08048cfd <+3>: mov ebx,DWORD PTR [esp+0x10] // First arg 0x08048d01 <+7>: mov edi,DWORD

我一直在尝试解决以下递归函数的功能:

func4:
0x08048cfa <+0>:    push   edi
0x08048cfb <+1>:    push   esi
0x08048cfc <+2>:    push   ebx
0x08048cfd <+3>:    mov    ebx,DWORD PTR [esp+0x10] // First arg
0x08048d01 <+7>:    mov    edi,DWORD PTR [esp+0x14] // Second arg
0x08048d05 <+11>:   test   ebx,ebx // if (ebx == 0) { eax = 0; return ???;}
0x08048d07 <+13>:   jle    0x8048d34 <func4+58>
0x08048d09 <+15>:   mov    eax,edi
0x08048d0b <+17>:   cmp    ebx,0x1 // if (ebx == 1) {return ???;} 
0x08048d0e <+20>:   je     0x8048d39 <func4+63>
0x08048d10 <+22>:   sub    esp,0x8
0x08048d13 <+25>:   push   edi
0x08048d14 <+26>:   lea    eax,[ebx-0x1]// eax = ebx-1
0x08048d17 <+29>:   push   eax
0x08048d18 <+30>:   call   0x8048cfa <func4>
0x08048d1d <+35>:   add    esp,0x8 // esp += 8
0x08048d20 <+38>:   lea    esi,[edi+eax*1] // esi = edi + eax
0x08048d23 <+41>:   push   edi
0x08048d24 <+42>:   sub    ebx,0x2 // ebx -= 2
0x08048d27 <+45>:   push   ebx
0x08048d28 <+46>:   call   0x8048cfa <func4>
0x08048d2d <+51>:   add    esp,0x10 // esp += 10
0x08048d30 <+54>:   add    eax,esi // eax += esi
0x08048d32 <+56>:   jmp    0x8048d39 <func4+63>
0x08048d34 <+58>:   mov    eax,0x0 // eax = 0
0x08048d39 <+63>:   pop    ebx
0x08048d3a <+64>:   pop    esi
0x08048d3b <+65>:   pop    edi
0x08048d3c <+66>:   ret
func4:
0x08048cfa:推式edi
0x08048cfb:推送esi
0x08048cfc:推动ebx
0x08048cfd:mov ebx,DWORD PTR[esp+0x10]//第一个参数
0x08048d01:mov edi,DWORD PTR[esp+0x14]//第二个参数
0x08048d05:测试ebx,ebx//if(ebx==0){eax=0;返回???;}
0x08048d07:jle 0x8048d34
0x08048d09:mov eax,edi
0x08048d0b:cmp-ebx,0x1//if(ebx==1){return???;}
0x08048d0e:je 0x8048d39
0x08048d10:子esp,0x8
0x08048d13:推式edi
0x08048d14:lea-eax[ebx-0x1]//eax=ebx-1
0x08048d17:推送eax
0x08048d18:调用0x8048cfa
0x08048d1d:添加esp,0x8//esp+=8
0x08048d20:lea esi[edi+eax*1]//esi=edi+eax
0x08048d23:推送edi
0x08048d24:子ebx,0x2//ebx-=2
0x08048d27:推送ebx
0x08048d28:调用0x8048cfa
0x08048d2d:添加esp,0x10//esp+=10
0x08048d30:添加eax,esi//eax+=esi
0x08048d32:jmp 0x8048d39
0x08048d34:mov-eax,0x0//eax=0
0x08048d39:pop ebx
0x08048d3a:pop esi
0x08048d3b:pop edi
0x08048d3c:ret
到目前为止,我已经发现它需要ebx,递减1,将其传递回自身并递归,直到它到达一个基本情况,然后继续递归的下一步。然而,我还没有完全理解递归的那个分支做了什么,或者
esp
在这个上下文中做了什么


有关于如何进行的提示吗?我已经在gdb中使用过很多次了,但是没有真正注意到任何类型的模式可以帮助我确定发生了什么。

您似乎不知道结果会在
eax
寄存器中返回。考虑到这一点,代码并不难理解。假设使用了cdecl调用约定(因为调用方清理了堆栈),它与此js函数相同:

function func4(a, b)
{
    if (a <= 0) return 0;
    if (a == 1) return b;
    return b + func4(a-1, b) + func4(a-2, b);
}
函数func4(a,b)
{

如果(a
lea
在这里用于简单的算术。与.lea的名称相同,它来自于使用CPU对寻址模式进行解码的能力,而不是因为它仅用于有效指针。我们不知道8086的设计者是什么(斯蒂芬·莫尔斯)当他添加它时,他正在考虑;他可能打算公开将2个reg添加到不同的目的地以供任意使用的能力。@Petercords名称的基(加载有效地址),源必须是内存操作数的要求,以及它不影响标志的事实,并在“地址对象传输”中列出在8086手册的第节中,如果它的初衷不是为了计算地址,我会感到惊讶。当然,除了架构师之外,没有人能确定这一点。但是初衷对程序员来说意义不大;他们总是找到最有效的方法来做事情。
xor eax、eax
或eax,-1
就是一个很好的例子s、 有趣的是,关于它在8086手册中的位置这一部分对我来说是新的。其他因素是我在回答中提到的,我在上面链接了这些,请参见我关于这一点的详细论述。(有趣的一点是,不像其他ALU那样设置标志是设计意图的标志。除非这只是8086中使用地址解码逻辑的实现细节。)不管怎么说,我只是对历史有点感兴趣,主要是对当前指令的最有用的思考。你可以也应该把它看作是一个整数移位和加法,因为386标度索引。@PeterCordes我同意。反正我主要用它来加法。
func4:
0x08048cfa <+0>:    push   edi              ; save non-volatile registers
0x08048cfb <+1>:    push   esi
0x08048cfc <+2>:    push   ebx
0x08048cfd <+3>:    mov    ebx, [esp+0x10]  ; ebx <- a
0x08048d01 <+7>:    mov    edi, [esp+0x14]  ; edi <- b
0x08048d05 <+11>:   test   ebx, ebx         ; if (a <= 0)
0x08048d07 <+13>:   jle    0x8048d34        ;   return 0
0x08048d09 <+15>:   mov    eax, edi         ; result <- 0
0x08048d0b <+17>:   cmp    ebx, 0x1         ; if (a == 1)
0x08048d0e <+20>:   je     0x8048d39        ;   return result;
0x08048d10 <+22>:   sub    esp, 0x8         ; this is useless
0x08048d13 <+25>:   push   edi              ; passing 2nd arguments
0x08048d14 <+26>:   lea    eax, [ebx-0x1]   ; 
0x08048d17 <+29>:   push   eax              ; passing 1st arguments
0x08048d18 <+30>:   call   0x8048cfa<func4> ; ax = func4(a - 1, b)
0x08048d1d <+35>:   add    esp, 0x8         ; clean up the stak after calling
0x08048d20 <+38>:   lea    esi, [edi+eax*1] ; temp = b + func4(a - 1, b)
0x08048d23 <+41>:   push   edi              ; passing 2nd arguments
0x08048d24 <+42>:   sub    ebx, 0x2         ;
0x08048d27 <+45>:   push   ebx              ; passing 1st arguments
0x08048d28 <+46>:   call   0x8048cfa<func4> ; ax = func4(a - 2, b)
0x08048d2d <+51>:   add    esp, 0x10        ; clean up the stak and the useless 8 bytes
0x08048d30 <+54>:   add    eax, esi         ; result = func4(a - 2, b) + temp
0x08048d32 <+56>:   jmp    0x8048d39        ; 
0x08048d34 <+58>:   mov    eax, 0x0         ; jump to here when a <= 0
0x08048d39 <+63>:   pop    ebx
0x08048d3a <+64>:   pop    esi
0x08048d3b <+65>:   pop    edi
0x08048d3c <+66>:   ret