免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12
最近访问板块 发新帖
楼主: nemona
打印 上一主题 下一主题

[C] Help !! gcc -O2 -g 编译时, 加上 -O2 就会出现段错误 !!! [复制链接]

论坛徽章:
0
11 [报告]
发表于 2008-06-26 15:45 |只看该作者

回复 #10 safedead 的帖子

gcc -Wall -O2编译是没有任何警告 .

论坛徽章:
0
12 [报告]
发表于 2008-06-26 15:47 |只看该作者
原帖由 nemona 于 2008-6-25 23:45 发表
gcc -Wall -O2编译是没有任何警告 .


代码看看。

论坛徽章:
0
13 [报告]
发表于 2008-06-26 15:52 |只看该作者
通常 -g 后生成的代码会比较大,不管你有没有 O2, 因为 添加了调试信息

论坛徽章:
0
14 [报告]
发表于 2008-06-30 23:12 |只看该作者
原帖由 zszyj 于 2008-6-26 12:14 发表

犯了在UNIX系统上编译的经验主义了, 刚测试了一下, 在LINUX上不存在这种情况. 谢谢指正, 看来gcc比其它UNIX上的编译器有所提升, 这是个好事, 我收回前面的观点.


不见得
/* popset.c */
int popset(int tval)
{
    int rval;
   
    asm("push1 %1; movl %%esp; popl %%esp;\
             movl %%esp, %0; movl %%edx, %%esp"\
             : "=r" (rval)
             : "r" (tval)
             : "%edx");

    return rval;
}

int main()
{
   popset(1);
}

gcc -O2 -g popset.c
./a.out
segmentation fault

-O2拿掉就好了

fc7 gcc 4.1.x

论坛徽章:
0
15 [报告]
发表于 2008-07-01 09:14 |只看该作者
再加个 -Wextra 吧

论坛徽章:
0
16 [报告]
发表于 2008-07-01 22:56 |只看该作者

回复 #14 blizzard213 的帖子

这到底怎么回事啊 ?? 郁闷 !!!

论坛徽章:
95
程序设计版块每日发帖之星
日期:2015-09-05 06:20:00程序设计版块每日发帖之星
日期:2015-09-17 06:20:00程序设计版块每日发帖之星
日期:2015-09-18 06:20:002015亚冠之阿尔艾因
日期:2015-09-18 10:35:08月度论坛发贴之星
日期:2015-09-30 22:25:002015亚冠之阿尔沙巴布
日期:2015-10-03 08:57:39程序设计版块每日发帖之星
日期:2015-10-05 06:20:00每日论坛发贴之星
日期:2015-10-05 06:20:002015年亚冠纪念徽章
日期:2015-10-06 10:06:482015亚冠之塔什干棉农
日期:2015-10-19 19:43:35程序设计版块每日发帖之星
日期:2015-10-21 06:20:00每日论坛发贴之星
日期:2015-09-14 06:20:00
17 [报告]
发表于 2008-07-01 23:23 |只看该作者
原帖由 blizzard213 于 2008-6-30 23:12 发表


不见得
/* popset.c */
int popset(int tval)
{
    int rval;
   
    asm("push1 %1; movl %%esp; popl %%esp;\
             movl %%esp, %0; movl %%edx, %%esp"\
             : "=r" (rval)
...

把不同编译选项生成的可执行文件反汇编出来比较下。

论坛徽章:
0
18 [报告]
发表于 2008-07-03 11:09 |只看该作者
内存越界了

论坛徽章:
0
19 [报告]
发表于 2008-07-03 19:56 |只看该作者

回复 #17 MMMIX 的帖子

随便写了一点探讨一下:
                                             -O2副作用分析一例
                                                          
                                                         -213@163.com[/email]

问题来源:
深入理解计算机系统(修订版) A.K.A CS:APP
中国电力出版社
[美]Randal E. Bryant & David O'Hallaron 著
[中]龚弈利 & 雷迎春 译

pp230 练习题 4.5
该题目测试x86指令popl %esp的语义为以下两种情况的哪一种
(1)将%esp置为从存储器中读出的值
(2)将%esp置为(%esp) + $4

测试代码为
/*** poptest.c ***/
int poptest(int tval)
{
        int rval;
        asm("push1 %1; movl %%esp, %%edx; popl %%esp;\
                 movl %%esp, %0; movl %%edx, %%esp"
                 : "=r" (rval)
                 : "r" (tval)
                 : "edx");
       
        return rval;
}
/*** main.c ***/
int main()
{
        if (1 == poptest(1)) {
                printf("semantics(1)\n");
        }
        else {
                printf("semantics(2)\n");
        }
       
        return 1;
}

先稍微解释下该代码的意图:
tval压栈
保存%esp到%edx以防测试结果为情况(1)时栈崩溃
popl %esp
将%esp的值保存到rval
从%edx还原%esp

这样如果poptest调用的返回与参数相同则说明popl %esp遵循语义(1)
否则(2)

测试结果为
$gcc poptest.c main.c
./a.out
semantics(1)

加上-O2优化
$gcc -O2 poptest.c main.c
./a.out
segmentation fault

这说明gcc又犯了经验主义的过渡优化错误,下面我们分析一下它是如何过渡优化的:
$gcc -O2 -S poptest.c
$cat poptest.s
...
...
push1 %ebp
movl %esp, %ebp
movl 8(%esp), %eax
leave
#APP
push1 %eax
movl %esp, %edx
popl %esp
movl %esp, %eax
movl %edx, %esp
#NO_APP
ret

很明显gcc自作聪明的把我们的嵌入汇编插入到leave/ret中间去,破坏了正确的堆栈操作过程。
在执行ret指令时,堆栈的情况为:

         %ebp --> +-----------+
                  |              |
                  |              |
                  |              |
                  |              |
                 +-----------+
                  |   tval      |
                 +-----------+
                  | 返回地址 |
                 +-----------+ <-- %esp (这里是没有-O2选项时%esp的正确位置)
                  |   tval      |
                 +-----------+ <-- %esp (因为将leave/ret拆开造成ret前%esp指向错误位置)
                  |              |
                  |              |
                  ~ ~ ~ ~ ~
                  |              |
                  |              |

我们知道leave指定等价于
movl %ebp, %esp
popl %ebp
它的作用是恢复调用者的帧指针,并且使栈指针指向返回地址,然后ret正确执行返回。
但经过gcc这样一优化在ret之前我们的栈指针并未指向正确的返回地址,而是tval的值,
程序当然会听话的跳转到tval地址处执行,结果是不可避免的segmentation fault.

为求信息的完全,下面附上未优化时的汇编代码:
push1 %ebp
movl %esp, %ebp
sub1 $4, %esp
movl 8(%ebp), %eax
#APP
同上
#NOAPP
movl %eax, -4(%ebp)
movl -4(%ebp), %eax
leave
ret

参考文献:
1. 深入理解计算机系统(修订版) 中国电力出版社
2. GNU binutils GAS文档

论坛徽章:
0
20 [报告]
发表于 2008-07-18 07:14 |只看该作者
Thanks !
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP