GNU gcc / ld - 使用在同一目标文件中定义的调用者和被调用者包装对符号的调用

时间:2012-12-19 21:45:29

标签: gcc linker mocking ld interceptor

澄清一下,我的问题是指当使用GCC编译器和链接器在同一个编译单元中定义调用者和被调用者时,从一个函数/符号到另一个函数/符号的包装/拦截调用

我的情况类似于以下情况:

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

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

我想包装对这些函数的调用,我可以使用ld's --wrap option来实现(到某一点)(然后我实现__wrap_foo和__wrap_bar,它依次调用__real_foo和__real_bar,因为结果是ld的--wrap选项。)

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

我遇到的问题是,这仅对foo的引用生效,并且在此编译单元外部来自(并在链接时解析)。也就是说,从foo.c 中的其他函数调用foo和bar不会被包装。

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

我尝试使用objcopy --redefine-sym,但只重命名符号及其引用。

我想将foobar(在foo.o内)的调用替换为__wrap_foo__wrap_bar(就像它们在其他目标文件中被解析一样)在我将* .o文件传递给链接器的--wrap选项之前,链接器的--wrap选项),而无需修改foo.c的源代码。

这样,对foobar的所有调用都会进行包装/拦截,而不仅仅是在foo.o之外发生的调用。

这可能吗?

7 个答案:

答案 0 :(得分:13)

你必须使用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

答案 1 :(得分:8)

#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选项

答案 2 :(得分:6)

您可以在执行被调用者之前使用__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;
}

答案 3 :(得分:5)

这似乎按照记录的方式运作:

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

请注意上面的未定义。当链接器处理foo.o时,bar() 未定义,因此链接器不会将其包装。我不确定为什么以这种方式完成,但可能有一个用例需要这样做。

答案 4 :(得分:4)

如果您将--undefined--wrap

一起使用,则可以达到您想要的效果
  -u SYMBOL, --undefined SYMBOL
                              Start with undefined reference to SYMBOL

答案 5 :(得分:3)

我尝试了solution中的@PeterHuewe,它可以工作,但是不允许从包装器调用原始函数。为此,我的解决方案如下:

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");
}

制作文件

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$

答案 6 :(得分:0)

使用链接器

$ /usr/bin/ld --version
GNU ld (GNU Binutils for Ubuntu) 2.30

我可以使用defsym选项解决该问题:

--defsym SYMBOL=EXPRESSION  Define a symbol`

代替

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

尝试

gcc -Wl,--defsym,foo=__wrap_foo -Wl,--defsym,bar=__wrap_bar ...

我也没有尝试定义__real_*符号。