[英]Where is source code for "cold" functions located in glibc?
我在 C 中有一个 hello world 程序:
#include <stdio.h>
int main() {
printf("Hello world!\n");
return 0;
}
编译gcc -static hello.c -o hello
在readelf -a --wide hello
我发现了一些带有冷后缀的函数
__assert_fail_base.cold
_nl_load_domain.cold
_IO_new_fclose.cold
_IO_fflush.cold
_IO_puts.cold
_IO_wfile_underflow.cold
_IO_new_file_underflow.cold
_IO_fputs.cold
_IO_fwrite.cold
_IO_getdelim.cold
__printf_fp_l.cold
__printf_fphex.cold
read_encoded_value_with_base.cold
base_of_encoded_value.cold
execute_cfa_program.cold
uw_frame_state_for.cold
uw_install_context_1.cold
execute_stack_op.cold
uw_update_context_1.cold
uw_init_context_1.cold
uw_update_context.cold
_Unwind_RaiseException_Phase2.cold
_Unwind_GetGR.cold
_Unwind_SetGR.cold
_Unwind_Resume.cold
_Unwind_Resume_or_Rethrow.cold
size_of_encoded_value.cold
base_from_object.cold
base_from_cb_data.cold
read_encoded_value_with_base.cold
_Unwind_IteratePhdrCallback.cold
search_object.cold
base_of_encoded_value.cold
read_encoded_value_with_base.cold
从这里:
函数的冷属性用于通知编译器该函数不太可能被执行。 该函数针对大小而不是速度进行了优化,并且在许多目标上,它被放置在文本部分的特殊子部分中,因此所有冷函数看起来很接近,从而提高了程序非冷部分的代码局部性。 分支预测机制将导致代码中冷函数调用的路径标记为不太可能。 因此,将用于处理不太可能的情况(例如 perror)的函数标记为冷函数是有用的,以改进在极少数情况下调用标记函数的热函数的优化。
当配置文件反馈可用时,通过 -fprofile-use 会自动检测冷功能并忽略此属性。
根据我下载glibc 并切换到提交160f6c36a374841ee6e2bf2ee0ba05b70634978e
,它指向我的版本git rev-list -n 1 $(git tag | grep 2.31-0ubuntu9.7)
,但是在所有这些操作之后我找不到上面标有的任何函数冷属性。
我知道 glibc 会生成一些系统调用,但我在glibc/sysdeps/unix/syscalls.list
中没有找到任何有趣的函数。
我还从libc.a
中提取了冷函数:
cd /usr/lib/x86_64-linux-gnu/
readelf -a --wide libc.a | egrep '\.cold' | awk '{print $NF}' > libc.a.cold
并将它们与readelf -a --wide hello | egrep '\.cold' | awk '{print $NF}' > hello.readelf
进行比较readelf -a --wide hello | egrep '\.cold' | awk '{print $NF}' > hello.readelf
readelf -a --wide hello | egrep '\.cold' | awk '{print $NF}' > hello.readelf
:
grep -f libc.a.cold hello.readelf
这些是匹配的功能:
__assert_fail_base.cold
_nl_load_domain.cold
_IO_new_fclose.cold
_IO_fflush.cold
_IO_puts.cold
_IO_wfile_underflow.cold
_IO_new_file_underflow.cold
_IO_fputs.cold
_IO_fwrite.cold
_IO_getdelim.cold
__printf_fp_l.cold
__printf_fphex.cold
并且使用grep -f libc.a.cold hello.readelf -v
我发现了不匹配的功能:
read_encoded_value_with_base.cold
base_of_encoded_value.cold
execute_cfa_program.cold
uw_frame_state_for.cold
uw_install_context_1.cold
execute_stack_op.cold
uw_update_context_1.cold
uw_init_context_1.cold
uw_update_context.cold
_Unwind_RaiseException_Phase2.cold
_Unwind_GetGR.cold
_Unwind_SetGR.cold
_Unwind_Resume.cold
_Unwind_Resume_or_Rethrow.cold
size_of_encoded_value.cold
base_from_object.cold
base_from_cb_data.cold
read_encoded_value_with_base.cold
_Unwind_IteratePhdrCallback.cold
search_object.cold
base_of_encoded_value.cold
read_encoded_value_with_base.cold
问题:
libc.a
和hello
二进制文件的不匹配函数的源代码在哪里,它们是从哪个库加载的?版本:
glibc: ldd (Ubuntu GLIBC 2.31-0ubuntu9.7) 2.31
gcc: gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Linux 发行版:
DISTRIB_ID=Ubuntu DISTRIB_RELEASE=20.04 DISTRIB_CODENAME=focal DISTRIB_DESCRIPTION="Ubuntu 20.04.4 LTS"
内核: 5.13.0-41-generic
函数不一定必须使用__attribute__((cold))
标记为冷,以便 GCC 意识到它们是“冷的”。 当启用适当的优化时,GCC 可以并且将自行执行此操作。
特别是引用 GCC 的优化文档:
-freorder-blocks-and-partition
除了在编译函数中重新排序基本块之外,为了减少采用的分支数量,将热和冷基本块划分为程序集和
.o
文件的单独部分,以提高分页和缓存局部性性能。在存在异常处理或展开表(在使用 setjump/longjump 或目标特定方案的目标上)、linkonce 部分、具有用户定义的部分属性的函数以及任何不支持命名的架构时,此优化会自动关闭部分。 当使用
-fsplit-stack
时,默认情况下不启用此选项(以避免链接器错误),但可以显式启用(如果使用工作链接器)。在
-O2
、-O3
、-Os
级别为 x86 启用。
如您所见,默认情况下,此优化在-O2
、 -O3
和-Os
处启用。
何时可以应用这种自动优化的一个简单示例如下所示:
void parent_function(int x) {
if (__builtin_expect(x == 1337, 1)) {
some_function(123);
} else {
some_function(456);
}
// ...
}
GCC 可以将some_function
(甚至parent_function
)拆分为some_function
和some_function.cold
,简化函数的内部逻辑。 因此,在您的情况下,您在编译后的二进制文件中看到的那些.cold
函数实际上并未在源代码中定义,而是由 GCC 自动生成。
在哪里可以找到冷函数的源代码?
在 glibc 源代码中。
我找不到上面标有冷属性的任何功能
这很奇怪,简单的grep -R
就足够了,但你可能对代码索引感兴趣——clangd、GLOBAL 标签、ctags 等。你可以对 the_silver_searcher 感兴趣以获得更快的grep
。
$ git clone git://git.launchpad.net/ubuntu/+source/glibc
Cloning into 'glibc'...
...
$ cd glibc/
$ grep -R --include '*.c' __assert_fail_base
assert/assert.c:__assert_fail_base (const char *fmt, const char *assertion, const char *file,
assert/assert.c: __assert_fail_base (_("%s%s%s:%u: %s%sAssertion `%s' failed.\n%n"),
assert/assert-perr.c: __assert_fail_base (_("%s%s%s:%u: %s%sUnexpected error: %s.\n%n"),
所以__assert_fail_base
在assert/assert.c
中定义。 您可以为每个功能重复该过程。
但是在 google 中输入函数或在 github 上找到项目并在那里搜索要简单得多。 还有https://code.woboq.org/ ,这使任务变得微不足道。
来自 libc.a 和 hello 二进制文件的不匹配函数的源代码在哪里以及它们从哪个库加载?
这看起来像在libgcc中,而Unwind
很可能在gcc libstdc++中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.