2010-08-30 42 views
6

Tôi đang tải động (whith dlopen()) một đối tượng được chia sẻ (có tên libprofile1.so) từ main.ràng buộc khi tự động tải một đối tượng được chia sẻ từ một đối tượng được chia sẻ khác?

Trong libprofile1.so Tôi đã xác định chức năng nhà máy CreateProfile và lớp Profile. Hàm CreateProfile tạo ra một thể hiện của lớp Profile và trả về một con trỏ tới nó. Lớp Profile có phương thức pMethod.

Trong chính, sau khi tải libprofile1.so, tôi gọi phương thức CreateProfile trả về con trỏ tới đối tượng của lớp Profile (gọi là p).
Sau đó, tôi gọi phương thức pMethod đối với đối tượng p (p->pMethod). Trong phương pháp này, tôi tự động tải đối tượng được chia sẻ khác (libdatasources.so).

Trong đối tượng dùng chung này, tôi có chức năng nhà máy CreateDataSource và lớp DataSource.
CreateDataSource chức năng tạo ra một thể hiện của lớp DataSource và trả về một con trỏ đến nó. DataSource lớp học có phương pháp dsMethod.

Như bạn có thể nhận thấy, cấu trúc của cả hai đối tượng dùng chung là tương tự nhau.

Từ pMethod sau khi tải libdatasources.so Tôi đang gọi phương thức CreateDataSource, trả về con trỏ đến một phiên bản DataSource, gọi là ds. Sau đó, tôi gọi số dsMethod của ds đối tượng
(ds->dsMethod).


Hiện tại, sự cố xảy ra.

Khi tôi cố gắng gọi dsMethod của đối tượng ds, đối tượng được chia sẻ mà tôi tải lần đầu tiên (libprofile1.so) không tải. Trên thực tế, dlopen() trả lại NULL. Khi tôi đọc dlerror sau dlopen tôi nhận được:

./libprofile1.so: undefined symbol: _ZN18DataSource13dsMethod

Vì vậy, nếu tôi có một cuộc gọi ds->Method, hơn đầu tiên chia sẻ đối tượng không tải!
Nếu tôi nhận xét cuộc gọi ds->dsMethod từ nguồn, thì libprofile1.solibdatasources.so của tôi được tải mà không gặp bất kỳ sự cố nào.
Tôi không thấy kết nối giữa cuộc gọi của một phương thức từ SO thứ hai, với tải SO đầu tiên ???

Có lẽ tôi không biết, nhưng có bất kỳ ràng buộc nào khi tải động một đối tượng được chia sẻ, từ một đối tượng được chia sẻ cũng được tải động không?

Btw, dlopen được sử dụng với RTLD_NOW|RTLD_GLOBAL. Tôi đã thử với RTLD_LAZY, nhưng vẫn còn cùng một vấn đề.

UPDATE:

Thư viện được xây dựng trong Eclipse. Các tùy chọn cho trình biên dịch và trình liên kết G ++ giống nhau cho cả hai thư viện.
Dưới đây là G ++:

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

và G ++ linker:

-shared 

tùy chọn, dán từ Project Properties -> Settings -> Tool Settings

Cảm ơn trước.

+0

Bạn có thể cập nhật câu hỏi của mình cho biết cách libprofile1.so và libdataresources.so được tạo không? –

Trả lời

3

Nếu bạn tải động DLL, bạn phải đảm bảo rằng nó không có biểu tượng chưa được giải quyết.

Cách dễ nhất để làm điều này là liên kết nó với các DLL khác mà nó cần để chúng tải tự động thay vì bạn phải tải và giải quyết tất cả các phụ thuộc theo cách thủ công.

Vì vậy, nếu libprofile1 luôn sử dụng libdatasources đảm bảo chúng được liên kết với nhau.

Nếu bạn phải thực hiện thủ công, hãy đảo ngược thứ tự mà bạn tải thư viện. Vì vậy, khi bạn tải libprofile1 các chức năng cần thiết đã được tải.

4

Như Martin York đã chỉ ra, đó là cách nó hoạt động trong Linux. Khi liên kết với thư viện, bạn phải liên kết với tất cả các phụ thuộc. Đó là khác nhau trong Windows, DLLs chăm sóc bản thân phụ thuộc của họ. Khi tải động thư viện có thư viện khác dưới dạng phụ thuộc, trước tiên bạn cần tải thư viện đó bằng cờ RTLD_GLOBAL. Điều này là khá awkard, imho, kể từ khi bạn có thể không thể biết được phụ thuộc khác chia sẻ các đối tượng yêu cầu, hoặc phụ thuộc có thể thay đổi với một phiên bản mới hơn đó là nếu không tương thích nhị phân. Từ những gì tôi biết (và từ việc đọc g ++ và ld manpages), không thể tạo ra một hành vi tương tự như các DLL của Windows. Dưới đây là một testcase nhỏ:

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 được biên dịch vào libone.so và hai.cpp trong libtwo.so, test.cpp được biên dịch thành test nhị phân. Điều này sẽ không thành công và chỉ thành công khi dòng nhận xét không được chú ý.

+0

ví dụ khá giống nhau, nhưng điều là trong 'foo()' tôi đang tải 'libtwo.so' và sau đó nhận con trỏ hàm tới' bar() ', mà tôi gọi từ' foo() 'cũng. Tôi đang sử dụng một giải pháp cho vấn đề này với một hàm bao hàm 'barWrap()' cho 'bar()'. 'barWrap()' không phải là một phần của lớp mà tôi đang sử dụng ('DataSource'), mà là hàm độc lập được xuất khẩu. Từ hàm này tôi gọi 'bar()' (trong trường hợp của tôi 'ds-> dsMethod') và nó hoạt động tốt. Bằng cách này tôi đã sử dụng 'RTLD_NOW | RTLD_GLOBAL' làm đối số của' dlopen' nhưng vẫn không có gì. Khá là một vấn đề kỳ lạ, tôi dường như không thể hiểu được. –

+0

Bạn có liên kết libone.so với libtwo.so (-ltwo cho trình liên kết) không? dlopen() sau đó sẽ không có vấn đề tải này. Tôi sẽ thêm các dòng lệnh gcc của mình làm nhận xét mới. –

+1

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

Các vấn đề liên quan