क्यों और कैसे कुछ साझा पुस्तकालय चलाने योग्य हैं, जैसे कि वे निष्पादन योग्य हैं?


55

32-बिट लिनक्स सिस्टम पर, इसे लागू करना

$ /lib/libc.so.6

और 64-बिट सिस्टम पर यह

$ /lib/x86_64-linux-gnu/libc.so.6

एक शेल में, इस तरह एक आउटपुट प्रदान करता है:

GNU C Library stable release version 2.10.1, by Roland McGrath et al.
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.4.0 20090506 (Red Hat 4.4.0-4).
Compiled on a Linux >>2.6.18-128.4.1.el5<< system on 2009-08-19.
Available extensions:
    The C stubs add-on version 2.1.2.
    crypt add-on version 2.1 by Michael Glad and others
    GNU Libidn by Simon Josefsson
    Native POSIX Threads Library by Ulrich Drepper et al
    BIND-8.2.3-T5B
    RT using linux kernel aio
For bug reporting instructions, please see:
<http://www.gnu.org/software/libc/bugs.html>.

ऐसा क्यों और कैसे होता है, और अन्य साझा पुस्तकालयों में भी ऐसा करना कैसे संभव है?

मैंने /usr/libनिष्पादनयोग्य खोजने के लिए देखा , और मैंने पाया /usr/lib/libvlc.so.5.5.0। इसे चलाने से एक विभाजन दोष हो गया । : - /


यह सभी निम्नलिखित उत्तरों के अलावा, मेरा स्मरण है यदि आप एक साझा पुस्तकालय पर x बिट सेट करते हैं, तो यह आर बिट स्पष्ट के साथ एक निष्पादन योग्य से लोड करने के लिए (शायद अभी भी है) संभव था। सिस्टम निष्पादकों और पुस्तकालयों पर दुनिया से r बिट को हटाने के लिए एक बार एक अच्छा सुरक्षा अभ्यास माना जाता था। व्यापक रूप से खुले स्रोत के कारण, यह केवल अनाम ftp निर्देशिका के / bin / ls पर ही लागू होता है। मेरे लिए, x बिट सेट को छोड़ना उस पुराने अभ्यास का एक दृश्य जैसा लगता है।
जोशुआ

जवाबों:


52

उस लाइब्रेरी में एक main()फ़ंक्शन या समकक्ष प्रविष्टि बिंदु है, और इस तरह से संकलित किया गया था कि यह निष्पादन योग्य और साझा वस्तु के रूप में उपयोगी है।

यह कैसे करना है, इसके बारे में एक सुझाव यहां दिया गया है, हालांकि यह मेरे लिए काम नहीं करता है।

यहाँ एसओ पर एक समान प्रश्न के उत्तर में एक और है , जिसे मैं बेशर्मी से तोड़-मरोड़, ट्विक करूंगा और थोड़ा सा स्पष्टीकरण दूंगा।

सबसे पहले, हमारे उदाहरण पुस्तकालय के लिए स्रोत test.c:

#include <stdio.h>                  

void sayHello (char *tag) {         
    printf("%s: Hello!\n", tag);    
}                                   

int main (int argc, char *argv[]) { 
    sayHello(argv[0]);              
    return 0;                       
}                   

संकलित करें कि:

gcc -fPIC -pie -o libtest.so test.c -Wl,-E

यहां, हम एक साझा लाइब्रेरी ( -fPIC) का संकलन कर रहे हैं , लेकिन लिंकर को बता रहे हैं कि यह एक नियमित निष्पादन योग्य ( -pie) है, और इसके प्रतीक तालिका को निर्यात करने योग्य ( -Wl,-E), जैसे कि इसे उपयोगी रूप से जोड़ा जा सकता है।

और, हालांकि fileकहेंगे कि यह एक साझा वस्तु है, यह एक निष्पादन योग्य के रूप में काम करता है:

> ./libtest.so 
./libtest.so: Hello!

अब हमें यह देखने की आवश्यकता है कि क्या यह वास्तव में गतिशील रूप से जुड़ा हो सकता है। एक उदाहरण कार्यक्रम program.c:

#include <stdio.h>

extern void sayHello (char*);

int main (int argc, char *argv[]) {
    puts("Test program.");
    sayHello(argv[0]);
    return 0;
}

का उपयोग externबचाता है हमें एक हेडर बनाने के लिए हो रही है। अब संकलित करें कि:

gcc program.c -L. -ltest

इससे पहले कि हम इसे निष्पादित कर सकें, हमें libtest.soगतिशील लोडर के लिए पथ जोड़ने की आवश्यकता है :

export LD_LIBRARY_PATH=./

अभी:

> ./a.out
Test program.
./a.out: Hello!

और ldd a.outलिंकेज को दिखाएगा libtest.so

ध्यान दें कि मुझे संदेह है कि वास्तव में ग्लिब कैसे संकलित किया जाता है, क्योंकि यह संभवतः ग्लिबिच के रूप में ही पोर्टेबल नहीं है (देखें और स्विच के man gccसंबंध में ), लेकिन यह बुनियादी तंत्र को दर्शाता है। वास्तविक विवरण के लिए आपको स्रोत मेकफाइल को देखना होगा।-fPIC-pie


1
शानदार जवाब, धन्यवाद! :-) मैंने nmसाझा लाइब्रेरी पर उपयोग करने की कोशिश की , लेकिन यह डिबग संस्करण नहीं था। तो, क्यों libvlcऔर अन्य दुर्घटना?

1
चूँकि अधिकांश साझा लाइब्रेरी निष्पादन योग्य नहीं हैं, इसलिए GNU libcएक अपवाद है।
गोल्डीलॉक्स

मुझे दो अन्य मिले: ldऔर libpthread
Ho1

@ हो ld.so1 अन्य तरीकों से विशेष है। कुछ हद तक, यह सामान्य गतिशील रूप से जुड़े निष्पादन योग्य की तुलना में वास्तविक निष्पादन योग्य है।
रैंडम 832

1
उपरोक्त विकल्प भले ही निष्पादन योग्य-साझा-पुस्तकालय बनाते हैं, लेकिन इस अर्थ में अधूरा है, कि यह त्रुटि को चिह्नित करता है, जब कुछ निष्पादन योग्य इससे लिंक करने का प्रयास करता है। विस्तृत नमूना संदर्भ यहां जोड़ा गया: unix.stackexchange.com/a/479334/152034
पैरा

21

आइए गीथूब में यादृच्छिक ग्लिब रेपो में उत्तर के लिए गोता लगाएँ। यह संस्करण फ़ाइल में एक provides बैनर प्रदान करता है version.c

उसी फ़ाइल में कुछ दिलचस्प बिंदु होते हैं: __libc_print_versionफ़ंक्शन जो स्टैडिंग को उसी पाठ और प्रतीक को मुद्रण प्रदान करता है __libc_main (void)जिसे एक प्रविष्टि बिंदु के रूप में प्रलेखित किया जाता है। इसलिए पुस्तकालय चलाते समय इस प्रतीक को कहा जाता है।

तो लिंकर / कंपाइलर कैसे जानता है कि यह बिल्कुल एंट्री पॉइंट फ़ंक्शन है?

चलो मेकफाइल में गोता लगाएँ । लिंकर झंडे में दिलचस्प झंडा है:

# Give libc.so an entry point and make it directly runnable itself.
LDFLAGS-c.so += -e __libc_main

तो यह पुस्तकालय में प्रवेश बिंदु स्थापित करने के लिए लिंकर ध्वज है। लायब्रेरी -e function_nameबनाते समय आप लिंकर के लिए प्रदान कर सकते हैं निष्पादन योग्य व्यवहार बनाएं। यह वास्तव में क्या करता है? आइए मैनुअल देखें (कुछ दिनांकित लेकिन अभी भी मान्य है) :

लिंकर कमांड भाषा में आउटपुट फ़ाइल में पहला निष्पादन योग्य निर्देश (इसकी प्रविष्टि बिंदु) को परिभाषित करने के लिए एक कमांड शामिल है। इसका तर्क एक प्रतीक नाम है:

प्रवेश (प्रतीक)

प्रतीक असाइनमेंट की तरह, ENTRY कमांड को या तो कमांड फाइल में एक स्वतंत्र कमांड के रूप में रखा जा सकता है, या सेक्शन कमांड के भीतर खंड परिभाषाओं के बीच - जो भी आपके लेआउट के लिए सबसे अधिक समझ में आता है।

प्रवेश बिंदु चुनने के कई तरीकों में से केवल एक ही है। आप इसे निम्न तरीकों में से किसी में भी संकेत कर सकते हैं (प्राथमिकता के अवरोही क्रम में दिखाया गया है: सूची में उच्चतर विधियाँ नीचे दी गई विधियों से अधिक हैं)।

the `-e' entry command-line option;
the ENTRY(symbol) command in a linker control script;
the value of the symbol start, if present;
the address of the first byte of the .text section, if present;
The address 0. 

उदाहरण के लिए, आप इन नियमों का उपयोग एक असाइनमेंट स्टेटमेंट के साथ एक एंट्री पॉइंट जेनरेट करने के लिए कर सकते हैं: यदि आपके इनपुट फाइलों में कोई सिंबल स्टार्ट परिभाषित नहीं किया गया है, तो आप बस इसे परिभाषित कर सकते हैं, इसे एक उचित मान बताएं ---

start = 0x2020;

उदाहरण एक पूर्ण पता दिखाता है, लेकिन आप किसी भी अभिव्यक्ति का उपयोग कर सकते हैं। उदाहरण के लिए, यदि आपकी इनपुट ऑब्जेक्ट फाइलें प्रविष्टि बिंदु के लिए कुछ अन्य प्रतीक-नाम सम्मेलन का उपयोग करती हैं, तो आप बस उस चिह्न के मूल्य को असाइन कर सकते हैं जिसमें प्रारंभ करने के लिए प्रारंभ पता शामिल है:

start = other_symbol;

(वर्तमान प्रलेखन यहाँ पाया जा सकता है )

ldयदि आप कमांड लाइन विकल्प -e(सबसे व्यावहारिक समाधान), फ़ंक्शन प्रतीक प्रदान करते हैं start, या कोडांतरक में प्रतीक चिह्न इंजेक्ट करते हैं तो वास्तव में लिंकर प्रवेश बिंदु फ़ंक्शन के साथ एक निष्पादन योग्य बनाता है ।

हालाँकि कृपया ध्यान दें कि यह स्पष्ट रूप से अन्य लिंकर्स के साथ काम करने की गारंटी नहीं है (मुझे नहीं पता कि llvm के lld में समान ध्वज है)। यह इतनी फाइल पर जानकारी प्रदान करने के अलावा अन्य उद्देश्यों के लिए उपयोगी क्यों होना चाहिए, मुझे जानकारी नहीं है।


1
यदि यह अजगर था तो यह इकाई परीक्षण प्रदान करेगा।
एरिक एरोनिटी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.