[英]C Code represented as Assembler Code - How to interpret?
I got this short C Code. 我得到了这段简短的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;
}
There is also an Assembler Code for the given C code above. 上面的给定C代码也有一个汇编程序代码。 I don't understand everything of that assembler code.
我不了解所有的汇编代码。 I commented each line and you will find my question in the comments for each line.
我评论了每一行,您会在每一行的评论中找到我的问题。
The Assembler Code is: 汇编代码是:
.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
Negative references relative to %ebp are for local variables on the stack. 相对于%ebp的负引用是针对堆栈上的局部变量的。
movl 8(%ebp), %eax // I don't know quite what "8(%ebp) mean? It has to do something with res, because`
%eax = x %eax = x
imull 12(%ebp), %eax // here is the multiplication done. And again "12(%ebp).
%eax = %eax * y %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 // sets low 32 bits of res (u_int32_t)res =%eax //设置低32位res
movl $0, -4(%ebp) // here as well
clears upper 32 bits of res to extend 32-bit multiplication result to uint64_t 清除res的高32位,以将32位乘法结果扩展到uint64_t
movl -8(%ebp), %eax // and here again.
movl -4(%ebp), %edx // also here
return ret; 返回ret //64-bit results are returned as a pair of 32-bit registers %edx:%eax
// 64位结果作为一对32位寄存器%edx:%eax返回
As for the main, see x86 calling convention which may help making sense of what happens. 至于主要方面,请参见x86调用约定 ,这可能有助于弄清发生的情况。
andl $-8, %esp // what happens here and why?
stack boundary is aligned by 8. I believe it's ABI requirement 堆栈边界以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
Multiples of 8 (probably due to alignment requirements) 8的倍数(可能是由于对齐要求)
movl $3, 20(%esp) // 3 gets pushed on the stack
a = 3 a = 3
movl $5, 16(%esp) // 5 also get pushed on the stack
b = 5 b = 5
movl 16(%esp), %eax // what does 16(%esp) mean and what happened with z?
%eax = b %eax = b
z is at 12(%esp) and is not used yet. z为12(%esp),尚未使用。
movl %eax, 4(%esp) // we got the here as well
put b on the stack (second argument to multiply()) 将b放在堆栈上(multiple()的第二个参数)
movl 20(%esp), %eax // and also here
%eax = a %eax = a
movl %eax, (%esp) // what does happen in this line?
put a on the stack (first argument to multiply()) 将一个放在堆栈上(multiple()的第一个参数)
call multiply // thats clear, the function multiply gets called
multiply returns 64-bit result in %edx:%eax 乘法返回%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() z =(uint32_t)乘法()
movl $0, %eax // I suppose, this line is because of "return 0;"
yup. 对。 return 0;
返回0;
Arguments are pushed onto the stack when the function is called. 调用函数时,参数会被压入堆栈。 Inside the function, the stack pointer at that time is saved as the base pointer.
在函数内部,此时的堆栈指针将另存为基本指针。 (You got that much already.) The base pointer is used as a fixed location from which to reference arguments (which are above it, hence the positive offsets) and local variables (which are below it, hence the negative offsets).
(您已经获得了很多。)基本指针用作固定的位置,从该位置可以引用参数(在它上面,因此是正偏移量)和局部变量(在它下面,因此是负偏移量)。
The advantage of using a base pointer is that it is stable throughout the entire function, even when the stack pointer changes (due to function calls and new scopes). 使用基本指针的优点是,即使堆栈指针发生更改(由于函数调用和新作用域),它也可以在整个函数中保持稳定。
So 8(%ebp)
is one argument, and 12(%ebp)
is the other. 因此
8(%ebp)
是一个参数,而12(%ebp)
是另一个参数。
The code is likely using more space on the stack than it needs to, because it is using temporary variables that could be optimized out of you had optimization turned on. 该代码可能在堆栈上使用了比其需要的空间更多的空间,因为它使用的临时变量可以在优化被打开的情况下进行优化。
You might find this helpful: http://en.wikibooks.org/wiki/X86_Disassembly/Functions_and_Stack_Frames 您可能会发现这很有帮助: http : //en.wikibooks.org/wiki/X86_Disassembly/Functions_and_Stack_Frames
I started typing this as a comment but it was getting too long to fit. 我开始输入此内容作为评论,但已太长而无法容纳。
You can compile your example with -masm=intel
so the assembly is more readable. 您可以使用
-masm=intel
编译示例,从而使程序集更具可读性。 Also, don't confuse the push
and pop
instructions with mov
. 另外,请勿将
push
和pop
指令与mov
混淆。 push
and pop
always increments and decrements esp
respectively before derefing the address whereas mov
does not. 在取消引用地址之前,
push
和pop
总是分别增加和减少esp
,而mov
则没有。
There are two ways to store values onto the stack. 有两种方法可以将值存储到堆栈中。 You can either
push
each item onto it one item at a time or you can allocate up-front the space required and then load each value onto the stackslot using mov
+ relative offset from either esp
or ebp
. 您可以一次
push
每个项目push
一个项目,也可以预先分配所需的空间,然后使用mov
+相对esp
或ebp
相对偏移将每个值加载到堆栈槽中。
In your example, gcc chose the second method since that's usually faster because, unlike the first method, you're not constantly incrementing esp
before saving the value onto the stack. 在您的示例中,gcc选择了第二种方法,因为它通常更快,因为与第一种方法不同,在将值保存到堆栈之前,您并不会不断地增加
esp
。
To address your other question in comment, x86 instruction set does not have a mov
instruction for copying values from memory location a
to another memory location b
directly. 为了解决您在注释中遇到的其他问题,x86指令集没有可将值直接从存储位置
a
复制到另一个存储位置b
的mov
指令。 It is not uncommon to see code like: 看到类似以下代码的情况并不少见:
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
Register eax
is being used as an intermediate temporary variable to help copy data between the two stack locations. 寄存器
eax
用作中间临时变量,以帮助在两个堆栈位置之间复制数据。 You can mentally translate the above as: 您可以将以上内容翻译为:
esp[4] = esp[16]; // argument 2
esp[0] = esp[20]; // argument 1
call multiply
esp[12] = eax; // eax has return value
Here's what the stack approximately looks like right before the call to multiply
: 这是调用
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.