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