लिनक्स में पुस्तकालयों को लोड करने के लिए किस सिस्टम कॉल का उपयोग किया जाता है?


23

में straceआउटपुट, पुस्तकालयों के लिए रास्तों कि निष्पादनयोग्य कॉल करने के लिए कॉल में हैं open()। क्या यह सिस्टम कॉल निष्पादक द्वारा उपयोग किया जाता है जो गतिशील रूप से जुड़ा हुआ है? किस बारे में dlopen()? open()ऐसा कोई फोन नहीं है जिसका मैंने अनुमान लगाया हो कि कार्यक्रमों के निष्पादन में कोई भूमिका होगी।

जवाबों:


33

dlopenसिस्टम कॉल नहीं है, यह libdl लाइब्रेरी में लाइब्रेरी फ़ंक्शन है । केवल सिस्टम कॉल में दिखाया गया है strace

लिनक्स पर और कई अन्य प्लेटफार्मों पर (विशेष रूप से वे जो निष्पादन के लिए ईएलएफ प्रारूप का उपयोग करते हैं), dlopenलक्ष्य लाइब्रेरी को खोलने open()और इसे मेमोरी में मैप करने के द्वारा कार्यान्वित किया जाता है mmap()mmap()यहाँ वास्तव में महत्वपूर्ण हिस्सा है, यह वही है जो लाइब्रेरी को प्रक्रिया के पते के स्थान में शामिल करता है, इसलिए सीपीयू अपने कोड को निष्पादित कर सकता है। लेकिन आपके पास open()फाइल करने से पहले आपको mmap()यह करना होगा!


2
"एमएमएपी () वास्तव में महत्वपूर्ण हिस्सा है": और फिर डायनेमिक लिंकर को रिलोकेशन, इनिशियलाइज़ेशन और एक ही करना होता है (लेकिन यह सिस्टम कॉल स्तर पर नहीं देखा जाता है)।
ysdx

1
चूंकि पुस्तकालयों को लोड करना एक लाइब्रेरी फ़ंक्शन द्वारा किया जाता है, इसलिए मुझे लगता है कि यह निष्पादन योग्य है ld-linuxकि execveसिस्टम कॉल के हिस्से के रूप में निष्पादन योग्य ही जोड़ा जाता है और कर्नेल द्वारा मैप किया जाता है ।
कैस्परर्ड

इस उत्तर के अनुसार मिमीप। यह भी ध्यान दें कि प्रत्येक लाइब्रेरी को "ओपन" करने के बाद, कुछ (832) बाइट्स को mmap कॉल से पहले पढ़ा जाता है, मैं यह जांचने के लिए मानता हूं कि लाइब्रेरी मान्य है।
जोहान

@kasperd क्या लिनक्स कर्नेल डायनेमिक लोडर से अवगत है? क्या इसे तब चलाया जाता है जब एप्लिकेशन चलाया जाता है? या क्या एप्लिकेशन ही ऐसा करता है? यदि बाद वाला, किसी अन्य निष्पादन योग्य के पास एप्लिकेशन की मेमोरी तक पहुंच कैसे होती है?
मेलब

@Melab हाँ, कर्नेल डायनेमिक लिंकर से अवगत है। कर्नेल निष्पादन योग्य के हेडर से डायनेमिक लिंकर का पथ पढ़ेगा। और कर्नेल मेमोरी में दोनों को मैप करेगा। मुझे नहीं पता कि प्रवेश बिंदु जिसे कर्नेल स्थानांतरण नियंत्रण पहले है, लिंकर या निष्पादन योग्य है। यदि मैं इसे लागू कर रहा था, तो मुझे संभवतः लिंक में एक प्रविष्टि बिंदु पर कर्नेल स्थानांतरण नियंत्रण होगा जो निष्पादन योग्य के प्रवेश बिंदु की ओर इशारा करते हुए स्टैक पर एक वापसी पते के साथ होगा।
कसपर

5

dlopen का साझा पुस्तकालयों से कोई लेना-देना नहीं है जैसा कि आप उनके बारे में सोचते हैं। साझा किए गए ऑब्जेक्ट को लोड करने के दो तरीके हैं:

  1. आप संकलन-समय लिंकर (ld, हालांकि इसे आमतौर पर कंपाइलर के माध्यम से कहा जाता है) को बताते हैं कि आप किसी विशेष साझा लाइब्रेरी से कार्यों का उपयोग करना चाहते हैं। इस दृष्टिकोण के साथ, आपको पता होना चाहिए कि कंपाइल-टाइम लिंकर को चलाने पर लाइब्रेरी का नाम क्या होगा, लेकिन आप लाइब्रेरी के कार्यों को कॉल कर सकते हैं जैसे कि वे आपके प्रोग्राम में स्टेटिकली लिंक थे। जब एप्लिकेशन चलाया mainजाता है, तो फ़ंक्शन को कॉल करने से ठीक पहले डायनामिक, रन-टाइम लिंकर (ld.so) को कॉल किया जाएगा , और एप्लिकेशन की प्रोसेस स्पेस सेट करें, ताकि एप्लिकेशन लाइब्रेरी के कार्यों को खोज ले। इसमें open()सबसे उलटा शामिल है , और फिर mmap()इसे संलग्न करना है, कुछ लुकअप टेबल सेट करके।
  2. आप संकलन समय लिंकर बताते हैं कि आप के साथ लिंक करना चाहते हैं libdl, तो आप (पहली विधि का उपयोग कर) है जहाँ से आप कॉल कर सकते हैं dlopen()औरdlsym()कार्य करता है। Dlopen के साथ आपको लाइब्रेरी का एक हैंडल मिलता है, जिसे आप किसी विशेष फ़ंक्शन के लिए फ़ंक्शन पॉइंटर प्राप्त करने के लिए dlsym के साथ उपयोग कर सकते हैं। यह विधि प्रोग्रामर के लिए पहली विधि की तुलना में बहुत अधिक जटिल है (क्योंकि आपको मैन्युअल रूप से सेटअप करना है, बजाय इसके कि लिंकर आपके लिए स्वचालित रूप से हो), और यह अधिक नाजुक भी है (क्योंकि आपको संकलन नहीं मिलता है -समय की जाँच करें कि आप सही तर्क प्रकारों के साथ कार्य कर रहे हैं जैसा कि आप पहली विधि में प्राप्त करते हैं), लेकिन लाभ यह है कि आप तय कर सकते हैं कि रनटाइम पर लोड करने के लिए किस साझा वस्तु को (या यहां तक ​​कि इसे लोड करने के लिए भी), बनाना यह प्लगइन प्रकार की कार्यक्षमता के लिए एक इंटरफेस है। अंत में, dlopen इंटरफ़ेस दूसरे तरीके की तुलना में कम पोर्टेबल है, क्योंकि इसके मैकेनिक गतिशील लिंकर के सटीक कार्यान्वयन पर निर्भर करते हैं (इसलिए libtoollibltdl, जो इन मतभेदों को दूर करने की कोशिश करता है)।

दिलचस्प; इसलिए गतिशील रूप से भरी हुई लाइब्रेरी को डायनेमिकली लिंक्ड लाइब्रेरी कहा जाता है, क्योंकि बाइनरी फ़ाइलों को मेमोरी में लोड करना कठिन हिस्सा नहीं है, इसमें उपयोग किए गए पतों को सही अर्थ में बनाया जाता है। जब मैं डायनेमिक लाइब्रेरी लोड करने के लिए कहता हूं, तो मैं वास्तव में लाइब्रेरी को अपने पते के स्थान में (या बाहर) लिंक करने (या बाहर निकालने) के लिए कह रहा हूं।
दिमित्री

4

आज, अधिकांश ऑपरेटिंग सिस्टम 1987 के अंत में SunOS-4.0 द्वारा साझा किए गए साझा पुस्तकालयों के लिए विधि का उपयोग करते हैं। यह विधि एमएमएपी () के माध्यम से मेमोरी को मैप करने पर आधारित है।

इस तथ्य को देखते हुए कि 1990 के दशक के प्रारंभ में, सन ने भी पुराने a.out आधारित कोड (सोलारिस उस समय पहले से ही ELF आधारित था) को FreeBSD लोगों को दान कर दिया था और यह कोड बाद में कई अन्य प्रणालियों (लिनक्स सहित) को सौंप दिया गया था , आप समझ सकते हैं कि प्लेटफार्मों के बीच कोई बड़ा अंतर क्यों नहीं है।


3

ltrace -Sन्यूनतम उदाहरण के विश्लेषण से पता चलता है कि mmapग्लिब 2.23 में उपयोग किया जाता है

2.23, उबंटू 16.04 में glibc में, latrace -Sएक न्यूनतम प्रोग्राम पर चल रहा है जो इसके dlopenसाथ उपयोग करता है:

ltrace -S ./dlopen.out

दिखाता है:

dlopen("libcirosantilli_ab.so", 1 <unfinished ...>
SYS_open("./x86_64/libcirosantilli_ab.so", 524288, 06267650550)      = -2
SYS_open("./libcirosantilli_ab.so", 524288, 06267650550)             = 3
SYS_read(3, "\177ELF\002\001\001", 832)                              = 832
SYS_brk(0)                                                           = 0x244c000
SYS_brk(0x246d000)                                                   = 0x246d000
SYS_fstat(3, 0x7fff42f9ce30)                                         = 0
SYS_getcwd("/home/ciro/bak/git/cpp-cheat"..., 128)                   = 54
SYS_mmap(0, 0x201028, 5, 2050)                                       = 0x7f1c323fe000
SYS_mprotect(0x7f1c323ff000, 2093056, 0)                             = 0
SYS_mmap(0x7f1c325fe000, 8192, 3, 2066)                              = 0x7f1c325fe000
SYS_close(3)                                                         = 0
SYS_mprotect(0x7f1c325fe000, 4096, 1)                                = 0

तो हम तुरंत कि देखने के dlopenकॉल open+ mmap

भयानक ltraceटूल लाइब्रेरी कॉल और सिस्टम कॉल दोनों का पता लगाता है, और इसलिए इस मामले में क्या चल रहा है, इसकी जांच करने के लिए एकदम सही है।

एक करीबी विश्लेषण से पता चलता है कि openफ़ाइल डिस्क्रिप्टर 3(स्टड, आउट और इरिट के बाद अगला मुफ़्त) है।

readफिर उस फाइल डिस्क्रिप्टर का उपयोग करता है, लेकिन टीओडीओ की mmapदलीलें चार तक ही सीमित हैं, और हम यह नहीं देख सकते हैं कि 5 वीं तर्क के बाद से कौन सी एफडी का उपयोग किया गया थाstraceउम्मीद के मुताबिक पुष्टि करता है कि 3एक है, और ब्रह्मांड का क्रम बहाल है।

बहादुर आत्माएं ग्लिबक कोड में भी उद्यम कर सकती हैं, लेकिन मुझे mmapजल्दी जीआरपी नहीं मिला और मैं आलसी हूं।

GitHub पर बायलरप्लेट के निर्माण के साथ इस न्यूनतम उदाहरण का परीक्षण किया गया


2

straceसिस्टम कॉल (यानी कर्नेल द्वारा सीधे लागू किए गए कार्य) पर रिपोर्ट। गतिशील पुस्तकालय एक कर्नेल फ़ंक्शन नहीं हैं; dlopenC लाइब्रेरी का हिस्सा है, कर्नेल का नहीं। लाइब्रेरी फ़ाइल खोलने के लिए dlopenवसीयत लागू करना open(जो एक सिस्टम कॉल है) इसलिए इसे पढ़ा जा सकता है।


5
लाइब्रेरी कॉल का उपयोग करते हुए देखा जा सकता है ltrace
कास्परड

@kasperd ltrace -Sइसका विश्लेषण करने के लिए एकदम सही है क्योंकि यह syscalls को भी दर्शाता है: unix.stackexchange.com/a/462710/32558
Ciro Santilli 新疆 is is is 事件
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.