Linux 32bit 程序的函数调用

原创 2015年07月09日 17:33:32

今天研究了一下Linux 32bit程序的函数调用过程,主要是从汇编指令角度去分析函数调用前后栈帧的变化。

首先解释一下什么是栈帧。

我们知道进程在执行时的地址空间包含3部分:代码段、全局数据段、堆以及栈。其中堆和栈都是动态变化的内存区,并且堆是向上生长,栈是向下生长。

那么栈有什么作用呢?一是保存函数(过程)的本地变量:如果函数的local变量都是基本类型并且数量较少,那么可以直接保存在通用寄存器里。但是总有些特殊的情况:1)变量较多,寄存器数量不够;2)存在结构型的变量;3)如果对local变量有取址的操作(&)。那么就需要把他们放在栈里。注意:对于动态分配的内存(malloc/new),数据是存放在堆区域的,但是指向数据区域的指针是放在栈里的。

此外,函数一般是需要caller传递参数过来的,既可以通过寄存器传递,也可以通过栈传递。

这样,其实栈是服务于函数 (过程)的,每一个函数所占用的栈区域就是一个栈帧。一个栈帧的范围由两个寄存器来指定:%ebp(frame pointer)和%esp(stack pointer)。%ebp在函数执行过程中是不变的;%esp是动态变化的。因此,在传递函数参数时,需要通过%ebp来定位参数的地址。

下面分析一下函数调用的过程,主要关注以下方面:

  • 参数如何传递
  • 结果如何返回
  • 栈帧如何转移
  • 栈帧如何恢复

测试分析的代码如下:

// code.c
int accum = 0;

int sum(int x, int y)
{
    int t = x + y;
    accum += t;
    return t;
}

int solve()
{
    int x = 3;
    int y = 4;
    return sum(x, y);
}

solve()函数是caller,sum()则是callee。看一下汇编之后的代码:

$ gcc -m32 -O1 -S code.c

因为是在64bit Linux上测试,因此加上-m32选项,表示编译32bit的程序。

// code.s
    .file   "code.c"
    .text
.globl sum
    .type   sum, @function
sum:
    pushl   %ebp
    movl    %esp, %ebp
    movl    12(%ebp), %eax
    addl    8(%ebp), %eax
    addl    %eax, accum
    popl    %ebp
    ret
    .size   sum, .-sum
.globl solve
    .type   solve, @function
solve:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $8, %esp
    movl    $4, 4(%esp)
    movl    $3, (%esp)
    call    sum
    leave
    ret
    .size   solve, .-solve
.globl accum
    .bss
    .align 4
    .type   accum, @object
    .size   accum, 4
accum:
    .zero   4
    .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
    .section    .note.GNU-stack,"",@progbits

上面的代码很简单,可以用来观察在进入sum()后栈帧的情况,如下图所示:

callee返回之前的栈帧

总的来说:

  1. caller负责在自己的栈帧中保存自己的ebp寄存器,并将传递参数之前的esp寄存器值放在ebp寄存器中;
  2. caller同时需要将函数参数入栈;
  3. callee需要将第1点中所述的caller保存在ebp中的esp值入栈,并从栈帧中取参数。
  4. caller执行call指令转移到callee,其中涉及到eip的保存;
  5. callee执行ret指令返回到caller,其中涉及到eip的恢复。但在这之前需要先将ebp寄存器的值pop出来;
  6. caller通过leave指令将保存在ebp中的值重新恢复到esp寄存器,从而恢复执行调用之前的现场。

需要提到的是,上面的代码实例中通过eax寄存器保存函数返回值。

版权声明:本文为博主原创文章,转载请注明出处。

64位系统由于找不到32位程序加载器而无法运行32位程序的分析过程

在http://wiki.ok-labs.com/Microkernel 下载 arm-linux-gnueabi-4.2.4.tar.gz,sdk-xscale-3.0.tar.gz,Skyey...
  • xuzhina
  • xuzhina
  • 2014年08月02日 22:40
  • 4844

linux系统调用:exit()与_exit()函数详解

exit()就是退出,传入的参数是程序退出时的状态码,0表示正常退出,其他表示非正常退出,一般都用-1或者1,标准C里有EXIT_SUCCESS和EXIT_FAILURE两个宏,用exit(EXIT_...
  • drdairen
  • drdairen
  • 2016年07月13日 13:39
  • 1949

linux下测试程序中各函数执行时间工具

时间都去哪了~,还没好好感受过你的速度呢,为什么修改了这么多次,你依然跑的这么慢呢?时间都去哪了~~ 好吧,跑题了,下面是两个转载的文章,主要介绍了两个测试程序的工具,代码改几次运行时间依然降不下来,...
  • davie1love
  • davie1love
  • 2015年07月24日 17:45
  • 1895

PowerBuilder Win32 API函数调用:参考手册 pdf

  • 2016年09月08日 09:17
  • 104.36MB
  • 下载

Linux开发系统函数调用

  • 2015年08月23日 15:50
  • 409KB
  • 下载

在Linux程序中输出函数调用栈

程序发生异常时,将函数的调用栈打印出来,可以大大提高定位效率。 Linux中提供了三个函数用来获取调用栈: /* 获取函数调用栈 */ int backtrace(void **buffe...
  • study_live
  • study_live
  • 2015年01月29日 17:54
  • 378

Linux内核情景分析之三中断和函数调用

  • 2009年11月10日 22:10
  • 137KB
  • 下载

linux启动的函数调用关系

  • 2008年10月27日 20:40
  • 11KB
  • 下载

Linux 系统函数调用大全

  • 2007年12月11日 12:49
  • 1.27MB
  • 下载

Linux+系统函数调用大全

  • 2009年09月22日 22:05
  • 1.34MB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Linux 32bit 程序的函数调用
举报原因:
原因补充:

(最多只允许输入30个字)