2014-10-30 115 views
1

我的理解是,如果我在程序中調用printf,默認情況下(如果程序沒有靜態編譯),它會在標準C庫中調用printf。但是,如果我打電話給memcpy,我希望代碼將被內聯,因爲如果memcpy僅複製幾個字節,則函數調用非常昂貴。如果有時內聯並調用其他方法,則libc升級後的程序行爲取決於實現。printf和memcpy鏈接到標準C庫

實際上在這兩種情況下都會發生什麼情況?

回答

1

這將取決於很多事情,這裏是你如何找出答案。 GNU Binutils附帶一個實用程序objdump,它提供關於二進制文件中各種細節的各種細節。

在我的系統(一個ARM的Chromebook),編譯test.c的:

#include <stdio.h> 

int main(void) { 
    printf("Hello, world!\n"); 
} 

gcc test.c -o test,然後運行objdump -R test

test:  file format elf32-littlearm 

DYNAMIC RELOCATION RECORDS 
OFFSET TYPE    VALUE 
000105e4 R_ARM_GLOB_DAT __gmon_start__ 
000105d4 R_ARM_JUMP_SLOT puts 
000105d8 R_ARM_JUMP_SLOT __libc_start_main 
000105dc R_ARM_JUMP_SLOT __gmon_start__ 
000105e0 R_ARM_JUMP_SLOT abort 

這些是文件中的動態重定位項,將從二進制文件的外部庫中鏈接到的所有東西。這裏看起來printf已經完全優化了,因爲它只給出一個常量字符串,因此puts就足夠了。如果我們修改這

printf("Hello world #%d\n", 1); 

那麼我們得到預期的

000105e0 R_ARM_JUMP_SLOT printf 

要獲得memcpy要明確掛鉤,我們要防止gcc使用它自己的內置版本-fno-buildin-memcpy

2

C標準允許一個實現來表現「好像」實際標準庫函數被調用。這確實是一個常見的優化:小的memcpy調用可以展開/內聯,等等。

你是對的,在某些情況下,你可以升級你的libc,而不會看到任何優化過的函數調用的變化。

1

您可以隨時嘗試驅動編譯器行爲。例如,對於gcc

gcc -fno-inline -fno-builtin-inline -fno-inline-functions -fno-builtin... 

您應該檢查與nm或直接在彙編源代碼的中斷調用不同的結果。

1

首先,函數永遠不會真正「內聯」 - 適用於您編寫的功能在同一個編譯單元中可見。

如果您有時正在內聯並調用其他人,則在升級libc後程序的行爲取決於實現。

事實並非如此。 memcpy可能在編譯時間處「內聯」。編譯完成後,您的libc版本沒有任何區別。

在GCC中,memcpy被識別爲builtin。這意味着如果GCC決定它,對memcpy的呼叫將被替換爲合適的實現。在x86上,這通常是rep movsb或類似的指令 - 取決於副本的大小,以及是否大小不變。