Linux ikili dosyası neden çalışmaz: glibc ve musl farkı
GLIBC_2.34 not found hatası, derleme yapılan makinenin sürüm sınırını belirlemesinden kaynaklanır. İkili dosyanın bağımlılıklarını inceleyip taşınabilir dağıtım yöntemlerini öğrenin.
Bir Linux ikili dosyasının eski bir sunucuda çalışmama nedeni
Taşınabilir bir Linux ikili dosyası oluşturmak zordur çünkü GNU C kütüphanesi olan glibc, uyumluluğu yalnızca tek yönde garanti eder. Eski bir ikili dosya, yeni bir glibc üzerinde çalışmaya devam eder. Yeni bir ikili dosya ise eski bir glibc üzerinde çalışmaz. Derleme yaptığınız makine, dağıtım yapacağınız her makine için alt sınırı belirler.
Hata mesajı, ihtiyaç duyulan tam sürümü belirtir:
./mytool: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.38' not found (required by ./mytool)Dosya bozuk değildir ve genellikle yanlış yapılandırılmış bir durum yoktur. Bunun tek istisnası, yükleyiciyi daha eski bir libc'ye yönlendiren bir LD_LIBRARY_PATH ayarıdır; mesajdaki yol, aslında hangi libc'nin okunduğunu gösterir. Dinamik yükleyici, ikili dosyanın içine kaydedilmiş bir sürüm etiketini okumuş, bu etiketi ilgili libc içinde aramış, bulamamış ve programı başlatmayı reddetmiştir. Dosyayı yeniden indirmek ve chmod +x komutunu çalıştırmak hiçbir şeyi değiştirmez çünkü gereksinim dosyanın kendi içine yazılmıştır. Ya ikili dosyanın oluşturulma biçimini değiştirmeli ya da dağıttığınız dosyayı değiştirmelisiniz.
glibc sembol sürümlemenin gerçek işlevi
glibc tarafından dışa aktarılan her fonksiyon bir sürüm etiketi taşır. printf içindeki libc.so.6 aslında printf@@GLIBC_2.2.5'dır. glibc bir fonksiyonun davranışını veya ABI'sini (uygulama ikili arayüzü) değiştirdiğinde, eskisini silmez. Eski kodu eski etiket altında tutar ve yeni kodu yeni bir etiketle ekler; böylece tek bir libc.so.6 aynı anda aynı sembolün birkaç sürümünü barındırabilir. 2009 yılından kalma bir ikili dosya, 2009 etiketini yerinde bulur; ileriye dönük uyumluluğun bu kadar iyi çalışmasının nedeni budur.
Bağlama adımı, kullanılan sürümü kaydeder. İkili dosyanız, özetle "libc.so.6 içinden GLIBC_2.38'a ihtiyacım var" diyen bir .gnu.version_r bölümü edinir. Daha eski bir libc bu etikete hiçbir zaman sahip olmadığı için, yükleyici main çalışmadan önce durur. Bir geri dönüş (fallback) mekanizması yoktur, çünkü geri dönüş, programın derlendiği fonksiyondan farklı bir fonksiyonun sessizce programa verilmesi anlamına gelir.
2021'den bu yana en yaygın tetikleyici glibc 2.34 sürümüdür. Bu sürüm, libpthread ve libdl'ü libc içinde birleştirmiş ve her C programını başlatan fonksiyon olan __libc_start_main'i GLIBC_2.34 etiketine taşımıştır. Bu nedenle, glibc 2.34 veya daha yeni bir sürüme sahip herhangi bir dağıtımda derlenen bir "hello-world" programı, kaynak kod modern hiçbir şey çağırmasa bile GLIBC_2.34 gerektirir. Kendi kodları yıllardır değişmeyen kişiler için sorunun aniden ortaya çıkmasının nedeni budur.
Dağıtımım hangi glibc sürümünü kullanıyor?
Bu grafiğin arkasındaki veriler
[
{
"distro": "CentOS 7",
"glibc": 2.17
},
{
"distro": "RHEL 8",
"glibc": 2.28
},
{
"distro": "Ubuntu 20.04",
"glibc": 2.31
},
{
"distro": "Debian 11",
"glibc": 2.31
},
{
"distro": "RHEL 9",
"glibc": 2.34
},
{
"distro": "Ubuntu 22.04",
"glibc": 2.35
},
{
"distro": "Debian 12",
"glibc": 2.36
},
{
"distro": "Ubuntu 24.04",
"glibc": 2.39
},
{
"distro": "Debian 13",
"glibc": 2.41
}
]Bunlar, dağıtımlar tarafından yayınlanan ve Ağustos 2026 itibarıyla güncel olan, her dağıtımın kendi depolarında sunduğu sürümlerdir. CentOS 7'nin kullanım ömrü çoktan dolmuştur; ancak devralınan sunucularda hala çalıştırıldığı için listede tutulmaktadır. Sahip olduğunuz herhangi bir sunucuyu, ilk satırında glibc sürümünü yazdıran ldd --version komutuyla veya getconf GNU_LIBC_VERSION ile kontrol edin.
Bu 9 satırı bir merdiven gibi okuyun. Debian 13 üzerinde glibc 2.41 sürümüyle derleme yaparsanız, elde ettiğiniz sonuç Debian 13'ten daha eski hiçbir sistemde çalışmaz. Ubuntu 20.04 üzerinde glibc 2.31 sürümüyle derleme yaparsanız, aynı kaynak kod bu satırın üzerindeki tüm sistemlerde (Debian 13 dahil) çalışır. Desteklemeniz gereken en eski sunucu, dikkate almanız gereken tek derleme ana bilgisayarıdır; bu nedenle standartlaştırdığınız dağıtım, derlediğiniz her şey için ABI tabanını belirler. Bu karar, VPS üzerinde hangi işletim sisteminin çalıştırılacağının seçilmesi sürecindeki diğer ödünleşimlerle birlikte, sunucu filosu kurulmadan önce verilmelidir.
Bir binary dosyasının ihtiyaç duyduğu minimum glibc sürümü nasıl bulunur?
Bunu dosyanın içinden okuyun. Bu yöntem, derleme notları olmayan ve bir tedarikçi tarafından sağlanan binary dosyaları dahil olmak üzere her şey üzerinde çalışır.
objdump -T ./mytool | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -5Son satır alt sınırı belirtir. Eğer objdump eksikse, binutils paketini kurun. Eğer o sunucuya herhangi bir şey kuramıyorsanız, strings -a ./mytool | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -5 komutu yaklaşık bir sonuç verir; çünkü etiketler dosya içinde düz metin dizileri olarak saklanır.
readelf -V ./mytool komutu aynı gereksinimi yapısal olarak gösterir. Version needs section '.gnu.version_r' başlığı altındaki bloğu inceleyin. Bu blok, ilgili kütüphane altında her etiket için bir Name: GLIBC_x.y satırı listeler.
Alt sınırı hangi fonksiyonun belirlediğini bulmak için etiketi grep ile aratın: objdump -T ./mytool | grep GLIBC_2.38. Bu genellikle tek bir semboldür ve bazen kaçınabileceğiniz bir sembol olabilir. Eğer objdump -T hiçbir çıktı vermiyorsa, binary statik olarak bağlanmıştır; dolayısıyla dinamik sembol tablosu ve yazdırılacak bir sürüm gereksinimi yoktur.
file ve readelf -d komutlarının size bir binary dosyası hakkında söyledikleri
file, size tek satırda mimariyi ve bağlantı türünü verir.
file ./mytoolSıradan bir glibc derlemesi şu şekilde görünür:
ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=..., for GNU/Linux 3.2.0, not strippedStatik bir derleme statically linked çıktısını verir ve herhangi bir yorumlayıcı (interpreter) belirtmez. Bir musl derlemesi ise farklı bir yorumlayıcı belirtir:
ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-x86_64.so.1, ...Önemli olan alan yorumlayıcıdır. Bu, çekirdeğin programınızdan önce çalıştırdığı dinamik yükleyicidir ve o tam yolun hedef makinede mevcut olması gerekir. /lib/ld-musl-x86_64.so.1, birisi oraya musl kurmadığı sürece bir Debian veya Ubuntu sunucusunda bulunmaz.
readelf -d ./mytool, binary dosyasının ihtiyaç duyduğu paylaşımlı kütüphaneleri isimleriyle listeler:
Dynamic section at offset 0x2d58 contains 27 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libssl.so.3]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000001d (RUNPATH) Library runpath: [$ORIGIN/../lib]Her NEEDED girdisi, soname ile eşleşen zorunlu bir gereksinimdir. libssl.so.3, OpenSSL 3'tür; dolayısıyla bu binary, sadece libssl.so.1.1 sürümüne sahip bir sunucuda çalışmayacaktır. Bunun nedeni soname bilgisidir: farklı bir soname, kasıtlı olarak uyumsuz bir ABI anlamına gelir. RUNPATH, yükleyicinin binary dosyasının doğrudan bağımlılıkları için arama yaptığı dizinleri, herhangi bir LD_LIBRARY_PATH ayarından sonra ancak sistem kütüphane önbelleğinden ve varsayılan yollardan önce ekler. $ORIGIN, binary dosyasını içeren dizine genişler; kendi kendine yeten bir paketin kendi kütüphanelerini bu şekilde bulması sağlanır.
Güvenmediğiniz bir binary üzerinde ldd çalıştırmayın. glibc üzerinde ldd, programı yükleyici altında çalıştırarak bağımlılıkları çözümleyebilir; bu nedenle kötü niyetli bir dosya kod çalıştırma imkanı bulur. readelf ve objdump ise yalnızca baytları okur. Tüm bunlardan önce, indirilen dosyanın projenin gerçekten yayınladığı dosya olduğunu doğrulayın; çünkü bir indirmeyi yayınlanan sağlama toplamı ile doğrulamak, elinizdeki baytların kime ait olduğunu size söyleyen tek adımdır.
Karşılaşacağınız hatalar ve her birinin anlamı
Yükleyici, bulamadığı bir GLIBC sürümünü belirtiyor. İkili dosya, hedef sistemdekinden daha yeni bir glibc sürümüne karşı derlenmiştir. Hedef sistemde yüklü hiçbir şey bunu güvenli bir şekilde düzeltemez. Aşağıdaki dört seçenekten birini seçin.
Kabuk, açıkça görebildiğiniz bir dosya için No such file or directory hatası veriyor. Eksik olan şey ikili dosya değil, yorumlayıcıdır. ELF başlığındaki yükleyici yolu bulunmadığında execve komutu ENOENT döndürür ve kabuk elindeki tek hata mesajını yazdırır. file komutunu çalıştırın ve yorumlayıcı yolunu okuyun. Sadece glibc içeren bir sunucuda musl ile bağlanmış bir ikili dosya tam olarak bu belirtiyi verir.
cannot execute binary file: Exec format error genellikle mimarinin yanlış olduğu anlamına gelir: arm64 sunucuda x86-64 ikili dosyası veya tam tersi. Ayrıca dosyanın, eksik veya hasarlı bir indirme gibi geçerli bir çalıştırılabilir dosya olmadığı anlamına da gelebilir. file çıktısının ilk satırı, elinizdeki dosyanın hangi mimariye ait olduğunu veya kullanılabilir bir ELF çalıştırılabilir dosyası olmadığını size söyler.
error while loading shared libraries: libssl.so.3: cannot open shared object file hatası, bir NEEDED kütüphanesinin eksik olduğu veya farklı bir soname altında mevcut olduğu anlamına gelir. İlgili dağıtım paketini yükleyin. Paket adları aileler arasında farklılık gösterir, bu nedenle bir README dosyasından apt satırını kopyalamadan önce çeviri yapın; dnf ve apt komut eşdeğerleri bu eşlemeyi kapsar.
Glibc altında sorunsuz çalışan bir musl derlemesinden gelen boş bir Segmentation fault. Genellikle 2. seçenek altında ele alınan iş parçacığı yığını boyutu (thread stack size) ile ilgilidir.
Taşınabilir bir Linux binary dosyasını dağıtmanın dört yolu
Her yöntemin kendine has bir maliyeti vardır. Seçiminizi programınızın çalışma zamanındaki davranışına göre yapın; kulağa en temiz gelen yöntemi seçmeyin.
Seçenek 1: Desteklediğiniz en eski dağıtım üzerinde derleme yapın
En sıkıcı ancak genellikle en doğru yöntem budur. Desteklemeyi taahhüt ettiğiniz en eski dağıtımın container imajı içerisinde derleme yapın. Bağlayıcı (linker), yalnızca eski glibc sürümünde mevcut olan etiketleri kaydedebilir; bu sayede ikili dosya (binary), tüm glibc davranışlarını koruyan standart bir dinamik ikili dosya olarak kalırken, alt sınır o sürüme çekilmiş olur.
docker run --rm -v "$PWD:/src" -w /src ubuntu:20.04 sh -c 'apt-get update && apt-get install -y build-essential && make'Çıktı, 2.31 ve üzerindeki tüm glibc sürümlerinde çalışır. Varsayımlarla hareket etmek yerine doğrulama yapın: daha önce belirtilen objdump -T tek satırlık komutunu sonuç üzerinde çalıştırın ve en yüksek etiketin beklediğiniz sürüm olup olmadığını kontrol edin.
Bunun maliyeti araç zinciridir (toolchain). Eski bir temel imaj, aynı zamanda eski bir derleyici içerir; bu durum, kodunuz güncel bir C++ standardına ihtiyaç duyduğunda sorun yaratır. Go ve Rust dilleri, araç zincirlerini dağıtım paketlerinden bağımsız olarak container içine kurdukları için bu sorundan büyük ölçüde etkilenmezler. C ve C++ için dağıtımın kendi araç zinciri kanalından daha yeni bir derleyici ekleyebilir veya tam olarak eski bir glibc ile güncel bir GCC'yi birleştirmek amacıyla oluşturulmuş manylinux imajlarını kullanabilirsiniz. Zig'in beraberinde gelen C derleyicisi de zig cc -target x86_64-linux-gnu.2.28 örneğinde olduğu gibi doğrudan hedeflenen bir glibc sürümünü baz alabilir; bu sayede eski bir imajı tutmanıza gerek kalmadan aynı alt sınır elde edilir.
Seçenek 2: musl ile statik bağlantı kurma
musl, statik bağlantı hedefiyle yazılmış küçük bir C kütüphanesidir. Statik bir musl ikili dosyası kendi libc kütüphanesini içerir, bir yorumlayıcı belirtmez ve uygun mimariye sahip her Linux çekirdeğinde çalışır. Tek dosya olarak indirilen araçların çoğu bu yöntemle oluşturulur.
sudo apt install -y musl-tools
musl-gcc -static -O2 hello.c -o hello
file ./hellofile artık herhangi bir yorumlayıcı alanı olmadan statically linked çıktısını vermelidir. Rust için hedefi ekleyin ve ona göre derleyin:
rustup target add x86_64-unknown-linux-musl
cargo build --release --target x86_64-unknown-linux-muslGo için bunlara gerek yoktur. CGO_ENABLED=0 go build ile sonuç zaten statiktir ve hiçbir libc kütüphanesine bağlantı kurmaz.
NSS aramaları değişir. glibc, kullanıcıları ve ana makine adlarını, program çalışırken dlopen ile libnss_* modüllerini yükleyen NSS (name service switch) aracılığıyla çözer. Statik olarak bağlanmış bir glibc bunu yapamaz ve bağlayıcı sizi şu sözlerle uyarır:
warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linkingmusl, NSS'yi hiç uygulamayarak bu uyarıyı devre dışı bırakır. Kendi çözümleyicisine sahiptir ve doğrudan /etc/resolv.conf ile /etc/hosts dosyalarını okur. Sıradan bir VPS üzerinde bu durum sorunsuzdur ve daha basittir. Hesapların veya isimlerin LDAP ya da SSSD'den geldiği bir ana makinede, ikili dosyanız bunları göremezken sunucudaki diğer tüm programlar görebilir. musl'un çözümleyicisi de glibc'ninkinden daha yenidir: UDP yanıtı için çok büyük olan DNS yanıtlarında TCP'ye geri dönüş özelliği, 2023 yılında musl 1.2.4 sürümüyle gelmiştir. Daha eski musl sürümleriyle yapılan derlemeler büyük DNS kayıtlarını sorgulayamaz ve kesilmiş bir UDP yanıtında kısmi sonuç döndürmeyen özyinelemeli isim sunucularına karşı sorguları başarısız olabilir.
dlopen çalışmaz. Statik bir musl ikili dosyasında dlopen, her zaman başarısız olan bir taslaktır. Çalışma zamanında kod yükleyen her şey devre dışı kalır: eklenti sistemleri, PAM modülleri, GPU sürücüleri ve glibc'nin kendi iconv karakter kümesi modülleri. Programınız dlopen gerektiriyorsa, statik bağlantı seçeneği elenir ve diğer üç çözümden birini kullanmanız gerekir.
Güvenlik güncellemeleri sizin sorumluluğunuzdadır. Dinamik olarak bağlanmış bir ikili dosya, sunucu paket yükseltmesini çalıştırdığı anda bir libc düzeltmesini alır. Statik bir ikili dosya ise asla almaz. libc kütüphanenize veya paketlediğiniz statik OpenSSL'e karşı bir CVE (ortak güvenlik açıkları ve maruziyetler) kaydı yayınlandığında, yeniden derleyip dağıtmanız gerekir; dosya değiştirilene kadar dağıtılmış tüm kopyalar savunmasız kalır. Neleri bağladığınızın kaydını tutun, çünkü hedef sistemdeki hiçbir şey bir yöneticiye tek dosyalık ikili dosyanızın iki yıl öncesine ait bir kütüphane içerdiğini söyleyemez.
Lisans değişir. glibc LGPL lisanslıdır ve statik bağlantı, LGPL'in yeniden bağlantı kurma yükümlülüğünü tetikler: alıcılara programınızı farklı bir glibc ile yeniden bağlamaları için gerekenleri sağlamalısınız. musl ise MIT lisanslıdır ve böyle bir koşul içermez. Tek dosya halinde ikili dosya dağıtan projelerin statik glibc yerine musl'u tercih etmelerinin temel nedeni budur.
İki çalışma zamanı farkı kafa karıştırıcı çökmelere neden olur. musl'un varsayılan iş parçacığı yığını 128 KiB'dir. glibc, varsayılan değerini programın başladığı yığın sınırından alır; bu genellikle 2 ile 10 MB arasındadır ve sınır sınırsız olduğunda çoğu mimaride 2 MB'a düşer. Bu nedenle, iş parçacığı yığınına büyük bir arabellek koyan kodlar, musl altında hiçbir mesaj veya günlük kaydı olmadan segfault hatası verebilir. Boyutu pthread_attr_setstacksize ile açıkça ayarlayın veya arabelleği yığına (heap) taşıyın. musl'un ayırıcısı da aynı anda birçok iş parçacığının bellek ayırdığı durumlar yerine, küçük boyut ve öngörülebilir davranış için yazılmıştır; bu nedenle bellek ayırma işleminin yoğun olduğu iş parçacıklı programlar ölçülebilir derecede daha yavaş çalışabilir. Her iki kütüphanenin itibarına güvenmek yerine kendi iş yükünüzü kıyaslayın.
Seçenek 3: Yükleyiciyi ve kütüphaneleri paketleme
Kütüphaneleri, eşleşen yükleyici ile birlikte ikili dosyanın yanına yerleştirin ve ardından programı bu yükleyici üzerinden başlatın.
./ld-linux-x86-64.so.2 --library-path ./lib ./mytoolBunu kalıcı hale getirmek için yolları patchelf dosyasına yazın:
patchelf --set-interpreter "$PWD/lib/ld-linux-x86-64.so.2" --set-rpath '$ORIGIN/lib' ./mytoolBunun çalışıp çalışmayacağına tek bir kural karar verir: yükleyici ve libc.so.6 aynı glibc sürümünden gelmelidir. Bunlar eşleşen bir çifttir; ana makinenin yükleyicisini paketlenmiş bir libc ile karıştırmak, okunabilir bir hata yerine başlatma sırasında çökmeye neden olur. Ya ikisini de paketleyin ya da hiçbirini paketlemeyin.
AppImage, bu modelin paketlenmiş halidir; yük (payload) bir squashfs imajı içindedir ve bunu bağlayan küçük bir çalışma zamanı (runtime) bulunur. İnsanların gözden kaçırdığı bir özelliği vardır: çoğu AppImage, glibc'yi paketlemek yerine ana makineye bırakır. Bu nedenle, güncel bir dağıtımda bu şekilde oluşturulan bir AppImage, eski bir sunucuda aynı sürüm hatasıyla başarısız olur. AppImage'in kendi en iyi uygulama kılavuzu, desteklediğiniz en eski sistemden daha yeni olmayan bir temel üzerinde derleme yapmanızı önerir. Bu da böyle bir AppImage'i, 1. seçeneğin alternatifi olmaktan ziyade onun üzerine katmanlanmış bir dağıtım formatı haline getirir. glibc'yi paketlemek de mümkündür: appimage-builder ve go-appimage araçlarının her ikisi de glibc'yi AppImage içine dahil edebilir. Bu şekilde oluşturulan bir AppImage, hem derleme sisteminden daha eski hem de daha yeni dağıtımlarda çalışabilir. Bunun maliyeti, daha büyük bir dosya boyutu ve libssl gibi kütüphanelerin paket içine sabitlenmesidir.
Başsız (headless) bir sunucuda AppImage, yükünü bağlamak için FUSE (filesystem in userspace) gerektirir ve minimal VPS imajları genellikle bunu içermez. Daha eski bir çalışma zamanı ile oluşturulan AppImage'ler, ana makinede FUSE 2 kütüphanesine ihtiyaç duyar. Bu kütüphane Debian ve Ubuntu'da libfuse2 olarak paketlenmiştir ve Ubuntu 24.04'te libfuse2t64 olarak yeniden adlandırılmıştır; eksik olduğunda AppImages require FUSE to run. hatasıyla dururlar. Daha yeni AppImage'ler FUSE 3 desteğine sahiptir ve ana makinede libfuse kurulu olmasına gerek duymazlar. Bağlama işlemini tamamen atlamak için, içeriği geçici bir dizine açıp oradan çalıştıran ./App.AppImage --appimage-extract-and-run parametresini kullanabilirsiniz.
Seçenek 4: Container imajı dağıtmak
Programla birlikte tüm kullanıcı alanını (userland) taşıyın. İmaj kendi libc kütüphanesini taşıdığı için ana makinenin glibc sürümü önemini yitirir; yalnızca çekirdek ve mimarinin uyumlu olması yeterlidir. Bu en az zekice olan seçenektir ancak tüm sorun sınıfını ortadan kaldırdığı için pek çok sunucu yazılımı bu şekilde dağıtılır. VPS üzerinde Docker çalıştırmak, bu yöntemi kullanmanın alışılagelmiş yoludur.
Ana makine çekirdeği yine de bir sınır belirler. Daha yeni glibc sürümleri daha yeni sistem çağrıları kullanır ve eski bir container ana makinesi bunları engelleyebilir: glibc 2.34 ve sonrası clone3 kullanır; eski varsayılan seccomp profilleri bunu reddeder. Belirti, glibc'den hiç bahsedilmeyen Operation not permitted hatasıyla anında başarısız olmaktır. Ana makinedeki container çalışma zamanını (runtime) yükseltmek bu sorunu çözer, çünkü engelleme çekirdekte değil, çalışma zamanının sistem çağrısı filtresindedir.
Maliyetler standarttır. Hedef sistemin bir container çalışma zamanına ve bunu kullanma iznine ihtiyacı vardır. İndirme boyutunuz tek bir dosyadan onlarca veya yüzlerce megabayta çıkar. Artık bir temel imaja ve onun yama takvimine sahipsiniz; bu nedenle 2. seçenekte kaçındığınız güvenlik iş yükü, imajı yeniden oluşturma gerekliliği olarak geri döner. Uzun süre çalışan bir servis için bu makul bir takastır. Birinin tek seferlik çalıştırdığı bir komut satırı aracı için ise değildir.
Hangi seçeneği tercih etmelisiniz?
- Kontrolünüz altındaki ve tamamı aynı dağıtımı çalıştıran sunucular için bir dahili araç: dinamik olarak o dağıtım üzerinde derleyin ve orada bırakın.
- Yabancıların indirip çalıştıracağı tek bir dosya: programın
dlopenihtiyacı yoksa ve NSS destekli aramalara gereksinim duymuyorsa, statik musl kullanın. - Eklentileri veya GPU erişimi olan bir program: dinamik yapıda kalın, eski bir temel üzerinde derleyin ve teslim etmek için 3. seçeneği kullanın.
- Halihazırda bir çalışma zamanına sahip bir makinede uzun süre çalışan bir servis: imajı gönderin.
Ne seçerseniz seçin, bunu not edin ve kontrol edin. Derleme sunucusunun glibc sürümü artık sürüm sürecinizin bir parçasıdır; bir LTS sürümünden diğerine yükseltilen bir derleme makinesi, alt sınırı sessizce yükseltir ve geçen ay sorunsuz çalışan kullanıcıların sistemini bozar. Derleme imajını etiket (tag) ile sabitleyin ve çıktıdaki en yüksek GLIBC_ etiketini bir derleme adımı olarak doğrulayın; böylece kontrol, başkasının terminali yerine kendi işlem hattınızda (pipeline) başarısız olur.
FAQ
Sunucumda neden "version GLIBC_2.38 not found" hatası alıyorum?
İkili dosya (binary), sunucunuzdakinden daha yeni bir glibc sürümüne sahip bir makinede derlenmiştir. glibc sembol sürümleme sistemi yalnızca ileriye dönük uyumluluk sağlar: eski ikili dosyalar yeni glibc üzerinde çalışır, ancak yeni ikili dosyalar eski glibc üzerinde çalışmaz. Bunun nedeni, yükleyicinin (loader) dosya içinde kayıtlı olan tam sürüm etiketine ihtiyaç duyması ve eski libc sürümlerinin bu etikete sahip olmamasıdır. Sunucuya kuracağınız hiçbir şey bu sorunu güvenli bir şekilde çözmez. Daha eski bir temel üzerinde yeniden derleme yapın, statik bir musl derlemesi dağıtın, kütüphaneleri eşleşen yükleyicileriyle birlikte paketleyin veya bir container imajı kullanın.
Bir ikili dosyanın hangi glibc sürümüne ihtiyaç duyduğunu nasıl anlarım?
Dosyadaki sürüm etiketlerini objdump -T ./mytool | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -5 ile okuyun. Son satır, dosyayı yükleyebilecek en düşük glibc sürümünü gösterir. readelf -V ./mytool, aynı gereksinimleri .gnu.version_r bölümünde, bunları sağlaması gereken kütüphanelere göre gruplandırılmış şekilde gösterir. Eğer objdump -T hiçbir çıktı vermiyorsa, ikili dosya statiktir ve glibc gereksinimi yoktur. Sonucu, ldd --version komutuyla sunucunuzun kendi sürümüyle karşılaştırın.
musl ikili dosyası, glibc ikili dosyasından daha mı yavaştır?
Bu, iş yüküne bağlıdır; dürüst cevap ölçüm yapılması gerektiğidir. Statik bir musl ikili dosyası daha hızlı başlar, çünkü çalıştırılacak bir yükleyici yoktur ve exec anında yeniden konumlandırma (relocation) işlemi gerekmez. Buna karşılık, musl'un bellek ayırıcısı (allocator) çok sayıda thread'in aynı anda bellek ayırdığı durumlar yerine, küçük boyut ve öngörülebilir davranış için tasarlanmıştır. Ayrıca musl, glibc'ye kıyasla daha az el ile optimize edilmiş string ve bellek rutinleri içerir; bu nedenle bellek ayırma veya string işleme yoğunluklu çok izlekli (threaded) programlar belirgin şekilde daha yavaş olabilir. Herhangi bir iddiayı kabul etmeden önce kendi programınızı kendi sunucunuzda kıyaslayın (benchmark).
bash neden var olan bir dosya için "No such file or directory" hatası veriyor?
Eksik olan dosya ikili dosyanız değil, dinamik yükleyicidir. Çekirdek, yorumlayıcı yolunu ELF başlığından okur ve bu yol mevcut olmadığında ENOENT hatasını döndürür; kabuk ise bunu elindeki tek mesajla bildirir. file ./mytool komutunu çalıştırın ve interpreter alanını okuyun. Eğer bir Debian veya Ubuntu sunucusunda /lib/ld-musl-x86_64.so.1 çıktısını alıyorsanız, glibc tabanlı bir sistemde musl ile bağlanmış bir ikili dosya kullanıyorsunuz demektir. Dağıtımın musl çalışma zamanını (Debian ve Ubuntu'da musl paketi) kurun veya bunun yerine programın glibc ile derlenmiş ya da statik bir sürümünü edinin.
Bu sorunu çözmek için libc.so.6 dosyasını daha yeni bir sunucudan kopyalayabilir miyim?
Hayır. libc.so.6 ve ld-linux-x86-64.so.2, tek bir glibc derlemesinden gelen eşleşmiş bir çifttir ve makinedeki dinamik olarak bağlanan her glibc programı bunları kullanır. Bu nedenle sistem kopyasının üzerine yazmak, sunucuyu, geri alma işlemini yapacak araçlar da dahil olmak üzere neredeyse hiçbir şeyi çalıştıramaz hale getirebilir. Eğer daha yeni bir ikili dosyayı eski bir ana makinede çalıştırmanız gerekiyorsa, daha yeni glibc'yi özel bir dizine açın ve o programı kendi yükleyicisiyle --library-path üzerinden başlatın; bu işlem yalnızca ilgili süreci etkiler. Eski glibc ile yeniden derleme yapmak, altı ay sonra sizi sürprizlerle karşılaştırmayacak tek çözümdür.