理解汇编语言

7

我正在尝试理解这段汇编代码与上面的C代码的关系。我不确定自己是否正确,希望有人能帮助我更好地理解。

int silly(int n, int *p)
{
    int val, val2;
    if (n > 0)
        val2 = silly(n << 1, &val);
    else
        val = val2 = 0;
    *p = val + val2 + n;
    return val + val2;
}

这将产生以下机器代码:
silly:
pushl %ebp             // Here I am making space for the function on the stack
movl %esp,%ebp         // Moving the stack pointer where the base pointer is
subl $20,%esp          // Subtracting 20 from the stack pointer to allocate more space
pushl %ebx             // Pushing the %ebx register on top of the stack
movl 8(%ebp),%ebx      // Getting the first argument(which is n) and store it in register %ebx
testl %ebx,%ebx        // The first if-statement which compares if n > 0
jle .L3                // Jump if less or equal - meaning if n < 0 then jump to .L3
addl $-8,%esp          // Add -8 to %esp to allocate more space
leal -4(%ebp),%eax     // Storing the first local variable (which is val) in %eax
pushl %eax             // Pushing the register %eax on top of the stack
leal (%ebx,%ebx),%eax  // n + n and stores it as 2n in %eax
pushl %eax             // Pushing register %eax on top of the stack (Which I find strange
                       // considering that I've just pushed %eax onto the stack above
call silly             // Call the function silly
jmp .L4                // Jump to .L4 (Unconditionally)
.p2align 4,,7          // Don't know what this means.
.L3:                   // .L3 is the else-statement
xorl %eax,%eax         // Basically making %eax = 0
movl %eax,-4(%ebp)     // Moving the value in %eax which is 0 to the first local variable 
                       // meaning val = 0
.L4:                   // .L4 is the section after the else-statement
movl -4(%ebp),%edx     // Getting val again and now storing it in %edx
addl %eax,%edx         // Adding what is in %eax (which is 0) to %edx
movl 12(%ebp),%eax     // Getting the second parameter (*p) and storing it in %eax
addl %edx,%ebx         // Adding value from %edx to %ebx - meaning val + n
movl %ebx,(%eax)       // Moving what is in %ebx and storing it in memory location of %eax
movl -24(%ebp),%ebx    // Getting the second local variable (val2) and moving it to %ebx
movl %edx,%eax         // Move val to %eax - and the return value will be in %eax
movl %ebp,%esp         
popl %ebp
ret

我正在努力理解它,刚开始学习汇编语言,所以关于这个主题的指针将非常有用。我有几个问题需要询问这个汇编代码,这可能有助于我理解堆栈:
(a) 变量 `val` 是否存储在堆栈中?
(b) 如果是,相对于 %ebp,它存储在什么字节偏移量上?
(c) 为什么需要将其存储在堆栈中?
(a) 变量 `val2` 是否存储在堆栈中?
(b) 如果是,相对于 %ebp,它存储在什么字节偏移量上?
(c) 为什么需要将其存储在堆栈中?
(a) -24(%ebp) 存储了什么(如果有)?
(b) 如果在此处存储了某些东西,为什么需要将其存储?
(a) -8(%ebp) 存储了什么(如果有)?
(b) 如果在此处存储了某些东西,为什么需要将其存储?
提前感谢您的帮助 :)

4
你的问题是什么? - o11c
我刚刚更新了问题。抱歉 :) - drleifz
注意:val2未初始化。有时候,val也是未初始化的。 - wildplasser
是的..这是一个奇怪的代码,不是吗? - drleifz
哦,天啊...这些注释都是你写的吗?你真是个勤奋的学生。我记得几天前我读过这段代码,这是作业还是其他什么东西? - Jason Hu
@wildplasser 不,它不是。 - o11c
2个回答

1
在回答您的问题之前,我会注释代码中所有值所在的寄存器或堆栈位置,而不是解释代码的功能。
参数在堆栈上,返回值在%eax中。
寄存器%eax%ecx%edx是调用者保存的。所有其他寄存器,包括%ebx%ebp%esp,都是被调用者保存的(%edi%esi未使用)。
我的堆栈表示法每次为4个字节,并且我使用;表示ebp指向的位置(如果已知)。
silly:                     ; eax: ?, ebx: ebx0, edx: ?, stack: [eip0, n, p]
    pushl %ebp             ; eax: ?, ebx: ebx0, edx: ?, stack: [ebp0, eip0, n, p]
    movl %esp,%ebp         ; eax: ?, ebx: ebx0, edx: ?, stack: [; ebp0, eip0, n, p]
    subl $20,%esp          ; eax: ?, ebx: ebx0, edx: ?, stack: [?, ?, ?, ?, ?; ebp0, eip0, n, p]
    pushl %ebx             ; eax: ?, ebx: ebx0, edx: ?, stack: [ebx0, ?, ?, ?, ?, ?; ebp0, eip0, n, p]
    movl 8(%ebp),%ebx      ; eax: ?, ebx: n, edx: ?, stack: [ebx0, ?, ?, ?, ?, ?; ebp0, eip0, n, p]
    testl %ebx,%ebx        ; set flags from n
    jle .L3                ; if flags indicates <= 0, goto .L3, else fallthrough

                           ; set up for calling the function
    addl $-8,%esp          ; eax: ?, ebx: n, edx: ?, stack: [?, ?, ebx0, ?, ?, ?, ?, ?; ebp0, eip0, n, p]
    leal -4(%ebp),%eax     ; eax: &val, ebx: n, edx: ?, stack: [?, ?, ebx0, ?, ?, ?, ?, (stackeax); ebp0, eip0, n, p]
    pushl %eax             ; eax: &val, ebx: n, edx: ?, stack: [&val, ?, ?, ebx0, ?, ?, ?, ?, val=?; ebp0, eip0, n, p]
    leal (%ebx,%ebx),%eax  ; eax: 2*n, ebx: n, edx: ?, stack: [&val, ?, ?, ebx0, ?, ?, ?, ?, val=?; ebp0, eip0, n, p]
    pushl %eax             ; eax: 2*n, ebx: n, edx: ?, stack: [2*n, &val, ?, ?, ebx0, ?, ?, ?, ?, val=?; ebp0, eip0, n, p]
    call silly             ; pushes eip; args: (2*n, &val); val will be initialized on return
    jmp .L4                ;
                           ;
.p2align 4,,7              ; request alignment (there should be one before `silly:` too)
.L3:                       ;
    xorl %eax,%eax         ; eax: val=0, ebx: n, edx: ?, stack: [ebx0, ?, ?, ?, ?, ?; ebp0, eip0, n, p]
    movl %eax,-4(%ebp)     ; eax: val=0, ebx: n, edx: ?, stack: [ebx0, ?, ?, ?, ?, val; ebp0, eip0, n, p]
                           ;
.L4:                       ; eax: val2=φ(function result, 0), ebx: n, edx: ?, stack: [..., ebx0, ?, ?, ?, ?, val; ebp0, eip0, n, p]
    movl -4(%ebp),%edx     ; eax: val2, ebx: n, edx: val, stack: [..., ebx0, ?, ?, ?, ?, val; ebp0, eip0, n, p]
    addl %eax,%edx         ; eax: val2, ebx: n, edx: val+val2, stack: [..., ebx0, ?, ?, ?, ?, val; ebp0, eip0, n, p]
    movl 12(%ebp),%eax     ; eax: p, ebx: n, edx: val+val2, stack: [..., ebx0, ?, ?, ?, ?, val; ebp0, eip0, n, p]
    addl %edx,%ebx         ; eax: p, ebx: n+val+val2, edx: val+val2, stack: [..., ebx0, ?, ?, ?, ?, val; ebp0, eip0, n, p]
    movl %ebx,(%eax)       ; *p = n+val+val2
    movl -24(%ebp),%ebx    ; eax: p, ebx: ebx0, edx: val+val2, stack: [..., ebx0, ?, ?, ?, ?, val; ebp0, eip0, n, p]
    movl %edx,%eax         ; eax: val+val2, ebx: ebx0, edx: val+val2, stack: [..., ebx0, ?, ?, ?, ?, val; ebp0, eip0, n, p]
    movl %ebp,%esp         ; eax: val+val2, ebx: ebx0, edx: val+val2, stack: [; ebp0, eip0, n, p]
    popl %ebp              ; eax: val+val2, ebx: ebx0, edx: val+val2, stack: [eip0, n, p]
    ret                    ; eax: val+val2, ebx: ebx0, edx: val+val2, stack: [n, p]

停止

回去重新阅读代码。如果你不自己找出答案,只会伤害自己。我的注释应该很容易理解。

但无论如何...


  1. a. val通常在堆栈上,位于-4(%ebp)。唯一不是的时候是在xorl %eax,%eax这一行。
    b. 它存储在-4(%ebp),正如leal -4(%ebp),%eaxmovl %eax,-4(%ebp)movl -4(%ebp),%edx所证明的那样。此外,前一个框架的val*p
    c. val必须在堆栈上,以便可以取其地址并传递给递归调用。
  2. a. val2从未存储在堆栈上,尽管最可能的是其中一些?是为其保留的空间。
    b. 它存储在寄存器eax中,在.L4处,在phi函数的第一个分支上是递归调用的返回值,在第二个分支上是值0,它也存储在val中。
    c. val2永远不需要在堆栈上,因为它的地址没有被取出,它在递归调用之前不存在,因此不需要保存,并且正在使用的寄存器很少,因此不需要溢出。
  3. a. -24(%ebp)%ebx的保存值,来自pushl %ebx这一行。
    b. %ebx是被调用者保存的寄存器,因此必须保存其值。
  4. a. 不,那里什么也没有。
    b. 如果有必要溢出,最可能的是val2。我最好的猜测是其他三个?是为未在递归调用中使用的调用方保存的寄存器保留的:%eax%ecx%edx

0
你问了很多问题。
我从这部分开始...

.p2align 4,,7 // 不知道这是什么意思。

明白了,有点模糊不清!
程序员(在你的情况下看起来像编译器)想要在L3:处的指令位于所谓的“16字节边界”上。
你可以在 这里阅读有关这些内容的详细信息。如果这不太容易理解,请回到这里询问,我会再解释一些。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接