2016-11-04 52 views
-1

我剛剛在一個C API中發現了一個缺少的函數(它編譯但它沒有鏈接)。我想測試整套API函數。當時的想法是讓事情這樣簡單:如何測試C函數是否可鏈接

include <myapi.h> 
void main(void) { 
    func1; 
    func2; 
    ... 
} 

但這編譯因爲編譯器(GCC)優化掉未使用的函數指針鏈接罰款。如果我使用func1()代替,如果該函數確實缺失,則會出現鏈接器錯誤。但是所有的函數調用都很複雜,需要花費幾天時間爲所有函數寫明確的參數。

我可以使用nm來查詢庫,但首先它是一整套.so文件,其次它可能取決於API頭中的#defines。

是否有一個簡單的語法,我可以在C文件中使用?或者,也許只是一個(未)優化選項的GCC?

+0

嘗試使用gcc「-O0」重新編譯它應該禁用所有優化 – Arseniy

+1

問題很不明確。你能更具體一些,並給出實際的用例和動機。我甚至不明白「API中缺少的功能」的意思,因爲API是一組記錄的公共功能,所以請**編輯您的問題**以改進它並激勵它。要更具體和具體。 –

+1

什麼是實際用途?你想從標準中獲取所有C函數,並檢查你的編譯器是否支持它們?這是很多工作。爲什麼不向編譯器的作者詢問編譯器支持什麼以及有哪些限制? –

回答

2

一個可能的解決方法可以是使用函數指針假用法:

include <myapi.h> 
typedef void Func_T(void); // Function type for easier handling 
Func_T * volatile Func; // Function pointer where functions are stored 
/* Macro to do the assignment */ 
#define TEST(f) Func = (Func_T*)f 
int main(void) { 
    TEST(func1); 
    TEST(func2); 
    ... 
} 

鏈接可能仍然刪除的功能,但它是值得嘗試。

編譯器通常會提供屬性或編譯指示來強制保留符號。如果鏈接器試圖刪除它,保留Func可能會很有用。

2

你很不清楚的問題提到.so文件(但沒有提及任何操作系統)和nm。所以我猜你在使用Linux,而我的答案只針對Linux。我不明白,如果你想編譯&構建時或在運行時。

給定的共享對象/some/path/to/foo.so您可以使用dlopen(3)dlsym(3)功能找出(在運行時),如果該共享對象定義了一個給定的符號。但請注意,在ELF文件符號是將近無類型(例如,您無法從ELF共享對象的名稱中知道某個函數的某些函數的簽名,而沒有某個C頭文件聲明它)。

或者,您可能需要更復雜的software build過程(例如,通過向您的Makefile添加臨時規則)。請記住,您可以使用metaprogramming技術並在您的構建中使用一些專門的C代碼生成器。如果你的軟件足夠複雜(例如花費數週時間在這些工具上),你甚至可以使用GCC MELT(或者編寫你自己的GCC插件)來定製GCC編譯器。

注意,一些頭文件(對於給定的庫)可以定義一個函數作爲inline或可限定具有用於它參數的宏(例如,參見waitpid(2),POSIX API的一部分; WIFEXITED實際上是一個宏)。在這兩種情況下,該函數都不是ELF共享庫的符號,但可以從源代碼正確使用該庫(並正確使用該頭文件)來使用該函數。換句話說,API與一組ELF符號不同。

另請參閱Drepper的Good Practices in Library Design, Implementation, and MaintenanceHow To Write Shared Libraries和D.Wheeler Program Library HOWTO

最後,如果根據已知始終爲假的條件添加一些代碼(例如,讀取約opaque predicates),則編譯器無法優化。

int main(int argc, char**argv) { 
    // in practice, all the tests above are false, 
    // but the compiler is not clever enough to optimize 
    if (getpid()==0) funct1(); // always false 
    if (argc<0) funct2(); //always false 
    if (argv[0][0]==(char)0) funct3(); //always false 
    /// etc 

如果職能的簽名需要一些參數,你可以簡單地測試他們的地址:

extern void func1(int); // actually, in some included header 
    if (argv[0]==NULL || (void*)func1 == NULL 
     || (void*)func1 == (void*)3) abort(); 

(我相信,在C標準允許編譯器優化(void*)func1 == NULL -as始終爲假 - 但它不會優化(void*)func1 == (void*)3其中練習在Linux上始終爲false ...)

但是,API又超過了一組ELF符號和一個API「函數」實際上可以是inline或宏。您可能會對weak symbols感興趣。

+0

有趣的閱讀,謝謝。 – dargaud

相關問題