0

我有这个简单的图书馆

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("%d\n", count);

    return 0;
}

我使用它编译到一个共享库

libabc.so: lib.o
    $(CC) -shared -o $@ $^ -ldl

lib.o: lib.c lib.h
    $(CC) -fPIC -g -Wall -c -o $@ $<

但是当我在应用程序中使用这个库时,vkEnumerateInstanceLayerProperties在第 18 行调用时会出现段错误。

更重要的是,如果我将名称更改为其他名称vkEnumerateInstanceLayerProperties,例如test,那么一切正常并且(在我的系统中)6被打印出来。如果我根本不使用动态库,它也可以工作,即我直接lib.c与一起编译而没有.main.c-fPIC

是什么原因造成的,我该如何解决?

4

1 回答 1

3

问题在于这两个定义:

PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;

定义名为和in 的全局符号。vkGetInstanceProcAddrvkEnumerateInstanceLayerPropertieslib.so

这些定义覆盖了内部的定义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失败。

还有如何"capture" the在 lib.so 中使用 vkGetInstanceProcAddr 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符号)的地址,并且不受符号覆盖的影响。

于 2021-11-29T04:07:20.567 回答