c-在共享库中动态加载函数会导致分段错误



我有这个简单的库

lib.h:

int lib()

lib.c:

#include <stdio.h>
#include <dlfcn.h>
#define VK_NO_PROTOTYPES
#include <vulkan/vulkan.h>
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;
int lib()
{
void *lib = dlopen("libvulkan.so.1", RTLD_NOW);
vkGetInstanceProcAddr = dlsym(lib, "vkGetInstanceProcAddr");
vkEnumerateInstanceLayerProperties = (PFN_vkEnumerateInstanceLayerProperties)vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceLayerProperties");
uint32_t count;
vkEnumerateInstanceLayerProperties(&count, NULL);
printf("%dn", count);
return 0;
}

我使用将其编译为共享库

libabc.so: lib.o
$(CC) -shared -o $@ $^ -ldl
lib.o: lib.c lib.h
$(CC) -fPIC -g -Wall -c -o $@ $<

但是当我在应用程序中使用这个库时,当在第18行调用vkEnumerateInstanceLayerProperties时,我会得到一个segfault。

更重要的是,如果我将名称vkEnumerateInstanceLayerProperties更改为其他名称,比如test,那么一切都很好,(在我的系统中(6就会打印出来。如果我根本不使用动态库,也可以使用它,即我直接将lib.cmain.c一起编译,而不使用-fPIC

造成这种情况的原因是什么?我该如何解决?

问题在于这两个定义:

PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;

在CCD_ 12中定义名为CCD_ 10和CCD_ 11的全局符号。

这些定义覆盖libvulkan内部的定义,因此vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceLayerProperties");调用返回lib.so内部的定义,而不是libvulcan.so.1内部的预期定义。并且该符号是不可调用的(在.bss部分中(,所以尝试调用它(自然(会生成SIGSEGV

要解决此问题,请将这些符号设为static,或者以不同的方式命名,例如p_vkGetInstanceProcAddrp_vkEnumerateInstanceLayerProperties

更新:

为什么直接将lib.c与main.c一起编译(中间没有中间共享库(有效?

因为符号(默认情况下(不会从动态符号表中的可执行文件导出,所以除非某些共享库引用了它们。

您可以通过将-Wl,--export-dynamic(这会导致主可执行文件导出所有非本地符号(添加到主可执行程序链接行来更改默认值。如果这样做,则将lib.cmain.c链接也将失败。

此外,如何在lib.so中实现vkGetInstanceProcAddr"capture" thevkEnumerateInstanceLayerProperties`?

通过使用正常的符号解析规则——定义符号的第一个ELF二进制文件获胜。

它不应该只返回某种指向正确函数的预定义地址吗?我想它是用类似if (!strcmp(...)) return vkGetInstanceProcAddr_internal的东西来实现的。

如果以这种方式实现,它就会起作用。

我能找到的实现不做..._internal部分:

void *globalGetProcAddr(const char *name) {
if (!name || name[0] != 'v' || name[1] != 'k') return NULL;
name += 2;
if (!strcmp(name, "CreateInstance")) return vkCreateInstance;
if (!strcmp(name, "EnumerateInstanceExtensionProperties")) return vkEnumerateInstanceExtensionProperties;
...

可以说,这是一个实现错误——它应该返回本地别名(..._internal符号(的地址,并且不受符号覆盖的影响。

最新更新