2015-09-21 4 views
16

मैं सिर्फ ELF फाइलों में के बारे में init and fini sections पढ़ सकते हैं और यह एक कोशिश दे दिया:को क्रियान्वित करने init और fini

#include <stdio.h> 
int main(){ 
    puts("main"); 
    return 0; 
} 

void init(){ 
    puts("init"); 
} 
void fini(){ 
    puts("fini"); 
} 

अगर मैं gcc -Wl,-init,init -Wl,-fini,fini foo.c करते हैं और परिणाम "init" भाग मुद्रित नहीं है चलाएँ:

$ ./a.out 
main 
fini 

क्या init हिस्सा नहीं चला था, या यह किसी भी तरह प्रिंट करने में सक्षम नहीं था?

क्या इनिट/फिनि सामान के बारे में कोई "आधिकारिक" दस्तावेज है?

man ld का कहना है:

-init=name 
    When creating an ELF executable or shared object, call 
    NAME when the executable or shared object is loaded, by 
    setting DT_INIT to the address of the function. By 
    default, the linker uses "_init" as the function to call. 

इसका मतलब यह नहीं है, तो यह है कि यह init समारोह _init नाम के लिए पर्याप्त होगा? (यदि मैं जीसीसी कई परिभाषाओं के बारे में शिकायत करता हूं।)

+0

यह अजीब है ... अपने पहले प्रश्न के बारे में - मेरे GDB पता चलता है कि init समारोह बिल्कुल नहीं चलता है। – MByD

+1

यह काफी संभव है कि यूनिट सेक्शन मानक लाइब्रेरी से पहले चलता है जो काम करने के लिए पर्याप्त प्रारंभ होता है। यूनिट फ़ंक्शन वास्तव में चल रहा है या नहीं, यह देखने के लिए कि आप ग्लोबल वैरिएबल सेट करने की तरह किसी निर्भरता के साथ कुछ क्यों नहीं करते हैं। –

उत्तर

8

ऐसा मत करो; अपने कंपाइलर और लिंकर को अनुभागों में भरने दें क्योंकि वे फिट दिखाई देते हैं।

इसके बजाय, अपने कार्यों को उपयुक्त function attributes के साथ चिह्नित करें, ताकि संकलक और लिंकर उन्हें सही अनुभागों में रख सकें।

उदाहरण के लिए

,

static void before_main(void) __attribute__((constructor)); 
static void after_main(void) __attribute__((destructor)); 

static void before_main(void) 
{ 
    /* This is run before main() */ 
} 

static void after_main(void) 
{ 
    /* This is run after main() returns (or exit() is called) */ 
} 

तुम भी एक प्राथमिकता (जैसे कि, __attribute__((constructor (300)))), 101 और 65535 सहित उनके बीच एक पूर्णांक प्रदान कर सकते हैं, पहले छोटे प्राथमिकता संख्या रन होने कार्यों के साथ।

ध्यान दें कि चित्रण के लिए, मैंने static कार्यों को चिह्नित किया। यही है, फ़ंक्शन फ़ाइल स्कोप के बाहर दिखाई नहीं देंगे। कार्यों को स्वचालित रूप से कॉल करने के लिए प्रतीकों को निर्यात करने की आवश्यकता नहीं है।


परीक्षण के लिए, मैं बचत की सलाह देते हैं एक अलग फ़ाइल में निम्न, का कहना है कि tructor.c:

#include <unistd.h> 
#include <string.h> 
#include <errno.h> 

static int outfd = -1; 

static void wrout(const char *const string) 
{ 
    if (string && *string && outfd != -1) { 
     const char  *p = string; 
     const char *const q = string + strlen(string); 

     while (p < q) { 
      ssize_t n = write(outfd, p, (size_t)(q - p)); 
      if (n > (ssize_t)0) 
       p += n; 
      else 
      if (n != (ssize_t)-1 || errno != EINTR) 
       break; 
     } 
    } 
} 

void before_main(void) __attribute__((constructor (101))); 
void before_main(void) 
{ 
    int saved_errno = errno; 

    /* This is run before main() */ 
    outfd = dup(STDERR_FILENO); 
    wrout("Before main()\n"); 

    errno = saved_errno; 
} 

static void after_main(void) __attribute__((destructor (65535))); 
static void after_main(void) 
{ 
    int saved_errno = errno; 

    /* This is run after main() returns (or exit() is called) */ 
    wrout("After main()\n"); 

    errno = saved_errno; 
} 

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

gcc -Wall -Wextra -fPIC -shared tructor.c -Wl,-soname,libtructor.so -o libtructor.so 

और आप

LD_PRELOAD=./libtructor.so some-command-or-binary 

का उपयोग कर कार्यों errno अपरिवर्तित रखने के किसी भी गतिशील रूप से जुड़े हुए आदेश या बाइनरी में लगाना कर सकते हैं, हालांकि यह व्यवहार में कोई फर्क नहीं करना चाहिए, और निम्न स्तर को write() syscall का उपयोग संदेशों को मानक त्रुटि में आउटपुट करें। प्रारंभिक मानक त्रुटि को एक नए वर्णनकर्ता को डुप्लिकेट किया गया है, क्योंकि कई मामलों में, मानक वैश्विक स्वयं को अंतिम वैश्विक विनाशक से पहले बंद कर दिया जाता है - यहां हमारे विनाशक - चला जाता है।

(कुछ पागल बाइनरी, आम तौर पर सुरक्षा के प्रति संवेदनशील हैं, करीब सभी वर्णनकर्ता वे के बारे में पता नहीं है, तो आप नहीं सभी मामलों में After main() संदेश दिखाई दे सकता।)

+0

उन funktion विशेषताएँ वास्तव में कैसे काम करते हैं? क्या कंपाइलर init/fini अनुभागों के लिए व्यक्तिगत कोड उत्पन्न करता है? लेकिन जहां तक ​​मुझे लगता है कि कोड एक अलग फ़ाइल (crti.o) से जुड़ा हुआ है। – michas

+0

@ मिचस मूल रूप से, उन विशेषताओं को सी भाषा में बिल्कुल उसी तंत्र के रूप में उजागर किया जाता है जो प्रारंभिक समय पर सी ++ वैश्विक वस्तुओं का निर्माण करने की अनुमति देता है। दोनों सी ++ कन्स्ट्रक्टर और '__attribute __ ((कन्स्ट्रक्टर)) '-नोटेटेड सी फ़ंक्शंस केवल कोड हैं जिन्हें" मुख्य' से पहले "कहा जाता है, ताकि (पूर्व मामले में) ऑब्जेक्ट्स 'मुख्य' शुरू होने तक उपयोग करने के लिए तैयार हों । –

+0

@ मिचस: हां, 'crti.o' ईएलएफ' _init' और '_fini' खंडों में जीएनयू सी लाइब्रेरी की आवश्यकता कोड प्रदान करता है। यह विशिष्ट एबीआई (x86, x86-64, एआरएम वेरिएंट इत्यादि) पर निर्भर करता है बिल्कुल चिह्नित कार्यों को कैसे कॉल किया जाता है। उदाहरण के लिए, x86-64 पर, मेरा मानना ​​है कि फ़ंक्शन पते क्रमशः '.init_array' और' .fini_array' खंडों में सूचीबद्ध हैं ('__frame_dummy_init_array_entry' और' __do_global_dtors_aux_fini_array_entry' प्रतीकों)। –

4

यह ld में एक बग नहीं है लेकिन मुख्य निष्पादन योग्य के लिए glibc स्टार्टअप कोड में।साझा वस्तुओं के लिए -init विकल्प द्वारा निर्धारित फ़ंक्शन को कॉल किया जाता है।


This ld को -init और -fini विकल्प जोड़ने की प्रतिबद्धता है।
कार्यक्रम के _init समारोह गतिशील लिंकर द्वारा DT_INIT प्रवेश द्वारा फ़ाइल glibc-2.21/elf/dl-init.c:58 से नहीं बुलाया जाता है, लेकिन मुख्य निष्पादन योग्य फ़ाइल glibc-2.21/csu/elf-init.c:83 में __libc_csu_init से कहा जाता है।

यही है, प्रोग्राम के DT_INIT में फ़ंक्शन पॉइंटर स्टार्टअप द्वारा अनदेखा किया जाता है।

यदि आप -static, fini के साथ संकलित नहीं करते हैं, तो भी नहीं कहा जाता है।

DT_INIT और DT_FINI निश्चित रूप से उपयोग नहीं किया जाना चाहिए, क्योंकि वे old-style, see line 255 हैं।

निम्नलिखित काम करता है:

#include <stdio.h> 

static void preinit(int argc, char **argv, char **envp) { 
    puts(__FUNCTION__); 
} 

static void init(int argc, char **argv, char **envp) { 
    puts(__FUNCTION__); 
} 

static void fini(void) { 
    puts(__FUNCTION__); 
} 


__attribute__((section(".preinit_array"), used)) static typeof(preinit) *preinit_p = preinit; 
__attribute__((section(".init_array"), used)) static typeof(init) *init_p = init; 
__attribute__((section(".fini_array"), used)) static typeof(fini) *fini_p = fini; 

int main(void) { 
    puts(__FUNCTION__); 
    return 0; 
} 

$ gcc -Wall a.c 
$ ./a.out 
preinit 
init 
main 
fini 
$ 
+0

इस मामले में यह मैन पेज या एलडी में एक बग होगा। या इस मामले में इसे अनदेखा करने का कोई अच्छा कारण है? – michas

+0

@ 4566976 - इस के लिए कोई दस्तावेज? – MByD

+0

एलडी अज्ञात फिनि नाम के बारे में शिकायत नहीं करता है, लेकिन फिर भी काम करता है। – michas

संबंधित मुद्दे