C $ C $的C解码相当于组装code [英] Decoding equivalent assembly code of C code

查看:258
本文介绍了C $ C $的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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆