#c #vulkan #dynamic-linking #dynamic-loading #libdl
#c #vulkan #динамическое соединение #динамическая загрузка #либдл
Вопрос:
У меня есть эта простая библиотека
lib.h
:
int lib()
lib.c
:
#include lt;stdio.hgt; #include lt;dlfcn.hgt; #define VK_NO_PROTOTYPES #include lt;vulkan/vulkan.hgt; 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(amp;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 $@ $lt;
Но когда я использую эту библиотеку в приложении, я получаю segfault при vkEnumerateInstanceLayerProperties
вызове в строке 18.
Более того, если я vkEnumerateInstanceLayerProperties
, скажем , изменю имя на что-то другое test
, то все будет работать просто отлично и (в моей системе) 6
будет напечатано. Это также работает, если я вообще не использую динамическую библиотеку, т. Е. Я компилирую lib.c
вместе с main.c
напрямую без -fPIC
.
Что является причиной этого и как мне это решить?
Комментарии:
1. Когда вы динамически связываете функции, каково значение
vkEnumerateInstanceLayerProperties
после получения указателя функции из библиотеки DLL vulkan?2. Отчеты GDB
(PFN_vkEnumerateInstanceLayerProperties) 0x7ffff7fc4050 lt;vkEnumerateInstanceLayerPropertiesgt;
3. А как насчет статической компиляции? Просто любопытно посмотреть, есть ли существенная разница в адресе.
4.
(PFN_vkEnumerateInstanceLayerProperties) 0x7ffff7da1810 lt;vkEnumerateInstanceLayerPropertiesgt;
Ответ №1:
Проблема в том, что эти два определения:
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;
определите глобальные символы с именем vkGetInstanceProcAddr
и vkEnumerateInstanceLayerProperties
в lib.so
.
Эти определения переопределяют определения внутри libvulkan
, и поэтому vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceLayerProperties");
вызов возвращает определение внутри lib.so
, а не предполагаемое внутри libvulcan.so.1
. И этот символ не может быть вызван (находится в .bss
разделе), поэтому попытка вызвать его (естественно) приводит к a SIGSEGV
.
Чтобы исправить это, либо сделайте эти символы static
, либо назовите их по-другому, например p_vkGetInstanceProcAddr
, и p_vkEnumerateInstanceLayerProperties
.
Обновить:
Почему компиляция lib.c вместе с main.c напрямую (без промежуточной общей библиотеки между ними) работает?
Поскольку символы (по умолчанию) не экспортируются из исполняемого файла в таблице динамических символов, если только на них не ссылается какая-либо общая библиотека.
Вы можете изменить значение по умолчанию, добавив -Wl,--export-dynamic
(что заставляет основной исполняемый файл экспортировать все нелокальные символы) в основную строку ссылки на исполняемый файл. Если вы сделаете это, связь lib.c
с main.c
также завершится ошибкой.
Также как vkGetInstanceProcAddr vkEnumerateInstanceLayerProperties может в
"capture" the
lib.so?
Используя обычные правила разрешения символов-выигрывает первый двоичный файл 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
символа) и быть невосприимчивым к переопределению символов.
Комментарии:
1. Почему компиляция
lib.c
вместе сmain.c
прямой (без промежуточной общей библиотеки между ними) работает? В таком случае, почемуvkGetInstanceProcAddr
возвращается правильный адрес? Также как можноvkGetInstanceProcAddr
«захватить»vkEnumerateInstanceLayerProperties
в lib.so? Разве он не должен просто возвращать какой — то заранее определенный адрес, который указывает на правильную функцию? Я представляю, что это реализовано с помощью чего-то вродеif (!strcmp(...)) return vkGetInstanceProcAddr_internal
2. @JimMorrison Я обновил ответ.