Statik olarak bağlantılı kitaplıklar arasındaki sembol çarpışmalarıyla nasıl başa çıkılır?


84

Bir kitaplık yazarken en önemli kurallardan ve en iyi uygulamalardan biri, kitaplığın tüm sembollerini kitaplığa özel bir ad alanına yerleştirmektir. C ++, namespaceanahtar kelime nedeniyle bunu kolaylaştırır . C'de alışılagelmiş yaklaşım, tanımlayıcıların önüne bazı kütüphaneye özgü önekler getirmektir.

C standardının Kuralları (güvenli derleme için) olanlar bazı kısıtlamalar koymak: AC derleyici bir tanımlayıcı sadece ilk 8 karakter bakmak olabilir, bu nedenle foobar2k_eggsve foobar2k_spamgeçerli bir şekilde aynı tanımlayıcıları olarak yorumlanabilir - ancak her modern derleyici keyfi uzun tanımlayıcıları için izin verir Bu nedenle, günümüzde (21. yüzyıl) bununla uğraşmamalıyız.

Peki ya sembol adlarını / tanımlayıcılarını değiştiremeyeceğiniz bazı kütüphanelerle karşı karşıyaysanız? Belki yalnızca statik bir ikili dosyaya ve başlıklara sahipsinizdir veya istemezsiniz veya kendinizi yeniden derlemenize ve ayarlamanıza izin verilmez.


Yanıtlar:


143

En azından statik kitaplıklar söz konusu olduğunda oldukça rahat bir şekilde çalışabilirsiniz.

Foo ve bar kitaplıklarının başlıklarını düşünün . Bu eğitimin iyiliği için size kaynak dosyaları da vereceğim

örnekler / ex01 / foo.h

örnekler / ex01 / foo.c (bu opak olabilir / mevcut olmayabilir)

örnek / ex01 / bar.h

örnekler / ex01 / bar.c (bu opak olabilir / mevcut olmayabilir)

Bunları bir program foobarında kullanmak istiyoruz

örnek / ex01 / foobar.c

Bir sorun hemen ortaya çıkıyor: C aşırı yüklemeyi bilmiyor. Yani aynı ada sahip ancak farklı imzaya sahip iki çarpı iki işlevimiz var. Bu yüzden bunları ayırt etmek için bir yola ihtiyacımız var. Her neyse, bir derleyicinin bu konuda ne söylediğini görelim:

Tamam, bu şaşırtıcı değildi, sadece bize zaten bildiklerimizi veya en azından şüphelendiklerimizi anlattı.

Öyleyse, orijinal kitaplıkların kaynak kodunu veya başlıklarını değiştirmeden bu tanımlayıcı çakışmasını bir şekilde çözebilir miyiz? Aslında yapabiliriz.

İlk olarak derleme zamanı sorunlarını çözelim. Bunun için, başlık #define, kitaplık tarafından dışa aktarılan tüm sembollerin önekini içeren bir dizi önişlemci yönergesi içerir. Daha sonra bunu hoş bir sarmalayıcı başlığıyla yapıyoruz, ancak sadece neler olduğunu göstermek adına foobar.c kaynak dosyasında aynen yapıyoruz :

örnek / ex02 / foobar.c

Şimdi bunu derlersek ...

... ilk önce işler daha da kötüye gidiyor gibi görünüyor. Ama yakından bakın: Aslında derleme aşaması gayet iyi gitti. Bu sadece şu anda çarpışan semboller olduğundan şikayet eden ve bize bunun gerçekleştiği yeri (kaynak dosya ve satır) söyleyen bağlayıcıdır. Ve gördüğümüz gibi bu semboller öneksizdir.

Nm yardımcı programıyla sembol tablolarına bir göz atalım :

Öyleyse şimdi opak bir ikili dosyada bu sembollerin önüne geçme alıştırması ile mücadele ediyoruz. Evet, bu örnekte kaynaklara sahip olduğumuzu ve bunu orada değiştirebileceğimizi biliyorum. Ama şimdilik, sadece bu .o dosyalarına veya bir .a'ya (aslında sadece bir grup .o ) sahip olduğunuzu varsayalım .

kurtarmaya itiraz etmek

Bizim için özellikle ilginç olan bir araç var: objcopy

objcopy geçici dosyalar üzerinde çalışır, böylece onu yerinde çalışıyormuş gibi kullanabiliriz. --Prefix-symbols adında bir seçenek / işlem vardır ve bunun ne yaptığını 3 tahmin edebilirsiniz.

Öyleyse bu adamı inatçı kütüphanelerimize atalım:

nm bize bunun işe yaradığını gösteriyor:

Tüm bu şeyi bağlamayı deneyelim:

Ve gerçekten işe yaradı:

Şimdi bunu okuyucuya, nm kullanarak bir kütüphanenin sembollerini otomatik olarak çıkaran , yapının bir sarmalayıcı başlık dosyasını yazan bir araç / komut dosyası uygulama alıştırması olarak bırakıyorum.

ve sembol önekini objcopy kullanarak statik kitaplığın nesne dosyalarına uygular .

Peki ya paylaşılan kitaplıklar?

Prensipte aynısı paylaşılan kütüphanelerle de yapılabilir. Ancak, adından da anlaşılacağı gibi paylaşılan kitaplıklar birden çok program arasında paylaşılır, bu nedenle paylaşılan bir kitaplıkla bu şekilde uğraşmak o kadar da iyi bir fikir değildir.

Bir trambolin ambalajı yazmakla uğraşmayacaksınız. Daha da kötüsü, nesne dosyası düzeyinde paylaşılan kitaplığa bağlanamazsınız, ancak dinamik yükleme yapmak zorunda kalırsınız. Ama bu kendi makalesini hak ediyor.

Bizi izlemeye devam edin ve iyi kodlamalar yapın.


4
Etkileyici! Onunla bu kadar kolay olmasını beklemiyordum objcopy.
Kos

12
Kendi sorunuzu sorduktan sonraki 1 dakika içinde mi cevapladınız?
Alex B

18
@Alex B: Bu bir öğretici makale ve meta.stackoverflow.com'da bana önerilen bir yolu takip ettim (ilginç?) Sorular ve çözümleri hakkında öğreticiler nasıl yerleştirilebilir. Kütüphanelerle çatışmakla ilgili soru gündeme geldi ve "hm, bu tür bir çözümle nasıl başa çıkacağımı biliyorum" diye düşündüm, bir makale yazdım ve buraya Soru-Cevap şeklinde gönderdim. meta.stackexchange.com/questions/97240/…
datenwolf

4
@datenwolf, iOS kitaplıkları için bu sorunu çözme konusunda herhangi bir fikir. Öğrendiğim gibi, objcopy iOS kitaplıklarını desteklemiyor: /
Ege Akpinar

6
Blah blah blah objcopy --prefix-symbols ... +1!
Ben Jackson

7

C standardının kuralları bunlara bazı kısıtlamalar getirmiştir (güvenli derleme için): AC derleyicisi bir tanımlayıcının yalnızca ilk 8 karakterine bakabilir, bu nedenle foobar2k_eggs ve foobar2k_spam aynı tanımlayıcılar olarak geçerli bir şekilde yorumlanabilir - ancak her modern derleyici keyfi olarak izin verir uzun tanımlayıcılar, bu nedenle zamanımızda (21. yüzyıl) bununla uğraşmamalıyız.

Bu sadece modern derleyicilerin bir uzantısı değildir; mevcut C standardı ayrıca derleyicinin oldukça uzun harici isimleri desteklemesini gerektirir . Tam uzunluğu unutuyorum ama doğru hatırlıyorsam 31 karakter gibi bir şey.

Peki ya sembol adlarını / tanımlayıcılarını değiştiremeyeceğiniz bazı kütüphanelerle karşı karşıyaysanız? Belki yalnızca statik bir ikili dosyaya ve başlıklara sahipsinizdir veya istemezsiniz veya kendinizi yeniden derlemenize ve ayarlamanıza izin verilmez.

O zaman sıkışırsın. Kütüphane yazarına şikayette bulunun. Bir keresinde böyle bir hatayla karşılaştım, uygulamamın kullanıcılarının Debian'ın libSDLbağlanması nedeniyle Debian üzerinde onu geliştiremedikleri libsoundfile, (en azından o sırada) küresel ad alanını korkunç bir şekilde dsp(şaka yapmıyorum!) Gibi değişkenlerle kirletti . Debian'a şikayette bulundum ve paketlerini düzelttiler ve düzeltmeyi yukarı akışa gönderdiler, çünkü sorunu bir daha hiç duymadım.

Bunun en iyi yaklaşım olduğunu düşünüyorum, çünkü sorunu herkes için çözüyor . Yapacağınız herhangi bir yerel hack, bir sonraki talihsiz kullanıcının karşılaşması ve tekrar mücadele etmesi için sorunu kütüphanede bırakacaktır.

Gerçekten hızlı bir düzeltmeye ihtiyacınız varsa ve kaynağınız varsa, -Dfoo=crappylib_foo -Dbar=crappylib_bardüzeltmek için makefile'a bir sürü vb. Ekleyebilirsiniz . Değilse, objcopybulduğunuz çözümü kullanın .


Elbette haklısınız, ancak bazen yukarıda gösterdiğim gibi kirli bir hack'e ihtiyacınız var. Örneğin, satıcının işsiz kaldığı veya benzeri eski bir kitaplıkta sıkışıp kaldıysanız. Bunu özellikle statik kitaplıklar için yazdım .
datenwolf

3

GCC kullanıyorsanız, --allow-multiple-definition linker anahtarı kullanışlı bir hata ayıklama aracıdır. Bu, bağlayıcıyı ilk tanımı kullanmaya zorlar (ve onun hakkında sızlanmamasına). Bununla ilgili daha fazla bilgiyi burada bulabilirsiniz .

Bu, geliştirme sırasında, satıcı tarafından sağlanan bir kitaplığın kaynağına sahip olduğumda ve herhangi bir nedenle bir kitaplık işlevini izlemem gerektiğinde bana yardımcı oldu. Anahtar, bir kaynak dosyanın yerel bir kopyasında derlemenize ve bağlanmanıza ve yine de değiştirilmemiş statik satıcı kitaplığına bağlanmanıza olanak tanır. Keşif yolculuğu tamamlandıktan sonra anahtarı, marka sembollerinden geri çekmeyi unutmayın. Kasıtlı ad alanı çakışmaları içeren gönderi sürüm kodu, kasıtsız ad alanı çarpışmaları dahil olmak üzere tuzaklara açıktır .

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.