GCC Built-in Functions

最近上課的時候鬧了一個大笑話(而且那門課還是組合語言不是程式設計...)。我寫了下面這隻程式:
int main()
{
    printf( "Hello\n" );
    return 0;
}
很明顯,這程式沒有 include 任何 header file,理論上應該是要包含 stdio.h。在編譯的過程中,compiler 吐出了下面的警告信息 (不是錯誤信息唷):

test.c: In function ‘main’:
test.c:3:5: warning: implicit declaration of function ‘printf’ [-Wimplicit-function-declaration]
     printf( "Hello\n" );
     ^
test.c:3:5: warning: incompatible implicit declaration of built-in function ‘printf’
test.c:3:5: note: include ‘<stdio.h>’ or provide a declaration of ‘printf’

但是程式會動,還是可以印出 Hello 的字樣。為什麽?我這時候信誓旦旦的跟大家說,因為 printf 在 libc.so 裡面有,因此就算編譯的時候找不到,gcc 在連結 libc.so 的時候還是會看的到 printf ,所以這時候還是可以執行的。為了證明這件事情,我用 nm 去看一下編出來的 test.o

0000000000000000 T main
                 U puts

等一下, where is my printf?? ...在學生面前要保持鎮定,大概 printf 在系統裡被改成 puts ... 然後再做實驗給同學看,這次換成利用 libm.so 的 pow 函式。
int main()
{
    printf( "2^3 = %f\n", pow( 2.0,3.0 ) );
    return 0;
}
然後說,這時候應該一定會有警告,而且程式還跑不起來,因為沒有 link 到 libm.so (我可沒有 -lm 的選項啊)。結果 ... 居然可以跑 ... ?? Why?? 趕快用 nm 看一下

0000000000000000 T main
                 U printf

恩,printf 出來了,萬歲,但我的 pow 呢?發生什麽事了,而且還可以執行,當場在課堂上楞在那邊發呆。掰不下去以後決定承認自己的無知 ... 還影響到後面上課個進行(因為都在想這個問題 ...)。

追下去,才發現真正的理由,那就是 ... gcc 它作弊。相關資料可以參考這個網頁:Other Built-in Functions Provided by GCC。資料節錄如下:

GCC provides a large number of built-in functions other than the ones mentioned above. Some of these are for internal use in the processing of exceptions or variable-length argument lists and are not documented here because they may change from time to time; we do not recommend general use of these functions.

The remaining functions are provided for optimization purposes.

With the exception of built-ins that have library equivalents such as the standard C library functions discussed below, or that expand to library calls, GCC built-in functions are always expanded inline and thus do not have corresponding entry points and their address cannot be obtained. Attempting to use them in an expression other than a function call results in a compile-time error.

簡單來說,gcc 內建了一堆函式而且不具有 symbol,在程式執行的時候他會直接以 inline 的方式去處理這些函式。所以根本就不會有 symbol 在那邊。要證明這件事情,我們可以使用 -fno-builtin 這個 option 來停用 gcc 的 built-in function。之後再來編編看剛剛的程式:

neokent@Banner /data/test/20181026 $ gcc -fno-builtin -c test2.c 
test2.c: In function ‘main’:
test2.c:3:5: warning: implicit declaration of function ‘printf’ [-Wimplicit-function-declaration]
     printf( "2^3 = %f\n", pow( 2.0, 3.0 ) );
     ^
test2.c:3:27: warning: implicit declaration of function ‘pow’ [-Wimplicit-function-declaration]
     printf( "2^3 = %f\n", pow( 2.0, 3.0 ) );
                           ^
neokent@Banner /data/test/20181026 $ gcc -fno-builtin test2.c -o test2
test2.c: In function ‘main’:
test2.c:3:5: warning: implicit declaration of function ‘printf’ [-Wimplicit-function-declaration]
     printf( "2^3 = %f\n", pow( 2.0, 3.0 ) );
     ^
test2.c:3:27: warning: implicit declaration of function ‘pow’ [-Wimplicit-function-declaration]
     printf( "2^3 = %f\n", pow( 2.0, 3.0 ) );
                           ^
/tmp/cczfqITn.o: 於函式 main:
test2.c:(.text+0x2d): 未定義參考到「pow」
collect2: error: ld returned 1 exit status

可以看到,pow 現在找不到定義了。解決方式?當然就是把 -lm 以及 header file加上去囉。

BTW ... built-in 的函式是為了加速,不要讓 gcc 加速的另一種方式 ... 讓使用者輸入變數即可。

留言

這個網誌中的熱門文章

我弟家的新居感恩禮拜分享:善頌善禱

如何將Linux打造成OpenFlow Switch:Openvswitch

Openssl 範例程式:建立SSL連線