2010-12-20 84 views
1

我有一個接口,我希望能夠靜態鏈接模塊。例如,我希望能夠調用稱爲FOO的所有函數(儘管在單獨的文件中)或匹配某個原型,最終在文件中調用一個函數,而不在其他文件中創建頭文件。不要說這是不可能的,因爲我發現了一個黑客可以做到這一點,但我想要一個非黑客入侵的方法。 (黑客使用nm來獲取函數和原型,然後我可以動態調用函數)。另外,我知道你可以用動態鏈接來做到這一點,但是,我想靜態鏈接這些文件。有任何想法嗎?如何在C中創建模塊

+0

如果您想通過名稱在C中調用函數,您必須將該名稱聲明爲某個函數。這是頭文件的用途。你爲什麼不要他們? – TToni 2010-12-20 22:28:34

+0

我看不出如何用'nm'推導出C函數的原型。爲了讓原型破碎,在那裏,你必須將文件編譯爲C++而不是C,否? – 2010-12-20 22:46:21

+0

您通過爲函數名稱刷頭文件獲得原型(hacks,同意) – chacham15 2010-12-21 08:27:17

回答

2

這在編寫測試代碼時有點常見。例如,您想調用以test_開頭的所有函數。因此,您有一個shell腳本,可以通過所有.C文件對grep進行操作,並提取與test _。*匹配的函數名稱。然後該腳本生成一個包含調用所有測試函數的函數的test.c文件。

例如,生成的程序看起來像:

int main() { 
    initTestCode(); 
    testA(); 
    testB(); 
    testC(); 
} 

另一種方式來做到這一點是使用一些技巧鏈接。這是Linux內核爲其初始化做的。作爲init代碼的函數用限定符__init標記。這在linux/init.h中定義如下:

#define __init   __section(.init.text) __cold notrace 

這會導致鏈接器將該函數放在.init.text節中。系統啓動後內核將從該部分回收內存。

爲了調用函數,每個模塊都會用一些其他的宏core_initcall(func),arch_initcall(func)等等(也在linux/init.h中定義)聲明一個initcall函數。這些宏將一個指向該函數的指針放入一個名爲.initcall的鏈接器部分。

在引導時,內核將「遍歷」.initcall節中的所有指針。通過走路看起來像這樣的代碼:

extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[]; 

static void __init do_initcalls(void) 
{ 
     initcall_t *fn; 

     for (fn = __early_initcall_end; fn < __initcall_end; fn++) 
       do_one_initcall(*fn); 

     /* Make sure there is no pending stuff from the initcall sequence */ 
     flush_scheduled_work(); 
} 

符號__initcall_start,__initcall_end等得到鏈接腳本定義。

一般來說,Linux內核對GCC預處理器,編譯器和鏈接器都有一些最聰明的技巧。它一直是C技巧的一個很好的參考。

0

你真的需要靜態鏈接,並且同時在運行時選擇所有匹配的函數,對吧?因爲後者是動態鏈接的典型例子,我會說。

您確實需要一些機制來註冊可用的功能。動態鏈接將提供這一點。

4

把所有功能的表到每個翻譯單元:

struct functions MOD1FUNCS[]={ 
    {"FOO", foo}, 
    {"BAR", bar}, 
    {0, 0} 
}; 

然後把一張桌子到主程序中列出所有這些表:

struct functions* ALLFUNCS[]={ 
    MOD1FUNCS, 
    MOD2FUNCS, 
    0 
}; 

然後,在運行時,通過搜索表,並查找相應的函數指針。

0

我真的不認爲你可以做到這一點。 C並不完全具備後期約束力或者你似乎需要的那種內省。

雖然我不太瞭解你的問題。你想靜態鏈接時動態鏈接庫的功能嗎?因爲這對靜態鏈接沒有任何意義,所以您需要手頭已經有了二進制文件,這會使動態加載函數浪費時間,即使您可以輕鬆完成。