C代码表示为汇编代码 - 如何解释?

时间:2013-11-14 17:56:21

标签: c assembly x86 disassembly

我得到了这个简短的C代码。

#include <stdint.h>
uint64_t multiply(uint32_t x, uint32_t y) {

uint64_t res;
res = x*y;
return res;
}

int main() {

uint32_t a = 3, b = 5, z;
z = multiply(a,b);
return 0;
}

上面给出的C代码还有一个汇编代码。 我不明白汇编代码的一切。我评论了每一行,你会在每行的评论中找到我的问题。

汇编代码是:

.text
multiply:
     pushl  %ebp  // stores the stack frame of the calling function on the stack
     movl   %esp, %ebp // takes the current stack pointer and uses it as the frame for the called function
     subl   $16, %esp // it leaves room on the stack, but why 16Bytes. sizeof(res) = 8Bytes
     movl   8(%ebp), %eax // I don't know quite what "8(%ebp) mean? It has to do something with res, because
     imull  12(%ebp), %eax // here is the multiplication done. And again "12(%ebp).
     movl   %eax, -8(%ebp) // Now, we got a negative number in front of. How to interpret this?
     movl   $0, -4(%ebp) // here as well
     movl   -8(%ebp), %eax // and here again.
     movl   -4(%ebp), %edx // also here
     leave
     ret
main:
     pushl  %ebp // stores the stack frame of the calling function on the stack
     movl   %esp, %ebp // // takes the current stack pointer and uses it as the frame for the called function
     andl   $-8, %esp // what happens here and why?
     subl   $24, %esp // here, it leaves room for local variables, but why 24 bytes? a, b, c: the size of each of them is 4 Bytes. So 3*4 = 12
     movl   $3, 20(%esp) // 3 gets pushed on the stack
     movl   $5, 16(%esp) // 5 also get pushed on the stack
     movl   16(%esp), %eax // what does 16(%esp) mean and what happened with z?
     movl   %eax, 4(%esp) // we got the here as well
     movl   20(%esp), %eax // and also here
     movl   %eax, (%esp) // what does happen in this line?
     call   multiply  // thats clear, the function multiply gets called
     movl   %eax, 12(%esp) // it looks like the same as two lines before, except it contains the number 12
     movl   $0, %eax // I suppose, this line is because of "return 0;"
     leave
     ret

3 个答案:

答案 0 :(得分:2)

相对于%ebp的负引用是针对堆栈上的局部变量。

 movl   8(%ebp), %eax // I don't know quite what "8(%ebp) mean? It has to do something with res, because`

%eax = x

 imull  12(%ebp), %eax // here is the multiplication done. And again "12(%ebp).

%eax =%eax * y

 movl   %eax, -8(%ebp) // Now, we got a negative number in front of. How to interpret this?

(u_int32_t)res =%eax //设置res的低32位

 movl   $0, -4(%ebp) // here as well

清除res的高32位以将32位乘法结果扩展到uint64_t

 movl   -8(%ebp), %eax // and here again.
 movl   -4(%ebp), %edx // also here

返回; // 64位结果作为一对32位寄存器%edx:%eax

返回

至于主要内容,请参阅x86 calling convention,这可能有助于理解发生的事情。

 andl   $-8, %esp // what happens here and why?

堆栈边界由8对齐。我相信它是ABI要求

 subl   $24, %esp // here, it leaves room for local variables, but why 24 bytes? a, b, c: the size of each of them is 4 Bytes. So 3*4 = 12

8的倍数(可能是由于对齐要求)

 movl   $3, 20(%esp) // 3 gets pushed on the stack

a = 3

 movl   $5, 16(%esp) // 5 also get pushed on the stack

b = 5

 movl   16(%esp), %eax // what does 16(%esp) mean and what happened with z?

%eax = b

z为12(%esp),尚未使用。

 movl   %eax, 4(%esp) // we got the here as well

将b放在堆栈上(第二个参数为multiply())

 movl   20(%esp), %eax // and also here

%eax = a

 movl   %eax, (%esp) // what does happen in this line?

把一个放在堆栈上(第一个参数为multiply())

 call   multiply  // thats clear, the function multiply gets called

multiply返回%edx:%eax

中的64位结果
 movl   %eax, 12(%esp) // it looks like the same as two lines before, except it contains the number 12

z =(uint32_t)multiply()

 movl   $0, %eax // I suppose, this line is because of "return 0;"

烨。返回0;

答案 1 :(得分:1)

调用函数时,参数被压入堆栈。在函数内部,此时的堆栈指针被保存为基指针。 (你已经得到了那么多。)基指针被用作一个固定的位置,从中引用参数(在它之上,因此是正偏移)和局部变量(在它下面,因此是负偏移)。

使用基指针的优点是它在整个函数中都是稳定的,即使堆栈指针发生变化(由于函数调用和新的作用域)。

所以8(%ebp)是一个参数,12(%ebp)是另一个参数。

代码可能在堆栈上占用的空间超出了需要的范围,因为它使用的是临时变量,可以在优化开启后对其进行优化。

您可能会发现这有用:http://en.wikibooks.org/wiki/X86_Disassembly/Functions_and_Stack_Frames

答案 2 :(得分:1)

我开始将此作为评论键入,但它太长而不适合。

您可以使用-masm=intel编译示例,以便程序集更具可读性。另外,请勿将pushpop说明与mov混淆。在解析地址之前,pushpop 总是分别递增和递减esp,而mov则没有。{/ p>

有两种方法可以将值存储到堆栈中。您可以push每个项目一次只有一个项目,或者您可以预先分配所需的空间,然后使用mov + esp的相对偏移量将每个值加载到堆栈图上}或ebp

在你的例子中,gcc选择了第二种方法,因为它通常更快,因为与第一种方法不同,在将值保存到堆栈之前,你不会经常递增esp

要在注释中解决您的其他问题,x86指令集没有mov指令,用于将值从内存位置a复制到另一个内存位置{{1}直接看到如下代码并不罕见:

b

寄存器 mov eax, [esp+16] mov [esp+4], eax mov eax, [esp+20] mov [esp], eax call multiply(unsigned int, unsigned int) mov [esp+12], eax 被用作中间临时变量,以帮助在两个堆栈位置之间复制数据。您可以在心理上将上述内容翻译为:

eax

这是在调用esp[4] = esp[16]; // argument 2 esp[0] = esp[20]; // argument 1 call multiply esp[12] = eax; // eax has return value 之前堆栈大致看起来的样子:

multiply