微软C/C++编译器cl.exe

    From: xuyibo.net  Date: 2007-10-31 08:52 AM

    【一点推证】
    1. cl并没有显式调用ML,所以cl模块中必定含有自己的汇编模块。而且功能应该比ML要强大。毕竟ML对外发布的版本已经很久没有更新了。

    2. 嵌入到VC中的/FA功能好像有点限制,就是在Release版本下无法生成.ASM文件(这又是微软加的一个可恶的限制,当然你完全可以用命令行实现反汇编输出代码)。这一点好像有点令人失望,但在CONSOLE下仍然可以生成汇编代码,而且生成的质量非常高,几乎跟IDA反汇编代码是对应的。
    /FA Assembly-Only Listing Assembly code; .ASM
    /FAc Assembly With Machine Code Machine and assembly code; .COD
    /FAs Assembly With Source Code Source and assembly code; .ASM
    /FAcs Assembly, Machine Code, and Source Machine, source, and assembly code; .COD
    /Fafilename Listing File Name Use /Fa to specify a directory and/or filename for the selected type of listing file. By default, the base name of the listing file is the base name of the source file.

    3.子函数堆栈排列
    ?pp@@YAHH@Z PROC NEAR ; pp
    ; File c.cpp
    ; {
    push ebp
    mov ebp, esp
    ; i = i++;
    mov eax, DWORD PTR _i$[ebp]
    mov DWORD PTR _i$[ebp], eax
    mov ecx, DWORD PTR _i$[ebp]
    add ecx, 1
    mov DWORD PTR _i$[ebp], ecx
    ; return i;
    mov eax, DWORD PTR _i$[ebp]
    ; }
    pop ebp
    ret 0
    PUBLIC ?pp@@YAHH@Z ; pp
    _TEXT SEGMENT
    _i$ = 8
    _i1$ = -4
    _i2$ = -8
    _i3$ = -12
    ?pp@@YAHH@Z PROC NEAR ; pp
    ; 4 : {
    push ebp
    mov ebp, esp
    sub esp, 12 ; 0000000cH
    ; 5 : int i1 =2;
    mov DWORD PTR _i1$[ebp], 2
    ; 6 : int i2 = 3;
    mov DWORD PTR _i2$[ebp], 3
    ; 7 : i = i++;
    mov eax, DWORD PTR _i$[ebp]
    mov DWORD PTR _i$[ebp], eax
    mov ecx, DWORD PTR _i$[ebp]
    add ecx, 1
    mov DWORD PTR _i$[ebp], ecx
    ; 8 : int i3 = 4;
    mov DWORD PTR _i3$[ebp], 4
    ; 9 : return i;
    mov eax, DWORD PTR _i$[ebp]
    ; 10 : }
    mov esp, ebp
    pop ebp
    ret 0
    ?pp@@YAHH@Z ENDP

    通过上面的汇编代码,我们发现C++编译器的作者给我们做了大部分的工作。我们都知道在C语言中,函数中的局部变量必须首先声明或定义一下,否则就会出错;而在C++语言中却没有这个限制,究其原因为什么?答案就在上面的代码中:C++编译器可以确定函数中用到的局部变量的个数,进而确定它们的总大小xxx,然后就像C语言一样在PUSH EBP, MOV EBP, ESP之后添加SUB ESP, xxx。有时候在SUB ESP, xxx之后还将可能用到的CPU寄存器压人堆栈。
    函数返回时,如果是__cdecl方式的调用,则为RET 0, ADD ESP yyy。其中yyy为传入的参数的总大小。
    好让我们看看函数堆栈的排布:函数__stdcall int pp(int i, int j)











    下面让我们深入的分析一下C/C++语言中函数的三种调用方式:__stdcall, __cdecl, __fastcall。
    #include

    int __stdcall pp(int i, int j, int k, int l, int f)
    {
    int i1 =2;
    int i2 = 3;
    i = i++ + j;
    int i3 = 4;
    return i;
    }

    void main()
    {

    int i;
    i= pp(12, 13, 14, 15, 16);
    int *j = &i;
    int z = 3;

    printf('%d', i);
    }

    __stdcall:
    PUBLIC ?pp@@YGHHHHHH@Z ; pp
    _TEXT SEGMENT
    _i$ = 8
    _j$ = 12
    _k$ = 16
    _t1$ = -4
    _t2$ = -8
    _t3$ = -12
    ?pp@@YGHHHHHH@Z PROC NEAR ; pp
    ; 4 : {
    push ebp
    mov ebp, esp
    sub esp, 12 ; 0000000cH
    ; 5 : int t1 =2;
    mov DWORD PTR _t1$[ebp], 2
    ; 6 : int t2 = 3;
    mov DWORD PTR _t2$[ebp], 3
    ; 7 : i = j + k;
    mov eax, DWORD PTR _j$[ebp]
    add eax, DWORD PTR _k$[ebp]
    mov DWORD PTR _i$[ebp], eax
    ; 8 : int t3 = 4;
    mov DWORD PTR _t3$[ebp], 4
    ; 9 : return i;
    mov eax, DWORD PTR _i$[ebp]
    ; 10 : }
    mov esp, ebp
    pop ebp
    ret 20 ; 00000014H
    ?pp@@YGHHHHHH@Z ENDP ; pp
    _TEXT ENDS
    PUBLIC _main
    EXTRN _printf:NEAR
    _DATA SEGMENT
    $SG590 DB '%d', 00H
    _DATA ENDS
    _TEXT SEGMENT
    _i$ = -4
    _j$ = -12
    _z$ = -8
    _main PROC NEAR
    ; 13 : {
    push ebp
    mov ebp, esp
    sub esp, 12 ; 0000000cH
    ; 14 :
    ; 15 : int i;
    ; 16 : i= pp(12, 13, 14, 15, 16);
    push 16 ; 00000010H
    push 15 ; 0000000fH
    push 14 ; 0000000eH
    push 13 ; 0000000dH
    push 12 ; 0000000cH
    call ?pp@@YGHHHHHH@Z ; pp
    mov DWORD PTR _i$[ebp], eax
    ; 17 : int *j = &i;
    lea eax, DWORD PTR _i$[ebp]
    mov DWORD PTR _j$[ebp], eax
    ; 18 : int z = 3;
    mov DWORD PTR _z$[ebp], 3
    ; 19 : printf('%d', i);
    mov ecx, DWORD PTR _i$[ebp]
    push ecx
    push OFFSET FLAT:$SG590
    call _printf
    add esp, 8
    ; 20 : }
    mov esp, ebp
    pop ebp
    ret 0
    _main ENDP
    _TEXT ENDS
    END

    __cdecl:
    PUBLIC ?pp@@YAHHHHHH@Z ; pp
    _TEXT SEGMENT
    _i$ = 8
    _j$ = 12
    _k$ = 16
    _t1$ = -4
    _t2$ = -8
    _t3$ = -12
    ?pp@@YAHHHHHH@Z PROC NEAR ; pp
    ; 4 : {
    push ebp
    mov ebp, esp
    sub esp, 12 ; 0000000cH
    ; 5 : int t1 =2;
    mov DWORD PTR _t1$[ebp], 2
    ; 6 : int t2 = 3;

    mov DWORD PTR _t2$[ebp], 3
    ; 7 : i = j + k;
    mov eax, DWORD PTR _j$[ebp]
    add eax, DWORD PTR _k$[ebp]
    mov DWORD PTR _i$[ebp], eax
    ; 8 : int t3 = 4;
    mov DWORD PTR _t3$[ebp], 4
    ; 9 : return i;
    mov eax, DWORD PTR _i$[ebp]
    ; 10 : }
    mov esp, ebp
    pop ebp
    ret 0
    ?pp@@YAHHHHHH@Z ENDP ; pp
    _TEXT ENDS
    PUBLIC _main
    EXTRN _printf:NEAR
    _DATA SEGMENT
    $SG590 DB '%d', 00H
    _DATA ENDS
    _TEXT SEGMENT
    _i$ = -4
    _j$ = -12
    _z$ = -8
    _main PROC NEAR
    ; 13 : {
    push ebp
    mov ebp, esp
    sub esp, 12 ; 0000000cH
    ; 14 :
    ; 15 : int i;
    ; 16 : i= pp(12, 13, 14, 15, 16);
    push 16 ; 00000010H
    push 15 ; 0000000fH
    push 14 ; 0000000eH
    push 13 ; 0000000dH
    push 12 ; 0000000cH
    call ?pp@@YAHHHHHH@Z ; pp
    add esp, 20 ; 00000014H
    mov DWORD PTR _i$[ebp], eax
    ; 17 : int *j = &i;
    lea eax, DWORD PTR _i$[ebp]
    mov DWORD PTR _j$[ebp], eax

    ; 18 : int z = 3;
    mov DWORD PTR _z$[ebp], 3
    ; 19 : printf('%d', i);
    mov ecx, DWORD PTR _i$[ebp]
    push ecx
    push OFFSET FLAT:$SG590
    call _printf
    add esp, 8
    ; 20 : }
    mov esp, ebp
    pop ebp
    ret 0
    _main ENDP
    _TEXT ENDS
    END

    __fastcall:
    PUBLIC ?pp@@YIHHHHHH@Z ; pp
    _TEXT SEGMENT
    _i$ = -16
    _j$ = -20
    _k$ = 8
    _t1$ = -4
    _t2$ = -8
    _t3$ = -12
    ?pp@@YIHHHHHH@Z PROC NEAR ; pp
    ; 4 : {
    push ebp
    mov ebp, esp
    sub esp, 20 ; 00000014H
    mov DWORD PTR _j$[ebp], edx
    mov DWORD PTR _i$[ebp], ecx
    ; 5 : int t1 =2;
    mov DWORD PTR _t1$[ebp], 2
    ; 6 : int t2 = 3;
    mov DWORD PTR _t2$[ebp], 3
    ; 7 : i = j + k;
    mov eax, DWORD PTR _j$[ebp]
    add eax, DWORD PTR _k$[ebp]
    mov DWORD PTR _i$[ebp], eax
    ; 8 : int t3 = 4;
    mov DWORD PTR _t3$[ebp], 4
    ; 9 : return i;
    mov eax, DWORD PTR _i$[ebp]
    ; 10 : }
    mov esp, ebp
    pop ebp
    ret 12 ; 0000000cH
    ?pp@@YIHHHHHH@Z ENDP ; pp
    _TEXT ENDS
    PUBLIC _main
    EXTRN _printf:NEAR
    _DATA SEGMENT
    $SG590 DB '%d', 00H
    _DATA ENDS
    _TEXT SEGMENT
    _i$ = -4
    _j$ = -12
    _z$ = -8
    _main PROC NEAR
    ; 13 : {
    push ebp
    mov ebp, esp
    sub esp, 12 ; 0000000cH
    ; 14 :
    ; 15 : int i;
    ; 16 : i= pp(12, 13, 14, 15, 16);
    push 16 ; 00000010H
    push 15 ; 0000000fH
    push 14 ; 0000000eH
    mov edx, 13 ; 0000000dH
    mov ecx, 12 ; 0000000cH
    call ?pp@@YIHHHHHH@Z ; pp
    mov DWORD PTR _i$[ebp], eax

    ; 17 : int *j = &i;
    lea eax, DWORD PTR _i$[ebp]
    mov DWORD PTR _j$[ebp], eax
    ; 18 : int z = 3;
    mov DWORD PTR _z$[ebp], 3
    ; 19 : printf('%d', i);
    mov ecx, DWORD PTR _i$[ebp]
    push ecx
    push OFFSET FLAT:$SG590
    call _printf
    add esp, 8
    ; 20 : }
    mov esp, ebp
    pop ebp
    ret 0

    如果在函数中分配一个char数组,那么编译器自动将其值增加到下一个4倍边界。Getchar()函数也会引起一个4Bytes的局部变量分配,为什么呢?为了保存函数执行时的临时变量。请看下面的程序代码:
    void main()
    {
    int i, j;
    i = 12;
    j = (i < 1) ? (g_test*5) : i*12;}
    }
    PUBLIC _main
    _TEXT SEGMENT
    _i$ = -4
    _j$ = -8
    // DWORD PTR -12+[ebp], this is the temp value position.
    _main PROC NEAR
    {
    push ebp
    mov ebp, esp
    sub esp, 12 ; 0000000cH
    i = 12;
    mov DWORD PTR _i$[ebp], 12 ; 0000000cH
    j = (i < 1) ? (g_test*5) : i*12;
    cmp DWORD PTR _i$[ebp], 1
    jge SHORT $L791
    mov eax, DWORD PTR _g_test
    imul eax, 5
    mov DWORD PTR -12+[ebp], eax
    jmp SHORT $L792
    $L791:
    mov ecx, DWORD PTR _i$[ebp]
    imul ecx, 12 ; 0000000cH
    mov DWORD PTR -12+[ebp], ecx
    $L792:
    mov edx, DWORD PTR -12+[ebp]
    mov DWORD PTR _j$[ebp], edx
    : }
    mov esp, ebp
    pop ebp
    ret 0
    _main ENDP


    【理论】
    C1.DLL 667KB, Microsoft Visual C Compiler Front End
    C1XX.DLL 1179KB, Microsoft Visual C++ Compiler Front End – QFE Version
    C2.DLL 721KB, Microsoft 32-Bit 80x86 Compiler Back End
    CL.EXE 50KB, Microsoft 32-Bit C/C++ Compiler Driver

    C1.DLL
    Import Librarys:
    MSVCRT.DLL
    KERNEL32.DLL
    USER32.DLL
    MSPDB60.DLL
    Export Functions:
    _AbortCompilerPass@4
    _InvokeCompilerPass@12

    C1XX.DLL
    Import Librarys:
    MSVCRT.DLL
    KERNEL32.DLL
    USER32.DLL
    MSPDB60.DLL
    OLE32.DLL
    OLEAUT32.DLL
    Export Functions:
    _AbortCompilerPass@4
    _InvokeCompilerPass@12

    C2.DLL
    Import Librarys:
    MSPDB60.DLL
    USER32.DLL
    MSVCRT.DLL
    KERNEL32.DLL,
    MSOBJ10.DLL
    Export Functions:
    _AbortCompilerPass@4
    _InvokeCompilerPass@12

    CL.EXE:
    Import Librarys:
    USER32.DLL
    MSPDB60.DLL
    MSVCRT.DLL
    KERNEL32.DLL
    VERSION.DLL


    EMail: * 填写邮箱将发送站长回复,邮箱掩码显示于网页