分类目录

展开|收起

看你喜欢

(1) (1) (42) (1) (1) (1) (16) (2) (1) (1) (4) (1) (2) (7) (4) (1) (1) (1) (1) (3) (1) (5) (1) (1) (1) (1) (1) (2) (1) (4) (4) (3) (1) (1) (2) (1) (37) (2) (1) (5) (3) (1) (4) (1) (1) (11) (3) (1) (9) (3) (1) (23) (2) (1) (2) (1) (1) (1) (1)

最新精华

函数调用汇编分析【2】- 函数调用规范(1)

分页阅读: 1 2 3

接着上一篇文章继续分析。

2.1 相关知识介绍

对于编译连接好的程序,其镜像一般是被加载到内存中来执行,如下图所示:

镜像

注:

(1)BSS是Block Started by Symbol Segment的简称,这个段用来存放没有初始化过的全局或static变量,一般操作系统会在加载程序时把这个段清0,所以有时也叫0初始化区(ZI)。需要说明的是这个段并不在编译出来的版本文件中占用空间,而只是在执行时才会存在这个段,用来放相应的变量内容。编译器在链接时会提供相应的宏来表示这个段的起始和长度,这样在代码中就可以利用这些宏来做清0操作。所以,对一个全局的数组,如果在定义时不进行初始化,而是在进程上电时才初始化,就可以减小版本文件的大小。

(2)从上图可见,堆和栈其实不是一个东西,平时我们常说的“堆栈”,其实指的是栈。

(3)本文关注的是栈区,因为对于x86体系,函数的入参和函数内部的非static类型的局部变量都要放在栈中,函数返回值长度如果超过8字节也要放到栈中;如果返回值为4字节,则通过EAX寄存器返回;如果为8字节,则通过EDX和EAX这两个寄存器返回,其中EDX存放高4字节。

函数调用时参数或返回值到底是通过栈还是寄存器返回是处理器类型密切相关的,一般每种处理器都会提供ABI(Application Binary Interface应用二进制接口),其中会规定寄存器的使用规则(这里的函数调用参数传递规则是其中之一),各种编译器和操作系统都需要根据这个规范来编写,只要遵守这个规范,不同的编译器或操作系统就可以在同一种类型的处理器上运行。

具体到函数调用规范,或者说过程调用规范(PCS,Procedure Call Standard),各种CPU是不同的。由于X86是复杂指令集CISC),其指令多而通用寄存器比较少(EAX、EBX、ECX、EDX、ESI和EDI),所以把函数参数主要是放在栈中来传递。而像ARM这样的精简指令集处理器(RISC)正好相反,其指令少而通用其存器多(R0-R15),所以如果参数不多,就可以放到寄存器中来传递,具体参见ATPCS(ARM-THUMB procedure call standard ARM-THUMB过程调用规范)。

回到X86体系的函数调用,看一个C例子:

int add(int c , int d)
{
    int e;

    e = c+d;
    return e;
}

void main()
{
    int a = 1;
    int b = 2;
    int c = 0;

    c = add(a,b);  // 调用函数add
    a = b;         // 返回地址
}
  打分:5.0/5 (共1人投票)
(浏览总计: 178 次)

分页阅读: 1 2 3

Add Comment Register



发表回复

  

  

  

您可以使用这些HTML标签

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>