Linux32和Linux64下进程栈分析

x86 和 x86-64 系统采用不同的方式来对函数参数进行传递。x86通过栈进行参数的传递,而x86-64通过寄存器进行参数的传递。
用一段简单的代码来进行分析:

using namespace std;
int add(int a, int b){
  return a + b;
}
int main(int argc, char const *argv[])
{
  int a, b;
  a = 3; 
  b = 4;
  add(3, 4);

  return 0;
}

x86-64

首先分析 x86-64 下函数传参的过程。

g++ -g test.cpp -o test

-g选项表示生成带debug信息的可执行文件。
然后用 file查看一下生成的test文件。

~/Desktop/stack> file test
test: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, 
BuildID[sha1]=f734d178caff1fb2bc93c301d110607da34b1336, for GNU/Linux 3.2.0, not stripped

再来看一下汇编代码:

objdump -d .text test

这里只截取了main函数和add函数的汇编。

0000000000001125 <_Z3addii>:
    1125:	55                   	push   %rbp
    1126:	48 89 e5             	mov    %rsp,%rbp
    1129:	89 7d fc             	mov    %edi,-0x4(%rbp)
    112c:	89 75 f8             	mov    %esi,-0x8(%rbp)
    112f:	8b 55 fc             	mov    -0x4(%rbp),%edx
    1132:	8b 45 f8             	mov    -0x8(%rbp),%eax
    1135:	01 d0                	add    %edx,%eax
    1137:	5d                   	pop    %rbp
    1138:	c3                   	retq   

0000000000001139 <main>:
    1139:	55                   	push   %rbp
    113a:	48 89 e5             	mov    %rsp,%rbp
    113d:	48 83 ec 20          	sub    $0x20,%rsp
    1141:	89 7d ec             	mov    %edi,-0x14(%rbp)
    1144:	48 89 75 e0          	mov    %rsi,-0x20(%rbp)
    1148:	c7 45 f8 03 00 00 00 	movl   $0x3,-0x8(%rbp)
    114f:	c7 45 fc 04 00 00 00 	movl   $0x4,-0x4(%rbp)
    1156:	be 04 00 00 00       	mov    $0x4,%esi
    115b:	bf 03 00 00 00       	mov    $0x3,%edi
    1160:	e8 c0 ff ff ff       	callq  1125 <_Z3addii>
    1165:	b8 00 00 00 00       	mov    $0x0,%eax
    116a:	c9                   	leaveq 
    116b:	c3                   	retq   
    116c:	0f 1f 40 00          	nopl   0x0(%rax)

函数汇编代码分析:

  • main函数部分:

edirsi两个寄存器里现有的值进行压栈保存,因为接下来这两个寄存器要用来传递函数参数。

    1141:	89 7d ec             	mov    %edi,-0x14(%rbp)
    1144:	48 89 75 e0          	mov    %rsi,-0x20(%rbp)

将局部类变量a,b压栈。

    1148:	c7 45 f8 03 00 00 00 	movl   $0x3,-0x8(%rbp)
    114f:	c7 45 fc 04 00 00 00 	movl   $0x4,-0x4(%rbp)

将函数参数放入寄存器,实现函数参数的传递。

    1156:	be 04 00 00 00       	mov    $0x4,%esi
    115b:	bf 03 00 00 00       	mov    $0x3,%edi

执行到这里add函数的两个参数都已经放到edi, esi 里了。那么怎么将这两个参数读取出来呢?这一部分工作由add函数完成。

  • add函数部分:

参数的读取就是通过这两行汇编实现的,将参数读出,然后压栈。

    1129:	89 7d fc             	mov    %edi,-0x4(%rbp)
    112c:	89 75 f8             	mov    %esi,-0x8(%rbp)

重点:x86-64的参数传递方式是通过寄存器传递,这点和x86还是很不一样的,下文会讲到。
当执行到:

 return a + b;

进程的栈布局如下:
image1

x86

同理,先来生成一下32位的可执行文件,然后用file查看一下。

g++ -g -m32 test.cpp -o test32
  • 如果这里执行报错,那是系统还没有安装多版本编译模式的缘故,安装如下插件即可。
sudo apt-get install gcc-multilib g++-multilib
~/Desktop/stack> file test32
test32: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux.so.2, 
BuildID[sha1]=f734d178caff1fb2bc93c301d110607da34b744b, for GNU/Linux 3.2.0, not stripped

然后来分析一下汇编代码:

00001189 <_Z3addii>:
    1189:	55                   	push   %ebp
    118a:	89 e5                	mov    %esp,%ebp
    118c:	e8 40 00 00 00       	call   11d1 <__x86.get_pc_thunk.ax>
    1191:	05 6f 2e 00 00       	add    $0x2e6f,%eax
    1196:	8b 55 08             	mov    0x8(%ebp),%edx
    1199:	8b 45 0c             	mov    0xc(%ebp),%eax
    119c:	01 d0                	add    %edx,%eax
    119e:	5d                   	pop    %ebp
    119f:	c3                   	ret    

000011a0 <main>:
    11a0:	55                   	push   %ebp
    11a1:	89 e5                	mov    %esp,%ebp
    11a3:	83 ec 10             	sub    $0x10,%esp
    11a6:	e8 26 00 00 00       	call   11d1 <__x86.get_pc_thunk.ax>
    11ab:	05 55 2e 00 00       	add    $0x2e55,%eax
    11b0:	c7 45 fc 03 00 00 00 	movl   $0x3,-0x4(%ebp)
    11b7:	c7 45 f8 04 00 00 00 	movl   $0x4,-0x8(%ebp)
    11be:	6a 04                	push   $0x4
    11c0:	6a 03                	push   $0x3
    11c2:	e8 c2 ff ff ff       	call   1189 <_Z3addii>
    11c7:	83 c4 08             	add    $0x8,%esp
    11ca:	b8 00 00 00 00       	mov    $0x0,%eax
    11cf:	c9                   	leave  
    11d0:	c3                   	ret 

可以看到,x86下函数参数的传递是通过push入栈来实现的。

    11be:	6a 04                	push   $0x4
    11c0:	6a 03                	push   $0x3

在add函数中,通过向高地址读取(即读取当前栈帧的前栈帧的内容)实现参数的读取。

    1196:	8b 55 08             	mov    0x8(%ebp),%edx
    1199:	8b 45 0c             	mov    0xc(%ebp),%eax
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值