45

澄清一下,我的问题是指当调用者和被调用者与 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 的引用生效(并在链接时解决)。也就是说,从foo.c中的其他函数调用 foo 和 bar不会被包装。

编译单元内的调用在链接器包装之前得到解决

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

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

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

这可能吗?

4

7 回答 7

21

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

这对我有用

酒吧.c:

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

富.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 中的 wrap 链接起来

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

测试

$ ./nowrap 
main
foo

还有包裹的:

$ ./wrapme 
main
Wrap-FU
于 2017-09-07T19:28:19.493 回答
9

您可以__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;
}
于 2017-11-09T12:05:49.830 回答
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);
}

然后只需编译此代码并进行调试。当你调用 realll malloc 时,被调用的函数会 __wrap_malloc 和 __real_malloc 会调用 malloc。

我认为这是拦截电话的方式。

基本上它是 ld 提供的 --wrap 选项。

于 2015-02-27T22:23:07.163 回答
6

这似乎按记录工作:

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

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

于 2012-12-23T15:43:44.370 回答
4

--undefined如果您使用with ,您可以实现您想要的--wrap

  -u SYMBOL, --undefined SYMBOL
                              Start with undefined reference to SYMBOL
于 2013-11-18T02:43:03.380 回答
3

我已经尝试了@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$
于 2020-03-09T16:01:36.023 回答
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_*符号。

于 2020-08-11T02:40:50.237 回答