免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 6834 | 回复: 7
打印 上一主题 下一主题

[C] gcc中关于函数局部变量,栈地址问题 [复制链接]

论坛徽章:
1
子鼠
日期:2013-08-23 16:36:37
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-08-21 13:37 |只看该作者 |倒序浏览
本帖最后由 float001 于 2013-08-21 15:09 编辑

按照gcc中正常情况来说,局部变量紧挨着ebp向低地址排列(不考虑对齐补齐问题),如下代码:

  1. int func(int a, int b)
  2. {
  3.     int i=0, j=0, k=0;
  4.    
  5.     return 0;
  6. }
复制代码
对应的汇编代码如下:
  1.          
  2.         push   %ebp
  3.            mov    %esp,%ebp
  4.            sub    $0x10,%esp
  5.         movl   $0x0,-0x4(%ebp)    // i=0
  6.         movl   $0x0,-0x8(%ebp)    // j=0
  7.         movl   $0x0,-0xc(%ebp)    // k=0
  8.         mov    $0x0,%eax
  9.         leave  
  10.         ret   
复制代码
i的地址是 ebp-4 , j的地址是ebp-8  , k的地址是 ebp-12

但是在函数中增加printf打印 ,则i,j,k的地址减少了8字节,如下代码:

  1. int func(int a, int b)
  2. {
  3.     int i=0, j=0, k=0;

  4.     printf("123\n");
  5.    
  6.     return 0;
  7. }
复制代码
对应的汇编代码如下:

  1. push   %ebp
  2. mov    %esp,%ebp
  3. sub    $0x28,%esp
  4. movl   $0x0,-0xc(%ebp)    // i=0
  5. movl   $0x0,-0x10(%ebp)  // j=0
  6. movl   $0x0,-0x14(%ebp)  // k=0
  7. movl   $0x8048510,(%esp)
  8. call   0x8048318 <puts@plt>
  9. mov    $0x0,%eax
  10. leave  
  11. ret  
复制代码
此时i的地址是 ebp-12 , j的地址是ebp-16  , k的地址是 ebp-20

不知道是什么原因,与printf有关么 ,请各位帮忙解答一下,谢谢。


VC++6.0中没有出现这种情况,应该是gcc编译器的问题, 谁能解答一下呢??????

论坛徽章:
5
双鱼座
日期:2013-11-26 17:56:26狮子座
日期:2013-11-29 15:41:32处女座
日期:2014-02-21 11:59:07技术图书徽章
日期:2014-03-06 15:33:53技术图书徽章
日期:2014-03-06 15:39:30
2 [报告]
发表于 2013-08-21 13:59 |只看该作者
诶。。这个能把那段地址的值打印下看看是什么吗?

论坛徽章:
1
子鼠
日期:2013-08-23 16:36:37
3 [报告]
发表于 2013-08-21 14:57 |只看该作者
回复 2# bottles


    输出的 ebp - 4 = 0xB779F080
             ebp - 8 = 0x8049FF4

感觉 0x8049FF4 像是一个地址 所以找到了以下代码:
  1. Disassembly of section .got.plt:

  2. 08049ff4 <_GLOBAL_OFFSET_TABLE_>:
  3. 8049ff4:        20 9f 04 08 00 00            and    %bl,0x804(%edi)
  4. 8049ffa:        00 00                        add    %al,(%eax)
  5. 8049ffc:        00 00                        add    %al,(%eax)
  6. 8049ffe:        00 00                        add    %al,(%eax)
  7. 804a000:        26 83 04 08 36               addl   $0x36,%es:(%eax,%ecx,1)
  8. 804a005:        83 04 08 46                  addl   $0x46,(%eax,%ecx,1)
  9. 804a009:        83 04 08 56                  addl   $0x56,(%eax,%ecx,1)
  10. 804a00d:        83                           .byte 0x83
  11. 804a00e:        04 08                        add    $0x8,%al
复制代码
不知道这8个字节存放这什么东西?????

论坛徽章:
5
双鱼座
日期:2013-11-26 17:56:26狮子座
日期:2013-11-29 15:41:32处女座
日期:2014-02-21 11:59:07技术图书徽章
日期:2014-03-06 15:33:53技术图书徽章
日期:2014-03-06 15:39:30
4 [报告]
发表于 2013-08-21 15:42 |只看该作者
我用gcc编译了下,好像木有这个问题。我用gcc version 4.3.1 (GCC) 。会不会是对齐之类原因?要不把printf换成一个调用空的函数,没有参数,也没有返回值的那种试试?

论坛徽章:
17
处女座
日期:2013-08-27 09:59:352015亚冠之柏太阳神
日期:2015-07-30 10:16:402015亚冠之萨济拖拉机
日期:2015-07-29 18:58:182015年亚洲杯之巴勒斯坦
日期:2015-03-06 17:38:17摩羯座
日期:2014-12-11 21:31:34戌狗
日期:2014-07-20 20:57:32子鼠
日期:2014-05-15 16:25:21亥猪
日期:2014-02-11 17:32:05丑牛
日期:2014-01-20 15:45:51丑牛
日期:2013-10-22 11:12:56双子座
日期:2013-10-18 16:28:17白羊座
日期:2013-10-18 10:50:45
5 [报告]
发表于 2013-08-21 16:13 |只看该作者
回复 1# float001


    关健的应该是movl   $0x8048510,(%esp),为了给printf传参所以多出的空间是为printf做准备,地址变化和$0x8048510的值有关。其实不光不同的编译器有差异,不同版本的gcc生成的代码在这方面应该也有差异,深究这个可就麻烦了

论坛徽章:
1
子鼠
日期:2013-08-23 16:36:37
6 [报告]
发表于 2013-08-21 16:43 |只看该作者
本帖最后由 float001 于 2013-08-21 16:54 编辑

    这个问题很纠结,还需要查看gcc的更多的说明,刚才共64位服务器上看了下,没有8个字节的分隔(gcc 版本 4.1.2 ),如下汇编
  1. push   %rbp
  2. mov    %rsp,%rbp
  3. sub    $0x20,%rsp
  4. mov    %edi,-0x14(%rbp)
  5. mov    %esi,-0x18(%rbp)
  6. mov    %edx,-0x1c(%rbp)
  7. mov    %ecx,-0x20(%rbp)
  8. movl   $0x0,-0xc(%rbp)
  9. movl   $0x0,-0x8(%rbp)
  10. movl   $0x1,-0x4(%rbp)
  11. mov    -0x14(%rbp),%eax
  12. mov    %eax,-0xc(%rbp)
  13. mov    $0x400618,%edi
  14. callq  0x400398 <puts@plt>
  15. mov    -0xc(%rbp),%eax
  16. leaveq
  17. retq   
复制代码
但是64为的函数参数和局部变量在栈中的存储方式又不同于32位 ,追究的越深发现越来越多的东西难以理解,我得继续看书去解决这些问题了,谢谢大家的回复
出现8字节空间的gcc版本:gcc version 4.4.3

论坛徽章:
0
7 [报告]
发表于 2013-08-21 20:18 |只看该作者
栈帧的分配情况完全由编译器自主决定,甚至ebp也可能不被用于定位局部变量(gcc参考--omit-framepointer),变量是由低地址到高地址排列等等,不一而足,甚至是包含在同一次编译也存在不同之处。

论坛徽章:
0
8 [报告]
发表于 2013-08-22 11:20 |只看该作者
具体原因估计要翻文档了,不过有多分配较多的栈空间原因: GCC 坚持Linux IA-32 的一个惯例是确保每个栈帧的所有长度都是16字节的整数倍, 包括调用时的 push %ebp 和 返回值共8个字节。 调用printf的函数, esp 减去了 0x28, 加上压栈的ebp和call printf前压栈的返回地址, 总共48个字节。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP