簡體   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