x86架构与x64架构在函数于栈中调用过程的不同之处

x86架构与x64架构

1、x86架构
x86架构是intel开发的一种32位的指令集。
8个32位通用寄存器eax,ebx,ecx,edx,ebp,esp,esi,edi

2、x64架构
x64架构是64位的,有16个通用寄存器rax,rbx,rcx,rdx,rsi,rdi,rsp,rbp,r8,r9,r10,r11,r12,r13,r14,r15

程序

1、代码

2、编译链接

1
2
3
4
5
32位:
sudo gcc stack.c -o stack -m32

64位:
sudo gcc stack.c -o stack1

x86架构下函数于栈中调用过程

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
0804840b <Add>:
804840b: 55 push ebp //将ebp压入栈
804840c: 89 e5 mov ebp,esp //将esp压入栈
804840e: 83 ec 08 sub esp,0x8 //抬高栈帧
8048411: 8b 55 08 mov edx,DWORD PTR [ebp+0x8] //将[ebp+0x8]压入edx
8048414: 8b 45 0c mov eax,DWORD PTR [ebp+0xc] //将[ebp+0xc]压入eax
8048417: 01 d0 add eax,edx //将eax与edx中的内容相加赋给eax
8048419: 83 ec 08 sub esp,0x8 //抬高栈帧
804841c: 50 push eax //将eax压入栈
804841d: 68 e0 84 04 08 push 0x80484e0 //压入x+y=
8048422: e8 b9 fe ff ff call 80482e0 <printf@plt> //调用printf函数
8048427: 83 c4 10 add esp,0x10 //调整栈帧
804842a: 90 nop
804842b: c9 leave
804842c: c3 ret

0804842d <main>:
804842d: 8d 4c 24 04 lea ecx,[esp+0x4]
8048431: 83 e4 f0 and esp,0xfffffff0
8048434: ff 71 fc push DWORD PTR [ecx-0x4]

8048437: 55 push ebp //将ebp压入栈
8048438: 89 e5 mov ebp,esp //将esp压入栈
804843a: 51 push ecx //将ecx压入栈
804843b: 83 ec 04 sub esp,0x4 //抬高栈帧,esp=esp+0x4
804843e: 83 ec 08 sub esp,0x8 //抬高栈帧
8048441: 6a 14 push 0x14 //压入参数20
8048443: 6a 0a push 0xa //压入参数10
8048445: e8 c1 ff ff ff call 804840b <Add> //调用Add函数
804844a: 83 c4 10 add esp,0x10 //调整栈帧
804844d: b8 00 00 00 00 mov eax,0x0 //清空eax
8048452: 8b 4d fc mov ecx,DWORD PTR [ebp-0x4] //将[ebp-0x4]内的数据赋给ecx
8048455: c9 leave //mov esp,ebp;pop ebp
8048456: 8d 61 fc lea esp,[ecx-0x4] //取[ecx-0x4]中的数据赋给esp
8048459: c3 ret //pop eip

804845a: 66 90 xchg ax,ax
804845c: 66 90 xchg ax,ax
804845e: 66 90 xchg ax,ax

x64架构下函数于栈中调用过程

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
0000000000400526 <Add>:
400526: 55 push rbp //将rbp压入栈中
400527: 48 89 e5 mov rbp,rsp //将rsp压入栈中
40052a: 48 83 ec 10 sub rsp,0x10 //抬高栈帧
40052e: 89 7d fc mov DWORD PTR [rbp-0x4],edi //将edi的值赋给[rbp-0x4]
400531: 89 75 f8 mov DWORD PTR [rbp-0x8],esi //将rsi的值赋给[rbp-0x10]
400534: 8b 55 fc mov edx,DWORD PTR [rbp-0x4] //将[rbp-0x4]的值赋给edx
400537: 8b 45 f8 mov eax,DWORD PTR [rbp-0x8] //将[rbp-0x8]的值赋给eax
40053a: 01 d0 add eax,edx //eax=eax+edx
40053c: 89 c6 mov esi,eax //将eax的值赋给esi
40053e: bf 04 06 40 00 mov edi,0x400604 //将0x400604对应的数据赋给edi
400543: b8 00 00 00 00 mov eax,0x0 //清空eax
400548: e8 b3 fe ff ff call 400400 <printf@plt> //调用printf函数
40054d: 90 nop
40054e: c9 leave
40054f: c3 ret

0000000000400550 <main>:
400550: 55 push rbp //将rbp压入栈中
400551: 48 89 e5 mov rbp,rsp //将rsp压入栈中
400554: 48 83 ec 10 sub rsp,0x10 //抬高栈帧
400558: 89 7d fc mov DWORD PTR [rbp-0x4],edi //将edi的值赋给[rbp-0x4]
40055b: 48 89 75 f0 mov QWORD PTR [rbp-0x10],rsi //将rsi的值赋给[rbp-0x10]
40055f: be 14 00 00 00 mov esi,0x14 //将参数20赋给esi
400564: bf 0a 00 00 00 mov edi,0xa //将参数10赋给edi
400569: e8 b8 ff ff ff call 400526 <Add> //调用Add函数
40056e: b8 00 00 00 00 mov eax,0x0 //清空eax
400573: c9 leave //mov esp,ebp;pop ebp
400574: c3 ret //pop eip
400575: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0]
40057c: 00 00 00
40057f: 90 nop

总结

x86架构与x64架构在函数于栈中调用过程的不同之处在于:
x86架构下,函数的参数是直接存放到栈帧中的;
而x64架构下,函数的参数是通过寄存器传参进入栈帧的。(当寄存器不够用,才会将参数直接存入栈帧)

下期预告

函数调用约定