GCC पैड NOP के साथ क्यों कार्य करता है?


81

मैं थोड़ी देर के लिए सी के साथ काम कर रहा हूं और हाल ही में एएसएम में आना शुरू हुआ। जब मैं एक कार्यक्रम संकलित करता हूं:

Objdump disassembly में कोड होता है, लेकिन रिट के बाद नोड्स:

मैंने जो सीखा है उससे कुछ भी नहीं होता है, और चूंकि रिट के बाद भी निष्पादित नहीं किया जाएगा।

मेरा सवाल है: क्यों परेशान? ELF (linux-x86) किसी भी आकार के .text सेक्शन (+ मुख्य) के साथ काम नहीं कर सकता है?

मैं किसी भी मदद की सराहना करता हूं, बस सीखने की कोशिश कर रहा हूं।


क्या वे एनओपी चलते रहते हैं? यदि वे रुक जाते हैं 80483af, तो हो सकता है कि अगले समारोह को 8 या 16 बाइट्स में संरेखित करें।
6

4 nops के बाद यह एक फंक्शन में स्ट्रेट हो जाता है: __libc_csu_fini
olly

1
अगर एनओपी को जीसीसी द्वारा डाला गया था, तो मुझे नहीं लगता कि यह केवल 0x90 का उपयोग करेगा क्योंकि 1-9 बाइट्स से आकार चर वाले कई एनओपी हैं (10 अगर गैस सिंटैक्स का उपयोग करें )
phuclv

जवाबों:


89

सबसे पहले, gccहमेशा ऐसा नहीं करता है। पैडिंग द्वारा नियंत्रित किया जाता है -falign-functions, जो स्वचालित रूप से चालू होता है -O2और -O3:

-falign-functions
-falign-functions=n

बाइट्स पर nलंघन से , अगले पावर-ऑफ-टू में कार्यों की शुरुआत को संरेखित करें n। उदाहरण के लिए, -falign-functions=32अगले 32-बाइट सीमा के लिए फ़ंक्शंस संरेखित करता है, लेकिन अगली 32-बाइट सीमा के -falign-functions=24लिए संरेखित करेगा केवल अगर यह 23 बाइट्स या कम को लंघन द्वारा किया जा सकता है।

-fno-align-functionsऔर -falign-functions=1समतुल्य हैं और इसका मतलब है कि कार्यों को संरेखित नहीं किया जाएगा।

कुछ एसेंबलर्स केवल इस झंडे का समर्थन करते हैं जब n दो की शक्ति है; उस मामले में, यह गोल है।

यदि n निर्दिष्ट नहीं है या शून्य है, तो मशीन-निर्भर डिफ़ॉल्ट का उपयोग करें।

स्तरों पर सक्षम -O2, -O3।

ऐसा करने के कई कारण हो सकते हैं, लेकिन x86 पर मुख्य शायद यही है:

अधिकांश प्रोसेसर 16-बाइट या 32-बाइट ब्लॉक में निर्देश प्राप्त करते हैं। कोड में 16-बाइट सीमाओं की संख्या को कम करने के लिए महत्वपूर्ण लूप प्रविष्टियों और सबरूटीन प्रविष्टियों को 16 से संरेखित करना फायदेमंद हो सकता है। वैकल्पिक रूप से, सुनिश्चित करें कि एक महत्वपूर्ण लूप प्रविष्टि या सबरूटीन प्रविष्टि के बाद पहले कुछ निर्देशों में कोई 16-बाइट सीमा नहीं है।

(Agner Fog द्वारा "असेंबली भाषा में उप-भाषा का अनुकूलन" से उद्धृत।)

संपादित करें: यहाँ एक उदाहरण है जो पैडिंग प्रदर्शित करता है:

जब डिफ़ॉल्ट सेटिंग्स के साथ gcc 4.4.5 का उपयोग करके संकलित किया जाता है, तो मुझे यह मिलता है:

निर्दिष्ट -falign-functionsकरता है:


1
मैंने किसी भी -O झंडे, सरल "gcc -o test test.c" का उपयोग नहीं किया।
०11

1
@ हंसली: मैंने इसे 64-बिट Ubuntu पर gcc 4.4.5 के साथ परीक्षण किया है और मेरे परीक्षणों में डिफ़ॉल्ट रूप से कोई पैडिंग नहीं है, और इसके साथ पैडिंग है -falign-functions
एनपीई

@ ऐक्स: मैं सेंटओएस 6.0 (32-बिट) पर हूं और बिना किसी झंडे के पैडिंग कर रहा हूं। कोई भी मुझे अपना पूरा "objdump -j .text -d ./test" आउटपुट डंप करना चाहता है?
ऑली

1
आगे के परीक्षण पर, जब मैं इसे एक ऑब्जेक्ट के रूप में संकलित करता हूं: "gcc -c test.c"। कोई पैडिंग नहीं है, लेकिन जब मैं लिंक करता हूं: "gcc -o test test.o" यह दिखाई देता है।
ओली

2
@ हंसली: लिंकेडर द्वारा निष्पादित की जाने वाली आवश्यकताओं को पूरा करने के लिए, mainजो निष्पादन योग्य (मेरे मामले में फ़ंक्शन है __libc_csu_fini) में अनुवर्ती आवश्यकताओं को पूरा करने के लिए है ।
एनपीई

15

यह 8, 16 या 32-बाइट सीमा द्वारा अगले फ़ंक्शन को संरेखित करने के लिए किया जाता है।

A.Fog द्वारा "असेंबली भाषा में उप-भाषा का अनुकूलन" से:

11.5 कोड का संरेखण

अधिकांश माइक्रोप्रोसेसर 16-बाइट या 32-बाइट ब्लॉक में कोड लाते हैं। यदि कोई आयातकर्ता जुबेरूटीन एंट्री या जंप लेबल 16-बाइट ब्लॉक के अंत के पास होता है तो कोड के उस ब्लॉक को लाते समय थीमोप्रोसेसर केवल कुछ उपयोगी बाइट्स प्राप्त करेगा। इटाले को अगले 16 बाइट्स लाने हैं, इससे पहले कि लैलाबेल के बाद पहले निर्देशों को डिकोड किया जा सके। यह महत्वपूर्ण सबरूटीन प्रविष्टियों और लूप प्रविष्टियों को 16 से संरेखित करके बचा जा सकता है।

[...]

एक सबरूटीन प्रविष्टि को संरेखित करना उतना ही सरल है जितना कि वांछित के रूप में 8, 16, 32 या 64 द्वारा पता को विभाज्य बनाने के लिए थिसुब्रुटीन प्रविष्टि से पहले एनओपी के रूप में आवश्यक है।


यह 25-29 बाइट्स (मुख्य के लिए) के बीच का अंतर है, क्या आप कुछ अधिक बात कर रहे हैं? टेक्स्ट सेक्शन की तरह, रीडफुल के माध्यम से मैंने इसे 364 बाइट्स पाया? मैंने _start पर 14 नोड्स भी देखे। ये चीजें "क्यों" नहीं होतीं? मैं बदमाश हूं, माफी मांगता हूं।
7

@ हंसली: मैंने ऐसी विकास प्रणालियाँ देखी हैं जो संकलित मशीन कोड पर पूरे कार्यक्रम का अनुकूलन करती हैं। यदि फ़ंक्शन fooका पता 0x1234 है, तो कोड जो कि शाब्दिक 0x1234 के करीब में उस पते का उपयोग करने के लिए होता है, मशीन कोड उत्पन्न करना समाप्त कर सकता है जैसे mov ax,0x1234 / push ax / mov ax,0x1234 / push axकि अनुकूलक तब बदल सकता है mov ax,0x1234 / push ax / push ax। ध्यान दें कि ऐसे अनुकूलन के बाद कार्यों को स्थानांतरित नहीं किया जाना चाहिए, इसलिए निर्देशों के उन्मूलन से निष्पादन की गति में सुधार होगा, लेकिन कोड आकार नहीं।
सुपरकैट

5

जहाँ तक मुझे याद है, निर्देश सीपीयू और अलग-अलग सीपीयू ब्लॉक (लोडर, डिकोडर और इस तरह की) प्रक्रिया में बाद के निर्देशों में दिए गए हैं। जब RETनिर्देशों को निष्पादित किया जा रहा है, तो कुछ अगले निर्देश पहले से ही सीपीयू पाइपलाइन में लोड किए गए हैं। यह एक अनुमान है, लेकिन आप यहां खुदाई शुरू कर सकते हैं और यदि आपको पता चलता है (शायद विशिष्ट संख्या NOPजो सुरक्षित हैं, तो कृपया अपने निष्कर्ष साझा करें।


@ ननजालज: हुंह? यह सवाल x86 के बारे में पूछ रहा है, जो कि पाइपलाइज्ड है (जैसा कि एमको ने कहा)। कई आधुनिक x86 प्रोसेसर भी सट्टेबाज़ी के निर्देशों को निष्पादित करते हैं जो "नहीं होना चाहिए" निष्पादित किया जाता है, शायद इन नोड्स सहित। शायद आपका मतलब कहीं और टिप्पणी करना था?
डेविड कैरी

3
@DavidCary: x86 में, यह प्रोग्रामर के लिए पूरी तरह से पारदर्शी है। गलत तरीके से अनुमान लगाए गए निर्देशों का निष्पादन केवल उनके परिणामों और प्रभावों को छोड़ दिया गया है। MIPS पर, कोई "सट्टा" नहीं है, सभी में, एक शाखा देरी स्लॉट में निर्देश हमेशा निष्पादित होता है, और प्रोग्रामर को देरी स्लॉट्स को भरना होता है (या कोडांतरक को ऐसा करने दें, जिसके परिणामस्वरूप संभवतः nopएस होगा)।
नवजलज

@njjalj: हां, गलत तरीके से अनुमान लगाया गया सट्टा निष्पादित ऑप्स और गैर-संरेखित निर्देश का प्रभाव पारदर्शी है, इस अर्थ में कि उनका आउटपुट डेटा मूल्यों पर कोई प्रभाव नहीं है। हालांकि, वे दोनों कार्यक्रम के समय पर प्रभाव डालते हैं, जिसका कारण यह हो सकता है कि gcc x86 कोड में nops जोड़ता है, जो कि मूल प्रश्न पूछा गया है।
डेविड कैरी

1
@ डेविडवीरी: यदि वह कारण था, तो आप इसे केवल सशर्त कूदने के बाद देखेंगे, न कि बिना शर्त के ret
नवजलज

1
ऐसा क्यों नहीं है। एक अप्रत्यक्ष छलांग की गिरावट की भविष्यवाणी (एक BTB मिस पर) अगला निर्देश है, लेकिन अगर वह गैर-निर्देश कचरा है, तो गलत अनुमानों को रोकने के लिए अनुशंसित अनुकूलन एक निर्देश है जैसे कि ud2या int3हमेशा दोष होता है, इसलिए सामने वाला इसके बजाय कोड को रोकना जानता है। divउदाहरण के लिए पाइपलाइन में एक संभावित महंगा या स्फ़ूर्ती टीएलबी-मिस लोड खिलाना । किसी फ़ंक्शन के अंत में retप्रत्यक्ष या सीधे jmpटेलकॉल के बाद इसकी आवश्यकता नहीं है ।
पीटर कॉर्डेस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.