2014-05-01 19 views
5

yüklenen paylaşılan kütüphane gecikme yapmayı gecikme Linux üzerinde yüklenen (kütüphanesini " libbar.so " adını let) ve umutla hayata geçirilmelidir yardımı ile sadece bir linker, C++ ile yazılmış kaynak koddaki herhangi bir şeyi değiştirmeyerek; ebeveyn kütüphanesinin kaynak kodunda ben Dlopen çağırmak istemiyorum demek() ne de dlsym() libbar.so bir fonksiyonu çağırmak için (en " libfoo.so " şeklinde adlandırır let) Çünkü kaynak kodu dağınık ve bakım sürecini zorlaştırıyorlar.nasıl paylaşılan kitaplık yapmak için bir yol aradım Linux

Neyse, bugüne kadar internette soruma ilişkin bazı belirsiz bilgi parçalarını bulduk (Kısacası, ben bile Linux üzerinde Visual Studio'nun /DELAYLOAD seçeneğe benzer şekilde devam bekliyorum), Bu nedenle, sizden gelen tüm soruların cevaplarını açıklığa kavuşturmak için aşağıdaki soruları cevaplamak çok güzel olurdu.

  1. GNU LD destek Linux üzerinde herhangi bir gecikmenin yükleme mekanizması mı?
  2. Yoksa, Clang?
  3. dlopen() ailesi, Linux'ta paylaşılan bir kitaplık gecikmesi oluşturmanın tek yolu nedir?

kütüphaneye bir yol ile GCC (g ++) ile -zlazy bayrağı geçmek test sahip değil (o bayrağı kabul gibiydi ama davranış yüklü gecikme libbar.so yapma bakmadı libbar.so, libbar.so'un ilk çağrısında bir istisna olmasını bekliyordum, ancak istisna aslında libfoo.so'a girmeden önce kaldırıldı. Öte yandan, Clang (clang ++) seçenek bayrağını dikkate almadığına dair bir uyarı yaptı.

+0

çınlama (daha önce en az 3.4 ve) bağlamak için gnu ld kullanır, böylece Orada herhangi bir yardım bulamazsınız. –

+0

@ Mats, yorumunuz için teşekkürler. Tamam, soru üzerine Clang'ı asla düşünmeyeceğim ve daha sonra yukarıdaki orijinal açıklamayı düzenleyeceğim. – Doofah

+0

Daha önce sorulmuştu: http://stackoverflow.com/questions/2957292/delay-load-equivalent-in-unix-based-systems – oakad

cevap

4

Gecikme yükleme bir çalışma zamanı özelliği DEĞİLDİR

Saygılarımızla,. MSVC++, Windows yardımı olmaksızın uyguladı. Ve dlopen, Linux'taki tek yoldur, GetProcAddress, Windows'daki tek çalışma zamanı yöntemidir.

Peki, gecikme yüklemesi nedir? Çok basit: Bir DLL'ye yapılan herhangi bir çağrı, bir işaretçiden geçmelidir (çünkü nereye yükleneceğini bilmiyorsunuz). Bu her zaman sizin için derleyici ve linker tarafından ele alınmıştır. Ancak, gecikme yüklemesiyle, MSVC++ bu işaretçiyi başlangıçta sizin için LoadLibrary ve GetProcAddress çağıran bir sapta ayarlar.

Clang, ld'un yardımı olmadan da aynısını yapabilir. Çalışma zamanında, sıradan bir dlopen çağrısı, ve Linux, Clang'ın onu eklediğini belirleyemiyor.

+0

@ MSalters, tekrar teşekkürler. Clang'in gecikme yüklemesini kendisi halledebileceğini mi kastediyorsun? Ben aslında MSVC++ gibi bir yardımcı işlevine sahip olmayı beklediğim gibi, derleyici veya linker'a bir seçenek bayraklarını geçirmemizi sağlıyor (yardımcı işlevinin adını unutmuşumdur) çünkü mekanizma gecikmeli yüklenen fonksiyonlar çağrısının arkasındaki her şeyi gizler (böylece Kullanıcı böyle bir yardımcı işlevine sahip olmak zorunda değildir, kaynak kodumuzun altında bir işlev denir). – Doofah

+0

Evet, ** __ delayLoadHelper2 ** yardımcı fonksiyon! Muhtemelen bazı derleyiciler ya da bağlayıcılar (Sun'ın bir şeyi ya da Apple'ın ld64'ü gibi) böyle bir yardımcı işlev sağlamış olmalıydı, fakat Linux'ta neler olduğunu bilmiyorum. (Ve Linux için çok yeni biriyim;) – Doofah

+0

@ user3591878: Linux'ta GCC, OS ile derleyici arasındaki ayrımın talihsiz bir alışkanlığına sahiptir, fakat bu bir UNIX geleneğidir. (Bkz. 'Ld' versus' ld.so'). Linux için yeni olman önemli değil, çünkü Linux'un önemi yok. GCC ve CLang yapıyor. Onlar "dlopen" olan herhangi bir işletim sisteminde Gecikmeli Yükleme uygulamasını uygulayabilirler. Aslında bunu yaptıkları anlamına gelmez. – MSalters

4

Bu işlev, Proxy design pattern kullanarak taşınabilir şekilde gerçekleştirilebilir.kodunda

şöyle bir şey olabilir: MSalters cevaplamak için

#include <memory> 

// SharedLibraryProxy.h 
struct SharedLibraryProxy 
{ 
    virtual ~SharedLibraryProxy() = 0; 

    // Shared library interface begin. 
    virtual void foo() = 0; 
    virtual void bar() = 0; 
    // Shared library interface end. 

    static std::unique_ptr<SharedLibraryProxy> create(); 
}; 

// SharedLibraryProxy.cc 
struct SharedLibraryProxyImp : SharedLibraryProxy 
{ 
    void* shared_lib_ = nullptr; 
    void (*foo_)() = nullptr; 
    void (*bar_)() = nullptr; 

    SharedLibraryProxyImp& load() { 
     // Platform-specific bit to load the shared library at run-time. 
     if(!shared_lib_) { 
      // shared_lib_ = dlopen(...); 
      // foo_ = dlsym(...) 
      // bar_ = dlsym(...) 
     } 
     return *this; 
    } 

    void foo() override { 
     return this->load().foo_(); 
    } 

    void bar() override { 
     return this->load().bar_(); 
    } 
}; 

SharedLibraryProxy::~SharedLibraryProxy() {} 

std::unique_ptr<SharedLibraryProxy> SharedLibraryProxy::create() { 
    return std::unique_ptr<SharedLibraryProxy>{new SharedLibraryProxyImp}; 
} 

// main.cc 
int main() { 
    auto shared_lib = SharedLibraryProxy::create(); 
    shared_lib->foo(); 
    shared_lib->bar(); 
} 
+0

@ Maxim, pratik sözde kodu bıraktığınız için teşekkürler. Bu tasarım deseni, her iki kütüphaneyi de birleştirmek için çok yararlı görünüyor :) – Doofah

+0

Bu sadece işlevler için çalışır. Çalışma zamanı bağlayıcıda uygulanmadığı sürece değişkenlere yönelik bir yol yoktur. Soru şu ki, bu mümkün olsa bile. – Lothar

+0

@Lothar Bu sonuca nasıl geldiniz? Bu mekanizma hem işlevler hem de nesneler için çalışır. –

0

eklemek için, bir can Linux üzerinde tembel yükleme kolayca taklit, Windows yaklaşımı gerekli dlopen etmeye çalışacağını küçük statik saplama kitaplığı oluşturarak herhangi bir işlevine ilk çağrı üzerine kütüphane (tanı mesajını yayan ve dlopen başarısız olursa sonlanma) ve daha sonra tüm çağrıları ona iletme.

Böyle saplama kütüphaneler proje/kitaplık özel komut dosyası tarafından oluşturulan veya evrensel alet Implib.so tarafından oluşturulan elle yazılabilir:

$ gen-implib.py libxyz.so 
$ gcc myapp.c libxyz.tramp.S libxyz.init.c ... 
İlgili konular