创建系统调用的编译器优化?
Posted
技术标签:
【中文标题】创建系统调用的编译器优化?【英文标题】:Compiler optimization creating a syscall? 【发布时间】:2014-05-13 18:59:40 【问题描述】:我正在以两种模式编译一个相当复杂的应用程序:调试和发布。在我看来,主要区别在于 -O0 与 -O3 (如果需要,我可以提供 makefile 的相关部分)。我试图尽可能避免系统调用生成,因为我在系统调用仿真模式下模拟这个应用程序(没有操作系统在下面运行)。我目前遇到的问题是,在发布模式下,编译器会生成一个额外的套接字系统调用,我不想发生这种情况(在调试模式下也不会发生)。
我认为可能会创建套接字的原因是我正在使用 pthreads 并且我的两个线程正在通过 volatile char* 进行通信。所以我猜当我设置 -O3 标志时,编译器可能试图以一种奇特的方式实现它?但我不确定这是否是一个合理的假设。
-
是否有可能因为 -O3 标志而生成了套接字系统调用? (没有太大意义)
如果是这样,我如何提示编译器避免生成此系统调用?
编辑:顺便说一句,代码是用 C 和 C++ 编写的
编辑:代码静态链接到以下共享库:
libstdc++.a
libm.a
libglib-2.0.a
-static-libgcc
*special pthreads library*
另外,我发现了在二进制文件中调用套接字的位置:
8c716: db28 blt.n 8c76a <openlog_internal+0xf2>
8c718: f8d9 1008 ldr.w r1, [r9, #8]
8c71c: 4620 mov r0, r4
8c71e: 2200 movs r2, #0
8c720: f441 2100 orr.w r1, r1, #524288 ; 0x80000
8c724: f001 e97c blx 8da20 <__socket>
8c728: 4b20 ldr r3, [pc, #128] ; (8c7ac <openlog_internal+0x134>)
8c72a: 681b ldr r3, [r3, #0]
8c72c: f8c9 0004 str.w r0, [r9, #4]
8c730: b943 cbnz r3, 8c744 <openlog_internal+0xcc>
8c732: 1c43 adds r3, r0, #1
编辑:我发现了为什么会发生这种情况(请参阅下面的答案)。如果有人解释为什么编译器会这样,请分享!!!
【问题讨论】:
发布编译器为其生成系统调用的代码。 除了发布代码之外,您可能还想通过比较为 -O0 与 -O3 生成的程序集来尝试自己解决这个问题。真的会生成系统调用吗? @CodyGray 感谢您的评论。我现在正在运行那个实验。为什么你认为这可能是有问题的?另外,你能建议一个简单的方法来做到这一点(我假设 objdump,但二进制文件是 2MB 和 5MB)。 @MichaelBurr 目前我不确定代码套接字是在哪里生成的,我会尽快发布确切位置。 -O3 可能是内联的东西。如果是这样,您可以在该函数上使用编译指示来停止它。 ...或在使用 -fno-inline 编译该对象时完全关闭内联 ...您可以将其切换为 -finiline-small-functions。 【参考方案1】:虽然可以想象这样的优化,但我还没有听说过这样的优化,我真的很怀疑,因为任何系统调用通常都非常昂贵。
如果你在*nix系统上,你可以通过nm
查找未定义的符号来验证它
nm -u file1.o file2.o | grep socket
应该在某处显示缺少的socket
符号为
U socket
如果在某处调用了套接字。
正如我所提到的,我怀疑是否存在插入任何系统调用的优化,并且我希望上面的命令行没有输出。
更新:
在我的系统(Ubuntu 12.04,gcc 4.6)上,我在man gcc
找到了以下注释
-O2 进一步优化。 ... 注意:在 Ubuntu 8.10 及更高版本中,默认设置 -D_FORTIFY_SOURCE=2, 并在 -O 设置为 2 或更高时激活。这使得额外的编译 - 几个 libc 函数的时间和运行时检查。要禁用,请指定 -U_FORTIFY_SOURCE 或 -D_FORTIFY_SOURCE=0。
所以,也许通过这种或类似的机制,当优化设置为-O2
或-O3
时包含一些代码。
【讨论】:
嗨奥拉夫。感谢您的答复。令人惊讶的是,这没有返回任何内容......这是否意味着正在从我链接的库中调用套接字? 如我所料。这意味着,没有对socket
的未定义引用(没有调用)。另请参阅我的更新答案。从你的评论来看,我想我给了一个错误的印象。很抱歉。
所以,我发现了系统调用的分支在二进制文件中发生的位置(请参阅更新的问题)。假设编译器仅在我执行 -O3 而不是在执行 -O0 时才在其中一个共享库中调用使用套接字的函数是否合理?
从汇编代码来看,我猜这是一些系统日志代码。你打电话给syslog()
还是openlog()
?
你在使用-O2
时有同样的“优化”吗?【参考方案2】:
经过一整天的调试,结果是 arm-cross-gcc 在 -O0 和 -O1,-O2,-O3 下编译 strcpy() 的方式不同是一个volatile char*。 -O0 使用标准用户模式程序集进行编译,而 -O1,-O2,-O3 使用额外的系统调用进行编译,例如套接字、连接和发送。
所以,毕竟,我最初的预感是有道理的:
"我认为可能创建套接字的原因是我正在使用 pthreads 和我的两个线程正在通过 volatile 进行通信 字符*。所以我猜也许编译器正试图在 我设置 -O3 标志时的一种奇特方式?但我不确定这是否是 合理的假设。”
编辑:以下是支持这一说法的一些观察结果:
我将我的代码编译成 4 个版本
1. without strcpy() -O0 => obj.o0
2. with strcpy() -O0 => obj_strcpy.o0
3. without strcpy() -O3 => obj.o3
4. with strcpy() -O3 => obj_strcpy.o3
我对以上所有内容都运行了 nm -u。
这里是差异:
$> diff obj.o0 obj_strcpy.o0
$> diff obj.o3 obj_strcpy.o3
> U __strcpy_chk
$>
这意味着当您将 strcpy() 添加到代码并使用 -O0 编译时,不会添加任何外部符号,而当您将 strcpy() 添加到代码并使用 -O3 编译时,会将 U __strcpy_chk
符号添加到对象文件。我将研究U __strcpy_chk
在 ARM 上的实现,以找出系统调用的来源。截至目前,U __strcpy_chk
似乎正在进行缓冲区溢出检查 - 这是参考:
http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/libc---strcpy-chk-1.html
编辑:到目前为止,有两种解决方案:Olaf Dietsche 提出的一种解决方案是使用除 -O3 之外的另一个编译器标志。另一种选择是完全避免 strcpy() 并使用如下内容:
for(int i=0;i<64;i++)
cmd[i] = msg[i];
【讨论】:
这(一个用套接字通信替换直接内存访问的优化器)似乎非常不太可能。 @AndrewMedico 我同意你的观点,当我用 -O0 或 -O3 编译 strcpy() 时,我看到了代码运行时的差异(-O3 版本调用套接字、连接和发送)。另外,请参阅我的答案中的编辑。如果您对此有更好的解释,请告诉我,因为我对答案也不完全满意。 @TayyarR 查看我更新的答案。这看起来像是一个额外的强化检查,在您的情况下包含在-O3
中。您可以通过将 -U_FORTIFY_SOURCE
添加到编译器标志来禁用此功能。
因此,通过将您的答案与 Olaf 的答案放在一起,问题是 -O3
(或 -O2
就此而言)导致 _FORTIFY_SOURCE
以调用 @987654333 的方式定义@ 编译为对 strcpy_chk()
的调用,其中显然包含(或导致具有)系统调用以记录有关缓冲区溢出问题的错误。
@MichaelBurr _FORTIFY_SOURCE
总是在一些系统上定义。在我和 @Tayyar 的系统上,_FORTIFY_SOURCE
和 -O*
的组合在 /usr/include/features.h 中定义了 __USE_FORTIFY_LEVEL
,这反过来又激活了一些额外的安全检查。以上是关于创建系统调用的编译器优化?的主要内容,如果未能解决你的问题,请参考以下文章