函数调用原理–局部变量篇

1、参考链接
1.1、函数调用原理–参数篇

2、函数需要解决的三大问题:参数、返回值、局部变量
2.1、参数在函数执行前压栈,执行结束后恢复内平栈或外平栈的方式恢复栈顶位置释放空间;
2.2、返回值一般直接存储在寄存器ax中,函数运行结束后在ax中读取
2.3、函数内部的局部变量也是通过栈来存储

3、函数局部变量面临的几个问题
3.1、专用空间,不被其他数据覆盖;
3.2、对栈进行操作,结束后需要对栈进行恢复操作;
3.3、临时空间使用结束后及时释放

4、基本实现思路
4.1、保存栈顶位置(sp);
4.2、将栈顶位置往前移动一段位置做为局部变量专有空间;
4.3、通过原栈顶+偏移位置(bp寄存器)来访问专用的临时空间;
4.4、结束后恢复原来栈顶位置;
4.5、考虑到函数的嵌套调用,保护和恢复bp同样重要!

5.0、汇编代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
assume cs:code, ds:data, ss:stack
 
stack segment
    db 20h dup(1)
stack ends
 
data segment
    db 20h dup(2)
data ends
 
code segment
start:
    ;初始化
    mov ax, stack
    mov ss, ax
    mov sp, 20h
    mov ax, data
    mov ds, ax  
    ;业务代码(函数局部变量实现) 
    push 3344h
    push 4455h
    call sum;计算结果保存在ax中
    add sp, 4
    ;退出             
    mov ah, 4ch
    int 21h
sum:
    push bp;保护bp
    mov bp, sp;保护sp
    sub sp, 10h;空出10个字节给局部变量使用
    ;----业务代码------start   
    ;取参数并计算,由于push bp占用了2个字节,参数从bp+4开始
    mov ax, [bp+6]
    add ax, [bp+4]
    mov [bp-2], 2324h;局部变量1
    mov [bp-4], 3435h;局部变量2  
    ;----业务代码-------end
    mov sp, bp;恢复sp
    pop bp;恢复bp
    ret 
code ends
end start

6、运行结果:

7、C++源代码反汇编结果
7.1、C++源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// VCTest.cpp : Defines the entry point for the console application.
//
 
#include "stdafx.h"
 
int sum(int a, int b)
{
	int c = 20;
	int d = 30;
	return a + b;
}
int main(int argc, char* argv[])
{
	sum(2, 3);
	return 0;
}

7.2、函数部分反汇编

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
6:    int sum(int a, int b)
7:    {
0040D7E0   push        ebp;保护bp
0040D7E1   mov         ebp,esp;保护sp
0040D7E3   sub         esp,48h;移动栈顶位置,预留局部变量专用空间
0040D7E6   push        ebx
0040D7E7   push        esi
0040D7E8   push        edi
0040D7E9   lea         edi,[ebp-48h]
0040D7EC   mov         ecx,12h
0040D7F1   mov         eax,0CCCCCCCCh
0040D7F6   rep stos    dword ptr [edi]
8:        int c = 20;
0040D7F8   mov         dword ptr [ebp-4],14h;通过bp段偏移地址访问专用空间,跳过bp从bp-4开始
9:        int d = 30;
0040D7FF   mov         dword ptr [ebp-8],1Eh;第二个局部变量递增bp-8
10:       return a + b;
0040D806   mov         eax,dword ptr [ebp+8]
0040D809   add         eax,dword ptr [ebp+0Ch];函数返回值保存在ax寄存器中
11:   }
0040D80C   pop         edi
0040D80D   pop         esi
0040D80E   pop         ebx
0040D80F   mov         esp,ebp;恢复sp
0040D811   pop         ebp;恢复bp
0040D812   ret;外平栈

8、汇编Demo中实现与Visual C++反汇编得到的结果基本一致!

Leave a Reply