Libstdc ++ 'yı statik olarak bağlamak: var mı?


93

GCC 4.7'nin libstdc ++ ile Ubuntu 12.10 üzerine kurulu bir C ++ uygulamasını, oldukça eski bir libstdc ++ sürümüyle birlikte gelen Ubuntu 10.04 çalıştıran sistemlere dağıtmam gerekiyor.

Şu anda, -static-libstdc++ -static-libgccbu blog gönderisinde önerildiği gibi, libstdc ++ 'statik olarak bağlama . Yazar, libstdc ++ 'yı statik olarak derlerken dinamik olarak yüklenmiş herhangi bir C ++ kodunu kullanmaya karşı uyarıyor, bu henüz kontrol etmediğim bir şey. Yine de, her şey şu ana kadar sorunsuz gidiyor gibi görünüyor: Ubuntu 10.04'te C ++ 11 özelliklerinden yararlanabiliyorum, ki bu peşindeydim.

Bu makalenin 2005 yılına ait olduğunu ve o zamandan beri belki de çok şey değiştiğini not ediyorum. Tavsiyeleri hala güncel mi? Farkında olmam gereken gizli sorunlar var mı?


Hayır, statik olarak libstdc ++ 'ya bağlanmak bunu ima etmez. O zaman -static-libstdc++seçeneğin bir anlamı olmayacağını ima etseydi, sadece kullanırdın-static
Jonathan Wakely

@JonathanWakely -static kernel too oldbazı ubuntu 1404 sistemlerinde hata alır. Glibc.so kernel32.dllpencerede olduğu gibidir , işletim sistemi arayüzünün bir parçasıdır, onu ikilimize yerleştirmemeliyiz. objdump -T [binary path]Dinamik olarak yüklü olup libstdc++.soolmadığını görmek için kullanabilirsiniz . Golang programcısı için, #cgo linux LDFLAGS: -static-libstdc++ -static-libgcciçe aktarmadan önce "C" ekleyebilirsiniz
bronz adam

@bronzeman ama neden bahsettiğimizi -static-libstdc++değil -staticbu yüzden libc.sostatik bağlantılı olmayacaktır.
Jonathan Wakely

1
@NickHutchinson, bağlantılı blog yazısı gitti. Bu SO sorusu, buradaki alakalı terimler için popüler bir arama sonucu. Sorunuzdaki o blog gönderisindeki kritik bilgileri yeniden üretebilir misiniz veya nereye taşındığını biliyorsanız yeni bir bağlantı önerebilir misiniz?
Brian Cain

Yanıtlar:


136

Bu blog yazısı oldukça yanlış.

Bildiğim kadarıyla C ++ ABI değişiklikleri, GCC'nin her büyük sürümünde (yani farklı birinci veya ikinci sürüm numarası bileşenlerine sahip olanlar) tanıtıldı.

Doğru değil. GCC 3.4'ten bu yana sunulan tek C ++ ABI değişiklikleri geriye dönük uyumludur, yani C ++ ABI yaklaşık dokuz yıldır kararlıdır.

Sorunları daha da kötüleştirmek için, çoğu büyük Linux dağıtımı GCC anlık görüntülerini kullanır ve / veya GCC sürümlerini yamalar, bu da ikili dosyaları dağıtırken hangi GCC sürümleriyle uğraştığınızı tam olarak bilmeyi neredeyse imkansız hale getirir.

Dağıtımların yamalı GCC sürümleri arasındaki farklar küçüktür ve ABI değişmez, örneğin Fedora'nın 4.6.3 20120306 (Red Hat 4.6.3-2) yukarı akış FSF 4.6.x sürümleriyle ABI uyumludur ve neredeyse kesin olarak herhangi bir 4.6 ile uyumludur. diğer dağıtımlardan x.

GNU / Linux'ta GCC'nin çalışma zamanı kitaplıkları ELF sembol versiyonunu kullanır, bu nedenle nesneler ve kütüphaneler için ihtiyaç duyulan sembol versiyonlarını kontrol etmek kolaydır ve eğer libstdc++.sobu sembolleri sağlayan bir şeye sahipseniz çalışır, biraz farklı bir yamalı versiyon olması fark etmez. dağıtımınızın başka bir sürümünden.

ancak C ++ kodu (veya C ++ çalışma zamanı desteğini kullanan herhangi bir kod) işe yarayacaksa dinamik olarak bağlanamaz.

Bu da doğru değil.

Bununla birlikte, statik olarak bağlantı kurmak libstdc++.asizin için bir seçenektir.

Bir kitaplığı dinamik olarak dlopenyüklerseniz (kullanarak ) işe yaramamasının nedeni, bağlı olduğu libstdc ++ sembollerine, onu bağladığınızda (statik olarak) uygulamanız tarafından ihtiyaç duyulmamış olabilir, bu nedenle bu semboller çalıştırılabilir dosyanızda bulunmayacaktır. Bu, paylaşılan kitaplığa dinamik olarak bağlanarak çözülebilir libstdc++.so(bu, buna bağlıysa yine de yapılacak doğru şeydir.) ELF simge enterpozisyonu, çalıştırılabilir dosyanızda bulunan sembollerin paylaşılan kitaplık tarafından kullanılacağı, ancak diğerlerinin kullanılmayacağı anlamına gelir. Yürütülebilir dosyanızda mevcut olan, libstdc++.sobağlantı verdiği her yerde bulunacaktır . Uygulamanız kullanmıyorsa dlopen, bunu önemsemenize gerek yoktur.

Başka bir seçenek (ve benim tercih ettiğim), libstdc++.souygulamanızın yanında yenisini dağıtmak ve varsayılan sistemden önce bulunmasını sağlamaktır; bu, libstdc++.sodinamik bağlayıcıyı doğru yere bakmaya zorlayarak, ya $LD_LIBRARY_PATHçalışma sırasında ortam değişkenini kullanarak yapılabilir . zaman veya RPATHbağlantı zamanında çalıştırılabilir bir dosya ayarlayarak . RPATHUygulamanın çalışması için doğru ayarlanan ortama bağlı olmadığı için kullanmayı tercih ediyorum . Eğer birlikte uygulama bağlantı varsa '-Wl,-rpath,$ORIGIN'(genişletmeye çalışırken kabuk önlemek için tek tırnak not $ORIGINardından yürütülebilir sahip bir olacaktır) RPATHait $ORIGINolan kendisi yürütülebilir aynı dizinde paylaşılan kütüphaneler için görünüm dinamik bağlayıcı söyler. Daha yenisini koyarsanlibstdc++.soçalıştırılabilir dosya ile aynı dizinde, çalışma zamanında bulunacaktır, sorun çözülmüştür. (Diğer bir seçenek olarak yürütülebilir koymaktır /some/path/bin/öylesine. Ve daha yeni libstdc ++ /some/path/lib/ile ve bağlantı '-Wl,-rpath,$ORIGIN/../lib'yürütülebilir için başka bir sabit konum göreli veya ve rpath göre ayarlanır $ORIGIN)


8
Özellikle RPATH ile ilgili bu açıklama şaşaalı.
nilweed

3
Linux'ta uygulamanızla birlikte libstdc ++ göndermek kötü bir tavsiye. Bunun getirdiği tüm dramayı görmek için Google "steam libstdc ++" için. Bu kütüphaneleri kullanarak olacaktır Kısacası, eğer istediğiniz exe yükler harici kütüphaneleri (gibi OpenGL) için Dlopen libstdc ++ tekrar (radeon sürücüleri gibi) sizin yerine hangi kendilerine ait, zaten yüklü çünkü ++ libstdc ne gerekmez ve beklemek. Yani, başa döndün.

7
@cap, OP özellikle libstdc ++ sisteminin daha eski olduğu bir dağıtıma dağıtmayı soruyor. Steam problemi, bir libstdc ++ paketlemiş olmalarıdır, bu yüzden sistemden daha eskiydi (muhtemelen paketledikleri sırada daha yeniydi, ancak dağıtımlar daha da yenilerine geçti). Bu, RPATH'nin libstdc++.so.6, kurulum sırasında paketlenmiş kütüphaneye veya daha yeniyse sisteme işaret edecek şekilde ayarlanmış bir sembolik bağ içeren bir dizini işaret etmesiyle çözülebilir . Red Hat DTS tarafından kullanılan daha karmaşık karma bağlantılı modeller vardır, ancak bunları kendi başınıza yapmak zordur.
Jonathan Wakely

5
Merhaba dostum, geriye doğru uyumlu ikili dosyalar göndermek için modelimin "diğer kişilere libstdc ++ ABI uyumlu tutmaya güvenmesi" veya "çalışma zamanında libstdc ++ koşullu olarak bağlanma" içermesini istemiyorsam özür dilerim. ve orada, ne yapabilirim ki, saygısızlık yok. Ve memcpy@GLIBC_2.14 dramasını hatırlarsanız, bununla ilgili güven sorunları yaşadığım için beni gerçekten

6
'-Wl, -rpath, $ ORIGIN' kullanmak zorunda kaldım (rpath'in önündeki '-' işaretine dikkat edin). Yanıtı düzenleyemiyorum çünkü düzenlemeler en az 6 karakter olmalı ....
user368507

11

Jonathan Wakely'nin mükemmel cevabına bir ekleme, neden dlopen () sorunludur:

GCC 5'teki yeni istisna işleme havuzu nedeniyle ( PR 64535 ve PR 65434'e bakın ), libstdc ++ ile statik olarak bağlantılı bir kitaplığı dlopen ve dlclose ederseniz, her seferinde bir bellek sızıntısı (havuz nesnesi için) alırsınız. Dolayısıyla, dlopen kullanma şansınız varsa, libstdc ++ 'yı statik olarak bağlamak gerçekten kötü bir fikir gibi görünüyor. PR 65434'te belirtilen zararsız olanın aksine bunun gerçek bir sızıntı olduğunu unutmayın .


1
İşlev __gnu_cxx::__freeres(), havuz nesnesinin dahili tamponunu serbest bıraktığı için bu sorunla ilgili en azından biraz yardım sağlıyor gibi görünüyor. Ancak benim için, bu işleve yapılan bir çağrının, sonradan kazara atılan istisnalara ilişkin olarak hangi anlamının olduğu oldukça açık değil.
phlipsy

3

Jonathan Wakely'nin RPATH ile ilgili cevabına bir eklenti:

RPATH yalnızca söz konusu RPATH, çalışan uygulamanın RPATH'si ise çalışacaktır . Kendi RPATH'si aracılığıyla herhangi bir kitaplığa dinamik olarak bağlanan bir kitaplığınız varsa, kitaplığın RPATH'ı, onu yükleyen uygulamanın RPATH'ı tarafından üzerine yazılacaktır. Bu, uygulamanın RPATH değerinin kitaplığınızın RPATH değeriyle aynı olduğunu garanti edemediğinizde, örneğin bağımlılıklarınızın belirli bir dizinde olmasını bekliyorsanız, ancak bu dizin uygulamanın RPATH'sinin bir parçası değilse bir sorundur.

Örneğin, GCC 4.9 için libstdc ++. So.x'e dinamik olarak bağlı bağımlılığı olan bir App.exe uygulamanız olduğunu varsayalım. App.exe, RPATH aracılığıyla bu bağımlılığı çözmüştür, yani

App.exe (RPATH=.:./gcc4_9/libstdc++.so.x)

Şimdi diyelim ki, GCC 5.5 için libstdc ++. So.y'ye dinamik olarak bağlı bağımlılığı olan başka bir Dependency.so kütüphanesi daha var. Buradaki bağımlılık, kütüphanenin RPATH'si aracılığıyla çözülür, yani

Dependency.so (RPATH=.:./gcc5_5/libstdc++.so.y)

App.exe Dependency.so yüklediğinde , kitaplığın RPATH'sini ne ekler ne de önüne ekler . Ona hiç danışmıyor. Dikkate alınan tek RPATH, çalışan uygulamanınki veya bu örnekte App.exe olacaktır. Bu, eğer kütüphane gcc5_5 / libstdc ++. So.y'de bulunan ancak gcc4_9 / libstdc ++. So.x'te olmayan sembollere dayanıyorsa, kütüphane yüklenemeyecek demektir.

Geçmişte bu sorunlarla bizzat kendim karşılaştığım için, bu sadece bir uyarı kelimesi. RPATH çok kullanışlı bir araçtır ancak uygulamasının hala bazı sorunları vardır.


bu yüzden paylaşılan kütüphaneler için RPATH bir nevi anlamsız! Ve son yirmi yılda Linux'u bu açıdan biraz geliştirdiklerini umuyordum ...
Frank Puck

2

Dinamik glibc'ye bağlı olmadığınızdan da emin olmanız gerekebilir. Run lddsonuçta belirlenen çalıştırılabilir ve herhangi bir dinamik bağımlılıkları not (libc / libm / başka libpthread evre usal şüpheliler).

Ek alıştırma, bu metodolojiyi kullanarak bir dizi C ++ 11 örneği oluşturmak ve sonuçta ortaya çıkan ikili dosyaları gerçek bir 10.04 sisteminde denemek olacaktır. Çoğu durumda, dinamik yüklemeyle garip bir şey yapmazsanız, programın çalışıp çalışmadığını hemen anlarsınız.


1
Dinamik glibc'ye bağlı olarak sorun nedir?
Nick Hutchinson

En azından bir süre önce libstdc ++ 'nın glibc'ye bağımlılığı ima ettiğine inanıyorum. Bugün işlerin nerede durduğundan emin değilim.
Alexander L. Belikoff

9
libstdc ++, glibc'ye bağlıdır (örneğin, iostreams açısından uygulanır printf) ancak Ubuntu 10.04'teki glibc, yeni libstdc ++ tarafından ihtiyaç duyulan tüm özellikleri sağladığı sürece, dinamik glibc'ye bağlı olarak herhangi bir sorun yoktur, aslında asla bağlanmaması şiddetle tavsiye edilir. statik olarak glibc'ye
Jonathan Wakely

1

Jonathan Wakely'nin cevabına aşağıdakileri eklemek istiyorum.

-static-libstdc++Linux üzerinde oynarken sorunla karşılaştım dlclose(). Diyelim ki statik olarak bağlantılı bir 'A' uygulamamız var ve çalışma zamanında 'P' eklentisine libstdc++dinamik olarak bağlı libstdc++yükleniyor. Bu iyi. Ancak "A", "P" yi kaldırdığında, segmentasyon hatası oluşur. Benim varsayımıma göre, boşaltma işleminden sonra libstdc++.so, 'A' artık ilgili sembolleri kullanamaz libstdc++. Hem 'A' hem de 'P' statik olarak libstdc++bağlıysa veya 'A' dinamik ve 'P' statik olarak bağlantılıysa, sorunun oluşmayacağını unutmayın.

Özet: Uygulamanız dinamik libstdc++olarak bağlanabilecek eklentileri yüklüyor / kaldırıyorsa , uygulamanın da ona dinamik olarak bağlanması gerekir. Bu sadece benim gözlemim ve yorumlarınızı almak istiyorum.


1
Bu muhtemelen libc uygulamalarını karıştırmaya benzer (örneğin, glibc'yi dinamik olarak bağlayan bir eklentiye dinamik olarak bağlanırken, uygulamanın kendisi statik olarak musl-libc'ye bağlıdır). Musl-libc'nin yazarı Rich Felker, böyle bir senaryodaki sorunun glibc bellek yönetiminin (kullanımının sbrk) belirli varsayımlar yapması ve hemen hemen tek bir süreç içinde yalnız kalmayı beklemesi olduğunu iddia ediyor ... belirli glibc sürümü veya her neyse.
0xC0000022L

ve insanlar, libc ++ / libc'nin birden çok bağımsız kopyasını tek bir süreç içinde ele alabilen windows yığın arayüzünün avantajlarını hala göremiyorlar. Böyle insanlar yazılım tasarlamamalı.
Frank Puck

@FrankPuck, hem Windows hem de Linux deneyimine sahip yeterli miktarda MSVC, hangi ayırıcının nasıl kullanılacağına karar veren taraf olduğunda "Windows" un size yardımcı olmayacağını söyleyebilirim. Windows'ta yığınlarla gördüğüm ana avantaj, bitleri ve parçaları dağıtabilmeniz ve ardından bunları tek bir hamlede serbest bırakabilmenizdir. Ancak MSVC ile, örneğin başka bir VC çalışma zamanı tarafından tahsis edilen işaretçilerin etrafından geçerken (serbest bırakmaya karşı hata ayıklamaya veya statik olarak veya dinamik olarak bağlantılı) yukarıda açıklanan sorunla karşılaşacaksınız. Yani "Windows" bundan muaf değildir. Her iki sistemde de dikkatli olunmalıdır.
0xC0000022L
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.