Linux binary पुरानी glibc पर क्यों नहीं चलती है
GLIBC_2.34 not found एरर का मतलब है कि आपके बिल्ड होस्ट ने वर्जन की सीमा तय कर दी है। अपनी बाइनरी की सटीक मांगें जानें और इसे पोर्टेबल बनाने के चार प्रभावी तरीके यहाँ देखें।
पुराने सर्वर पर Linux binary क्यों नहीं चलती है
एक portable Linux binary बनाना कठिन है क्योंकि glibc, यानी GNU C library, केवल एक दिशा में compatibility का वादा करती है। एक पुरानी binary नई glibc पर चलती रहती है। एक नई binary पुरानी glibc पर नहीं चलती है। जिस machine पर आप compile करते हैं, वह हर उस machine के लिए न्यूनतम आवश्यकता तय कर देती है जहाँ आप इसे deploy करते हैं।
error उस सटीक version का नाम बताती है जिसकी उसे आवश्यकता थी:
./mytool: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.38' not found (required by ./mytool)कुछ भी corrupt नहीं है और कुछ भी गलत configure नहीं है। dynamic loader ने binary के अंदर दर्ज एक version tag को पढ़ा, system libc में उस tag को खोजा, उसे नहीं पाया, और program को start करने से मना कर दिया। file को फिर से download करने और chmod +x चलाने से कुछ नहीं बदलेगा, क्योंकि आवश्यकता file के अंदर ही लिखी गई है। आपको या तो binary बनाने का तरीका बदलना होगा, या जो आप ship करते हैं उसे बदलना होगा।
glibc symbol versioning वास्तव में क्या करता है
glibc द्वारा export किए जाने वाले प्रत्येक function में एक version tag होता है। printf के अंदर libc.so.6 वास्तव में printf@@GLIBC_2.2.5 है। जब glibc किसी function के व्यवहार या ABI (application binary interface) को बदलता है, तो वह पुराने function को हटाता नहीं है। यह पुराने code को पुराने tag के तहत रखता है और नए code को एक नए tag के तहत जोड़ता है, इसलिए एक एकल libc.so.6 में एक ही symbol के कई version एक साथ मौजूद रहते हैं। 2009 का एक binary यह पाता है कि 2009 वाला tag अभी भी वहां मौजूद है, यही कारण है कि forward compatibility इतनी अच्छी तरह काम करती है।
Link step यह record करता है कि उसने क्या उपयोग किया है। आपके binary में एक .gnu.version_r section होता है जो प्रभावी रूप से कहता है, "मुझे libc.so.6 से GLIBC_2.38 की आवश्यकता है"। एक पुराने libc में वह tag कभी नहीं था, इसलिए loader main के चलने से पहले ही रुक जाता है। इसमें कोई fallback नहीं होता, क्योंकि fallback का अर्थ होगा program को चुपचाप वह function दे देना जिसके विरुद्ध उसे compile नहीं किया गया था।
2021 के बाद से सबसे सामान्य trigger glibc 2.34 है। उस release ने libpthread और libdl को libc में merge कर दिया और __libc_start_main को, जो कि हर C program को शुरू करने वाला function है, GLIBC_2.34 tag पर स्थानांतरित कर दिया। इसलिए, glibc 2.34 या उससे नए version वाले किसी भी distribution पर compile किया गया hello-world, GLIBC_2.34 की मांग करता है, भले ही source code में कुछ भी आधुनिक call न किया गया हो। यही कारण है कि जिन लोगों का अपना code वर्षों से नहीं बदला था, उनके लिए यह समस्या अचानक सामने आई।
मेरा distro glibc का कौन सा version ship करता है?
The data behind this chart
[
{
"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
}
]ये वे versions हैं जिन्हें प्रत्येक distribution अपने repositories में ship करता है, जैसा कि distributions द्वारा प्रकाशित किया गया है और अगस्त 2026 में वर्तमान है। CentOS 7 का end of life काफी पहले हो चुका है, और यह सूची में इसलिए है क्योंकि विरासत (inherited) सर्वर अभी भी इसे चला रहे हैं। अपने किसी भी box को ldd --version के साथ जाँचें, जो अपनी पहली पंक्ति पर glibc release को print करता है, या getconf GNU_LIBC_VERSION के साथ।
उन 9 पंक्तियों को एक सीढ़ी के रूप में पढ़ें। Debian 13 पर glibc 2.41 के साथ build करें और परिणाम Debian 13 से पुराने किसी भी सिस्टम पर नहीं चलेगा। Ubuntu 20.04 पर glibc 2.31 के साथ build करें और वही source उस पंक्ति के ऊपर की हर पंक्ति पर चलेगा, जिसमें Debian 13 box भी शामिल है। जिस सबसे पुराने सर्वर को आपको support करना है, वही एकमात्र build host है जो मायने रखता है, इसलिए जिस distribution को आप standardise करते हैं, वह आपके द्वारा compile की जाने वाली हर चीज़ के लिए ABI floor निर्धारित करता है। fleet के अस्तित्व में आने से पहले, अपने VPS पर कौन सा OS चलाएं, यह चुनने में शामिल अन्य trade-offs के साथ-साथ, इस पर निर्णय लेना सार्थक है।
किसी binary के लिए आवश्यक न्यूनतम glibc कैसे पता करें?
इसे सीधे फाइल से पढ़ें। यह किसी भी फाइल पर काम करता है, जिसमें वे binaries भी शामिल हैं जो आपको बिना किसी build notes के vendor से मिली हैं।
objdump -T ./mytool | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -5अंतिम पंक्ति न्यूनतम आवश्यकता (floor) दर्शाती है। यदि objdump मौजूद नहीं है, तो binutils पैकेज इंस्टॉल करें। यदि आप उस मशीन पर कुछ भी इंस्टॉल नहीं कर सकते, तो strings -a ./mytool | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -5 का उपयोग करें, क्योंकि ये tags फाइल के अंदर plain strings के रूप में संग्रहीत होते हैं।
readelf -V ./mytool समान आवश्यकता को संरचना के साथ दिखाता है। Version needs section '.gnu.version_r' शीर्षक वाले ब्लॉक को देखें। यह उस library के अंतर्गत, जिसे इसे प्रदान करना है, प्रत्येक tag के लिए एक Name: GLIBC_x.y पंक्ति सूचीबद्ध करता है।
यह पता लगाने के लिए कि किस function ने यह न्यूनतम सीमा निर्धारित की है, tag के लिए grep करें: objdump -T ./mytool | grep GLIBC_2.38। यह अक्सर एक एकल symbol होता है, और कभी-कभी ऐसा जिसे आप avoid कर सकते हैं। यदि objdump -T कुछ भी प्रिंट नहीं करता है, तो binary statically linked है, इसलिए इसमें कोई dynamic symbol table नहीं है और प्रिंट करने के लिए कोई version requirement भी नहीं है।
file और readelf -d आपको किसी बाइनरी के बारे में क्या बताते हैं
file एक ही पंक्ति में आर्किटेक्चर और लिंक का प्रकार बताता है।
file ./mytoolएक सामान्य glibc बिल्ड इस तरह दिखता है:
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 strippedएक स्टैटिक बिल्ड statically linked दिखाता है और किसी इंटरप्रेटर का नाम नहीं देता। एक musl बिल्ड एक अलग इंटरप्रेटर का नाम देता है:
ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-x86_64.so.1, ...इंटरप्रेटर ही वह महत्वपूर्ण फील्ड है। यह वह डायनामिक लोडर है जिसे कर्नल आपके प्रोग्राम से पहले चलाता है, और उस सटीक पाथ का टारगेट मशीन पर मौजूद होना आवश्यक है। /lib/ld-musl-x86_64.so.1 किसी Debian या Ubuntu सर्वर पर तब तक मौजूद नहीं होता जब तक कि किसी ने वहां musl इंस्टॉल न किया हो।
readelf -d ./mytool उन शेयर्ड लाइब्रेरीज़ की सूची देता है जिनकी बाइनरी को आवश्यकता होती है, उनके नाम के साथ:
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]प्रत्येक NEEDED एंट्री एक अनिवार्य आवश्यकता है, जो soname द्वारा मेल खाती है। libssl.so.3 OpenSSL 3 है, इसलिए वह बाइनरी ऐसे सर्वर पर लोड नहीं होगी जिसमें केवल libssl.so.1.1 है, और इसका कारण soname है: एक अलग soname का अर्थ है जानबूझकर असंगत ABI। RUNPATH लोडर को बताता है कि सबसे पहले कहाँ खोजना है, और $ORIGIN उस डायरेक्टरी तक विस्तारित होता है जिसमें बाइनरी मौजूद है, इसी तरह एक सेल्फ-कंटेंड बंडल अपनी लाइब्रेरीज़ को ढूंढता है।
ऐसी बाइनरी पर ldd न चलाएं जिस पर आप भरोसा नहीं करते। glibc पर, ldd लोडर के तहत प्रोग्राम को चलाकर डिपेंडेंसी को हल कर सकता है, इसलिए एक हानिकारक फाइल कोड निष्पादित कर सकती है। readelf और objdump केवल बाइट्स को पढ़ते हैं। इन सबसे पहले, यह पुष्टि करें कि डाउनलोड की गई फाइल वही है जिसे प्रोजेक्ट ने वास्तव में पब्लिश किया है, क्योंकि पब्लिश किए गए चेकसम के साथ डाउनलोड को वेरिफाई करना ही एकमात्र ऐसा चरण है जो आपको बताता है कि आपके पास किसकी बाइट्स हैं।
वे त्रुटियाँ जो आपको वास्तव में दिखाई देंगी, और प्रत्येक का क्या अर्थ है
लोडर एक ऐसी GLIBC version का नाम बताता है जिसे वह ढूंढ नहीं पा रहा है। बाइनरी को target की तुलना में एक नए glibc के साथ compile किया गया था। target पर इंस्टॉल की गई कोई भी चीज़ इसे सुरक्षित रूप से ठीक नहीं करती है। नीचे दिए गए चार विकल्पों में से एक चुनें।
शेल एक ऐसी फ़ाइल के लिए No such file or directory रिपोर्ट करता है जिसे आप स्पष्ट रूप से देख सकते हैं। जो चीज़ गायब है वह interpreter है, न कि बाइनरी। जब ELF header में लोडर पाथ अनुपस्थित होता है, तो execve, ENOENT रिटर्न करता है, और शेल केवल वही संदेश प्रिंट करता है जो उसके पास होता है। file चलाएँ और interpreter पाथ पढ़ें। एक glibc-only सर्वर पर musl-linked बाइनरी बिल्कुल यही लक्षण देती है।
cannot execute binary file: Exec format error का अर्थ है कि आर्किटेक्चर गलत है: arm64 सर्वर पर x86-64 बाइनरी, या इसके विपरीत। file आउटपुट की पहली पंक्ति आपको बताती है कि आपके पास कौन सा है।
error while loading shared libraries: libssl.so.3: cannot open shared object file का अर्थ है कि एक NEEDED लाइब्रेरी अनुपस्थित है या किसी अलग soname के तहत मौजूद है। मेल खाने वाला डिस्ट्रीब्यूशन पैकेज इंस्टॉल करें। पैकेज के नाम परिवारों के बीच भिन्न होते हैं, इसलिए README से apt लाइन कॉपी करने से पहले अनुवाद करें, और dnf और apt कमांड के समकक्ष उस मैपिंग को कवर करते हैं।
musl बिल्ड से एक खाली Segmentation fault जो glibc के तहत ठीक चलता है। आमतौर पर थ्रेड स्टैक साइज़, जो विकल्प 2 के अंतर्गत कवर किया गया है।
पोर्टेबल Linux बाइनरी को शिप करने के चार तरीके
प्रत्येक विकल्प की अपनी एक वास्तविक लागत है। अपने प्रोग्राम के रनटाइम व्यवहार के आधार पर चुनाव करें, न कि इस आधार पर कि कौन सा तरीका सुनने में सबसे साफ लगता है।
विकल्प 1: जिस सबसे पुराने डिस्ट्रो को आप सपोर्ट करते हैं, उस पर बिल्ड करें
यह सबसे सरल और आमतौर पर सही तरीका है। जिस सबसे पुराने डिस्ट्रीब्यूशन को आप सपोर्ट करने का वादा करते हैं, उसके कंटेनर इमेज के भीतर कंपाइल करें। लिंकर केवल उन्हीं टैग्स को रिकॉर्ड कर सकता है जो पुराने glibc में मौजूद हैं, इसलिए न्यूनतम आवश्यकता उस रिलीज तक गिर जाती है, जबकि बाइनरी एक सामान्य डायनामिक बाइनरी बनी रहती है और glibc का सारा व्यवहार बरकरार रहता है।
docker run --rm -v "$PWD:/src" -w /src ubuntu:20.04 sh -c 'apt-get update && apt-get install -y build-essential && make'आउटपुट glibc 2.31 और उससे ऊपर के सभी वर्जन्स पर चलता है। अनुमान लगाने के बजाय सत्यापित करें: पहले बताए गए objdump -T वन-लाइनर को परिणाम पर चलाएं और जांचें कि उच्चतम टैग वही है जिसकी आपने अपेक्षा की थी।
इसकी कीमत टूलचेन है। एक पुरानी बेस इमेज में पुराना कंपाइलर भी होता है, जो तब समस्या पैदा करता है जब आपके कोड को हालिया C++ स्टैंडर्ड की आवश्यकता होती है। Go और Rust ज्यादातर इससे बच जाते हैं, क्योंकि उनकी टूलचेन डिस्ट्रीब्यूशन पैकेज से स्वतंत्र रूप से कंटेनर में इंस्टॉल हो जाती है। C और C++ के लिए आप डिस्ट्रीब्यूशन के अपने टूलचेन चैनल से एक नया कंपाइलर जोड़ सकते हैं, या manylinux इमेज का उपयोग कर सकते हैं, जो विशेष रूप से एक प्राचीन glibc को वर्तमान GCC के साथ संयोजित करने के लिए मौजूद हैं। Zig का बंडल किया गया C कंपाइलर भी सीधे एक चुने हुए glibc को टारगेट कर सकता है, जैसा कि zig cc -target x86_64-linux-gnu.2.28 में है, जो पुरानी इमेज को रखे बिना वही न्यूनतम आवश्यकता प्रदान करता है।
विकल्प 2: musl के साथ statically link करना
musl एक छोटी C library है जिसे static linking को ध्यान में रखकर लिखा गया है। एक static musl binary में अपनी libc होती है, इसमें कोई interpreter नहीं होता, और यह सही architecture वाले किसी भी Linux kernel पर चल सकती है। अधिकांश single-file tool downloads इसी तरह बनाए जाते हैं।
sudo apt install -y musl-tools
musl-gcc -static -O2 hello.c -o hello
file ./hellofile में अब statically linked दिखना चाहिए, जिसमें कोई interpreter field न हो। Rust के लिए, target जोड़ें और उसके विरुद्ध build करें:
rustup target add x86_64-unknown-linux-musl
cargo build --release --target x86_64-unknown-linux-muslGo को इसकी आवश्यकता नहीं है। CGO_ENABLED=0 go build के साथ परिणाम पहले से ही static होता है और किसी भी libc से link नहीं होता।
NSS lookups बदल जाते हैं। glibc, NSS (name service switch) के माध्यम से users और host names को resolve करता है, जो प्रोग्राम चलते समय dlopen के साथ libnss_* modules को load करता है। एक statically linked glibc ऐसा नहीं कर सकता, और linker आपको इन शब्दों में चेतावनी देता है:
warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linkingmusl, NSS को implement न करके इस चेतावनी से बच जाता है। इसका अपना resolver है और यह सीधे /etc/resolv.conf और /etc/hosts को पढ़ता है। एक सामान्य VPS पर यह ठीक और सरल है। ऐसे host पर जहाँ accounts या names LDAP या SSSD से आते हैं, आपकी binary उन्हें नहीं देख पाएगी जबकि box पर मौजूद अन्य सभी प्रोग्राम उन्हें देख सकते हैं। musl का resolver, glibc के resolver से नया है: 512 bytes से बड़े DNS replies के लिए TCP fallback, 2023 में musl 1.2.4 में आया था, इसलिए पुराने musl के साथ किए गए builds बड़े answers को truncate कर देते हैं।
dlopen काम नहीं करता है। एक static musl binary में, dlopen एक stub है जो हमेशा fail होता है। run time पर code load करने वाली कोई भी चीज़ काम नहीं करेगी: plugin systems, PAM modules, GPU drivers, glibc के अपने iconv character-set modules। यदि आपके प्रोग्राम को dlopen की आवश्यकता है, तो static linking संभव नहीं है और आपको अन्य तीन विकल्पों में से किसी एक की आवश्यकता होगी।
Security updates आपकी जिम्मेदारी बन जाते हैं। एक dynamically linked binary, server द्वारा package upgrade चलाते ही libc fix प्राप्त कर लेती है। एक static binary कभी ऐसा नहीं करती। जब आपकी libc या आपके द्वारा bundled किसी static OpenSSL के खिलाफ कोई CVE (common vulnerabilities and exposures) entry आती है, तो आपको rebuild और redistribute करना पड़ता है, और जब तक कोई उस file को replace नहीं करता, तब तक deployed हर copy vulnerable बनी रहती है। आपने क्या link किया है, इसका record रखें, क्योंकि target पर मौजूद कोई भी चीज़ administrator को यह नहीं बता सकती कि आपकी single-file binary में दो साल पुरानी library शामिल है।
Licence बदल जाता है। glibc, LGPL है, और static linking, LGPL की relinking obligation को trigger करती है: आपको प्राप्तकर्ताओं को वह सब देना होगा जो उन्हें आपके प्रोग्राम को किसी अलग glibc के साथ relink करने के लिए चाहिए। musl, MIT है और इसमें ऐसी कोई शर्त नहीं है। यही मुख्य कारण है कि single-file binaries distribute करने वाले projects static glibc के बजाय musl चुनते हैं।
Runtime के दो अंतर भ्रमित करने वाले crashes पैदा करते हैं। musl का default thread stack 128 KiB है, जबकि glibc पर यह 8 MiB है, इसलिए जो code thread stack पर बड़ा buffer रखता है, वह बिना किसी message या log line के segfault हो जाता है। pthread_attr_setstacksize के साथ size को स्पष्ट रूप से set करें, या buffer को heap पर ले जाएँ। musl का allocator भी एक साथ कई threads द्वारा allocation के बजाय छोटे size और predictable behaviour के लिए लिखा गया है, इसलिए allocation-heavy threaded programs काफी धीमे चल सकते हैं। किसी भी library की reputation पर भरोसा करने के बजाय अपने workload का benchmark करें।
विकल्प 3: लोडर और लाइब्रेरी को बंडल करना
बाइनरी के साथ लाइब्रेरी और उनसे मेल खाने वाले लोडर को रखें, फिर उस लोडर के माध्यम से प्रोग्राम को शुरू करें।
./ld-linux-x86-64.so.2 --library-path ./lib ./mytoolइसे स्थायी बनाने के लिए, patchelf का उपयोग करके फ़ाइल में पाथ लिखें:
patchelf --set-interpreter "$PWD/lib/ld-linux-x86-64.so.2" --set-rpath '$ORIGIN/lib' ./mytoolएक नियम यह तय करता है कि यह काम करेगा या नहीं: लोडर और libc.so.6 एक ही glibc बिल्ड से होने चाहिए। वे एक मेल खाते हुए जोड़े हैं, और होस्ट के लोडर को बंडल की गई libc के साथ मिलाने पर स्टार्ट-अप के दौरान क्रैश हो जाता है, न कि कोई पठनीय त्रुटि मिलती है। या तो दोनों को बंडल करें या किसी को भी नहीं।
AppImage इसी पैटर्न का एक पैकेज्ड रूप है, जिसमें पेलोड एक squashfs इमेज में होता है और एक छोटा रनटाइम इसे माउंट करता है। इसमें एक विशेषता है जिसे लोग अक्सर अनदेखा कर देते हैं: AppImage में glibc बंडल नहीं होता है, इसलिए एक वर्तमान डिस्ट्रीब्यूशन पर बनाया गया AppImage पुराने सर्वर पर उसी वर्ज़न त्रुटि के साथ विफल हो जाता है। AppImage का अपना मार्गदर्शन यह है कि आप जिस सबसे पुराने बेस को सपोर्ट करते हैं, उसी पर बिल्ड करें, जो इसे विकल्प 1 के विकल्प के बजाय उसके ऊपर एक डिलीवरी फॉर्मेट बनाता है।
एक हेडलेस सर्वर पर, AppImage को अपना पेलोड माउंट करने के लिए FUSE (filesystem in userspace) की आवश्यकता होती है, और एक न्यूनतम VPS इमेज में अक्सर यह शामिल नहीं होता है। विफलता का नाम libfuse.so.2 है। आप ./App.AppImage --appimage-extract-and-run के साथ माउंट को पूरी तरह से छोड़ सकते हैं, जो एक अस्थायी निर्देशिका में अनपैक करता है और वहीं से निष्पादित (execute) होता है।
विकल्प 4: container image को ship करना
प्रोग्राम के साथ पूरे userland को move करें। Image में अपनी libc होती है, इसलिए host की glibc का महत्व खत्म हो जाता है और केवल kernel तथा architecture का मेल खाना आवश्यक होता है। यह सबसे कम जटिल विकल्प है और यह समस्याओं के पूरे वर्ग को ही समाप्त कर देता है, यही कारण है कि अधिकांश server software इसी तरह वितरित किए जाते हैं। VPS पर Docker चलाना इसे उपयोग करने का सामान्य तरीका है।
Host kernel अभी भी एक सीमा निर्धारित करता है। नए glibc संस्करण नए system calls का उपयोग करते हैं, और एक पुराना container host उन्हें block कर सकता है: glibc 2.34 और उसके बाद के संस्करण clone3 का उपयोग करते हैं, जिसे पुराने default seccomp profiles अस्वीकार कर देते हैं। इसका लक्षण Operation not permitted के साथ तत्काल विफलता है, जिसमें glibc का कहीं कोई उल्लेख नहीं होता। Host पर container runtime को अपग्रेड करने से यह समस्या ठीक हो जाती है, क्योंकि यह अवरोध kernel के बजाय runtime के syscall filter में होता है।
इसकी लागत सामान्य है। Target को एक container runtime और उसे उपयोग करने की अनुमति की आवश्यकता होती है। आपका download एक फ़ाइल से बढ़कर दस या सैकड़ों megabytes का हो जाता है। अब आप एक base image और उसके patch schedule के लिए जिम्मेदार हैं, इसलिए जिस सुरक्षा कार्य से आपने विकल्प 2 में बचाव किया था, वह image rebuilds के रूप में वापस आ जाता है। लंबे समय तक चलने वाली service के लिए यह एक उचित समझौता है। किसी ऐसे command-line tool के लिए जिसे कोई एक बार चलाता है, यह उचित नहीं है।
आपको कौन सा विकल्प चुनना चाहिए?
- उन सर्वर्स के लिए एक आंतरिक टूल जिन्हें आप नियंत्रित करते हैं और जो सभी एक ही distribution पर चलते हैं: उस distribution पर dynamically build करें और वहीं रुक जाएं।
- एक एकल फाइल जिसे अनजान लोग डाउनलोड करके चलाते हैं: static musl का उपयोग करें, यदि प्रोग्राम को किसी
dlopenकी आवश्यकता नहीं है और कोई NSS-आधारित lookup नहीं करना है। - प्लगइन्स या GPU एक्सेस वाला प्रोग्राम: dynamic बने रहें, एक पुराने base पर build करें, और इसे डिलीवर करने के लिए विकल्प 3 का उपयोग करें।
- एक ऐसी मशीन पर लंबे समय तक चलने वाली सर्विस जिसमें पहले से ही runtime मौजूद है: image को ship करें।
आप जो भी चुनें, उसे लिख लें और उसकी जांच करें। build host की glibc अब आपकी release प्रक्रिया का हिस्सा है, और एक LTS release से अगले पर अपग्रेड की गई build मशीन चुपचाप न्यूनतम आवश्यकता को बढ़ा देती है और उन उपयोगकर्ताओं के लिए समस्या पैदा करती है जो पिछले महीने ठीक थे। build image को tag द्वारा pin करें, और build चरण के रूप में आउटपुट में उच्चतम GLIBC_ tag का दावा (assert) करें, ताकि जांच किसी और के टर्मिनल के बजाय आपके pipeline में ही विफल हो जाए।
FAQ
मेरे सर्वर पर "version GLIBC_2.38 not found" त्रुटि क्यों आती है?
यह बाइनरी एक ऐसी मशीन पर कंपाइल की गई है जिसमें आपके सर्वर की तुलना में नया glibc संस्करण है। glibc की सिंबल वर्ज़निंग केवल फॉरवर्ड कम्पैटिबिलिटी प्रदान करती है: पुरानी बाइनरी नए glibc पर चल सकती हैं, लेकिन नई बाइनरी पुराने glibc पर नहीं चल सकतीं। इसका कारण यह है कि लोडर को फाइल में दर्ज सटीक वर्ज़न टैग की आवश्यकता होती है और पुराने libc में वह टैग मौजूद नहीं होता। सर्वर पर कुछ भी इंस्टॉल करने से यह समस्या सुरक्षित रूप से हल नहीं होगी। इसे पुराने बेस पर फिर से बिल्ड करें, static musl बिल्ड का उपयोग करें, लाइब्रेरीज़ को उनके संबंधित लोडर के साथ बंडल करें, या कंटेनर इमेज का उपयोग करें।
मुझे कैसे पता चलेगा कि किसी बाइनरी को किस glibc संस्करण की आवश्यकता है?
objdump -T ./mytool | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -5 का उपयोग करके फाइल से वर्ज़न टैग पढ़ें। अंतिम पंक्ति वह न्यूनतम glibc संस्करण है जो इसे लोड कर सकता है। readelf -V ./mytool समान आवश्यकताओं को .gnu.version_r सेक्शन में दिखाता है, जिन्हें उस लाइब्रेरी के आधार पर समूहीकृत किया गया है जो उन्हें प्रदान करती है। यदि objdump -T कुछ भी प्रिंट नहीं करता है, तो बाइनरी static है और उसे किसी glibc की आवश्यकता नहीं है। परिणाम की तुलना ldd --version से प्राप्त अपने सर्वर के संस्करण से करें।
क्या musl बाइनरी, glibc बाइनरी से धीमी होती है?
यह वर्कलोड पर निर्भर करता है, और इसका सही उत्तर मापने से ही मिलेगा। एक static musl बाइनरी तेज़ी से शुरू होती है, क्योंकि इसमें कोई लोडर नहीं चलाना पड़ता और exec के समय कोई रिलोकेशन का काम नहीं होता। इसके विपरीत, musl का एलोकेटर बहुत अधिक थ्रेड्स के एक साथ एलोकेशन के बजाय छोटे आकार और अनुमानित व्यवहार के लिए बनाया गया है। साथ ही, musl में glibc की तुलना में कम हैंड-ऑप्टिमाइज़्ड स्ट्रिंग और मेमोरी रूटीन होते हैं, इसलिए एलोकेशन या स्ट्रिंग-आधारित थ्रेडेड प्रोग्राम काफी धीमे हो सकते हैं। किसी भी दावे को मानने से पहले अपने सर्वर पर अपने प्रोग्राम का बेंचमार्क करें।
bash किसी मौजूद फाइल के लिए "No such file or directory" क्यों दिखाता है?
गायब फाइल डायनामिक लोडर है, न कि आपकी बाइनरी। कर्नल ELF हेडर से इंटरप्रेटर पाथ पढ़ता है और जब वह पाथ मौजूद नहीं होता तो ENOENT त्रुटि देता है, जिसे शेल अपने उपलब्ध एकमात्र संदेश के रूप में दिखाता है। file ./mytool चलाएं और interpreter फील्ड को पढ़ें। यदि Debian या Ubuntu सर्वर पर यह /lib/ld-musl-x86_64.so.1 दिखाता है, तो आपके पास glibc सिस्टम पर musl-लिंक्ड बाइनरी है। आपको musl-कम्पैटिबल डाउनलोड या static बिल्ड की आवश्यकता है।
क्या मैं इसे ठीक करने के लिए नए सर्वर से libc.so.6 कॉपी कर सकता हूँ?
नहीं। libc.so.6 और ld-linux-x86-64.so.2 एक ही glibc बिल्ड के मेल खाते जोड़े हैं, और मशीन पर हर प्रोसेस इनका उपयोग करती है। सिस्टम कॉपी को ओवरराइट करने से सर्वर पर कोई भी टूल चलना बंद हो सकता है, यहाँ तक कि वे टूल्स भी नहीं चलेंगे जिनकी आवश्यकता इसे ठीक करने के लिए होगी। यदि आपको पुराने होस्ट पर एक नई बाइनरी चलानी ही है, तो नए glibc को एक प्राइवेट डायरेक्टरी में अनपैक करें और उस प्रोग्राम को उसके अपने लोडर के साथ --library-path का उपयोग करके शुरू करें, जो केवल उसी प्रोसेस को प्रभावित करेगा। पुराने glibc के साथ रीबिल्ड करना ही वह समाधान है जो आपको छह महीने बाद परेशानी में नहीं डालेगा।