嵌入式linux c 学习笔记7-函数调用

2019-07-13 00:46发布

/*
 * =====================================================================================
 *
 *       Filename:  diaoyong.c
 *
 *    Description:  用汇编来研究程序的调用过程
 *
 *        Version:  1.0
 *        Created:  2010年11月01日 12时32分58秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Yang Shao Kun (), cdutyangshaokun@163.com
 *        Company:  College of Information Engineering of CDUT
 *
 * =====================================================================================
 */
int bar(int c,int d)
{
    int e=c+d;
    return e;
}
int foo(int a,int b)
{
    return bar(a,b);
}
int main(void)
{
    foo(2,3);
    return 0;
}

我们反汇编的结果是这样的;
[yskcg@yskcg workc]$ gcc -g diaoyong.c -o diaoyong
[yskcg@yskcg workc]$ objdump -dS diaoyong

diaoyong:     file format elf32-i386


Disassembly of section .init:

08048274 <_init>:
 8048274:    55                       push   %ebp
 8048275:    89 e5                    mov    %esp,%ebp
 8048277:    53                       push   %ebx
 8048278:    83 ec 04                 sub    $0x4,%esp
 804827b:    e8 00 00 00 00           call   8048280 <_init+0xc>
 8048280:    5b                       pop    %ebx
 8048281:    81 c3 b4 13 00 00        add    $0x13b4,%ebx
 8048287:    8b 93 fc ff ff ff        mov    -0x4(%ebx),%edx
 804828d:    85 d2                    test   %edx,%edx
 804828f:    74 05                    je     8048296 <_init+0x22>
 8048291:    e8 1e 00 00 00           call   80482b4 <__gmon_start__@plt>
 8048296:    e8 d5 00 00 00           call   8048370
 804829b:    e8 c0 01 00 00           call   8048460 <__do_global_ctors_aux>
 80482a0:    58                       pop    %eax
 80482a1:    5b                       pop    %ebx
 80482a2:    c9                       leave 
 80482a3:    c3                       ret   

Disassembly of section .plt:

080482a4 <__gmon_start__@plt-0x10>:
 80482a4:    ff 35 38 96 04 08        pushl  0x8049638
 80482aa:    ff 25 3c 96 04 08        jmp    *0x804963c
 80482b0:    00 00                    add    %al,(%eax)
    ...

080482b4 <__gmon_start__@plt>:
 80482b4:    ff 25 40 96 04 08        jmp    *0x8049640
 80482ba:    68 00 00 00 00           push   $0x0
 80482bf:    e9 e0 ff ff ff           jmp    80482a4 <_init+0x30>

080482c4 <__libc_start_main@plt>:
 80482c4:    ff 25 44 96 04 08        jmp    *0x8049644
 80482ca:    68 08 00 00 00           push   $0x8
 80482cf:    e9 d0 ff ff ff           jmp    80482a4 <_init+0x30>

Disassembly of section .text:

080482e0 <_start>:
 80482e0:    31 ed                    xor    %ebp,%ebp
 80482e2:    5e                       pop    %esi
 80482e3:    89 e1                    mov    %esp,%ecx
 80482e5:    83 e4 f0                 and    $0xfffffff0,%esp
 80482e8:    50                       push   %eax
 80482e9:    54                       push   %esp
 80482ea:    52                       push   %edx
 80482eb:    68 f0 83 04 08           push   $0x80483f0
 80482f0:    68 00 84 04 08           push   $0x8048400
 80482f5:    51                       push   %ecx
 80482f6:    56                       push   %esi
 80482f7:    68 c5 83 04 08           push   $0x80483c5
 80482fc:    e8 c3 ff ff ff           call   80482c4 <__libc_start_main@plt>
 8048301:    f4                       hlt   
 8048302:    90                       nop
 8048303:    90                       nop
 8048304:    90                       nop
 8048305:    90                       nop
 8048306:    90                       nop
 8048307:    90                       nop
 8048308:    90                       nop
 8048309:    90                       nop
 804830a:    90                       nop
 804830b:    90                       nop
 804830c:    90                       nop
 804830d:    90                       nop
 804830e:    90                       nop
 804830f:    90                       nop

08048310 <__do_global_dtors_aux>:
 8048310:    55                       push   %ebp
 8048311:    89 e5                    mov    %esp,%ebp
 8048313:    53                       push   %ebx
 8048314:    8d 64 24 fc              lea    -0x4(%esp),%esp
 8048318:    80 3d 4c 96 04 08 00     cmpb   $0x0,0x804964c
 804831f:    75 3e                    jne    804835f <__do_global_dtors_aux+0x4f>
 8048321:    bb 60 95 04 08           mov    $0x8049560,%ebx
 8048326:    a1 50 96 04 08           mov    0x8049650,%eax
 804832b:    81 eb 5c 95 04 08        sub    $0x804955c,%ebx
 8048331:    c1 fb 02                 sar    $0x2,%ebx
 8048334:    83 eb 01                 sub    $0x1,%ebx
 8048337:    39 d8                    cmp    %ebx,%eax
 8048339:    73 1d                    jae    8048358 <__do_global_dtors_aux+0x48>
 804833b:    90                       nop
 804833c:    8d 74 26 00              lea    0x0(%esi,%eiz,1),%esi
 8048340:    83 c0 01                 add    $0x1,%eax
 8048343:    a3 50 96 04 08           mov    %eax,0x8049650
 8048348:    ff 14 85 5c 95 04 08     call   *0x804955c(,%eax,4)
 804834f:    a1 50 96 04 08           mov    0x8049650,%eax
 8048354:    39 d8                    cmp    %ebx,%eax
 8048356:    72 e8                    jb     8048340 <__do_global_dtors_aux+0x30>
 8048358:    c6 05 4c 96 04 08 01     movb   $0x1,0x804964c
 804835f:    8d 64 24 04              lea    0x4(%esp),%esp
 8048363:    5b                       pop    %ebx
 8048364:    5d                       pop    %ebp
 8048365:    c3                       ret   
 8048366:    8d 76 00                 lea    0x0(%esi),%esi
 8048369:    8d bc 27 00 00 00 00     lea    0x0(%edi,%eiz,1),%edi

08048370 :
 8048370:    55                       push   %ebp
 8048371:    89 e5                    mov    %esp,%ebp
 8048373:    8d 64 24 e8              lea    -0x18(%esp),%esp
 8048377:    a1 64 95 04 08           mov    0x8049564,%eax
 804837c:    85 c0                    test   %eax,%eax
 804837e:    74 12                    je     8048392
 8048380:    b8 00 00 00 00           mov    $0x0,%eax
 8048385:    85 c0                    test   %eax,%eax
 8048387:    74 09                    je     8048392
 8048389:    c7 04 24 64 95 04 08     movl   $0x8049564,(%esp)
 8048390:    ff d0                    call   *%eax
 8048392:    c9                       leave 
 8048393:    c3                       ret   

08048394 :
 *        Company:  College of Information Engineering of CDUT
 *
 * =====================================================================================
 */
int bar(int c,int d)
{
 8048394:    55                       push   %ebp
 8048395:    89 e5                    mov    %esp,%ebp
 8048397:    83 ec 10                 sub    $0x10,%esp
    int e=c+d;
 804839a:    8b 45 0c                 mov    0xc(%ebp),%eax
 804839d:    8b 55 08                 mov    0x8(%ebp),%edx
 80483a0:    8d 04 02                 lea    (%edx,%eax,1),%eax
 80483a3:    89 45 fc                 mov    %eax,-0x4(%ebp)
    return e;
 80483a6:    8b 45 fc                 mov    -0x4(%ebp),%eax
}
 80483a9:    c9                       leave 
 80483aa:    c3                       ret   

080483ab :
int foo(int a,int b)
{
 80483ab:    55                       push   %ebp
 80483ac:    89 e5                    mov    %esp,%ebp
 80483ae:    83 ec 08                 sub    $0x8,%esp
    return bar(a,b);
 80483b1:    8b 45 0c                 mov    0xc(%ebp),%eax
 80483b4:    89 44 24 04              mov    %eax,0x4(%esp)
 80483b8:    8b 45 08                 mov    0x8(%ebp),%eax
 80483bb:    89 04 24                 mov    %eax,(%esp)
 80483be:    e8 d1 ff ff ff           call   8048394
}
 80483c3:    c9                       leave 
 80483c4:    c3                       ret   

080483c5
:
int main(void)
{
 80483c5:    55                       push   %ebp
 80483c6:    89 e5                    mov    %esp,%ebp
 80483c8:    83 ec 08                 sub    $0x8,%esp
    foo(2,3);
 80483cb:    c7 44 24 04 03 00 00     movl   $0x3,0x4(%esp)
 80483d2:    00
 80483d3:    c7 04 24 02 00 00 00     movl   $0x2,(%esp)
 80483da:    e8 cc ff ff ff           call   80483ab
    return 0;
 80483df:    b8 00 00 00 00           mov    $0x0,%eax
}

我们来分析这段代码:foo(2,3)
    我们知道:esp寄存器是存放栈区的内存地址的
    我们看到,foo(2,3)函数的第二个参数是先执行保存的,然后在保存的是第一个参
    数,第二个参数保存在esp+4指向的内存地址-----这个是基址寻址方式。
    第一个参数保存在esp指向的内存位置。-----可见参数是从右像左依次压栈的。
    然后执行call 指令:
    它有以下两个作用:
        1,foo函数调用完之后,要返回到call的下一条指令继续执行,所以call的下
        一条指令的地址ox80483e9压栈,同时把esp的值减4,现在的值为oxbfflc418
        2,修改程序计数器eip,跳转到foo函数的开头执行。
 80483e4:    c9                       leave 
 80483e5:    c3                       ret   
 80483e6:    90                       nop
 80483e7:    90                       nop
 80483e8:    90                       nop
 80483e9:    90                       nop
 80483ea:    90                       nop
 80483eb:    90                       nop
 80483ec:    90                       nop
 80483ed:    90                       nop
 80483ee:    90                       nop
 80483ef:    90                       nop

080483f0 <__libc_csu_fini>:
 80483f0:    55                       push   %ebp
 80483f1:    89 e5                    mov    %esp,%ebp
 80483f3:    5d                       pop    %ebp
 80483f4:    c3                       ret   
 80483f5:    8d 74 26 00              lea    0x0(%esi,%eiz,1),%esi
 80483f9:    8d bc 27 00 00 00 00     lea    0x0(%edi,%eiz,1),%edi

08048400 <__libc_csu_init>:
 8048400:    55                       push   %ebp
 8048401:    89 e5                    mov    %esp,%ebp
 8048403:    57                       push   %edi
 8048404:    56                       push   %esi
 8048405:    53                       push   %ebx
 8048406:    e8 4f 00 00 00           call   804845a <__i686.get_pc_thunk.bx>
 804840b:    81 c3 29 12 00 00        add    $0x1229,%ebx
 8048411:    83 ec 1c                 sub    $0x1c,%esp
 8048414:    e8 5b fe ff ff           call   8048274 <_init>
 8048419:    8d bb 20 ff ff ff        lea    -0xe0(%ebx),%edi
 804841f:    8d 83 20 ff ff ff        lea    -0xe0(%ebx),%eax
 8048425:    29 c7                    sub    %eax,%edi
 8048427:    c1 ff 02                 sar    $0x2,%edi
 804842a:    85 ff                    test   %edi,%edi
 804842c:    74 24                    je     8048452 <__libc_csu_init+0x52>
 804842e:    31 f6                    xor    %esi,%esi
 8048430:    8b 45 10                 mov    0x10(%ebp),%eax
 8048433:    89 44 24 08              mov    %eax,0x8(%esp)
 8048437:    8b 45 0c                 mov    0xc(%ebp),%eax
 804843a:    89 44 24 04              mov    %eax,0x4(%esp)
 804843e:    8b 45 08                 mov    0x8(%ebp),%eax
 8048441:    89 04 24                 mov    %eax,(%esp)
 8048444:    ff 94 b3 20 ff ff ff     call   *-0xe0(%ebx,%esi,4)
 804844b:    83 c6 01                 add    $0x1,%esi
 804844e:    39 fe                    cmp    %edi,%esi
 8048450:    72 de                    jb     8048430 <__libc_csu_init+0x30>
 8048452:    83 c4 1c                 add    $0x1c,%esp
 8048455:    5b                       pop    %ebx
 8048456:    5e                       pop    %esi
 8048457:    5f                       pop    %edi
 8048458:    5d                       pop    %ebp
 8048459:    c3                       ret   

0804845a <__i686.get_pc_thunk.bx>:
 804845a:    8b 1c 24                 mov    (%esp),%ebx
 804845d:    c3                       ret   
 804845e:    90                       nop
 804845f:    90                       nop

08048460 <__do_global_ctors_aux>:
 8048460:    55                       push   %ebp
 8048461:    89 e5                    mov    %esp,%ebp
 8048463:    53                       push   %ebx
 8048464:    8d 64 24 fc              lea    -0x4(%esp),%esp
 8048468:    a1 54 95 04 08           mov    0x8049554,%eax
 804846d:    83 f8 ff                 cmp    $0xffffffff,%eax
 8048470:    74 12                    je     8048484 <__do_global_ctors_aux+0x24>
 8048472:    bb 54 95 04 08           mov    $0x8049554,%ebx
 8048477:    90                       nop
 8048478:    8d 5b fc                 lea    -0x4(%ebx),%ebx
 804847b:    ff d0                    call   *%eax
 804847d:    8b 03                    mov    (%ebx),%eax
 804847f:    83 f8 ff                 cmp    $0xffffffff,%eax
 8048482:    75 f4                    jne    8048478 <__do_global_ctors_aux+0x18>
 8048484:    8d 64 24 04              lea    0x4(%esp),%esp
 8048488:    5b                       pop    %ebx
 8048489:    5d                       pop    %ebp
 804848a:    c3                       ret   
 804848b:    90                       nop

Disassembly of section .fini:

0804848c <_fini>:
 804848c:    55                       push   %ebp
 804848d:    89 e5                    mov    %esp,%ebp
 804848f:    53                       push   %ebx
 8048490:    83 ec 04                 sub    $0x4,%esp
 8048493:    e8 00 00 00 00           call   8048498 <_fini+0xc>
 8048498:    5b                       pop    %ebx
 8048499:    81 c3 9c 11 00 00        add    $0x119c,%ebx
 804849f:    e8 6c fe ff ff           call   8048310 <__do_global_dtors_aux>
 80484a4:    59                       pop    %ecx
 80484a5:    5b                       pop    %ebx
 80484a6:    c9                       leave 
 80484a7:    c3                       ret   


接下来我们用gdb 调试的结果和步骤为:
[yskcg@yskcg workc]$ gdb diaoyong
GNU gdb (GDB) Fedora (7.1-34.fc13)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-redhat-linux-gnu".
For bug reporting instructions, please see:
...
Reading symbols from /home/yskcg/workc/diaoyong...done.
(gdb) start
Temporary breakpoint 1 at 0x80483cb: file diaoyong.c, line 29.
Starting program: /home/yskcg/workc/diaoyong

Temporary breakpoint 1, main () at diaoyong.c:29
29        foo(2,3);
Missing separate debuginfos, use: debuginfo-install glibc-2.12.1-4.i686
(gdb) s
foo (a=2, b=3) at diaoyong.c:25
25        return bar(a,b);
(gdb)
bar (c=2, d=3) at diaoyong.c:20
20        int e=c+d;
(gdb) disassemble
Dump of assembler code for function bar:
   0x08048394 <+0>:    push   %ebp
   0x08048395 <+1>:    mov    %esp,%ebp
   0x08048397 <+3>:    sub    $0x10,%esp
=> 0x0804839a <+6>:    mov    0xc(%ebp),%eax
   0x0804839d <+9>:    mov    0x8(%ebp),%edx
   0x080483a0 <+12>:    lea    (%edx,%eax,1),%eax
   0x080483a3 <+15>:    mov    %eax,-0x4(%ebp)
   0x080483a6 <+18>:    mov    -0x4(%ebp),%eax
   0x080483a9 <+21>:    leave /* 下面的两句是对开始的逆操作--push %ebp
                                   和mov %esp ,%ebp*/
   0x080483aa <+22>:    ret   
End of assembler dump.
(gdb) si
0x0804839d    20        int e=c+d;
(gdb)
0x080483a0    20        int e=c+d;
(gdb)
0x080483a3    20        int e=c+d;
(gdb)
21        return e;
(gdb) si
22    }
(gdb)
0x080483aa in bar (c=can't compute CFA for this frame
) at diaoyong.c:22
22    }
(gdb) bt
#0  0x080483aa in bar (c=can't compute CFA for this frame
) at diaoyong.c:22
#1  0x080483c3 in foo (a=2, b=3) at diaoyong.c:25
#2  0x080483df in main () at diaoyong.c:29
(gdb) info registers
eax            0x5    5
ecx            0x381adf3a    941285178
edx            0x2    2
ebx            0xa19ff4    10592244
esp            0xbffff2bc    0xbffff2bc
ebp            0xbffff2c8    0xbffff2c8
esi            0x0    0
edi            0x0    0
eip            0x80483aa    0x80483aa
eflags         0x200282    [ SF IF ID ]
cs             0x73    115
ss             0x7b    123
ds             0x7b    123
es             0x7b    123
fs             0x0    0
gs             0x33    51
(gdb) x/20 $esp
0xbffff2bc:    134513603    2    3    -1073745192
0xbffff2cc:    134513631    2    3    -1073745064
0xbffff2dc:    9080006    1    -1073745020    -1073745012
0xbffff2ec:    -1207962672    134513376    -1    8974276
0xbffff2fc:    134513173    1    -1073745088    8906549
(gdb)
这里我们用到的新的命令有:
disassemble:可以反汇编当前的函数。如果加上函数名,则反汇编指定的函数。
si:可以逐条指令的进行单步的测试。
info register 可以显示所有寄存器当前的值。
gdb中表示寄存器的时候要在前面加上$.


esp寄存器是存放栈区的内存地址的.
以 Push ebp 和mov %esp,%ebp这两句开始的,不用我多说大家也知道这两句的意思是以EBP代替ESP,作为访问堆栈的指针。

在gdb中可以用bt命令和fram命令查看每层栈桢上的参数和局部变量.




 EBP、ESP、BP和SP都称为指针寄存器,主要用于存放堆栈内存储单元的偏移量,用它们可实现多种存储器操作数的寻址方式。
    现在我们来说一说EBP:
       EBP是基址指针寄存器:一般用来确认堆栈帧的起始位置,也就是指向栈底。也就是说,一般一个函数入口的地址也就存放在EBP中(所以一般在进入函数的时候将ebp寄存器内容压栈,即保存其函数的上级调用函数的栈基地址,以便于以后返回调用。

       在每个函数的栈桢中,ebp指向栈底,而esp指向栈顶,在函数的执行过程中esp
会随着压栈和出栈操作随时变化,而ebp的是不变的,函数的参数和局部变量都是通过e
bp的值加上一个偏移量来访问。


现在知道main函数是如何把里面的函数调用起来的了:
        首先,要有一个ebp寄存器,因为我们的ebp寄存器一般(人为习惯哈!并不是
        是规定的,你可以用任何一个通用寄存器,除了一些特殊的指令)用来存放函
        数的基地址,我们通过基地址可以来查找我们调用函数的位置,因为函数的执
        行时,会产生一个栈,用来保存函数执行过程中的参数和变量,同时我们知道
        栈是内存的一种申请方式之一,同时,栈的大小是有限的,一般用来存放局部
        变量和参数,像全局变量和static之类的内存申请是--向高地址扩增,可以
        非常的大,那也就是说,我们栈的大小是有限的,那么栈的扩展方向是向低地
        址,因为,大小确定了嘛!
        其次,因为,我们的ebp寄存器存放的是基地址,那么我们把这个值压栈,既
        然说是基地址,那么,我们的ebp寄存器就是指向栈的底部了萨,之前,知道
        栈是向低地址扩展的嘛!我们把ebp的值压栈之后,但我们调用函数的时候--
        假设我们当前是在main函数中哈!就本例来说,程序接着会调用函数foo函数
        那么我们,在调用foo函数的时候,会执行push %ebp---和--mov %esp,%ebp
        语句,那么,什么又是esp呢?其实,esp也是一个通用寄存器,其实其作用
        相当与一个指针的,esp是指向栈的栈顶,我们也可以通过esp来找到函数的参
        数和元素。(先入栈的a1,a2,a3,a4,a5...an)----那么a1的位置是栈顶元素
        ,an是栈底元素。那我们执行相应的汇编语句的作用是什么呢?push %ebp,
        是把前面一个函数的栈底地址压栈,然后,把现在调用的函数的esp寄存器中
        的值赋值给ebp,也即是,我们把前面的一个函数的ebp的值(地址),存放在当
        前调用函数的ebp中,那么当前的ebp的地址在执行mov %esp,%ebp 后,就把es
        p寄存器中的值赋值给了,ebp,esp当然是上一个被执行函数的值了萨,内存
        是连续的嘛!那么,这样,就把里面的函数调用起来了。


        反过来,我们通过最后调用的函数,中的ebp的值,我们知道,上一个函数的
        ebp的值,直到我们找到main函数的ebp的值。

    因此,各层函数栈桢通过保存在栈上的ebp的值串起来了。
   
在调用函数结束后,有两个指令:
    leave
    ret
其中:leave 是push %ebp ---和----mov %esp,%ebp  的逆操作
      ret   是call 指令的逆操作。
       
函数调用和返回过程中的规则:
  1:参数压栈传递,并且是从右向左依次压栈。
  2:ebp 总是指向当前栈桢的栈底
  3:返回值通过eax寄存器进行传递。