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.