[英]C Code represented as Assembler Code - How to interpret?
我得到了这段简短的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
相对于%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 //设置低32位res
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
返回ret // 64位结果作为一对32位寄存器%edx:%eax返回
至于主要方面,请参见x86调用约定 ,这可能有助于弄清发生的情况。
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放在堆栈上(multiple()的第二个参数)
movl 20(%esp), %eax // and also here
%eax = a
movl %eax, (%esp) // what does happen in this line?
将一个放在堆栈上(multiple()的第一个参数)
call multiply // thats clear, the function multiply gets called
乘法返回%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)乘法()
movl $0, %eax // I suppose, this line is because of "return 0;"
对。 返回0;
调用函数时,参数会被压入堆栈。 在函数内部,此时的堆栈指针将另存为基本指针。 (您已经获得了很多。)基本指针用作固定的位置,从该位置可以引用参数(在它上面,因此是正偏移量)和局部变量(在它下面,因此是负偏移量)。
使用基本指针的优点是,即使堆栈指针发生更改(由于函数调用和新作用域),它也可以在整个函数中保持稳定。
因此8(%ebp)
是一个参数,而12(%ebp)
是另一个参数。
该代码可能在堆栈上使用了比其需要的空间更多的空间,因为它使用的临时变量可以在优化被打开的情况下进行优化。
您可能会发现这很有帮助: http : //en.wikibooks.org/wiki/X86_Disassembly/Functions_and_Stack_Frames
我开始输入此内容作为评论,但已太长而无法容纳。
您可以使用-masm=intel
编译示例,从而使程序集更具可读性。 另外,请勿将push
和pop
指令与mov
混淆。 在取消引用地址之前, push
和pop
总是分别增加和减少esp
,而mov
则没有。
有两种方法可以将值存储到堆栈中。 您可以一次push
每个项目push
一个项目,也可以预先分配所需的空间,然后使用mov
+相对esp
或ebp
相对偏移将每个值加载到堆栈槽中。
在您的示例中,gcc选择了第二种方法,因为它通常更快,因为与第一种方法不同,在将值保存到堆栈之前,您并不会不断地增加esp
。
为了解决您在注释中遇到的其他问题,x86指令集没有可将值直接从存储位置a
复制到另一个存储位置b
的mov
指令。 看到类似以下代码的情况并不少见:
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
之前堆栈的大致样子:
lower addr esp => uint32_t:a_copy = 3 <--. arg1 to 'multiply'
esp + 4 uint32_t:b_copy = 5 <--. arg2 to 'multiply'
^ esp + 8 ????
^ esp + 12 uint32_t:z = ? <--.
| esp + 16 uint32_t:b = 5 | local variables in 'main'
| esp + 20 uint32_t:a = 3 <--.
| ...
| ...
higher addr ebp previous frame
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.