понедельник, 28 ноября 2011 г.

C++ библиотека-плагин для Unix. Часть 5-я. Cannot dlopen или как исключить LD_PRELOAD

На HP-UX иногда возникает ситуация, когда стороннее приложение не сможет загрузить нашу библиотеку динамически (dlopen/shl_load). Ошибка возникает когда библиотека содержит внутри статические Thread Local Storage (TLS) либо ссылается на другие библиотеки ее содержащие.
Ошибка выводится на консоль в виде строки:
/usr/lib/pa20_64/dld.sl: Cannot dlopen load module 
'/usr/lib/pa20_64/libcl.2' because it contains thread specific data.

В интернете можно встретить на эту тему советы использовать LD_PRELOAD, например тут.
Это неплохой совет, если у вас небольшое приложение, использующее стандартные менеджеры памяти например.
LD_PRELOAD опасен тем, что он переопределяет естественный порядок нахождения символов в библиотеках и при более сложных зависимостях библиотек - приложения - ведет к ошибкам во времени выполнения.
Как-же быть? Наверно нужно исключить из нашей библиотеки статические секции TLS.
Как я уже говорил выше, статические секции TLS могут содержатся в нашей библиотеке или в сторонних. В большинстве случаев, нужно и можно найти замену сторонней библиотеке, либо, в случае если доступен исходный код, то можно легко заменить статическую секцию на динамическую. Статические секции в коде декларируются определением __thread.
Есть еще один источник статической секции TLS - это библиотека времени выполнения g++: libsupc++.a - при статической линковке или libstdc++.so - при динамической. Динамическую линковку (libstdc++.so) мы можем использовать, если только приложение и наша библиотека собраны одной версией компилятора. Т.е. это не наш случай.
В libsupc++.a статическая секция TLS содержится в объектном файле eh_globals.o:
nm -C eh_globals.o | grep tbss
[12] |0| 0|SECT |LOCAL|0|.tbss._ZZN12_GLOBAL__N_110get_globalEvE6global|
[9]  |0| 8|TLS  |LOCAL|0|.tbss._ZZN12_GLOBAL__N_110get_globalEvE6global|
{_GLOBAL__N_1::get_global()} global


 Для ее устранения, нужно либо компилятор пересобрать, чтобы не использовал статические секции (почитать про опции --with-tls --with-__thread), либо - более простой способ - пересобрать данный файл: eh_globals.c. Для этого, потребуется всего лишь немного его подправить, на предмет зависимостей. Затем из локальной копии удаляем из libsupc++.a оригинальный eh_globals.o и добавляем туда наш.
ar d libsupc++.a eh_globals.o
ar r  libsupc++.a eh_globals_no_tls.o
Используем при линковке локальную копию библиотеки.
Все. Теперь нашей библиотеке не нужен никакой LD_PRELOAD.

Комментариев нет:

Отправить комментарий