明確にするために、私の質問は、ある関数/シンボルから別の関数/シンボルへの呼び出しのラップ/インターセプトについて言及しています呼び出し元と呼び出し先が同じコンパイルユニットで定義されている場合 GCCコンパイラとリンカー。
次のような状況です。
/* foo.c */
void foo(void)
{
/* ... some stuff */
bar();
}
void bar(void)
{
/* ... some other stuff */
}
これらの関数の呼び出しをラップしたいのですが、 ldの--wrap
オプション を使用して(ある程度)実行できます(次に、__ wrap_fooと__wrap_barを実装して、__ real_fooと__real_barを呼び出しますldの--wrap
オプションの結果から予想されるとおり)。
gcc -Wl,--wrap=foo -Wl,--wrap=bar ...
私が抱えている問題は、これがfooおよびbar fromへの参照に対してのみ有効であり、このコンパイル単位の外にある(そしてリンク時に解決される)ことです。つまり、他の関数からのfooおよびbarの呼び出しfoo.c内はラップされません。
objcopy --redefine-sym を使用してみましたが、これはシンボルとその参照の名前を変更するだけです。
foo
とbar
(foo.o内)への呼び出しを__wrap_foo
と__wrap_bar
に置き換えます(それらが他のオブジェクトファイルで解決されるようにリンカーの--wrap
オプション)* .oファイルをリンカーの--wrap
オプションに渡す前に、foo.cのソースコードを変更する必要はありません。
このように、ラッピング/インターセプトは、foo.oの外で行われる呼び出しだけでなく、foo
およびbar
へのすべての呼び出しに対して行われます。
これは可能ですか?
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シンボルを弱め、それをグローバルにして、再びリンカで利用できるようにします。
$ objcopy foo.o --globalize-symbol=foo --weaken-symbol=foo foo2.o
これで、新しいobjをbar.cのラップにリンクできます。
$ gcc -o nowrap foo.o #for reference
$ gcc -o wrapme foo2.o bar.o
テスト
$ ./nowrap
main
foo
そしてラップされたもの:
$ ./wrapme
main
Wrap-FU
#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を呼び出します。
これが通話を傍受する方法だと思います。
基本的には、ldによって提供される--wrapオプションです。
呼び出し先を実装する前に__attribute__((weak))
を使用して、GCCが複数の定義について叫ぶことなく誰かが再実装できるようにすることができます。
たとえば、次のhello.cコード単位でworld
関数をモックしたいとします。属性を上書きできるようにするために、属性を先頭に追加できます。
#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;
}
これはドキュメントどおりに機能しているようです:
_ --wrap=symbol
Use a wrapper function for symbol.
Any undefined reference to symbol will be resolved to "__wrap_symbol". ...
_
上記のndefinedに注意してください。リンカが_foo.o
_を処理するとき、bar()
はnot未定義であるため、リンカはラップしません。 なぜそれがそのように行われるかはわかりませんが、おそらくこれを必要とするユースケースがあります。
--undefined
を--wrap
と組み合わせて使用すると、希望どおりの結果を得ることができます
-u SYMBOL, --undefined SYMBOL
Start with undefined reference to SYMBOL
@ PeterHuewe の solution を試してみましたが、機能しますが、ラッパーから元の関数を呼び出すことができません。これを可能にするための私の解決策は次のとおりです:
foo.c
#include <stdio.h>
void foo(){
printf("This is real foo\n");
}
int main(){
printf("main\n");
foo();
}
foo_hook.c
#include <stdio.h>
void real_foo();
int foo(){
printf("HOOK: BEFORE\n");
real_foo();
printf("HOOK: AFTER\n");
}
Makefile
all: link
link: hook
gcc -o wo_hook foo.o
gcc -o w_hook foo_hooked.o foo_hook.o
hook: build_o
objcopy \
foo.o \
--add-symbol real_foo=.text:$(Shell objdump -t foo.o | grep foo | grep .text | cut -d' ' -f 1),function,global \
--globalize-symbol=foo \
--weaken-symbol=foo \
foo_hooked.o
build_o:
gcc -c foo.c foo_hook.c
clean:
-rm w_hook wo_hook *.o
例
virtualuser@virtualhost:~/tmp/link_time_hook$ make
gcc -c foo.c foo_hook.c
objcopy foo.o \
--add-symbol real_foo=.text:0000000000000000,function,global \
--globalize-symbol=foo \
--weaken-symbol=foo \
foo_hooked.o
gcc -o wo_hook foo.o
gcc -o w_hook foo_hooked.o foo_hook.o
virtualuser@virtualhost:~/tmp/link_time_hook$ ls
Makefile foo.c foo.o foo_hook.c foo_hook.o foo_hooked.o w_hook wo_hook
virtualuser@virtualhost:~/tmp/link_time_hook$ ./w_hook
main
HOOK: BEFORE
This is real foo
HOOK: AFTER
virtualuser@virtualhost:~/tmp/link_time_hook$
virtualuser@virtualhost:~/tmp/link_time_hook$ ./wo_hook
main
This is real foo
virtualuser@virtualhost:~/tmp/link_time_hook$