我有这个简单的库
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.c
与main.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_vkGetInstanceProcAddr
和p_vkEnumerateInstanceLayerProperties
。
更新:
为什么直接将lib.c与main.c一起编译(中间没有中间共享库(有效?
因为符号(默认情况下(不会从动态符号表中的可执行文件导出,所以除非某些共享库引用了它们。
您可以通过将-Wl,--export-dynamic
(这会导致主可执行文件导出所有非本地符号(添加到主可执行程序链接行来更改默认值。如果这样做,则将lib.c
与main.c
链接也将失败。
此外,如何在lib.so中实现vkGetInstanceProcAddr
"capture" the
vkEnumerateInstanceLayerProperties`?
通过使用正常的符号解析规则——定义符号的第一个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
符号(的地址,并且不受符号覆盖的影响。