在共享库中动态加载函数会导致分段错误
dynamically loading a function in a shared library causes a segmentation fault
我有这个简单的库
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 $@ $<
但是当我在应用程序中使用这个库时,在第 18 行调用 vkEnumerateInstanceLayerProperties
时出现段错误。
此外,如果我将名称 vkEnumerateInstanceLayerProperties
更改为其他名称,比如 test
,那么一切正常并且(在我的系统中)打印 6
。如果我根本不使用动态库,它也可以工作,即我直接编译 lib.c
和 main.c
而没有 -fPIC
.
这是什么原因造成的,我该如何解决?
问题是这两个定义:
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;
在 lib.so
.
中定义名为 vkGetInstanceProcAddr
和 vkEnumerateInstanceLayerProperties
的 全局 符号
这些定义覆盖 libvulkan
中的定义,因此 vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceLayerProperties");
调用 return 定义 中的定义 lib.so
,而不是 libvulcan.so.1
中预期的那个。并且该符号不可调用(在 .bss
部分),因此尝试调用它(自然地)会产生 SIGSEGV
.
要解决此问题,请制作这些符号 static
,或以不同的方式命名它们,例如p_vkGetInstanceProcAddr
和 p_vkEnumerateInstanceLayerProperties
.
更新:
Why compiling lib.c together with main.c directly (without an intermediate shared library inbetween) works?
因为符号(默认情况下)不会从动态符号 table 中的 executable 导出,除非 某些共享库引用它们。
您可以通过将 -Wl,--export-dynamic
(这会导致主 executable 导出所有非本地符号)添加到主 executable link 来更改默认值线。如果这样做,linking lib.c
和 main.c
也会失败。
Also how can vkGetInstanceProcAddr"capture" the
vkEnumerateInstanceLayerProperties` in lib.so?
通过使用正常的符号解析规则——第一个定义符号的 ELF 二进制文件获胜。
Shouldn't it just return some kind of predefined address that points to the correct function? I imagine that it is implemented with something like 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;
...
可以说这是一个实现错误——它应该 return 本地别名(..._internal
符号)的地址并且不受符号覆盖的影响。
我有这个简单的库
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 $@ $<
但是当我在应用程序中使用这个库时,在第 18 行调用 vkEnumerateInstanceLayerProperties
时出现段错误。
此外,如果我将名称 vkEnumerateInstanceLayerProperties
更改为其他名称,比如 test
,那么一切正常并且(在我的系统中)打印 6
。如果我根本不使用动态库,它也可以工作,即我直接编译 lib.c
和 main.c
而没有 -fPIC
.
这是什么原因造成的,我该如何解决?
问题是这两个定义:
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;
在 lib.so
.
vkGetInstanceProcAddr
和 vkEnumerateInstanceLayerProperties
的 全局 符号
这些定义覆盖 libvulkan
中的定义,因此 vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceLayerProperties");
调用 return 定义 中的定义 lib.so
,而不是 libvulcan.so.1
中预期的那个。并且该符号不可调用(在 .bss
部分),因此尝试调用它(自然地)会产生 SIGSEGV
.
要解决此问题,请制作这些符号 static
,或以不同的方式命名它们,例如p_vkGetInstanceProcAddr
和 p_vkEnumerateInstanceLayerProperties
.
更新:
Why compiling lib.c together with main.c directly (without an intermediate shared library inbetween) works?
因为符号(默认情况下)不会从动态符号 table 中的 executable 导出,除非 某些共享库引用它们。
您可以通过将 -Wl,--export-dynamic
(这会导致主 executable 导出所有非本地符号)添加到主 executable link 来更改默认值线。如果这样做,linking lib.c
和 main.c
也会失败。
Also how can vkGetInstanceProcAddr
"capture" the
vkEnumerateInstanceLayerProperties` in lib.so?
通过使用正常的符号解析规则——第一个定义符号的 ELF 二进制文件获胜。
Shouldn't it just return some kind of predefined address that points to the correct function? I imagine that it is implemented with something like
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;
...
可以说这是一个实现错误——它应该 return 本地别名(..._internal
符号)的地址并且不受符号覆盖的影响。