C $ C $的C解码相当于组装code [英] Decoding equivalent assembly code of C code
问题描述
希望看到的编译器,其中c code输出(汇编),我在C写一个简单的程序,并使用gcc产生的汇编文件。
Wanting to see the output of the compiler (in assembly) for some C code, I wrote a simple program in C and generated its assembly file using gcc.
在code是这样的:
#include <stdio.h>
int main()
{
int i = 0;
if ( i == 0 )
{
printf("testing\n");
}
return 0;
}
它生成的程序集是在这里(只有主功能):
The generated assembly for it is here (only the main function):
_main:
pushl %ebpz
movl %esp, %ebp
subl $24, %esp
andl $-16, %esp
movl $0, %eax
addl $15, %eax
addl $15, %eax
shrl $4, %eax
sall $4, %eax
movl %eax, -8(%ebp)
movl -8(%ebp), %eax
call __alloca
call ___main
movl $0, -4(%ebp)
cmpl $0, -4(%ebp)
jne L2
movl $LC0, (%esp)
call _printf
L2:
movl $0, %eax
leave
ret
我是一个绝对的损失对C code和汇编code相关。所有的code要做的就是商店0在寄存器中,并将其与常数0比较,并采取适当的行动。但是,到底是怎么回事在组装?
I am at an absolute loss to correlate the C code and assembly code. All that the code has to do is store 0 in a register and compare it with a constant 0 and take suitable action. But what is going on in the assembly?
推荐答案
由于主
是特殊的,你通常可以通过在另一个函数做这种类型的东西(获得更好的结果在它自己的文件,没有preferably 主
)。例如:
Since main
is special you can often get better results by doing this type of thing in another function (preferably in it's own file with no main
). For example:
void foo(int x) {
if (x == 0) {
printf("testing\n");
}
}
很可能是因为组装更加清晰。这样做也将允许您编译优化和仍然观察条件行为。如果你是以上所有0优化级别来编译原来的程序就可能会做客场与比较,因为编译器可以继续和计算的结果。有了这个$ C比较$ C部分是由编译器隐藏(在参数 X
),所以编译器无法做到这一点的优化。
would probably be much more clear as assembly. Doing this would also allow you to compile with optimizations and still observe the conditional behavior. If you were to compile your original program with any optimization level above 0 it would probably do away with the comparison since the compiler could go ahead and calculate the result of that. With this code part of the comparison is hidden from the compiler (in the parameter x
) so the compiler can't do this optimization.
_main:
pushl %ebpz
movl %esp, %ebp
subl $24, %esp
andl $-16, %esp
这是建立对于当前函数堆栈帧。在86堆栈帧是堆栈指针的值(SP,ESP,或RSP 16,32或64位)和基指针的值(BP,EBP,或RBP)之间的区域。这就是所谓的局部变量住的地方,但不是真的,和明确的堆栈帧是在大多数情况下可选。使用的alloca
和/或变长数组将需要使用它们,虽然。
This is setting up a stack frame for the current function. In x86 a stack frame is the area between the stack pointer's value (SP, ESP, or RSP for 16, 32, or 64 bit) and the base pointer's value (BP, EBP, or RBP). This is supposedly where local variables live, but not really, and explicit stack frames are optional in most cases. The use of alloca
and/or variable length arrays would require their use, though.
这个特殊的栈帧结构比非不同 - 主
的功能,因为它也可以确保堆栈对齐的16字节。从ESP减法通过绰绰有余增加堆栈大小保持局部变量和和L
有效减去从0到15,从它,使它16字节对齐。这种一致性似乎过高,除了它会迫使堆栈,开始时也会缓存对齐以及字对齐。
This particular stack frame construction is different than for non-main
functions because it also makes sure that the stack is 16 byte aligned. The subtraction from ESP increases the stack size by more than enough to hold local variables and the andl
effectively subtracts from 0 to 15 from it, making it 16 byte aligned. This alignment seems excessive except that it would force the stack to also start out cache aligned as well as word aligned.
movl $0, %eax
addl $15, %eax
addl $15, %eax
shrl $4, %eax
sall $4, %eax
movl %eax, -8(%ebp)
movl -8(%ebp), %eax
call __alloca
call ___main
我不知道这一切呢。 的alloca
通过改变堆栈指针的值会增加堆栈帧的大小。
I don't know what all this does. alloca
increases the stack frame size by altering the value of the stack pointer.
movl $0, -4(%ebp)
cmpl $0, -4(%ebp)
jne L2
movl $LC0, (%esp)
call _printf
L2:
movl $0, %eax
我想你知道这是什么一样。如果没有, MOVL
只是befrore 移动您的字符串的地址到堆栈的顶部位置,因此通话它可以通过printf的被retrived。它必须在堆栈上传递,这样的printf可以用它的地址推断出的printf的其他参数(如果有,它不会有这种情况)。
I think you know what this does. If not, the movl
just befrore the call
is moving the address of your string into the top location of the stack so that it may be retrived by printf. It must be passed on the stack so that printf can use it's address to infer the addresses of printf's other arguments (if any, which there aren't in this case).
leave
该指令删除堆栈帧前面谈到。它本质上是 MOVL%EBP,ESP%其次为
。还有一个
popl%EBP 输入
指令,可以用来构建栈帧,但GCC没有使用它。当不显式使用栈帧, EBP
可作为一般puropose寄存器,而不是离开
编译想补充堆栈帧大小的堆栈指针,将由帧尺寸减小堆栈大小。
This instruction removes the stack frame talked about earlier. It is essentially movl %ebp, %esp
followed by popl %ebp
. There is also an enter
instruction which can be used to construct stack frames, but gcc didn't use it. When stack frames aren't explicitly used, EBP
may be used as a general puropose register and instead of leave
the compiler would just add the stack frame size to the stack pointer, which would decrease the stack size by the frame size.
ret
我不需要解释这个问题。
I don't need to explain this.
我敢肯定,你会重新编译所有FO与此不同的优化级别,所以我会指出一些可能发生,你可能会发现奇怪。我观察到 GCC
替换的printf
和 fprintf中
与看跌
和的fputs
,分别在格式字符串不包含任何%
并有没有通过其他参数。这是因为(原因很多),它是便宜得多调用看跌
和的fputs
,并在最后你还是得你就想打印。
I'm sure you will recompile all fo this with different optimization levels, so I will point out something that may happen that you will probably find odd. I have observed gcc
replacing printf
and fprintf
with puts
and fputs
, respectively, when the format string did not contain any %
and there were no additional parameters passed. This is because (for many reasons) it is much cheaper to call puts
and fputs
and in the end you still get what you wanted printed.
这篇关于C $ C $的C解码相当于组装code的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!