2016-05-18 2 views
1

Справочная информация: У меня есть приложение, часть которого используется как библиотека для другого независимого приложения. Они ссылаются на эту библиотеку (скажем, lib.so) на время связывания. Проблема с таким подходом заключается в том, что мы должны использовать такие же внешние библиотеки, как boost, ace и т. Д., Или у нас будут дублированные символы, которые в итоге вызовут сбой. Мы хотим решить эту проблему.Динамическая загрузка разделяемой библиотеки с помощью RTLD_DEEPBIND

Я знаю два метода - один скрывает все символы (не уверен в порядках областей видимости глобальных/локальных для общей библиотеки), а другой - для динамической компоновки. Мы выбрали 2-й вариант (динамическое связывание), так как это дало возможность клиенту провести легкое тестирование с помощью stubbed lib.so. и у нас очень простое api.

Я написал ниже небольшой пример приложения, которое загружает пример разделяемой библиотеки и сбой (я хочу понять, почему он разбился и как он должен быть написан). Crash находится в dlopen, точно в инициализации глобальной переменной при присваивании std :: string (конструктор типа Aclass). Из нашего тестирования видно, что любой доступ к библиотеке std при текущей инициализации библиотеки приведет к сбою.

Нам удалось удалить сбой, добавив флаг -fPIC в EXECUTABLE (почему это разрешило нашу проблему, я думал, что он должен быть установлен для общей библиотеки, может ли кто-нибудь объяснить мне это более точно)? Ненужное для моего понимания этот флаг проблематичен, поскольку он замедляет приложение, и в моем случае (приложения с низкой задержкой) это довольно проблематично.

К резюме: 1. Почему произошел сбой? 2. Почему флага -fPIC достаточно, чтобы решить эту проблему? 3. Почему достаточно установить флаг -fPIC в исполняемый файл? 4. Возможно ли разрешить мою проблему другим способом, чтобы разделяемая библиотека и клиентское приложение могли использовать разные версии библиотек (например, boost, ace и т. Д., Компилятор, Linux-версия и библиотеки std, чтобы быть одинаковыми)? 5. Удаление флага RTLD_DEEPBIND также исправляет ошибку, но из gcc man он выглядит так, что я должен использовать этот флаг, поскольку он изменит порядок видимости символа для общей библиотеки - сначала он будет искать символы в локальной области, а затем в глобальном - выглядит так, как это должно быть для я как разделяемая библиотека будет использовать разные внешние библиотеки, чем исполняемый файл (а динамическая загрузка защитит исполняемый файл, загрязняя его область символов). Зачем удалять эту ошибку с исправлением флага в этом простом случае?

Shared библиотека dynLib.cpp:

#include <string> 
class Aclass 
{ 
    std::string s; 
    s = "123"; 
} 

Aclass a; 

Exacutable main.cpp:

#include <stdlib.h> 
#include <dlfcn.h> 
#include <string> 
#include <unistd.h> 
#include <iostream> 

int main() 
{ 
    std::string dummyCrasher; 
    dlerror(); 
    void* handle = dlopen("./libdynLib.so", RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND); 
    if(!handle) 
    { 
     std::cout << "handle is null" << dlerror(); 
    } 

    usleep(1000 * 1000 * 10); 
} 

Makefile: Makefile

CXXFLAGS=-m32 -march=x86-64 -Wl,v -g -O3 -Wformat -Werror=format -c 
CLINKFLAGS=-Wl,-Bstatic -Wl,Bdynamic -ldl -m32 -march=x86-64 

all: dynLib.so dynamiclinking 

dynLib.so: dynLib.o 
    g++44 $(CLINKFLAGS) -shared -o libdynLib.so dynLib.o 

dynLib.o: dynLib.cpp 
    g++44 $(CXXFLAGS) dynLib.cpp 

dynamiclinking: main.o 
    g++44 $(CLINKFLAGS) -o dynamiclinking main.o -ldl 

main.o: main.cpp 
    g++44 (CXXFLAGS) main.cpp 

.PHONY: clean 
clean: 
    rm dynLib.o main.o dynamiclinking libdynLib.so 

PS. Я пишу этот код вручную (может сделал некоторые ошибки правописания) PS 2. с -fPIC флагом он будет работать:

main.o: main.cpp 
    g++44 (CXXFLAGS) main.cpp -fPIC 

UPDATE можно решить эту проблему путем статического связывания libstdC++. Но все еще мои вопросы не отвечают :(Может быть, у кого-то есть время, чтобы посмотреть на это?

UPDATE2 Такая же проблема возникает в GCC 4.4.6 и 4.8.1.

+0

У меня недавно возникла аналогичная проблема и нашла этот отчет об ошибке: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=42679 Это не дает полного решения, но, надеюсь, некоторое понимание проблемы на наименее. – Filip

ответ

0

Я думаю, что вы столкнулись с той же проблемой, как и в When we are supposed to use RTLD_DEEPBIND?, где исполняемый файл получает копию глобальных переменных:

хорошо, что это замечательная особенность вас строительство основного приложения без-fPIC вариант. [...] Это означает, что, когда символ находится в libdep.so, он копируется в начальный сегмент данных основного исполняемого файла по этому адресу. Затем просматривается ссылка на duplicate в libdep.so и указывает на копию символа, который находится в главном исполняемом файле.

Благодаря RTLD_DEEPBIND, dynLib.so видит неправильный набор глобальных переменных из исходной libstdC++ при инициализации std::string и таким образом сбоев.

А почему линкер имеет такое поведение, this article имеет подробное объяснение (курсив мой):

Напомним, что программа/исполняемый файл не перемещаемый, и, таким образом, его адреса данных должны связаны во время компоновки , Поэтому компоновщик должен создать копию переменной в адресном пространстве программы, а динамический загрузчик будет использовать это в качестве адреса перемещения. Это похоже на обсуждение в предыдущем разделе - в некотором смысле, myglob в основной программе переопределяет одно в общей библиотеке, и в соответствии с глобальными правилами поиска символов используется вместо этого.

Последнее замечание: это поведение зависит от платформы, по крайней мере, на PowerPC такой дополнительной копии глобальных переменных в основном исполняемом файле нет.

Смежные вопросы