web-dev-qa-db-ja.com

GNU gcc / ld-シンボルへの呼び出しを、同じオブジェクトファイルで定義された呼び出し元と呼び出し先でラップする

明確にするために、私の質問は、ある関数/シンボルから別の関数/シンボルへの呼び出しのラップ/インターセプトについて言及しています呼び出し元と呼び出し先が同じコンパイルユニットで定義されている場合 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内はラップされません。

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のソースコードを変更する必要はありません。

このように、ラッピング/インターセプトは、foo.oの外で行われる呼び出しだけでなく、fooおよびbarへのすべての呼び出しに対して行われます。

これは可能ですか?

35
luis.espinal

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
16
PeterHuewe
#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オプションです。

11
Ritesh

呼び出し先を実装する前に__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;
}
6
MicroJoe

これはドキュメントどおりに機能しているようです:

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

上記のndefinedに注意してください。リンカが_foo.o_を処理するとき、bar()not未定義であるため、リンカはラップしません。 なぜそれがそのように行われるかはわかりませんが、おそらくこれを必要とするユースケースがあります。

5

--undefined--wrapと組み合わせて使用​​すると、希望どおりの結果を得ることができます

  -u SYMBOL, --undefined SYMBOL
                              Start with undefined reference to SYMBOL
4
jhyoon

@ PeterHuewesolution を試してみましたが、機能しますが、ラッパーから元の関数を呼び出すことができません。これを可能にするための私の解決策は次のとおりです:

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$
1
Javier Escalada