回 帖 发 新 帖 刷新版面

主题:[讨论]C语言跟汇编,指点下新手

最近学了下汇编,然后老拿VC的disassembly看目标码,感觉像看到了C到底是怎样做的,蛮爽的感觉

打开VC
写个简单的程序:
int f(int a,int b)
{
    int c=1;
    return a+b/c;
}
int main(void)
{
    int a=1;
    f(a,100);
    return 0;
}
编译,连接,然后进入debug,然后点disassembly看目标代码:

6:    int main(void)
7:    {
00401060   push        ebp
00401061   mov         ebp,esp //从这句知道ebp堆栈段基址是相对的
00401063   sub         esp,44h //为什么开这么大的存储?
00401066   push        ebx //为什么要保护ebx?这个寄存器有啥用?
00401067   push        esi //同上
00401068   push        edi //同上
00401069   lea         edi,[ebp-44h]
0040106C   mov         ecx,11h
00401071   mov         eax,0CCCCCCCCh //而且还全部初始化成这个值,WHY?
00401076   rep stos    dword ptr [edi]
8:        int a=1;
00401078   mov         dword ptr [ebp-4],1
9:        f(a,100);
0040107F   push        64h
00401081   mov         eax,dword ptr [ebp-4]
00401084   push        eax //从这里看出是从右往左填参数
00401085   call        @ILT+0(f) (00401005)
0040108A   add         esp,8
10:       return 0;
0040108D   xor         eax,eax //从这句看,函数返回值放在eax,固定的吗?
11:   }
0040108F   pop         edi
00401090   pop         esi
00401091   pop         ebx
00401092   add         esp,44h
00401095   cmp         ebp,esp
00401097   call        __chkesp (004010b0)//这一句啥意思?
0040109C   mov         esp,ebp
0040109E   pop         ebp
0040109F   ret

我对要保护的寄存器不太明白,感觉高级语言面向的是存储器,就算是register变量也只是推荐性,寄存器在程序员眼中是透明的,那所有的语句都是寄存器独立的,按理可以不用保护,除非有特殊用途.

回复列表 (共9个回复)

沙发

我想是和编译器有关的,如下
[code]
        .file   "test.c"
        .text
        .p2align 2,,3
.globl f
        .type   f, @function
f:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        movl    $1, -4(%ebp)
        movl    12(%ebp), %edx
        leal    -4(%ebp), %eax
        movl    %eax, -8(%ebp)
        movl    %edx, %eax
        movl    -8(%ebp), %ecx
        cltd
        idivl   (%ecx)
        movl    %eax, -8(%ebp)
        movl    -8(%ebp), %eax
        addl    8(%ebp), %eax
        leave
        ret
        .size   f, .-f
        .p2align 2,,3
.globl main
        .type   main, @function
main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        andl    $-16, %esp
        movl    $0, %eax
        addl    $15, %eax
        addl    $15, %eax
        shrl    $4, %eax
        sall    $4, %eax
        subl    %eax, %esp
        movl    $1, -4(%ebp)
        pushl   $100
        pushl   -4(%ebp)
        call    f
        addl    $8, %esp
        movl    $0, %eax
        leave
        ret       
[/code]

板凳

楼上这是哪种汇编呀,我学的是80x86的,不过还是看得大部分懂~~:)

3 楼

*^_^*,,我也是80x86的,这是AT&T的语法,我是用楼主的C程序,然后用gcc生成的汇编码的

4 楼

main开始处多开的40h内容确实是编译器相关的,可能目的是为了方便发生堆栈访问错误时的调试吧,这一段东西似乎在release版本下就没有了?

main函数也是一个普通的函数,在函数中用到的寄存器在使用前都必须把它们的值保存起来,这是汇编写子函数的一个基本要求;

用eax存放函数返回值似乎是个通用的约定,至少所有windows API都遵从这一点;

call __chkesp就是一个普通的函数调用,作用是检查esp的值,可以试着跟踪进去的。

5 楼

像eax使用的时候就不用保护起来呀,我觉得需要保护的不仅是要用的,而是调用它之前没有正在用的,而C++是高级语言,没有面向寄存器,任何一句高级语言的表达式都是面向存储器的,在调用子函数的时候就没有哪个寄存器在使用啊,除非有特别的功能,我这样想。是吗?

6 楼

建议用DASM反汇编一个C语言(不是C++)写的最简单的程序:
main()
{}
看看。
反汇编的结果肯定比你上面的详细。

7 楼

6楼什么意思?

用VC,disassembly看的不是目标程序吗?如果说反汇编得更详细,应该是更具体吧,细心的人会发现VC反汇编的左边的地址对应每一句指令并不是等长的,有的指令可能比较‘长’,有的‘短’些。

我想知道的重点是,为什么要保护那三个寄存器?

8 楼

你可以这么理解:C语言是帮助你开发汇编的工具,你用C写了一个函数,C编译器帮助生成对应的汇编代码,根本的还是产生一个汇编形式的函数。

在函数实际执行的过程中,始终需要使用寄存器的。并不是说用C开发的程序执行的时候就不用寄存器,只是在C代码的层面看不到也不需要关心寄存器的问题而已。

至于eax,约定了用它来保存返回值,即使需要保护它的值,也是调用者在调函数前做的事,而不是函数内部要做的。

9 楼

你看一下WIN32汇编,他们在调用的过程的时候一般都是保存EBX,ESI,EDI的,EAX一般是用来返回参数。
从系统的角度来看,MAIN也是系统的一个子过程。
更具体的话就要涉及操作系统的中断系统了,这个太复杂了。

我来回复

您尚未登录,请登录后再回复。点此登录或注册