2012-12-19 51 views
21

澄清定義被叫符號的電話,我的問題是指包裝/攔截從一個函數/符號調用另一個函數/符號當主叫用戶和被調用者在與GCC編譯器和鏈接器相同的編譯單元中定義。GNU GCC/LD - 包裝與主叫方和同一個目標文件

我有一個情況類似如下:

/* foo.c */ 
void foo(void) 
{ 
    /* ... some stuff */ 
    bar(); 
} 

void bar(void) 
{ 
    /* ... some other stuff */ 
} 

我想換到這些函數的調用,我可以用ld's --wrap option做到這一點(一個點)(然後我實現__wrap_foo和__wrap_bar這然後依據ld的--wrap選項的結果調用__real_foo和__real_bar)。

gcc -Wl,--wrap=foo -Wl,--wrap=bar ... 

我遇到的問題是,這只是生效的引用foo和從超出這個編譯單元的酒吧(和鏈接時解析)。也就是說,調用foo和foo.c的內的其它功能酒吧沒有得到包裹。

calls from within the compilation unit get resolved before the linker's wrapping

我試着用objcopy --redefine-sym,但僅重命名符號及其引用。

我想調用替換到foobar(foo.o的內)__wrap_foo__wrap_bar(就像他們獲得其他目標文件鏈接程序的--wrap選項解決)我之前的* .o文件傳遞到鏈接器的--wrap選項,而不必修改foo.c的源代碼。

這樣,所有對foobar的調用都會進行包裝/攔截,而不僅僅發生在foo.o以外的調用。

這可能嗎?

+0

你也許可以與find解決您的問題,您編輯/替換,或使用SED ... –

+0

你是在暗示簡單地用一個編輯器破解OBJ:單元測試/嘲弄非常有用嗎? –

+0

我建議你批量修改源代碼以將這些調用替換爲包裝器的調用,或者將其定義爲真實函數或包裝器。 –

回答

5

你必須使用objcopy弱化和全局化符號。

-W symbolname 
--weaken-symbol=symbolname 
    Make symbol symbolname weak. This option may be given more than once. 
--globalize-symbol=symbolname 
    Give symbol symbolname global scoping so that it is visible outside of the file in which it is defined. This option may be given more than once. 

這爲我工作

bar.c:

#include <stdio.h> 
int foo(){ 
    printf("Wrap-FU\n"); 
} 

foo.c的:

#include <stdio.h> 

void foo(){ 
printf("foo\n"); 
} 

int main(){ 
printf("main\n"); 
foo(); 
} 

編譯

$ gcc -c foo.c bar.c 

削弱foo符號並使其成爲全局符號,因此它可以再次用於鏈接器。

$ ./wrapme 
main 
Wrap-FU 
+0

這將工作。謝謝! –

+0

我試過這招在以下情況:1 我有一個嵌入式平臺,有一個功能,我需要通過另一減速取代的SDK。 2-我在編譯後使用gcc-objcopy從目標庫中的目標文件再次使符號變弱和全局。 構建過程包括創建包含舊庫對象文件的歸檔文件(稱爲core.a)的問題。 3-我添加了一個步驟來刪除目標文件,並使用cora.a中的gcc-ar將其替換爲新的(使用弱符號)。 結果這個訣竅沒有成功(多重定義..)幫助? –

4

這似乎是工作的記載:

--wrap=symbol 
     Use a wrapper function for symbol. 
     Any undefined reference to symbol will be resolved to "__wrap_symbol". ... 

注意上面的不確定。當連接器處理foo.o,該bar()定義,因此鏈接器並不把它包起來。我不知道爲什麼的完成這種方式,但有可能是需要這樣的使用情況。

+0

我使用它來將呼叫包裹在編譯單元中(請參閱我的原始問題的示例)。但是,它不適用於編譯單元內的攔截/包裹所有內容(這是我感興趣的攔截)。顯然,在**編譯單元中,**參考已解決。在鏈接器進入時,使用'--wrap'鏈接器選項來**這些調用已經太晚了。 –

+0

@ luis.espinal「已經太晚了」 - 不,它不是。鏈接器*可以*輕鬆更改呼叫目標;它只是不(因爲我不知道的原因)。 –

+0

好吧,當我說「爲時已晚」,我GNU LD的上下文中這樣說(不是一般的接頭範圍內。)是的,* A *連接器可以很容易地改變調用目標。但** **鏈接器(GNU LD)沒有。原因是它限制自己在編譯單元**內替換/重寫未解析的引用**。這是因爲最後一步,我說GN ld的連接階段已經太晚了(雖然對於更聰明的連接器來說不會太晚)。 –

3

你可以達到你想要什麼,如果你使用--undefined--wrap

-u SYMBOL, --undefined SYMBOL 
           Start with undefined reference to SYMBOL 
+0

試過這個但是id也沒有工作... – Ale

5
#include <stdio.h> 
#include <stdlib.h> 

//gcc -ggdb -o test test.c -Wl,-wrap,malloc 
void* __real_malloc(size_t bytes); 

int main() 
{ 
    int *p = NULL; 
    int i = 0; 

    p = malloc(100*sizeof(int)); 

    for (i=0; i < 100; i++) 
     p[i] = i; 

    free(p); 
    return 0; 
} 

void* __wrap_malloc(size_t bytes) 
{ 
     return __real_malloc(bytes); 
} 

然後就是編譯此代碼和調試。當你調用reall malloc時,被調用的函數__wrap_malloc和__real_malloc將調用malloc。

我覺得這是攔截來電的方式。

基本上其--wrap選項由LD提供。

+4

我知道這個選項。這幾乎是我使用的。這在我提到的場景中不起作用。再次看到我的原始問題。 –

1

您可以:

$ objcopy foo.o --globalize-symbol=foo --weaken-symbol=foo foo2.o 

現在你可以用包裹從bar.c

$ gcc -o nowrap foo.o #for reference 
$ gcc -o wrapme foo2.o bar.o 

測試

$ ./nowrap 
main 
foo 

而且包裹一個鏈接新的OBJ在執行之前使用__attribute__((weak))爲了讓有人重新實現它而不需要GCC大肆鼓吹多個定義。

例如,假設您想模擬以下函數中的world函數hello.c代碼單元。您可以預先設置該屬性,以便能夠覆蓋該屬性。

#include "hello.h" 
#include <stdio.h> 

__attribute__((weak)) 
void world(void) 
{ 
    printf("world from lib\n"); 
} 

void hello(void) 
{ 
    printf("hello\n"); 
    world(); 
} 

然後你就可以將其覆蓋在另一個單元文件。

#include <stdio.h> 
#include "hello.h" 

/* overrides */ 
void world(void) 
{ 
    printf("world from main.c"\n); 
} 

void main(void) 
{ 
    hello(); 
    return 0; 
} 
+0

這是個好主意。下次會用。不幸的是,當我問這個問題時,我正在處理那些我無法修改的軟件來添加這樣一個屬性。不過,這很好,將來我的工具箱中肯定會使用它。 –

+0

嗯,是的,如果你不能修改源,那麼@ PeterHuewe的答案是使用objcpy的解決方案。如果你可以修改源代碼,那麼這個設置似乎更容易。 – MicroJoe

相關問題