Загрузка классов - динамическая библиотека ссылок вернула неопределенную ошибку символа - PullRequest
0 голосов
/ 05 апреля 2019

Я использую C ++ dlopen (), чтобы связать совместно используемую библиотеку с именем lib * .so (в каталоге A) в моей основной программе (в каталоге B).

Я экспериментировал над загрузкой некоторых простых функций,Все работает очень хорошо.Тем не менее, это доставило мне головную боль, когда я пытался загрузить функции класса и фабрики, которые возвращают указатель на объект класса.(Я использую термины из учебника ниже)

Методология, которую я использовал, была основана на примерах в главе 3.3 этого урока https://www.tldp.org/HOWTO/C++-dlopen/thesolution.html#externC.

Здесь есть немного полиморфизма... lib * .so содержит дочерний класс, который наследует родительский абстрактный класс из основного каталога программы (каталог B).Когда dlopen () пытается загрузить lib * .so в основную программу, происходит сбой из-за «неопределенного символа».

Я использовал команду nm для проверки таблиц символов в lib * .so и двоичном коде основной программы.Символы в этих двоичных файлах:

lib * .so: U _ZTI7ParentBox
двоичный файл основной программы: V _ZTI7ParentBox

ParentBox - это имя родительского класса, унаследованного ChildBox в lib *.так.Обратите внимание, что файл заголовка родительского класса находится в другом проекте в каталоге B.

Несмотря на то, что имя искажает имена символов, они точно такие же.Мне просто интересно, почему динамический компоновщик не может связать их?и дает мне непонятную ошибку символа для dlopen ()?

Мне не хватает понимания некоторых ключевых понятий здесь?

PS еще более странно, что он смог разрешить символы для функций-членов между дочерним классом (символ типа U) в lib * .so (символ типа T) и родительским классом.Почему он может это сделать, но не может разрешить неопределенный символ для имени родительского класса?

(Я долго искал и пробовал -rdynamic, -ldl, хотя я неполностью понял, что они есть, но ничего не помогло)

Обновление 04 апреля 2019 года: Это командная строка g ++, которую я использовал, чтобы сделать основную программу двоичной.

g++ -fvisibility=hidden -pthread -static-libgcc -static-libstdc++ \
-m64 -fpic -ggdb3 -fno-var-tracking-assignments -std=c++14 \
-rdynamic \
-o ./build/main-prog \
/some_absolute_path/ParentBox.o \
/some_other_pathen/Triangle.o \
/some_other_pathen/Circle.o \
/some_other_pathen/<lots_of_depending_obj> \
/some_absolute_path/librandom.a \
-lz -ldl -lrt -lbz2

Я искал каждый аргумент этой командной строки в https://gcc.gnu.org/onlinedocs/gcc/Option-Index.html (Это хороший справочный сайт для всех коллег-программистов, работающих с большими проектами со сложной строкой g ++ :))

Спасибо @ Занятым русским .С его указаниями проблема сужается для экспорта символов в двоичный файл основной программы.

Однако двоичный файл основной программы имеет множество зависимостей, как вы можете видеть из приведенной выше команды, Circle, Triangle и множества других объектных файлов.Нам также нужно добавить «-rdynamic» к компиляции Circle, Triangle и других объектных файлов зависимостей.В противном случае это не работает.

В моем случае я добавил «-rdynamic» ко всем файлам в моем проекте, чтобы экспортировать все символы.Не уверен, что "-fvisibility = hidden" делает что-то хорошее.В любом случае я удалил все из них в своем Makefile ... Я знаю, что это не лучший способ, но я буду беспокоиться о скорости позже, когда все будет функционально правильно.:)

Больше обновлений: Правильное решение в обновлении @Employed Russian в ответе.Мое предыдущее решение сработало, потому что я также удалил «-fvisibility = hidden».Нет необходимости (и, вероятно, неправильно) добавлять -rdynamic ко всем объектам, используемым в последней ссылке.Пожалуйста, обратитесь к объяснению @Employed Russian, которое решает основную проблему.

Окончательное обновление: Для коллег-программистов, которые интересуются тем, как выполняется программа на C / C ++ и как можно связать библиотеку, вот хороший справочный веб-курс (Life of Binary) от XenoKovah: http://opensecuritytraining.info/LifeOfBinaries.html

Вы также можете найти плейлист на YouTube.Просто поиск "Бинарная жизнь"

1 Ответ

0 голосов
/ 05 апреля 2019

Хотя есть искажение имени, имена символов точно такие же.Мне просто интересно, почему динамический компоновщик не может связать их?

Наиболее вероятное объяснение: символ не экспортирован из основного двоичного файла.

Повторите команду с nm -D:

nm -AD lib*.so main-prog | grep ' _ZTI7ParentBox$'

Скорее всего, вы увидите lib*.so: U _ZTI7ParentBox и ничего из main-prog.

Это происходит потому, что обычно компоновщик не экспортирует какой-либо символ из main-prog, на который не ссылается какая-либо участвующая общая библиотекав ссылке (и ваш lib*.so не связан с main-prog, иначе вам не понадобится dlopen его).

Чтобы изменить это поведение, вы можете добавить -Wl,--export-dynamic компоновщикфлаг при ссылке main-prog.Это указывает компоновщику экспортировать все , которое связано с main-prog.

try -rdynamic

Это эквивалентно -Wl,--export-dynamic,и должен был сработать (при условии, что вы добавили его в строку ссылки main-prog, а не где-то еще).

Обновление:

Все работает сейчас!Поскольку main-prog также зависит от некоторых других объектов, похоже, что простое добавление -rdynamic к окончательному линкованию main-prog не решает проблему.Нам нужно добавить «-rdynamic» к компиляции этих зависимых объектов.

Это неправильное решение.Ваша проблема в том, что -fvisibility=hidden говорит компилятору пометить все символы, которые входят в main-prog, как , а не экспортировано, а -rdynamic не экспортирует никаких скрытых символов.

ПравильноРешение состоит в том, чтобы удалить -fvisibility=hidden из любых объектов, которые определяют символы, которые вы делаете , которые хотите экспортировать, и добавьте -rdynamic к окончательной ссылке.

...