2013-06-20 32 views
1

我正在玩dylib多重負載,並試圖瞭解是什麼使符號differents。什麼使得Dylib與另一個Dylib不同?

這裏是我的步驟:

  • 構建lib_a.dylib用下面的切入點:

    FactoryA : IFActory() {} 
    extern "C" IFactory* GetFactory() { return new FactoryA(); } 
    
  • 複製lib_a.dylib到lib_b.dylib從lib_a
  • 負載GetFactory。 dylib和lib_b.dylib

    void * module=dlopen(fileName,RTLD_LAZY); 
    void * proc = (void *)dlsym(module, "GetFactory");   
    

加載第二個dylib(lib_b.dylib)時,GetFactory被視爲已由lib_a.dylib定義。
實際上,nm輸出具有相同的結果。

但我認爲編譯標籤-two_level_namespace保證2 dylib是在一種不同的命名空間,我錯了嗎?

我可以改變我的兩個dylib加載?


下面是我的測試。

myclass.h:

#include <stdio.h> 
class IFactory { 
public: 
    virtual int GetCount() = 0; 
}; 
extern "C" 
{ 
    extern IFactory* GetFactory(); 
} 

myclass.cpp

#include <stdio.h> 
#include "myclass.h" 

class MyFactory : public IFactory { 
public: 
    virtual int GetCount() { mCount++; return mCount; } 
    static int mCount; 
}; 
int MyFactory::mCount = 0; 


IFactory* GetFactory() { 
    return new MyFactory; 
} 

mytest.cpp

#include <stdio.h> 
#include <dlfcn.h> 
#include <mach-o/dyld.h> 
#include "myclass.h" 

typedef IFactory* (*factoryPtr)(); 

int main() 
{ 
    void* handleA = dlopen("libmylib.dylib", RTLD_LAZY); 
    factoryPtr functionA = (IFactory*(*)())dlsym(handleA, "GetFactory"); 
    IFactory* factoryA = (*functionA)(); 
    fprintf(stderr, "Handle A: %p\tFunction A: %p\t Count: %d\n", handleA, functionA, factoryA->GetCount()); 

    // Reload same library 
    void* handleB = dlopen("libmylib.dylib", RTLD_LAZY); 
    factoryPtr functionB = (IFactory*(*)())dlsym(handleB, "GetFactory"); 
    IFactory* factoryB = (*functionB)(); 
    fprintf(stderr, "Handle B: %p\tFunction B: %p\t Count: %d\n", handleB, functionB, factoryB->GetCount()); 

    // Load copy of first library (just rename) 
    void* handleC = dlopen("libmylib_copy.dylib", RTLD_LAZY); 
    factoryPtr functionC = (IFactory*(*)())dlsym(handleC, "GetFactory"); 
    IFactory* factoryC = (*functionC)(); 
    fprintf(stderr, "Handle C: %p\tFunction C: %p\t Count: %d\n", handleC, functionC, factoryC->GetCount()); 

    return 0; 
} 

命令:

clang++ -dynamiclib myclass.cpp -o libmylib.dylib 
cp libmylib.dylib libmylib_copy.dylib 
clang++ mytest.cpp -o mytest 
./mytest 

輸出:

Handle A: 0x7fe5dac039b0 Function A: 0x106d49d30 Count: 1  
Handle B: 0x7fe5dac039b0 Function B: 0x106d49d30 Count: 2  
Handle C: 0x7fe5dac03e00 Function C: 0x106d7cd30 Count: 3 

。爲什麼我們要在最後數= 3?


屬性-fvisibility = hidden -fvisibility-inlines-hidden允許做同樣的事情。

修改myclass.h:

#include <stdio.h> 
#define EXPORT_FACTORY __attribute__ ((visibility ("default"))) 
class IFactory { 
public: 
    virtual int GetCount() = 0; 
}; 
extern "C" 
{ 
    extern EXPORT_FACTORY IFactory* GetFactory(); 
} 

體形:

clang++ -dynamiclib myclass.cpp -o libmylib.dylib -fvisibility=hidden -fvisibility-inlines-hidden 
cp libmylib.dylib libmylib_copy.dylib 
clang++ mytest.cpp -o mytest 
./mytest 

輸出:

Handle A: 0x7fe078c039b0 Function A: 0x1076e1c00 Count: 1 
Handle B: 0x7fe078c039b0 Function B: 0x1076e1c00 Count: 2 
Handle C: 0x7fe078c03e20 Function C: 0x107714c00 Count: 1 
+0

在兩個dylib上調用dlopen之後,如果使用dlsym(RTLD_NEXT,「GetFactory」)並調用它兩次,是否返回兩個函數? – TheDarkKnight

+0

我收到以下錯誤錯誤:dlsym(RTLD_NEXT,GetProcessorFactory):找不到符號 – PLL

+0

您可以將相關代碼添加到您的問題嗎? – TheDarkKnight

回答

0

這裏的根本問題是你的庫中的符號的可見性。您可以通過nm -m看到有除了GetFactory功能幾個外部符號,你打算出口:

$ nm -m libmylib.dylib 
0000000000000f30 (__TEXT,__text) external _GetFactory 
0000000000001068 (__DATA,__common) external __ZN9MyFactory6mCountE 
0000000000000f50 (__TEXT,__text) weak external __ZN9MyFactory8GetCountEv 
0000000000001038 (__DATA,__data) weak external __ZTI8IFactory 
0000000000001050 (__DATA,__data) weak external __ZTI9MyFactory 
0000000000000f91 (__TEXT,__const) weak external __ZTS8IFactory 
0000000000000f86 (__TEXT,__const) weak external __ZTS9MyFactory 
0000000000001020 (__DATA,__data) weak external __ZTV9MyFactory 
       (undefined) external __ZTVN10__cxxabiv117__class_type_infoE (from libc++) 
       (undefined) external __ZTVN10__cxxabiv120__si_class_type_infoE (from libc++) 
       (undefined) weak external __Znwm (from libc++) 
       (undefined) external dyld_stub_binder (from libSystem) 

它被標記爲weak external這是你的問題的原因的符號。

DYLD_PRINT_BINDINGS=YES運行測試應用程序顯示:

$ DYLD_PRINT_BINDINGS=YES ./mytest 
[ … output showing initialization of libstdc++.dylib and libmylib.dylib omitted …] 
Handle A: 0x7fc729c03810 Function A: 0x102a51ee0 Count: 1 
Handle B: 0x7fc729c03810 Function B: 0x102a51ee0 Count: 2 
dyld: bind: libmylib_copy.dylib:0x102A85038 = libc++abi.dylib:__ZTVN10__cxxabiv117__class_type_infoE, *0x102A85038 = 0x7FFF7CA67B50 + 16 
dyld: bind: libmylib_copy.dylib:0x102A85050 = libc++abi.dylib:__ZTVN10__cxxabiv120__si_class_type_infoE, *0x102A85050 = 0x7FFF7CA67BD0 + 16 
dyld: bind: libmylib_copy.dylib:0x102A85018 = libstdc++.6.dylib:__Znwm, *0x102A85018 = 0x7FFF938F0325 
dyld: bind: libmylib_copy.dylib:0x102A85000 = libdyld.dylib:dyld_stub_binder, *0x102A85000 = 0x7FFF9084E878 
dyld: weak bind: libmylib_copy.dylib:0x102A85030 = libmylib.dylib:__ZN9MyFactory8GetCountEv, *0x102A85030 = 0x102A51F00 
dyld: weak bind: libmylib_copy.dylib:0x102A85060 = libmylib.dylib:__ZTI8IFactory, *0x102A85060 = 0x102A52038 
dyld: weak bind: libmylib_copy.dylib:0x102A85028 = libmylib.dylib:__ZTI9MyFactory, *0x102A85028 = 0x102A52050 
dyld: weak bind: libmylib_copy.dylib:0x102A85040 = libmylib.dylib:__ZTS8IFactory, *0x102A85040 = 0x102A51F41 
dyld: weak bind: libmylib_copy.dylib:0x102A85058 = libmylib.dylib:__ZTS9MyFactory, *0x102A85058 = 0x102A51F36 
dyld: weak bind: libmylib_copy.dylib:0x102A85010 = libmylib.dylib:__ZTV9MyFactory, *0x102A85010 = 0x102A52020 
dyld: weak bind: libmylib_copy.dylib:0x102A85018 = libstdc++.6.dylib:__Znwm, *0x102A85018 = 0x7FFF938F0325 
Handle C: 0x7fc729c03c20 Function C: 0x102a84ee0 Count: 3 

如果你看一下weak bind行,你會看到我們前面看到被標記爲weak external這些符號被解析爲相同的符號名字在libmylib.dylib。我相信這個行爲與C++的One Definition Rule相關(「每個程序都應該包含該程序中使用的每個非內聯函數或對象的一個​​定義」)。由於程序中有相同非內聯函數的多個定義,因此鏈接器正試圖在加載時合併這些符號,因此只使用一個函數。

這裏最好的解決方案是每個庫將它的符號放在不同的命名空間中。既然你打算只導出C工廠函數,匿名命名空間就足夠了。這也具有將剩餘符號標記爲非外部的效果,這對於庫加載時間可能是有益的。

$ clang++ -Wl,-exported_symbol -Wl,_GetFactory -dynamiclib myclass.cpp -o libmylib.dylib 
$ cp libmylib.dylib libmylib_copy.dylib 
$ ./mytest 
Handle A: 0x7fc593403910 Function A: 0x1009e4e90 Count: 1 
Handle B: 0x7fc593403910 Function B: 0x1009e4e90 Count: 2 
Handle C: 0x7fc593403b10 Function C: 0x1009e7e90 Count: 1 

這導致我們前面看到被標記爲weak external符號:

或者,您可以只導出一個您的圖書館需要客戶端的符號欺騙的一個定義規則一點點私人的,因此他們只會解決相同圖像中的符號。

+0

匿名命名空間工作正常,謝謝! – PLL

+0

編譯時使用「-fvisibility = hidden -fvisibility-inlines-hidden」選項可以做同樣的事情並將符號設置爲非外部(私有外部) – PLL

+0

是的,這只是控制導出哪些符號的另一種方式,比如'-Wl, -exported_symbol'在我的例子中。 – bdash