免费注册 查看新帖 |

Chinaunix

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

[C] 请教有关栈帧大小对齐的问题 [复制链接]

论坛徽章:
11
摩羯座
日期:2013-09-29 17:39:09白羊座
日期:2014-11-13 09:38:14技术图书徽章
日期:2014-01-17 15:07:36狮子座
日期:2013-12-25 14:01:52技术图书徽章
日期:2013-12-17 11:33:22技术图书徽章
日期:2013-12-03 10:27:57天秤座
日期:2013-11-08 15:47:19申猴
日期:2013-10-29 13:16:32未羊
日期:2013-10-12 22:28:56辰龙
日期:2013-10-09 14:39:5515-16赛季CBA联赛之山东
日期:2016-07-25 10:23:00
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-09-25 21:31 |只看该作者 |倒序浏览
一个例子:
  1. vi stack.c
复制代码
  1. int sum (int x, int y)
  2. {
  3.     int a = 0;
  4.     a = x;
  5.     a += y;
  6.     return a;
  7. }
  8. int main (int argc, char *argv[])
  9. {
  10.     int x, y, result;
  11.     x = 0x12;
  12.     y = 0x34;
  13.     result = sum (x, y);
  14.     return 0;
  15. }
复制代码
  1. gcc -m32 stack.c -o stack
复制代码
  1. objdump -d stack > stack.dump
复制代码
  1. 080483b2 <main>:
  2. 80483b2:   55                      push   %ebp
  3. 80483b3:   89 e5                   mov    %esp,%ebp
  4. 80483b5:   83 ec 18                sub    $0x18,%esp
  5. 80483b8:   c7 45 f4 12 00 00 00    movl   $0x12,-0xc(%ebp)
  6. 80483bf:   c7 45 f8 34 00 00 00    movl   $0x34,-0x8(%ebp)
  7. 80483c6:   8b 45 f8                mov    -0x8(%ebp),%eax
  8. 80483c9:   89 44 24 04             mov    %eax,0x4(%esp)
  9. 80483cd:   8b 45 f4                mov    -0xc(%ebp),%eax
  10. 80483d0:   89 04 24                mov    %eax,(%esp)
  11. 80483d3:   e8 bc ff ff ff          call   8048394 <sum>
  12. 80483d8:   89 45 fc                mov    %eax,-0x4(%ebp)
  13. 80483db:   b8 00 00 00 00          mov    $0x0,%eax
  14. 80483e0:   c9                      leave  
  15. 80483e1:   c3                      ret
复制代码
汇编指令中的sub    $0x18,%esp为main()的栈帧预留了0x18字节的空间,这里一直没想明白,网上有些资料说ABI约定栈帧按照16字节对齐,哪位大神帮忙解释一下这段代码中的0x18字节是怎样得出的,16字节边界对齐的原理是怎样的?谢谢。

论坛徽章:
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
2 [报告]
发表于 2013-09-26 00:33 |只看该作者
回复 1# superwujc
为什么对齐到16字节: http://bbs.chinaunix.net/forum.p ... ;page=1#pid23893511
为什么是预留0x18(24)字节的空间?
x,y,result各4字节,sum的两个参数各4字节,总共为20字节。
一般为了对齐到16字节是这样的:and    $0xfffffff0,%esp,然后减去一个16倍数的字节,这里减去的不是16的倍数,它怎样保证对齐到16字节?
我猜测它是这样计算的:假设调用它的上层的函数是对齐到16字节的,
这样加上函数的返回地址和开始的push   %ebp,就余下了8个字节,再减上24字节就对齐到了16字节.

   

论坛徽章:
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
3 [报告]
发表于 2013-09-26 10:32 |只看该作者
本帖最后由 myworkstation 于 2013-09-26 10:48 编辑
井蛙夏虫 发表于 2013-09-26 00:33
回复 1# superwujc
为什么对齐到16字节: http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=408558 ...


补充一下对齐到16字节是某些CPU指令集要求的。

Explicitly-aligned SIMD load and store instructions accessing 16 bytes of memory (e.g. MOVAPD, MOVAPS,
MOVDQA, etc.). These instructions always require memory address to be aligned on 16-byte boundary.

The vast majority of arithmetic and data processing instructions in legacy SSE instructions (non-VEX-encoded
SIMD instructions) support memory access semantics. When these instructions access 16 bytes of data from
memory, the memory address must be aligned on 16-byte boundary.


amd64-ABI中的描述:

In addition to registers, each function has a frame on the run-time stack. This stack
grows downwards from high addresses. Figure 3.3 shows the stack organization.
The end of the input argument area shall be aligned on a 16 byte boundary.
In other words, the value (%rsp − is always a multiple of 16 when control is
transferred to the function entry point. The stack pointer, %rsp, always points to
the end of the latest allocated stack frame

论坛徽章:
1
申猴
日期:2014-02-11 14:50:31
4 [报告]
发表于 2013-09-27 11:39 |只看该作者
myworkstation 发表于 2013-09-26 10:32
补充一下对齐到16字节是某些CPU指令集要求的。

Explicitly-aligned SIMD load and store instructio ...



看下面:
void func1()
{            
   char x1;  
   return ;  
}            
            
void func2()
{            
   func1();  
}            
            
int main()   
{            
   func2();  
}  

Dump of assembler code for function main():
0x08048406 <main()+0>:  lea    0x4(%esp),%ecx
0x0804840a <main()+4>:  and    $0xfffffff0,%esp
0x0804840d <main()+7>:  pushl  -0x4(%ecx)
0x08048410 <main()+10>: push   %ebp
0x08048411 <main()+11>: mov    %esp,%ebp
0x08048413 <main()+13>: push   %ecx
0x08048414 <main()+14>: call   0x80483fc <func2()>
0x08048419 <main()+19>: mov    $0x0,%eax
0x0804841e <main()+24>: pop    %ecx
0x0804841f <main()+25>: pop    %ebp
0x08048420 <main()+26>: lea    -0x4(%ecx),%esp
0x08048423 <main()+29>: ret

Dump of assembler code for function func2():
0x080483fc <func2()+0>: push   %ebp
0x080483fd <func2()+1>: mov    %esp,%ebp
0x080483ff <func2()+3>: call   0x80483f4 <func1()>
0x08048404 <func2()+8>: pop    %ebp
0x08048405 <func2()+9>: ret

Dump of assembler code for function func1():
0x080483f4 <func1()+0>: push   %ebp
0x080483f5 <func1()+1>: mov    %esp,%ebp
0x080483f7 <func1()+3>: sub    $0x10,%esp
0x080483fa <func1()+6>: leave  
0x080483fb <func1()+7>: ret  

你会发现,根本没有对齐的。

gcc 版本 4.1.2 20080704 (Red Hat 4.1.2-4

Linux 2.6.18-194.el5xen #1 SMP Tue Mar 16 22:08:06 EDT 2010 i686 i686 i386 GNU/Linux

我一直没搞懂这个对齐是怎么回事儿。
gcc的手册讲得很含糊

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
5 [报告]
发表于 2013-09-27 11:53 |只看该作者
你丫研究编译器的。

论坛徽章:
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
6 [报告]
发表于 2013-09-27 15:05 |只看该作者
回复 4# chenzhanyiczy

  1. // 简单分析几条指令

  2. lea    0x4(%esp),%ecx
  3. // 0x4(%esp)指向是函数返回地址,由于是在main函数中所以实际上这个地址指向了stack顶端。这句话把函数返回地址保存到ecx寄存器,
  4. and    $0xfffffff0,%esp 
  5. //0xfffffff是二进制的-16,这句话是让stack在16字节边界对齐

  6. sub    $0x10,%esp
  7. // 0x10实际上是16,虽然只有一个char局部变量,但依旧对齐到16 bytes的边界上



  8. 请参考:
  9. http://www.tenouk.com/Bufferoverflowc/bufferoverflowvulexploitdemo31.html
  10. http://unixresources.net/linux/clf/linuxK/archive/00/00/39/26/392651.html
复制代码

论坛徽章:
1
申猴
日期:2014-02-11 14:50:31
7 [报告]
发表于 2013-09-27 21:35 |只看该作者
本帖最后由 chenzhanyiczy 于 2013-09-27 21:40 编辑
myworkstation 发表于 2013-09-27 15:05
回复 4# chenzhanyiczy



。。。。

Dump of assembler code for function main():
0x08048406 <main()+0>:  lea    0x4(%esp),%ecx
0x0804840a <main()+4>:  and    $0xfffffff0,%esp     --> 对齐16字节,也esp是16字节对齐
0x0804840d <main()+7>:  pushl  -0x4(%ecx)          --> 压栈4字节
0x08048410 <main()+10>: push   %ebp                  --> 压栈4字节
0x08048411 <main()+11>: mov    %esp,%ebp
0x08048413 <main()+13>: push   %ecx                  --> 压栈4字节
0x08048414 <main()+14>: call   0x80483fc <func2()>   --> 压栈4字节
0x08048419 <main()+19>: mov    $0x0,%eax
0x0804841e <main()+24>: pop    %ecx
0x0804841f <main()+25>: pop    %ebp
0x08048420 <main()+26>: lea    -0x4(%ecx),%esp
0x08048423 <main()+29>: ret

Dump of assembler code for function func2():
0x080483fc <func2()+0>: push   %ebp                  --> 压栈4字节
0x080483fd <func2()+1>: mov    %esp,%ebp
0x080483ff <func2()+3>: call   0x80483f4 <func1()>
0x08048404 <func2()+8>: pop    %ebp
0x08048405 <func2()+9>: ret

当eip在0x080483ff的时候,esp的地址已经不是16字节对齐了,也即进入func1的时候,esp也不是16字节对齐了,也即func1的栈不是16字节对齐了。
这就跟下面粗体描述的相冲突了:

amd64-ABI中的描述:

In addition to registers, each function has a frame on the run-time stack. This stack
grows downwards from high addresses. Figure 3.3 shows the stack organization.
The end of the input argument area shall be aligned on a 16 byte boundary.
In other words, the value (%rsp −  is always a multiple of 16 when control is
transferred to the function entry point
. The stack pointer, %rsp, always points to
the end of the latest allocated stack frame


To ensure proper alignment of this values on the stack, the stack boundary must
be as aligned as that required by any value stored on the stack. Further, every
function must be generated such that it keeps the stack aligned.

<gcc手册>

论坛徽章:
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
8 [报告]
发表于 2013-09-27 22:12 |只看该作者
回复 7# chenzhanyiczy


    你的理解有问题,这个对齐是从特定地址开始算的。就通常的函数调用代码stack frame来讲是在push   %ebp之后的地址要保证16节字对齐,而fun2函数内之所以没有对齐的代码是因为没有局部变量(具体的说是就没有使用变量所以不直接使用stack,所以这个过程省略了,在其下一级调用fun1中进行对齐)。对每个函数调用stack frame来讲如果有stack变量那么stack变量所使用的地址需要对齐,不包括隐式的函数返回地址以及常见的push ebp。

论坛徽章:
1
申猴
日期:2014-02-11 14:50:31
9 [报告]
发表于 2013-09-28 01:09 |只看该作者
本帖最后由 chenzhanyiczy 于 2013-09-28 01:14 编辑
myworkstation 发表于 2013-09-27 22:12
回复 7# chenzhanyiczy



1、“fun2函数内之所以没有对齐的代码是因为没有局部变量(具体的说是就没有使用变量所以不直接使用stack,所以这个过程省略了,在其下一级调用fun1中进行对齐)”
-> 这结论根据是什么? 哪个手册说了,没有局部变量就是不会使用stack,就不会栈对齐。有些函数即使没有局部变量,也会有类似sub 0x30 %esp的指令开辟栈空间,姑且不论它的作用。

2、Dump of assembler code for function func1():
0x080483f4 <func1()+0>: push   %ebp
0x080483f5 <func1()+1>: mov    %esp,%ebp
0x080483f7 <func1()+3>: sub    $0x10,%esp
0x080483fa <func1()+6>: leave  
0x080483fb <func1()+7>: ret  

ok,即使1说的正确,那这里func1的局部变量地址16字节对齐了吗? 也没有对齐。

你仔细看看汇编语句。

论坛徽章:
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-09-28 12:02 |只看该作者
回复 4# chenzhanyiczy
默认情况下,对齐是建议,并非强制
我没有你用的版本的编译器,你编译一下下面这个程序,把汇编代码发上来看看
  1. void func1();
  2. void func2();
  3.    
  4. int main()   
  5. {            
  6.    func2();  
  7. }

  8. void func2()
  9. {            
  10.    func1();  
  11. }

  12. void func1()
  13. {            
  14.    char x1;  
  15.    return ;  
  16. }
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP