c++栈调用的汇编研究

<!-- @page { margin: 0.79in } P { margin-bottom: 0.08in } -->

先写一段程序,用的编辑环境为 Netbeans 6.9 gcc gdb

int main(int argc, char** argv) {

int i=1;

i++;

return (EXIT_SUCCESS);

}

 

编译为汇编指令为

 

!int main(int argc, char** argv) {

main+0: push %ebp

main+1: mov %esp,%ebp

main+3: sub $0x10,%esp

! int i=1;

main()

main+6: movl $0x1,-0x4(%ebp)

! i++;

main+13: addl $0x1,-0x4(%ebp)

! return (EXIT_SUCCESS);

main+17: mov $0x0,%eax

!}

main+22: leave

main+23: ret

 

!应该为注释,标记正在进行到代码的哪一个步骤

 

main+0: push %ebp

ebp是基址指针寄存器。这里应该是说将原来的 ebp压入栈,这里的 ebp应该是作为一个栈的基地址而从栈里面取地址就要用 ebp做基准,所以这里存入上一级栈的 ebp

 

看: main+1: mov %esp,%ebp

这里把 esp的值传给 ebp,也就是说 ebp现在可以当作目前这一级栈的基地址,而因为 esp经常改变,所以要用 ebp来做基址。

 

main+3: sub $0x10,%esp

这里因为有一个局部变量 i,所以要把 esp向上调,不知道 esp是不是就是指栈顶的意思?这里貌似还要 RoundUp变到 16 的倍数,所以总是 0xn0。如果有 5个局部变量,就会移动 x020。(这里还是有问题,见下一个例子)

 

main()

 

main+6: movl $0x1,-0x4(%ebp)

这里可以看到将 1付给 i。这里用到了偏址 4,得到 i的指,这里很奇怪为什么 i在那个位置。注意这里只有当运行到这一个语句的时候才会将 i的值压栈,而 esp会预留全部的局部变量的空间。

 

main+13: addl $0x1,-0x4(%ebp)

这里将 i的值加上 1

 

main+17: mov $0x0,%eax

这里将 0付给 eax寄存器,可见这里 eax是存储返回值的寄存器。

 

看: main+22: leave

main+23: ret

这里应该是函数返回。

 

如果有一个语句为 j=i+j

则对应于汇编为

main+24: mov -0x4(%ebp),%eax// i的值付给 eax

main+27: add %eax,-0x8(%ebp) // eax付给 j。可见这里 eax也是可以作为临时寄存器。

 

 

继续编一段程序:

int add(int i,int p){

int j=i+p;

return j;

}

int main(int argc, char** argv) {

int i=1;

 

i=add(i,8);

 

return (i);

}

 

 

main+0: push %ebp

main+1: mov %esp,%ebp

main+3: sub $0x18,%esp

! int i=1;

main+6: movl $0x1,-0x4(%ebp)

!

! i=add(i,8);

main+13: movl $0x8,0x4(%esp)

main+21: mov -0x4(%ebp),%eax

main+24: mov %eax,(%esp)

main+27: call 0x80483b4 <add>

main()

main+32: mov %eax,-0x4(%ebp)

!

! return (i);

main+35: mov -0x4(%ebp),%eax

!}

main+38: leave

main+39: ret

 

!int add(int i,int p){

add()

add+0: push %ebp

add+1: mov %esp,%ebp

add+3: sub $0x10,%esp

! int j=i+p;

add+6: mov 0xc(%ebp),%eax

add+9: mov 0x8(%ebp),%edx

add+12: lea (%edx,%eax,1),%eax

add+15: mov %eax,-0x4(%ebp)

! return j;

add+18: mov -0x4(%ebp),%eax

!}

add+21: leave

add+22: ret

 

main+13: movl $0x8,0x4(%esp)

main+21: mov -0x4(%ebp),%eax

main+24: mov %eax,(%esp)

这里其实就是把参数压栈,把 8 i压进去。

 

main+27: call 0x80483b4 <add>

这里调用 add函数。

add+0: push %ebp

add+1: mov %esp,%ebp

add+3: sub $0x10,%esp

还是和 main函数开头一样的操作。存储旧的 ebp值压栈。这里刚开头的时候其实 esp的值已经变掉了从 0x560变到了 0x558,这里应该是 call指令完成的。

 

add+6: mov 0xc(%ebp),%eax

add+9: mov 0x8(%ebp),%edx

add+12: lea (%edx,%eax,1),%eax

add+15: mov %eax,-0x4(%ebp)

这里计算那个表达式的值。

最后返回。

 

这里我把栈的图画一下。

 

esp 0x548

 

 

0xbf899554 j

ebp 0xbf899558 0xbf899578(上一级的 ebp )

0xbf89955c 0x8048431(这里己有可能是调用着执行 call时的下一条语句)

esp 0xbf899560 i

0xbf899564 8

 

 

 

0xbf899574 i

esp 0xbf899578 上一级的 ebp

 

但是如果我把 add函数写成这样

int add(int i,int p){

int j=1;

printf("i:%p\n",&i);

printf("p:%p\n",&p);

printf("j:%p\n",&j);

return j;

}

 

结果是:

i:0xbffff550

p:0xbffff554

j:0xbffff53c

 

发现局部变量和函数参数之间有四个空即 16个字节。发现其实 j并不是压在 ebp上一格的,而是 (ebp+12),这个很诡异。所以会出现在这种情况,我原来还以为是局部变量与参数间空 16个字节放各种 pointer以及返回值这些东西呢,这个实在是太诡异了。

 

宗其所述:正常情况下,局部变量与参数间空了 8个字节(linux下)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值