В моем каталоге у меня есть два файла. Один foo.cpp
, другой bar.so
. В foo.cpp
я пытаюсь загрузить библиотеку bar.so
:
#include <dlfcn.h>
#include <iostream>
int main()
{
void* handle = dlopen("bar.so", RTLD_NOW | RTLD_GLOBAL);
std::cout << handle << std::endl;
return 0;
}
Затем в этом же каталоге я компилирую код из командной строки с помощью:
g++ foo.cpp -ldl -o test
Однако при выполнении test
выводится 0
, и согласно документации для dlopen
:
If dlopen() fails for any reason, it returns NULL
Так почему же это возвращает NULL, когда файл библиотеки находится в том же каталоге, что и файл CPP?
Обновлять:
Теперь я добавил dlopen()
в свой файл CPP, и это выводит:
bar.so: cannot open shared object file: No such file or directory
Но я не понимаю... bar.so
и foo.cpp
находятся в одном каталоге, исполняемый файл создается в этом же каталоге, и я нахожусь в этом же каталоге, когда запускаю исполняемый файл.
Итак, я попытался использовать абсолютный путь для bar.so
, но затем получаю новую ошибку:
invalid ELF header
После быстрого поиска в Google я думаю, что это может быть связано с моей установкой Ubuntu. На самом деле я использую MacBook и установил родную копию Ubuntu (а не виртуальную машину). Кажется, что это вызывает проблему, но я не знаю, как это исправить. Возможно, этот файл библиотеки просто не будет работать на MacBook Ubuntu.
Вывести dlerror(). Это должно дать вам хорошее направление. Вы можете отредактировать сообщение с выводом dlerror().
Используйте objdump
в исполняемом файле, чтобы увидеть, есть ли запись RPATH
, в которой указан каталог, содержащий библиотеку.
Вам нужно "./bar.so"
. В противном случае dlopen()
не будет искать файл в текущем каталоге.
Однако другое решение — добавить -Wl,-rpath='$ORIGIN'
к флагам компиляции при компиляции foo.cpp
. Таким образом, не имеет значения, какой текущий каталог. dlopen()
всегда будет сначала искать в каталоге исполняемого файла. Но обратите внимание, что это приведет к тому, что все библиотеки будут искать в текущем каталоге в первую очередь, а не только те, которые вы пытаетесь найти dlopen()
. Это может быть или не быть тем, что вы хотите. Абсолютно лучшее решение - получить каталог исполняемого файла и среды выполнения и использовать его вместо этого.
Первое, что нужно изменить, это ./bar.so
, как предлагает @Nikos. Во-вторых, попробуйте разные dlopen
флаги. Debian и Red Hat различаются тем, что им требуется для dlopen
. Попробуйте и RTLD_GLOBAL
, и затем RTLD_GLOBAL | RTLD_LAZY
. В-третьих, используйте RUNPATH
(не RPATH
) при создании программы. Используйте что-то вроде -Wl,-R,<path> -Wl,--enable-new-dtags
.
Я проверил свои записи в Линукс | Примечание для общих вызывающих объектов. Похоже, что Debian и Ubuntu требуют dlopen
флагов RTLD_GLOBAL | RTLD_LAZY
.
@ πάντα - Этот вопрос не может быть дубликатом приведенных вопросов.
So why is this returning NULL, when the library file is in the same directory as the CPP file?
Расположение файла.cpp
здесь не имеет значения.
Расположение исполняемого файла, соответственно, настройка LD_LIBRRY_PATH
— это то, что используется для разрешения во время выполнения.
В любом случае, LD_LIBRRY_PATH
не является рекомендуемым долгосрочным решением. Самый простой — использовать "./bar.so"
вместо "bar.so"
, чтобы dlopen()
сначала искал в текущем каталоге. Но текущий каталог может не совпадать с каталогом, в котором хранится исполняемый файл. В этом случае dlopen()
все равно не удастся.
Другое решение — добавить -Wl,-rpath='$ORIGIN'
к флагам компиляции при компиляции исполняемого файла (в данном случае foo.cpp
) и передать "bar.so"
, как вы это уже делали. При использовании $ORIGIN
в качестве rpath не имеет значения, какой текущий каталог. dlopen() всегда будет сначала искать в каталоге исполняемого файла. Но учтите, что это приведет к тому, что сначала будут искаться все библиотеки в текущем каталоге, а не только те, которые вы пытаетесь вызвать с помощью dlopen(). Это может быть или не быть тем, что вы хотите.
Таким образом, лучшее решение — получить путь к каталогу, в котором находится исполняемый файл, во время выполнения и использовать его как путь к bar.so
. Это зависит от системы. В Linux см.: Получить путь к исполняемому файлу
Не могу сказать вам, почему это не удается, может быть любая из ряда причин. Вы должны просто напечатать
dlerror()
и посмотреть, что там написано.