GCC错误地优化了自定义地址处变量的指针相等测试

优化时,GCC 似乎错误地绕过了#define测试。

首先,我使用我自己的link.ld链接器脚本__foo__在地址处提供一个符号0xFFF实际上是最低位,而不是整个地址):

INCLUDE ./default.ld
__foo__ = 0xFFF;
INCLUDE ./default.ld
__foo__ = 0xFFF;
  • 注意:default.ld为默认链接脚本,通过gcc ... -Wl,-verbose命令结果获取

然后,一个foo.c源文件检查__foo__的地址:

我期待***Expected ***打印消息,这FOO_ADDR_IS_EXPECTED()应该是真的。

使用-O0选项编译,它按预期执行:

$ gcc -Wall -Wextra -Werror foo.c -O0 -o foo_O0 -T link.ld && ./foo_O0
__foo__ at 0x5603f4005fff
FOO_ADDR=0xfff
EXPECTED_ADDR=0xfff
***Expected ***

但是有了-O1选项,它不会:

$ gcc -Wall -Wextra -Werror foo.c -O1 -o foo_O1 -T link.ld && ./foo_O1
__foo__ at 0x5580202d0fff
FOO_ADDR=0xfff
EXPECTED_ADDR=0xfff
### UNEXPECTED ###

这里是拆解-O0

$ objdump -d ./foo_O0
...
0000000000001169 <main>:
...
    11b5:       b8 00 00 00 00          mov    $0x0,%eax
    11ba:       e8 b1 fe ff ff          callq  1070 <printf@plt>
    11bf:       48 8d 05 39 fe ff ff    lea    -0x1c7(%rip),%rax        # fff <__foo__>
    11c6:       25 ff 0f 00 00          and    $0xfff,%eax
    11cb:       48 3d ff 0f 00 00       cmp    $0xfff,%rax
    11d1:       75 0e                   jne    11e1 <main+0x78>
    11d3:       48 8d 3d 5e 0e 00 00    lea    0xe5e(%rip),%rdi        # 2038 <_IO_stdin_used+0x38>
    11da:       e8 81 fe ff ff          callq  1060 <puts@plt>
    11df:       eb 0c                   jmp    11ed <main+0x84>
    11e1:       48 8d 3d 60 0e 00 00    lea    0xe60(%rip),%rdi        # 2048 <_IO_stdin_used+0x48>
    11e8:       e8 73 fe ff ff          callq  1060 <puts@plt>
    11ed:       b8 00 00 00 00          mov    $0x0,%eax
...

我不是专家,但我可以看到一个jne条件和两次调用puts, 与该if (FOO_ADDR_IS_EXPECTED())语句匹配。

这里是拆解-O1

$ objdump -d ./foo_O1
...
0000000000001169 <main>:
...
    11c2:       b8 00 00 00 00          mov    $0x0,%eax
    11c7:       e8 a4 fe ff ff          callq  1070 <__printf_chk@plt>
    11cc:       48 8d 3d 65 0e 00 00    lea    0xe65(%rip),%rdi        # 2038 <_IO_stdin_used+0x38>
    11d3:       e8 88 fe ff ff          callq  1060 <puts@plt>
...

这一次,我没有看到任何条件和直接调用puts(for the printf("### UNEXPECTED ###n");statement)。

为什么-O1优化会修改行为?为什么优化FOO_ADDR_IS_EXPECTED()为 false ?

一些有助于您分析的上下文:

$ uname -rm
5.4.0-73-generic x86_64
$ gcc --version
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

编辑:
令人惊讶的是,修改0xFFF值以0xABC更改行为:

$ gcc -Wall -Wextra -Werror foo.c -O0 -o foo_O0 -T link.ld && ./foo_O0
__foo__ at 0x5653a7d4eabc
FOO_ADDR=0xabc
EXPECTED_ADDR=0xabc
***Expected ***

$ gcc -Wall -Wextra -Werror foo.c -O1 -o foo_O1 -T link.ld && ./foo_O1
__foo__ at 0x564323dddabc
FOO_ADDR=0xabc
EXPECTED_ADDR=0xabc
***Expected ***

正如Andrew Henle所指出的,地址对齐似乎很重要:使用0xABF而不是0xABC产生与0xFFF.

以上是GCC错误地优化了自定义地址处变量的指针相等测试的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>