在我们的 libdisksupervisor.so 代码中,有什么方法可以在继续之前测试这些外部变量(从我们的库的角度来看)是否存在,可能是通过一些神秘的链接器或 GCC 魔法?
您在这里遇到了时间问题。实际上,您不需要做任何特殊的事情来测试这些符号的存在和可见性在编译时,在您链接的diskmanager 版本中 .当您尝试将 libdisksupervisor.so 与运行时不兼容的 diskmanager 版本一起使用时,就会出现问题。
我知道我可以将 nm 或 objdump 作为预验证脚本的一部分扔给它,但我们需要单独在我们的 c 库中完成此操作。
我不知道有任何方法可以与您运行程序的方式配合使用,并且不会轻易被diskmanager维护意外挫败。
但也许有一种方法可以改变你运行程序的方式。如果你现在调用的libdisksupervisor.so 提供了一个程序入口点(即main())并且你直接运行它,它可以dlopen()diskmanager 并通过dlsym() 检查是否存在所需的符号。然后它可以将控制权转移到diskmanager 的main()(也可以通过dlsym() 访问)。您可以将其视为在系统的动态链接器和diskmanager 之间插入一个垫片。
更新:
好消息是我有一个概念证明证明它是可以做到的(见下文)。坏消息是,将主可执行文件作为共享库加载需要特殊的构建选项,听起来让对方使用这些选项进行构建可能会很麻烦。另一方面,这种方法可以让他们准确控制和记录哪些符号暴露在你的身边,也许这将成为一个合适的胡萝卜。
总之,POC 由三个 C 源文件、两个辅助文件和一个 Makefile 组成:
dummy.c
int dummy(void) {
return 0;
}
main.c
#include <stdio.h>
int dummy(void);
#ifndef BREAKME
int internalStatus = 42;
#endif
int main(int argc, char *argv[]) {
printf("dummy() returns %d\n", dummy());
return 0;
}
shim.c
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
#include <assert.h>
#define TARGET_PATH "./mainprog"
#define NOT_FOUND_STATUS 127
#define MISSING_SYM_STATUS 126
typedef int (*main_type)(int, char **);
static int *internalStatus_p;
#define internalStatus (*internalStatus_p);
int dummy(void) {
return internalStatus;
}
#define LOAD_SYM(dso, name, var) do { \
char *e_; \
var = dlsym(dso, name); \
e_ = dlerror(); \
if (e_) { \
fprintf(stderr, "%s\n", e_); \
return MISSING_SYM_STATUS; \
} \
} while (0)
int main(int argc, char *argv[]) {
void *diskmanager_bin = dlopen(TARGET_PATH, RTLD_LAZY | RTLD_GLOBAL);
char *error;
main_type main_p;
if (!diskmanager_bin) {
fprintf(stderr, "Could not load " TARGET_PATH ": %s\naborting\n", dlerror());
return NOT_FOUND_STATUS;
} else {
error = dlerror();
assert(!error);
}
LOAD_SYM(diskmanager_bin, "internalStatus", internalStatus_p);
LOAD_SYM(diskmanager_bin, "main", main_p);
return main_p(argc, argv);
}
#undef LOAD_SYM
mainprog_dynamic
{
main; internalStatus;
};
shim_dynamic
{
dummy;
};
生成文件
# sources contributing to a shared library must be built with -fpic or -fPIC
CFLAGS = -fPIC -std=c99
LDFLAGS =
SHLIB_LDFLAGS = -shared
SHLIB_EXTRALIBS = -lc
# Sources contributing to the main program should be built with -fpie or -fPIE
SHMAIN_CFLAGS = -fpie
# The main program must be linked with -pie
SHMAIN_LDFLAGS = -pie
DL_EXTRALIBS = -ldl
LIBDUMMY_SO_VER = 0
LIBDUMMY = libdummy.so.$(LIBDUMMY_SO_VER)
all: mainprog shim
mainprog: main.o $(LIBDUMMY) mainprog_dynamic
$(CC) $(CFLAGS) $(SHMAIN_CFLAGS) $(LDFLAGS) $(SHMAIN_LDFLAGS) -Wl,--dynamic-list=mainprog_dynamic -o $@ $< $(LIBDUMMY) $(SHLIB_EXTRALIBS)
main.o: main.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(SHMAIN_CFLAGS) -c -o $@ $<
libdummy.so.$(LIBDUMMY_SO_VER): libdummy.so
ln -sf $< $@
libdummy.so: dummy.o
$(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(SHLIB_LDFLAGS) -Wl,-soname,libdummy.so.$(LIBDUMMY_SO_VER) $^ $(SHLIB_EXTRALIBS)
shim: shim.o shim_dynamic
$(CC) $(CFLAGS) $(LDFLAGS) -Wl,--dynamic-list=shim_dynamic -o $@ $< $(DL_EXTRALIBS)
test: all
@echo "LD_LIBRARY_PATH=`pwd` ./mainprog :"
@LD_LIBRARY_PATH=`pwd` ./mainprog
@echo "LD_LIBRARY_PATH=`pwd` ./shim :"
@LD_LIBRARY_PATH=`pwd` ./shim
clean:
rm -f *.o *.so *.so.* mainprog shim
这模拟了您描述的情况,您要覆盖的函数位于单独的共享库中。它假定使用 GNU 工具链。成功构建示例 (make all) 后,您可以 make test 进行演示:
$ make test
LD_LIBRARY_PATH=/tmp/dl ./mainprog :
dummy() returns 0
LD_LIBRARY_PATH=/tmp/dl ./shim :
dummy() returns 42
*_dynamic 文件告诉链接器两个可执行文件中应包含在导出(动态)符号中的符号,即使链接中没有任何内容引用它们。
这种方法不允许 shim 直接引用主程序的internalStatus 变量,因为这样 shim 需要将主程序作为库链接,并且当 shim 时动态链接器会自动加载它运行。对变量的引用总是被立即绑定,所以如果internalStatus 消失了,就会导致动态链接器出错,这超出了 shim 的控制。