免费注册 查看新帖 |

Chinaunix

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

[C] 关于main函数的在调用其他函数前栈帧的困惑 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-06-08 15:52 |只看该作者 |倒序浏览
我写了一个函数用来理解函数调用栈帧的变化
有几个地方不理解,请大家帮忙分析下:
例子如下
#include<stdio.h>
static void swap(int *a,int *b);

int main(int argc,char *argv[])
{
        int a,b;

        a = 16;
        b = 8;
        swap( &a, &b);
        printf("a=%d,b=%d\n",a,b);
        return (a - b);
}

static void swap(int *a,int *b)
{
        int c;

        c = *a;
        *a = *b;
        *b = c;

}
生成的AT&T汇编
如下(部分)
main:                                                                        
  .LFB0:                                                                       
          .cfi_startproc                                                      
          pushl   %ebp                                                         
          .cfi_def_cfa_offset 8                                                
          .cfi_offset 5, -8                                                   
          movl    %esp, %ebp                                                   
          .cfi_def_cfa_register 5                                             
          andl    $-16, %esp                                                   
          subl    $32, %esp                                                   
          movl    $16, 24(%esp)                                                
          movl    $8, 28(%esp)                                                
          leal    28(%esp), %eax
          movl    %eax, 4(%esp)                                                
          leal    24(%esp), %eax
          movl    %eax, (%esp)                                                
          call    swap                                                         
          movl    24(%esp), %edx
          movl    28(%esp), %eax                                               
          movl    %edx, %ecx
          subl    %eax, %ecx                                                   
          movl    %ecx, %eax                                                   
          leave   
          .cfi_restore 5                                                      
          .cfi_def_cfa 4, 4                                                   
          ret
          .cfi_endproc  
这里让我迷糊的是
  pushl   %ebp                                                         
          .cfi_def_cfa_offset 8                                                
          .cfi_offset 5, -8                                                   
          movl    %esp, %ebp                                                   
          .cfi_def_cfa_register 5                                             
          andl    $-16, %esp                                                   
          subl    $32, %esp   
第一,main函数被操作系统C库函数调用的时候将帧指针压入栈中,那么此时的esp(栈指针)指向的是这里的ebp(对于main函数来说)么,
第二,为什么要将esp andl -16,他的作用是什么啊。
第三,为什么要将esp -32,这个32 是不是太大了,我的main函数好像只有两个临时变量要用到栈。
这里我有个猜测,andl -16,%esp是不是为了保存main函数的参数地址和环境变量地址啊?

论坛徽章:
0
2 [报告]
发表于 2013-06-08 18:03 |只看该作者
1,函数调用开始,一般有这两句。
pushl %ebp    //这句之前属于caller的帧栈。 这句开始进入callee的帧栈,先保存旧的帧指针
movl %esp, %ebp //帧指针移动到栈指针位置。 这时候2个都指向新帧栈的起始处。
2,endl $-16是取整
3,subl $32是为了对齐。

论坛徽章:
0
3 [报告]
发表于 2013-06-08 18:16 |只看该作者
andl $-16的作用到底是什么啊。难道main函数的栈就固定在虚拟地址这个位置么,如果说是取整,为什么啊。为了4字节对齐?
subl $32是为了main的变量开辟空间。一共32字节,我的两个变量加上环境变量以及命令行参数。

论坛徽章:
0
4 [报告]
发表于 2013-06-08 18:22 |只看该作者
嗯,取16的整,4字节对齐。
-16是0xF...F0,andl就是清空最后四位,esp向下移动到16的整。
subl 32也是一样的,为了对齐会多渐一些

论坛徽章:
4
白羊座
日期:2013-09-17 21:59:30技术图书徽章
日期:2013-10-12 22:16:03白羊座
日期:2013-10-14 11:01:40双子座
日期:2013-12-17 18:26:39
5 [报告]
发表于 2013-06-08 19:58 |只看该作者
回复 3# dxyf1524
1.
pushl   %ebp: 保存调用者的ebp寄存器
movl    %esp, %ebp: 将函数当前的esp寄存器值保存在ebp寄存器中,此时,被调用者的ebp指向的位置是调用者的ebp值
这两句与leave做的是相反的事,leave相当于movl %ebp,%esp; popl %ebp;
2.
andl    $-16, %esp: 是为了对齐到16字节的边界
3.
subl    $32, %esp: 为函数分配变量空间,并且也为了对齐.之所以为32是因为:
a,b各占4个字节;printf的三个参数各占4个字节;这样为20个字节,再为了对齐到16的边界,所以就为32了


   

论坛徽章:
0
6 [报告]
发表于 2013-06-09 09:17 |只看该作者
大哥,andl $-16,%esp是在subl $32,%esp前执行的,为什么要取16字节对齐,难道4字节对齐不行么,有什么原因么,还是平台和处理器间的因素。

论坛徽章:
0
7 [报告]
发表于 2013-06-09 09:20 |只看该作者
找到原因了,
深入理解计算机系统一书P154这样描述:“GCC坚持一个x86编程指导方针,也就是一个函数使用的所有栈空间必须是16字节的整数倍。

论坛徽章:
0
8 [报告]
发表于 2013-06-09 10:45 |只看该作者
本帖最后由 sacry 于 2013-06-09 10:49 编辑

回复 7# dxyf1524


没反映过来,顺着你的话说了
这里取整是为了对齐是没错的。
andl $-16,取下个16的整,是16对齐...

你问为什么不是4对齐不行,我也不知道。只能理解为就这么定的,不同平台,处理器是不一样的。
gcc有参数可以指定对齐,虽然从来没用过~

你发的深入里的那段话,严格来说并不完全是这里andl $-16的理由。
这句话解释的“需要对函数进行16byte对齐”,但没说怎么对齐。
比如你看你swap函数的汇编,可能就是没有andl $-16而直接是subl $24 %esp这样的数字。

需要对齐的内容包括 return address + saved ebp + local + arg
如果函数起始出就andl $-16那说明return address + saved ebp部分已经对齐,所以之后subl是16的倍数就可以了。
如果起始处没有andl $-16,那后面subl的部分说可能就是16的倍数减去8(return address + saved ebp)比如24这样的.

PS:
subl $32是为了main的变量开辟空间。一共32字节,我的两个变量加上环境变量以及命令行参数。

这个解释是不对的。
这里和环境变量,命令行参数没有什么关系。
主要就是a,b, &a, &b

论坛徽章:
0
9 [报告]
发表于 2013-06-09 12:01 |只看该作者
我总结下大家的说法吧
我所用的机器X86_64,我用GCC编译器生成32位汇编(-m32 选项)
我有个猜测,这里可能跟C运行时库有关(懂的人可以说一下,我瞎猜的),在运行时库调用main函数时,它也有栈的操作,将命令行和环境变量入栈(也许是用栈分配空间)。
但是他可能没有保证栈是16字节对齐,所以在进入main函数的时候将栈指针esp对齐,以后所有的函数都会对齐了(前提是所有的函数都按照16字节为临时变量等分配空间)
这里的andl $-16,%esp,就是最开始的16字节对齐吧。
至于别的,sacry和井蛙夏虫说的很给力,
再次引用下

2.
andl    $-16, %esp: 是为了对齐到16字节的边界
3.
subl    $32, %esp: 为函数分配变量空间,并且也为了对齐.之所以为32是因为:
a,b各占4个字节;printf的三个参数各占4个字节;这样为20个字节,再为了对齐到16的边界,所以就为32了

需要对齐的内容包括 return address + saved ebp + local + arg
如果函数起始出就andl $-16那说明return address + saved ebp部分已经对齐,所以之后subl是16的倍数就可以了。
如果起始处没有andl $-16,那后面subl的部分说可能就是16的倍数减去8(return address + saved ebp)比如24这样的

以上就是我认为很里的说明和自己的胡乱猜测。

论坛徽章:
4
白羊座
日期:2013-09-17 21:59:30技术图书徽章
日期:2013-10-12 22:16:03白羊座
日期:2013-10-14 11:01:40双子座
日期:2013-12-17 18:26:39
10 [报告]
发表于 2013-06-09 12:43 |只看该作者
回复 9# dxyf1524
这个16字节的对齐是可以改的。在gcc(4.6.3)文档的第196页:
  1. -mpreferred-stack-boundary=num
  2. Attempt to keep the stack boundary aligned to a 2 raised to num byte boundary.
  3. If ‘-mpreferred-stack-boundary’ is not specified, the default is 4 (16 bytes or
  4. 128 bits).
复制代码
为什么默认是16字节,在文档的同一页:
  1. On Pentium III, the Streaming SIMD Extension
  2. (SSE) data type __m128 may not work properly if it is not 16 byte aligned.
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP