2014-08-27 5 views
28

लिनक्स कर्नेल 0.12 में एक कोड स्निपेट इस तरह एक समारोह पैरामीटर का उपयोग:इस तरह से फ़ंक्शन पैरामीटर 'foo' का उपयोग क्यों करें: * (& foo)?

int do_signal(int signr, int eax /* other parameters... */) { 
    /* ... */ 

    *(&eax) = -EINTR; 

    /* ... */ 
} 

कोड के प्रयोजन डाल करने के लिए -EINTR स्मृति जहां eax जीवन के लिए है, लेकिन मैं नहीं बता सकता कि ऐसा क्यों नहीं होगा बस EAX को बताए अगर काम:

eax = -EINTR 

कैसे संकलक eax और * (& eax) के बीच एक अंतर होगा?

+2

क्या आप हमें उस कर्नेल स्रोत का लिंक प्रदान कर सकते हैं ?? या फ़ंक्शन की पूर्ण परिभाषा जोड़ें (यदि यह बहुत लंबा नहीं है)। –

+6

यह कंपाइलर को ईएक्स के लिए रजिस्टर का उपयोग करने से रोक सकता है और इसे स्टैक पर मेमोरी का उपयोग करने के लिए मजबूर कर सकता है। –

+0

@ KlasLindbäck: अच्छा !!! आपको इसे लिखना चाहिए क्योंकि यह संभवतः सही उत्तर है (नाम 'eax' वास्तव में insinuates कि यह कारण है)। –

उत्तर

22

एक संभावित उद्देश्य eax एक रजिस्टर से बाहर रखने के लिए हो सकता है। अगर हम C99 draft standard को देखो हम उस खंड देखें 6.5.3.2पता और अविवेक ऑपरेटरों कहते हैं (जोर मेरा):

एकल & ऑपरेटर अपनी संकार्य का पता अर्जित करता है। [...] संकार्य एक एकल * ऑपरेटर का परिणाम है, न तो उस ऑपरेटर है और न ही & ऑपरेटर मूल्यांकन किया जाता है और परिणाम के रूप में यदि दोनों को छोड़ दिया गया, कि छोड़कर ऑपरेटरों पर कमी अभी भी लागू है और परिणाम एक lvalue नहीं है [...]

फुटनोट में

यह कहता है (जोर मेरा आगे जा रहा):।

इस प्रकार, & * ई ई (भले ही ई एक अशक्त सूचक है) के बराबर है, और & (ई 1 [E2]) ((ई 1) + (E2)) करने के लिए। यह हमेशा सच है कि अगर ई एक समारोह डेसिग्नेटर या एक lvalue एकल & ऑपरेटर का एक मान्य संकार्य है कि, * & ई एक समारोह डेसिग्नेटर या एक lvalue ई के बराबर है।

हम & operator पर निम्नलिखित बाधा लगता है:

एकल & ऑपरेटर के संकार्य किया जाएगा या तो एक समारोह डेसिग्नेटर, एक [] या एकल * ऑपरेटर, या एक lvalue का परिणाम है कि एक ऑब्जेक्ट निर्दिष्ट करता है जो बिट-फ़ील्ड नहीं है और रजिस्टर स्टोरेज-क्लास विनिर्देशक के साथ घोषित नहीं किया गया है।

कौन समझ में आता है, क्योंकि हम एक रजिस्टर का पता है और इसलिए आपरेशन वे रजिस्टर में पूरी तरह से ऑपरेशनों को करने से संकलक को रोकने और उस डेटा को सुनिश्चित करने के प्रयास कर रहे हैं हो सकता है की एक पते के प्रदर्शन से नहीं ले जा सकते विशिष्ट स्मृति स्थानों में संशोधित हैं।

ouah बताते हैं के रूप में इस के अनुकूलन क्या प्रभावी रूप से एक कोई सेशन की दूरी पर है से संकलक नहीं रोकता है, लेकिन जैसा कि GCC hacks in the Linux kernel में प्रलेखित। लिनक्स ने कई gcc एक्सटेंशन पर भरोसा किया है और 0.12 पर विचार करना बहुत पुराना कर्नेल gcc यह गारंटी दे सकता है कि व्यवहार या दुर्घटना से हो सकता है कि इस तरह से काम किया जाए लेकिन मुझे ऐसा कोई दस्तावेज नहीं मिल रहा है जो ऐसा कहता है।

+3

+1, यह एक अच्छा जवाब है, लेकिन कंपाइलर अभी भी 'eax' के लिए एक रजिस्टर का उपयोग करने से रोकता है और '* &' को नो-ऑप के रूप में मानता है? यह एक विशिष्ट संकलक के लिए एक हैक अच्छी तरह से हो सकता है। – ouah

+2

अच्छा जवाब, वास्तव में। लेकिन '&' का संचालन असाइन '*' का नतीजा नहीं है, यह एक और तरीका है (हमारे पास '* और eax' है, न कि' & * eax') और मुझे '*' के लिए समान गारंटी नहीं मिल रही है। मानक में शायद यह आंशिक रूप से Ouah की चिंताओं का जवाब देता है? – mafso

+0

मैं यह भी जोड़ना चाहता हूं कि 'रजिस्टर' की अनुपस्थिति कंपाइलर को कुछ भी नहीं कहती है। बाधा घोषणा पर लागू होती है, लेकिन यह नहीं कि वास्तव में मूल्य कहां रखा जाता है। जहां तक ​​मुझे पता है, मानक सी 99 में मूल्य कहां जाता है, यह गारंटी देने का कोई तरीका नहीं है, यहां तक ​​कि 'रजिस्टर' कीवर्ड भी संकलक को केवल एक सुझाव है। हो सकता है कि अगर पता वास्तव में उपयोग किया जाता है, तो इसे यादों पर जाना होगा, लेकिन मुझे यकीन नहीं है कि अनुकूलन इसे बदल नहीं सकते हैं। – Malcolm

31

आपके द्वारा पोस्ट किया गया पुराना लिनक्स एक बहुत नाजुक हैक करने की कोशिश कर रहा था। समारोह इस तरह परिभाषित किया गया था:

int do_signal(long signr,long eax,long ebx, long ecx, long edx, long orig_eax, 
    long fs, long es, long ds, 
    long eip, long cs, long eflags, 
    unsigned long * esp, long ss) 

समारोह तर्क वास्तव में समारोह (signr को छोड़कर) के लिए तर्क का प्रतिनिधित्व नहीं करते, लेकिन मानों फ़ंक्शन कॉल (एक कर्नेल व्यवधान/अपवाद संचालक विधानसभा में लिखित) पर संरक्षित do_signal पर कॉल करने से पहले ढेर। *(&eax) = -EINTR कथन स्टैक पर ईएक्स के संरक्षित मूल्य को संशोधित करने के लिए है। इसी प्रकार कथन *(&eip) = old_eip -= 2 कॉलिंग हैंडलर के रिटर्न पते को संशोधित करने के लिए है। do_signal के बाद हैंडलर नामित रजिस्टरों को बहाल करने वाले स्टैक से पहले 9 "तर्क" पॉप करता है। इसके बाद यह IRETD निर्देश निष्पादित करता है जो स्टैक से शेष तर्कों को पॉप करता है और उपयोगकर्ता मोड पर वापस आ जाता है।

यह हैक कहने की जरूरत नहीं है कि यह हैक अविश्वसनीय रूप से अविश्वसनीय है। यह कंपाइलर जेनरेटिंग कोड पर निर्भर करता है जिस तरह से उन्होंने उम्मीद की थी। मुझे आश्चर्य है कि इसने युग के जीसीसी कंपाइलर को भी काम किया है, मुझे संदेह है कि जीसीसी ने इसे ऑप्टिमाइज़ेशन शुरू करने से बहुत पहले ही इसे तोड़ दिया था।

+0

मुझे आश्चर्य है कि पुराने 'gcc' संस्करण' eax = -EINTR' और '* (और eax) = -EINTR' और अनुकूलन सक्षम के साथ अलग-अलग व्यवहार करेंगे। – ouah

+0

हाँ, आधुनिक प्रणाली पर पुराने कर्नेल को संकलित करना वास्तव में परेशानी है, लेकिन किसी ने इसे पूरा कर लिया है, [Oldlinux.org] देखें (http: //www.oldlinux।संगठन) – Gurity

+6

यदि आप उनका पता लेते हैं तो जीसीसी के पुराने संस्करण regsiters में चीजें नहीं डालेंगे। (ध्यान दें कि इस व्यवहार को मानक द्वारा आवश्यक नहीं है, केवल तभी जब आप इसका पता लेते हैं तो आप 'रजिस्टर' की कुछ घोषणा नहीं कर सकते हैं।) कंपाइलर अभी भी असाइनमेंट को हटा सकता है या इसे सरल स्थिरता के बाद ले जा सकता है और सभी कोड पथ इसका उपयोग नहीं करते हैं। –

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