2010-08-30 3 views
6

Я динамически загружаю (dlopen()) общий объект (по имени libprofile1.so) от main.ограничения при динамической загрузке общего объекта из другого общего объекта?

В libprofile1.so Я определил заводскую функцию CreateProfile и класс Profile. Функция CreateProfile создает экземпляр класса Profile и возвращает указатель на него. Класс Profile имеет способ pMethod.

В основном, после загрузки libprofile1.so, я звоню CreateProfile метод, который возвращает указатель на объект класса Profile (назовем его p).
Впоследствии, я звоню pMethod метод против объекта p (p->pMethod). В этом методе я динамически загружаю другой общий объект (libdatasources.so).

В этом общем объекте у меня есть заводская функция CreateDataSource и класс DataSource.
CreateDataSource Функция создает экземпляр класса DataSource и возвращает указатель на него. DataSource класс есть способ dsMethod.

Как вы можете заметить, структуры обоих общих объектов схожи.

С pMethod после загрузки libdatasources.so я звоню CreateDataSource метод, который возвращает меня указатель на экземпляр класса DataSource, назовем его ds. Затем я звоню dsMethod от ds объект
(ds->dsMethod).


Проблема в следующем.

Когда я пытаюсь позвонить dsMethod из ds объект, общий объект, который я загружаю сначала (libprofile1.so), не загружается. Фактически dlopen() возвращает NULL. Когда я прочитал dlerror после dlopen я получаю:

./libprofile1.so: undefined symbol: _ZN18DataSource13dsMethod

Так что, если у меня есть вызов ds->Method, чем первый общий объект не загружается!
Если я прокомментирую звонок ds->dsMethod от источника, тогда мои libprofile1.so и libdatasources.so загружаются без проблем.
Я не вижу связи между вызовом метода со второго SO, с загрузкой сначала SO ???

Возможно, я не знаю, но существуют ли какие-либо ограничения при динамической загрузке общего объекта из общего объекта, который также был динамически загружен?

Btw, dlopen Используется с RTLD_NOW|RTLD_GLOBAL. Я пробовал с RTLD_LAZY, но все та же проблема.

UPDATE:

библиотеки построены в Eclipse. Параметры для компилятора и компоновщика G ++ одинаковы для обеих библиотек.
Вот G ++ компилятор:

-O0 -g3 -Wall -c -fmessage-length=0 

и G ++ линкер:

-shared 

варианты, вставленный из Project Properties -> Settings -> Tool Settings

Заранее спасибо.

+0

Можете ли вы обновить свой вопрос о том, как создаются libprofile1.so и libdataresources.so? –

ответ

3

Если вы динамически загружаете DLL, вы должны убедиться, что у нее нет неразрешенных символов.

Самый простой способ сделать это - связать его с другими DLL, которые ему нужны, чтобы они загружались автоматически, а не вам приходилось загружать и разрешать все зависимости вручную.

Так что если libprofile1 всегда использует libdatasources, убедитесь, что они связаны друг с другом.

Если вы должны сделать это вручную, то измените порядок загрузки библиотек. Так что, когда вы загружаете libprofile1, функции, которые ему нужны, уже загружены.

4

Как отметил Мартин Йорк, так оно и работает в Linux. При связывании с библиотекой вы также должны ссылаться на все зависимости. В Windows это по-другому, библиотеки DLL сами заботятся о своих зависимостях. При динамической загрузке библиотеки, имеющей другую библиотеку в качестве зависимости, вам необходимо сначала загрузить эту библиотеку с флагом RTLD_GLOBAL. Это довольно awkard, imho, так как вы не можете знать, какие зависимости требуют другие общие объекты, или зависимости могут измениться с более новой версией, которая в противном случае является бинарной совместимостью. Из того, что я знаю (и от чтения g ++ и ld manpages), невозможно создать поведение, подобное DLL Windows. Вот немного TestCase:

two.cpp:

#include <iostream> 

extern "C" 
{ 
    void bar() 
    { 
     std::cout << "bar()\n"; 
    } 
} 

one.cpp:

#include <iostream> 

extern "C" 
{ 
    void bar(); 

    void foo() 
    { 
     std::cout << "foo()\n"; 
     bar(); 
    } 
} 

test.cpp:

#include <dlfcn.h> 
#include <iostream> 

int main (int argc, char *argv[]) 
{ 
    using namespace std; 
//  void *libtwo = dlopen("./libtwo.so", RTLD_NOW | RTLD_GLOBAL); 
    void *libone = dlopen("./libone.so", RTLD_NOW); 
    if (!libone) 
    { 
     cout << "dlopen(libone.so) failed: " << dlerror() << "\n"; 
     return 1; 
    } 
    typedef void (*foo_t)(); 
    foo_t foo = reinterpret_cast<foo_t>(dlsym(libone, "foo")); 
    if (!foo) 
    { 
     cout << "dlsym(libone.so, foo) failed\n"; 
     return 2; 
    } 
} 

one.cpp компилируется в libone.so и two.cpp в libtwo.so, test.cpp скомпилирован в test двоичный. Это не удастся и будет успешным только в том случае, если прокомментированная строка раскоментирована.

+0

довольно похожий пример, но дело в том, что в 'foo()' я загружаю 'libtwo.so', а затем получаю указатель на' bar() ', который я вызываю также из' foo() '. Я использую обходной путь для этой проблемы с помощью функции-оболочки 'barWrap()' для 'bar()'. 'barWrap()' не является частью класса, который я использую ('DataSource'), а скорее экспортирует независимую функцию. Из этой функции я вызываю 'bar()' (в моем случае 'ds-> dsMethod'), и он отлично работает. Кстати, я использовал 'RTLD_NOW | RTLD_GLOBAL' как аргумент' dlopen', но все равно ничего. Довольно странная проблема, я не могу понять. –

+0

Вы связываете libone.so с libtwo.so (-ltwo для компоновщика)? dlopen() не должен иметь проблем с загрузкой этого. Я добавлю свои командные строки gcc как новый комментарий. –

+1

$ g ++ -shared -fpic -o libtwo.so -Wl, -no-undefined two.cpp $ g ++ -shared -fpic -o libone.so -Wl, -no-undefined one.cpp -ltwo -L. -Wl, -rpath ,. $ g ++ test.cpp -ldl $ ./a.out –

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