C语言 是否可以将动态对象包装在 ELF 动态链接的二进制文件中?



我试图用我自己对该符号的定义来包装一个glibc符号。对于 glibc 公开的函数,到目前为止,这就像在我的源代码中定义一个__wrap_function_name,然后将Wl,--wrap=external_function_name添加到我的构建系统链接步骤的链接器标志中一样简单。在实践中,这看起来像这样:

extern "C" void __wrap_external_function_name(void) {
my_internal function();
}

但是,我最近在 glibc 暴露的变量上尝试了相同的操作,在这种情况下__signgam。我再次为其包装器定义了链接器标志,但我不确定如何以及是否可以为变量定义包装器。我尝试__wrap__signgam = signgam,但没有效果。事实上,当使用objdump -T binary | grep signgam公开时,符号表具有以下内容,表明即使定义了包装函数,原始符号仍然保持不变:

0000000000000000      DO *UND*  0000000000000000  GLIBC_2.23  __signgam
0000000000000000      DO *UND*  0000000000000000  GLIBC_2.2.5 signgam
0000000001509d24 g    DO .bss   0000000000000004  Base        __wrap___signgam

有没有一种规范的方式来包装这些动态对象?

您可以使用动态荷载库(例如-ldl(并使用其某些功能,特别是dlsym

有三个步骤:

  1. 首先创建源文件。
  2. 从该源文件创建共享库(.so文件(
  3. 调用目标程序(设置环境变量LD_PRELOAD以指向.so文件

请注意,要截获给定的函数,必须使用相同的名称定义函数。

您可以在源文件中拥有任意数量的拦截函数。

以下是一些用于拦截(例如(read的示例源代码...

// NOTE: need _GNU_SOURCE above for RTLD_NEXT in dlfcn.h
#define _GNU_SOURCE
#include <unistd.h>
#include <dlfcn.h>
#include <string.h>
static void
msg(const char *buf)
{
int len;
len = strlen(buf);
write(1,buf,len);
}
// read -- read a file
ssize_t
read(int unit,void *buf,size_t buflen)
{
static ssize_t (*proc)(int,void *,size_t) = NULL;
ssize_t rlen;
// get pointer to real function (only needs to be done once)
if (proc == NULL)
proc = dlsym(RTLD_NEXT,"read");
// do [whatever] stuff before real read ...
msg("PHONY: beforen");
// invoke the real function
rlen = proc(unit,buf,buflen);
// do [whatever] stuff after real read ...
msg("PHONY: aftern");
return rlen;
}

下面是一个示例目标程序:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int
main(void)
{
int fd;
int rlen;
char buf[100];
fd = open("/etc/passwd",O_RDONLY);
rlen = read(fd,buf,sizeof(buf));
close(fd);
printf("main: read %d bytesn",rlen);
return 0;
}

现在创建一个共享库(.so(。

请注意,下面只有一个源文件。但是,您可以根据需要从任意数量的单独源文件创建库(例如,您可以为每个源文件放置一个截距函数,也可以根据需要将所有内容放在一个源文件中(。

下面是一个 [粗略]Makefile(创建共享库示例目标程序(:

all: mylib.so target
read.o: read.c
cc -c -fPIC read.c
mylib.so: read.o
cc -shared -o mylib.so read.o -ldl
target: target.c
cc -o target target.c
test:
env LD_PRELOAD=./mylib.so ./target
clean:
rm -f mylib.so *.o target

现在,要调用目标程序(例如(:

make test

下面是测试的生成输出:

env LD_PRELOAD=./mylib.so ./target
PHONY: before
PHONY: after
main: read 100 bytes

最新更新