繁体   English   中英

glibc 中“冷”函数的源代码在哪里?

[英]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

问题:

  1. 有人可以帮我弄清楚在哪里可以找到函数的源代码吗?
  2. 来自libc.ahello二进制文件的不匹配函数的源代码在哪里,它们是从哪个库加载的?

版本:

  • 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_functionsome_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_baseassert/assert.c中定义。 您可以为每个功能重复该过程。

但是在 google 中输入函数或在 github 上找到项目并在那里搜索要简单得多。 还有https://code.woboq.org/ ,这使任务变得微不足道。

来自 libc.a 和 hello 二进制文件的不匹配函数的源代码在哪里以及它们从哪个库加载?

这看起来像在libgcc中,而Unwind很可能在gcc libstdc++中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM