मेरे लिए, यह सिर्फ एक कायरतापूर्ण MOV की तरह लगता है। इसका उद्देश्य क्या है और मुझे इसका उपयोग कब करना चाहिए?
मेरे लिए, यह सिर्फ एक कायरतापूर्ण MOV की तरह लगता है। इसका उद्देश्य क्या है और मुझे इसका उपयोग कब करना चाहिए?
जवाबों:
जैसा कि दूसरों ने बताया है, LEA (लोड प्रभावी पता) का उपयोग अक्सर कुछ गणनाओं को करने के लिए "ट्रिक" के रूप में किया जाता है, लेकिन यह इसका प्राथमिक उद्देश्य नहीं है। X86 इंस्ट्रक्शन सेट को पास्कल और सी जैसी उच्च-स्तरीय भाषाओं का समर्थन करने के लिए डिज़ाइन किया गया था, जहां विशेष रूप से इन्ट्स या छोटे स्ट्रक्चर्स के एरे- कॉमन हैं। उदाहरण के लिए, एक संरचना का प्रतिनिधित्व करने वाले (x, y) निर्देशांक पर विचार करें:
struct Point
{
int xcoord;
int ycoord;
};
अब एक बयान की तरह कल्पना करें:
int y = points[i].ycoord;
जहां points[]की एक सरणी है Point। सरणी के आधार मान लिया जाये कि में पहले से ही है EBX, और चर iमें है EAX, और xcoordऔर ycoordप्रत्येक 32 बिट (ताकि हैं ycoordstruct में ऑफसेट 4 बाइट पर है), इस बयान का संकलन किया जा सकता है:
MOV EDX, [EBX + 8*EAX + 4] ; right side is "effective address"
जो पहुंचेंगे yमें EDX। 8 का पैमाना कारक है क्योंकि प्रत्येक Pointआकार में 8 बाइट्स हैं। अब उसी अभिव्यक्ति पर विचार करें जिसका उपयोग "ऑपरेटर" के पते और:
int *p = &points[i].ycoord;
इस स्थिति में, आप इसका मान नहीं चाहते हैं ycoord, लेकिन इसका पता है। यही वह जगह है जहां LEA(प्रभावी पते को लोड करें) इसके बजाय MOV, संकलक उत्पन्न कर सकता है
LEA ESI, [EBX + 8*EAX + 4]
जिसमें पता लोड होगा ESI।
movनिर्देश का विस्तार करना और कोष्ठक से बाहर जाना क्लीनर नहीं था ? MOV EDX, EBX + 8*EAX + 4
MOVएक अप्रत्यक्ष स्रोत की तरह है, सिवाय इसके कि यह केवल अप्रत्यक्ष है और नहीं MOV। यह वास्तव में गणना पते से नहीं पढ़ता है , बस गणना करता है।
से "सभा के ज़ेन" Abrash द्वारा:
LEA, एकमात्र निर्देश जो गणना को संबोधित करने वाली मेमोरी करता है लेकिन वास्तव में मेमोरी को संबोधित नहीं करता है।LEAएक मानक मेमोरी एड्रेसिंग ऑपरेंड को स्वीकार करता है, लेकिन निर्दिष्ट रजिस्टर में गणना की गई मेमोरी ऑफसेट से अधिक कुछ भी नहीं करता है, जो किसी भी सामान्य उद्देश्य रजिस्टर हो सकता है।वह हमें क्या देता है? दो चीजें जो
ADDप्रदान नहीं करती हैं:
- दो या तीन ऑपरेंड के साथ प्रदर्शन करने की क्षमता, और
- किसी भी रजिस्टर में परिणाम को संग्रहीत करने की क्षमता ; स्रोत ऑपरेंड में से केवल एक नहीं।
और LEAझंडे को नहीं बदलता है।
उदाहरण
LEA EAX, [ EAX + EBX + 1234567 ]गणना EAX + EBX + 1234567(वह तीन ऑपरेंड)LEA EAX, [ EBX + ECX ]EBX + ECXपरिणाम के साथ या तो ओवरराइड किए बिना गणना करता है।LEA EAX, [ EBX + N * EBX ](एन 1,2,4,8 हो सकता है)।अन्य usecase छोरों में काम है: के बीच का अंतर है LEA EAX, [ EAX + 1 ]और INC EAXयह है कि बाद में परिवर्तन होता है EFLAGSलेकिन पूर्व नहीं करता है; यह CMPराज्य को संरक्षित करता है।
LEA EAX, [ EAX + EBX + 1234567 ]के योग की गणना करता है EAX, EBXऔर 1234567(यह तीन ऑपरेंड है)। परिणाम के साथ या तो ओवरराइड किए बिनाLEA EAX, [ EBX + ECX ] गणना करता है। तीसरी चीज (फ्रैंक द्वारा सूचीबद्ध नहीं है) के लिए उपयोग किया जाता है, लगातार (दो, तीन, पांच या नौ से गुणा ), यदि आप इसे पसंद करते हैं ( 1,2,4,8 हो सकता है)। अन्य usecase छोरों में काम है: के बीच का अंतर है और यह है कि बाद में परिवर्तन होता है लेकिन पूर्व नहीं करता है; यह राज्य को संरक्षित करता हैEBX + ECX LEALEA EAX, [ EBX + N * EBX ]NLEA EAX, [ EAX + 1 ]INC EAXEFLAGSCMP
LEAका उपयोग किया जा सकता है ... (देखें "LEA (लोड प्रभावी पता) को अक्सर IJ कैनेडी के लोकप्रिय उत्तर में कुछ कम्प्यूटेशंस करने के लिए" ट्रिक "के रूप में उपयोग किया जाता है)
का एक अन्य महत्वपूर्ण विशेषता LEAअनुदेश है कि यह इस तरह के रूप हालत कोड में परिवर्तन नहीं करता है CFऔर ZF, जैसे गणित निर्देश से पता कंप्यूटिंग, जबकि ADDया MULनहीं करता है। यह सुविधा निर्देशों के बीच निर्भरता के स्तर को कम करती है और इस प्रकार संकलक या हार्डवेयर अनुसूचक द्वारा आगे अनुकूलन के लिए जगह बनाती है।
leaकभी-कभी कंपाइलर (या मानव कोडर) के लिए एक फ्लैग रिजल्ट को क्लोब किए बिना गणित करने के लिए उपयोगी होता है। लेकिन leaतेजी से नहीं है add। अधिकांश x86 निर्देश झंडे लिखते हैं। उच्च-प्रदर्शन x86 कार्यान्वयन को EFLAGS का नाम बदलना है या अन्यथा सामान्य कोड के लिए तेजी से चलाने के लिए लिखने के बाद लिखने के खतरे से बचें, इसलिए ध्वज लिखने से बचने वाले निर्देश उस वजह से बेहतर नहीं हैं। ( आंशिक ध्वज सामान समस्याएँ पैदा कर सकता है, INC निर्देश 1 ADD देखें : क्या यह बात है? )
सभी स्पष्टीकरणों के बावजूद, LEA एक अंकगणितीय ऑपरेशन है:
LEA Rt, [Rs1+a*Rs2+b] => Rt = Rs1 + a*Rs2 + b
यह सिर्फ इतना है कि इसका नाम शिफ्ट + ऐड ऑपरेशन के लिए एक्सटेलीली बेवकूफ है। इसका कारण पहले से ही शीर्ष रेटेड जवाबों में समझाया गया था (यानी इसे सीधे उच्च स्तरीय मेमोरी संदर्भों को मैप करने के लिए डिज़ाइन किया गया था)।
LEAबल्कि साधारण पूर्णांक ALU पर निष्पादित करने के लिए चुना है । सीपीयू स्पेक्स को इन दिनों बहुत बारीकी से पढ़ना पड़ता है ताकि पता लगाया जा सके कि "सामान कहाँ चलता है" ...
LEAआपको वह पता देता है जो किसी भी मेमोरी से संबंधित एड्रेसिंग मोड से उत्पन्न होता है। यह एक बदलाव नहीं है और ऑपरेशन जोड़ें।
शायद LEA निर्देश के बारे में एक और बात। आप तेजी से बढ़ते रजिस्टर के लिए LEA का उपयोग 3, 5 या 9 से भी कर सकते हैं।
LEA EAX, [EAX * 2 + EAX] ;EAX = EAX * 3
LEA EAX, [EAX * 4 + EAX] ;EAX = EAX * 5
LEA EAX, [EAX * 8 + EAX] ;EAX = EAX * 9
LEA EAX, [EAX*3]?
shlरजिस्टरों को 2,4,8,16 से गुणा करने के निर्देश की तरह बाईं ओर की शिफ्ट का उपयोग कर सकते हैं ... यह तेज और छोटा है। लेकिन 2 की शक्ति के विभिन्न संख्याओं के साथ गुणा करने के लिए हम सामान्य रूप से mulनिर्देश का उपयोग करते हैं जो अधिक दिखावा और धीमा है।
lea eax,[eax*3]बराबर अनुवाद होगा lea eax,[eax+eax*2]।
lea"लोड प्रभावी पता" का एक संक्षिप्त नाम है। यह गंतव्य संचालक के स्रोत स्रोत द्वारा स्थान संदर्भ के पते को लोड करता है। उदाहरण के लिए, आप इसका उपयोग कर सकते हैं:
lea ebx, [ebx+eax*8]
एक निर्देश के साथ आगे (64-बिट / तत्व सरणी में) ebxपॉइंटर eaxआइटम को स्थानांतरित करने के लिए । मूल रूप से, आप x86 आर्किटेक्चर द्वारा समर्थित जटिल एड्रेसिंग मोड्स से लाभान्वित होते हैं ताकि पॉइंटर्स को कुशलता से जोड़ सकें।
यदि आप LEAएक MOVसे अधिक उपयोग करते हैं , तो आपको पता की गणना करने के लिए उपयोग किए जाने वाले रजिस्टरों पर अंकगणित करने की आवश्यकता है। प्रभावी रूप से, आप "फ्री" के लिए प्रभावी ढंग से संयोजन में रजिस्टरों में से कई पर अंकगणितीय अंकगणित करने के लिए क्या मात्रा में प्रदर्शन कर सकते हैं।
इसके बारे में वास्तव में भ्रमित करने वाली बात यह है कि आप आम तौर पर लिखने की LEAतरह ही MOVकरते हैं लेकिन आप वास्तव में मेमोरी को डीरफेर नहीं कर रहे हैं। दूसरे शब्दों में:
MOV EAX, [ESP+4]
यह किस ESP+4बिंदु की सामग्री को आगे बढ़ाएगा EAX।
LEA EAX, [EBX*8]
यह प्रभावी पते EBX * 8को EAX में ले जाएगा , न कि उस स्थान पर जो मिला है। जैसा कि आप देख सकते हैं, यह भी, दो (स्केलिंग) के कारकों से गुणा करना संभव है, जबकि एक MOVजोड़ने / घटाने के लिए सीमित है।
LEAवे क्या करते हैं।
8086 में निर्देशों का एक बड़ा परिवार है जो एक रजिस्टर ऑपरेंड और एक प्रभावी पते को स्वीकार करता है, उस प्रभावी पते के ऑफसेट भाग की गणना करने के लिए कुछ संगणना करता है, और कुछ ऑपरेशन करता है जिसमें रजिस्टर और मेमोरी को गणना पते से संदर्भित किया जाता है। उस परिवार के निर्देशों में से एक का व्यवहार करना काफी सरल था, उस वास्तविक मेमोरी ऑपरेशन को छोड़कर। यह, निर्देश:
mov ax,[bx+si+5]
lea ax,[bx+si+5]
लगभग आंतरिक रूप से लागू किए गए थे। अंतर एक छोड़ दिया गया कदम है। दोनों निर्देश कुछ इस तरह काम करते हैं:
temp = fetched immediate operand (5)
temp += bx
temp += si
address_out = temp (skipped for LEA)
trigger 16-bit read (skipped for LEA)
temp = data_in (skipped for LEA)
ax = temp
जैसा कि इंटेल ने सोचा था कि यह निर्देश शामिल करने लायक था, मुझे बिल्कुल यकीन नहीं है, लेकिन यह तथ्य कि इसे लागू करना सस्ता था, एक बड़ा कारक रहा होगा। एक अन्य कारक यह तथ्य होगा कि इंटेल के असेंबलर ने बीपी रजिस्टर के सापेक्ष प्रतीकों को परिभाषित करने की अनुमति दी थी। यदि fnordबीपी-सापेक्ष प्रतीक के रूप में परिभाषित किया गया था (उदाहरण बीपी + 8), तो कोई कह सकता है:
mov ax,fnord ; Equivalent to "mov ax,[BP+8]"
अगर कोई बीपी-रिलेटिव एड्रेस में डेटा स्टोर करने के लिए स्टोसव जैसा कुछ इस्तेमाल करना चाहता है, तो कहने में सक्षम है
mov ax,0 ; Data to store
mov cx,16 ; Number of words
lea di,fnord
rep movs fnord ; Address is ignored EXCEPT to note that it's an SS-relative word ptr
से अधिक सुविधाजनक था:
mov ax,0 ; Data to store
mov cx,16 ; Number of words
mov di,bp
add di,offset fnord (i.e. 8)
rep movs fnord ; Address is ignored EXCEPT to note that it's an SS-relative word ptr
ध्यान दें कि दुनिया को "ऑफ़सेट" भूल जाने से स्थान की सामग्री [बीपी + 8] का मूल्य होगा, मूल्य 8 के बजाय, डीआई में जोड़ा जाएगा। उफ़।
जैसा कि मौजूदा उत्तर दिए गए हैं, LEAमेमोरी तक पहुंचने के बिना अंकगणित को संबोधित करने के लिए स्मृति को जोड़ने के फायदे हैं, अंकगणित के परिणाम को निर्देश के सरल रूप के बजाय एक अलग रजिस्टर में सहेजना है। वास्तविक अंतर्निहित प्रदर्शन लाभ यह है कि आधुनिक प्रोसेसर में एक अलग LEA ALU इकाई है और प्रभावी पता पीढ़ी (सहित LEAऔर अन्य मेमोरी संदर्भ पता) के लिए पोर्ट है , इसका मतलब है कि LEAALU में अंकगणितीय ऑपरेशन और अन्य सामान्य अंकगणितीय ऑपरेशन समानांतर में किए जा सकते हैं। कोर।
LEA इकाई के बारे में कुछ विवरणों के लिए Haswell वास्तुकला के इस लेख की जाँच करें: http://www.realworldtech.com/haswell-cpu/4/
एक अन्य महत्वपूर्ण बिंदु जो अन्य उत्तरों में उल्लिखित नहीं है, वह LEA REG, [MemoryAddress]निर्देश है PIC (स्थिति स्वतंत्र कोड) जो संदर्भ के लिए इस निर्देश में पीसी सापेक्ष पते को एन्कोड करता है MemoryAddress। यह अलग है, MOV REG, MemoryAddressजो सापेक्ष आभासी पते को एन्कोड करता है और आधुनिक ऑपरेटिंग सिस्टम (जैसे एएसएलआर सामान्य विशेषता है) में रिलोकेटिंग / पैचिंग की आवश्यकता होती है। तो LEAऐसे नॉन PIC को PIC में बदलने के लिए इस्तेमाल किया जा सकता है।
leaएक या अधिक ALU पर निष्पादित होते हैं जो अन्य अंकगणितीय निर्देशों को निष्पादित करते हैं (लेकिन आम तौर पर उनमें से अन्य अंकगणित की तुलना में कम)। उदाहरण के लिए, उल्लिखित हसवेल सीपीयू चार अलग-अलग ALU पर या अन्य सबसे बुनियादी अंकगणितीय कार्यों को निष्पादित addया subकर सकता है, लेकिन केवल एक (जटिल ) या दो (सरल ) पर निष्पादित कर सकता है । इससे भी महत्वपूर्ण बात, उन दो- योग्य ALU केवल चार में से दो हैं जो अन्य निर्देशों को निष्पादित कर सकते हैं, इसलिए दावा किए गए कोई समानता लाभ नहीं है। lealealealea
LEA निर्देश का उपयोग CPU द्वारा प्रभावी पते की गणना के समय से बचने के लिए किया जा सकता है। यदि किसी पते का बार-बार उपयोग किया जाता है, तो हर बार उपयोग किए जाने वाले प्रभावी पते की गणना करने के बजाय उसे रजिस्टर में संग्रहीत करना अधिक प्रभावी होता है।
[esi]कहने की तुलना में शायद ही कभी सस्ता है [esi + 4200]और केवल की तुलना में सस्ता है [esi + ecx*8 + 4200]।
[esi]से सस्ता नहीं है [esi + ecx*8 + 4200]। लेकिन परेशान करने की तुलना क्यों? वे समकक्ष नहीं हैं। यदि आप चाहते हैं कि पूर्व को उसी मेमोरी लोकेशन को बाद के रूप में नामित किया जाए, तो आपको अतिरिक्त निर्देशों की आवश्यकता है: आपको 8. esiसे ecxगुणा के मूल्य में जोड़ना होगा । उह ओह, गुणा आपके सीपीयू झंडे को बंद करने वाला है! फिर आपको 4200 जोड़ना होगा। ये अतिरिक्त निर्देश कोड आकार में जोड़ते हैं (अनुदेश कैश में जगह लेने के लिए, साइकिल लाने के लिए)।
[esi + 4200]निर्देशों के अनुक्रम में बार-बार कुछ का उपयोग करने जा रहे हैं , तो पहले प्रभावी पते को एक रजिस्टर में लोड करना और उसका उपयोग करना बेहतर होता है। उदाहरण के लिए, लिखने के बजाय add eax, [esi + 4200]; add ebx, [esi + 4200]; add ecx, [esi + 4200], आपको पसंद करना चाहिए lea edi, [esi + 4200]; add eax, [edi]; add ebx, [edi]; add ecx, [edi], जो शायद ही कभी तेज होता है। कम से कम इस उत्तर की स्पष्ट व्याख्या है।
[esi]और [esi + 4200](या [esi + ecx*8 + 4200](मैं इसे समझ) जो इस सरलीकरण ओपी का प्रस्ताव किया गया है है: कि एक ही परिसर पते के साथ एन निर्देश सरल (एक reg) को संबोधित करते हुए, प्लस वन के साथ एन निर्देश के रूप में तब्दील कर रहे हैं lea, चूंकि जटिल संबोधन "समय लेने वाला" है। वास्तव में, यह आधुनिक x86 पर भी धीमा है, लेकिन केवल विलंबता-वार जो एक ही पते के साथ लगातार निर्देशों के लिए मायने नहीं रखता है।
leaताकि यह उस स्थिति में दबाव बढ़ाए। सामान्य तौर पर, इंटरमीडिएट स्टोर करना रजिस्टर दबाव का एक कारण है, इसका कोई समाधान नहीं है - लेकिन मुझे लगता है कि ज्यादातर स्थितियों में यह एक धोने वाला है। @ काज़
LEA (लोड प्रभावी पता) निर्देश, उस पते को प्राप्त करने का एक तरीका है जो इंटेल प्रोसेसर के मेमोरी एड्रेसिंग मोड में से किसी से उत्पन्न होता है।
यह कहना है, अगर हमारे पास इस तरह से एक डेटा चाल है:
MOV EAX, <MEM-OPERAND>
यह निर्दिष्ट मेमोरी स्थान की सामग्री को लक्ष्य रजिस्टर में ले जाता है।
यदि हम MOVद्वारा प्रतिस्थापित करते हैं LEA, तो मेमोरी लोकेशन के पते की गणना उसी तरह से की जाती है जैसे कि <MEM-OPERAND>एड्रेसिंग एक्सप्रेशन। लेकिन मेमोरी लोकेशन के कंटेंट के बजाय हम लोकेशन को ही डेस्टिनेशन में ले आते हैं।
LEAएक विशिष्ट अंकगणितीय अनुदेश नहीं है; यह प्रोसेसर के मेमोरी एड्रेसिंग मोड में से किसी एक से उत्पन्न होने वाले प्रभावी पते को इंटरसेप्ट करने का एक तरीका है।
उदाहरण के लिए, हम LEAकेवल एक साधारण प्रत्यक्ष पते पर उपयोग कर सकते हैं । कोई अंकगणित इसमें शामिल नहीं है:
MOV EAX, GLOBALVAR ; fetch the value of GLOBALVAR into EAX
LEA EAX, GLOBALVAR ; fetch the address of GLOBALVAR into EAX.
यह मान्य है; हम इसे लिनक्स प्रांप्ट पर परख सकते हैं:
$ as
LEA 0, %eax
$ objdump -d a.out
a.out: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 8d 04 25 00 00 00 00 lea 0x0,%eax
यहां, स्केल किए गए मूल्य का कोई जोड़ नहीं है, और कोई ऑफसेट नहीं है। शून्य को EAX में ले जाया जाता है। हम यह कर सकते हैं कि एक तत्काल ऑपरेंड के साथ भी एमओवी का उपयोग करना।
यही कारण है कि लोग जो सोचते हैं कि कोष्ठक LEAअतिसुंदर हैं, उनसे गंभीर रूप से गलती होती है; कोष्ठक LEAवाक्यविन्यास नहीं हैं, लेकिन संबोधित मोड का हिस्सा हैं।
LEA हार्डवेयर स्तर पर वास्तविक है। उत्पन्न निर्देश वास्तविक पते मोड को एन्कोड करता है और प्रोसेसर पते की गणना करने के बिंदु पर ले जाता है। फिर यह उस पते को मेमोरी रेफरेंस जनरेट करने के बजाय गंतव्य तक ले जाता है। (चूंकि किसी अन्य निर्देश में एड्रेसिंग मोड की पता गणना का सीपीयू झंडे LEAपर कोई प्रभाव नहीं है, सीपीयू झंडे पर कोई प्रभाव नहीं है)।
पता शून्य से मान लोड करने में विरोधाभास:
$ as
movl 0, %eax
$ objdump -d a.out | grep mov
0: 8b 04 25 00 00 00 00 mov 0x0,%eax
यह बहुत समान एन्कोडिंग है, देखिए? बस 8dके LEAलिए बदल गया है 8b।
बेशक, यह LEAएन्कोडिंग तत्काल शून्य में जाने से अधिक है EAX:
$ as
movl $0, %eax
$ objdump -d a.out | grep mov
0: b8 00 00 00 00 mov $0x0,%eax
LEAइस संभावना को बाहर करने का कोई कारण नहीं है, हालांकि सिर्फ इसलिए कि एक छोटा विकल्प है; यह केवल उपलब्ध एड्रेसिंग मोड के साथ एक ऑर्थोगोनल तरीके से संयोजन कर रहा है।
यहाँ एक उदाहरण है।
// compute parity of permutation from lexicographic index
int parity (int p)
{
assert (p >= 0);
int r = p, k = 1, d = 2;
while (p >= k) {
p /= d;
d += (k << 2) + 6; // only one lea instruction
k += 2;
r ^= p;
}
return r & 1;
}
संकलक विकल्प के रूप में ओ (ऑप्टिमाइज़) के साथ, संकेतित कोड लाइन के लिए gcc को अंतिम निर्देश मिलेगा।
ऐसा लगता है कि बहुत सारे उत्तर पहले ही पूरे हो चुके हैं, मैं यह दिखाने के लिए एक और उदाहरण कोड जोड़ना चाहूंगा कि कैसे एक ही अभिव्यक्ति प्रारूप होने पर लीक और निर्देश निर्देश अलग तरीके से काम करते हैं।
एक लंबी कहानी को छोटा करने के लिए, निर्देश और s निर्देशों के src ऑपरेंड को संलग्न करने वाले कोष्ठक के साथ अंतिम निर्देश और mov निर्देश दोनों का उपयोग किया जा सकता है। वे के साथ संलग्न कर रहे हैं () , में अभिव्यक्ति () एक ही तरीके से की जाती है; हालाँकि, दो निर्देश एक अलग तरीके से src ऑपरेंड में परिकलित मान की व्याख्या करेंगे।
चाहे एक्सप्रेशन का उपयोग लीक या मूव के साथ किया जाए, src मूल्य की गणना नीचे की तरह की जाती है।
D (Rb, Ri, S) => (Reg [Rb] + S * Reg [Ri] + D)
हालाँकि, जब इसे मूव इंस्ट्रक्शन के साथ प्रयोग किया जाता है, तो यह उपरोक्त अभिव्यक्ति द्वारा उत्पन्न पते द्वारा इंगित मूल्य तक पहुँचने और इसे गंतव्य पर संग्रहीत करने का प्रयास करता है।
इसके विपरीत, जब उपरोक्त अनुदेश के साथ अंतिम निर्देश निष्पादित किया जाता है, तो यह उत्पन्न मूल्य को लोड करता है क्योंकि यह गंतव्य के लिए है।
नीचे दिया गया कोड एक ही पैरामीटर के साथ अंतिम अनुदेश और mov निर्देश निष्पादित करता है। हालांकि, अंतर को पकड़ने के लिए, मैंने एक उपयोगकर्ता-स्तरीय सिग्नल हैंडलर को एक निर्देश के रूप में गलत निर्देश तक पहुंचने के कारण विभाजन दोष को पकड़ने के लिए जोड़ा।
उदाहरण कोड
#define _GNU_SOURCE 1 /* To pick up REG_RIP */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <signal.h>
uint32_t
register_handler (uint32_t event, void (*handler)(int, siginfo_t*, void*))
{
uint32_t ret = 0;
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_sigaction = handler;
act.sa_flags = SA_SIGINFO;
ret = sigaction(event, &act, NULL);
return ret;
}
void
segfault_handler (int signum, siginfo_t *info, void *priv)
{
ucontext_t *context = (ucontext_t *)(priv);
uint64_t rip = (uint64_t)(context->uc_mcontext.gregs[REG_RIP]);
uint64_t faulty_addr = (uint64_t)(info->si_addr);
printf("inst at 0x%lx tries to access memory at %ld, but failed\n",
rip,faulty_addr);
exit(1);
}
int
main(void)
{
int result_of_lea = 0;
register_handler(SIGSEGV, segfault_handler);
//initialize registers %eax = 1, %ebx = 2
// the compiler will emit something like
// mov $1, %eax
// mov $2, %ebx
// because of the input operands
asm("lea 4(%%rbx, %%rax, 8), %%edx \t\n"
:"=d" (result_of_lea) // output in EDX
: "a"(1), "b"(2) // inputs in EAX and EBX
: // no clobbers
);
//lea 4(rbx, rax, 8),%edx == lea (rbx + 8*rax + 4),%edx == lea(14),%edx
printf("Result of lea instruction: %d\n", result_of_lea);
asm volatile ("mov 4(%%rbx, %%rax, 8), %%edx"
:
: "a"(1), "b"(2)
: "edx" // if it didn't segfault, it would write EDX
);
}
निष्पादन परिणाम
Result of lea instruction: 14
inst at 0x4007b5 tries to access memory at 14, but failed
=dसंकलक को यह बताने के लिए उपयोग कर सकते हैं कि परिणाम EDX में है, जिससे बचत होगी mov। आपने आउटपुट पर एक प्रारंभिक-क्लॉबर घोषणा भी छोड़ दी। यह प्रदर्शित करता है कि आप क्या प्रदर्शित करने की कोशिश कर रहे हैं, लेकिन इनलाइन एएसएम का एक भ्रामक बुरा उदाहरण भी है जो अन्य संदर्भों में उपयोग होने पर टूट जाएगा। यह एक स्टैक अतिप्रवाह उत्तर के लिए एक खराब चीज है।
%%विस्तारित asm में उन सभी रजिस्टर नामों पर लिखना नहीं चाहते हैं , तो इनपुट बाधाओं का उपयोग करें। पसंद है asm("lea 4(%%ebx, %%eax, 8), %%edx" : "=d"(result_of_lea) : "a"(1), "b"(2));। संकलक init रजिस्टरों को बताने का मतलब है कि आपको क्लोबर घोषित करने की आवश्यकता नहीं है। इससे पहले कि आप झट-झटके से चीजों को ओवर-कॉंप्लिमेट कर रहे हों, इससे पहले कि तत्काल-रजिस्टर पूरे रजिस्टर को ओवरराइट कर दे, भी।
mov 4(%ebx, %eax, 8), %edxयह अमान्य है? वैसे भी, हाँ, इसके लिए movयह समझने के लिए लिखना "a"(1ULL)होगा कि संकलक को आपके पास 64-बिट मान है, और इस प्रकार यह सुनिश्चित करने की आवश्यकता है कि यह पूरे रजिस्टर को भरने के लिए विस्तारित है। व्यवहार में यह अभी भी उपयोग करेगा mov $1, %eax, क्योंकि EAX शून्य लिखना RAX में विस्तारित होता है, जब तक कि आपके पास आसपास के कोड की एक अजीब स्थिति न हो, जहां कंपाइलर को पता था कि RAX = 0xff00000001या कुछ और। के लिए lea, आप अभी भी 32-बिट ऑपरेंड-आकार का उपयोग कर रहे हैं, इसलिए इनपुट रजिस्टरों में किसी भी आवारा उच्च बिट्स का 32-बिट परिणाम पर कोई प्रभाव नहीं पड़ता है।
LEA: सिर्फ एक "अंकगणित" निर्देश ।।
MOV ऑपरेंड्स के बीच डेटा ट्रांसफर करता है लेकिन लीक सिर्फ गणना है
mov eax, offset GLOBALVARइसके बजाय का उपयोग करें । आप LEA का उपयोग कर सकते हैं, लेकिन इसकी तुलना में यह थोड़ा बड़ा कोड-आकार है mov r32, imm32और कम बंदरगाहों पर चलता है, क्योंकि यह अभी भी पता-गणना प्रक्रिया से गुजरता है । lea reg, symbolकेवल RIP- सापेक्ष LEA के लिए 64-बिट में उपयोगी है, जब आपको निम्न 32 बिट्स के बाहर PIC और / या पते की आवश्यकता होती है। 32 या 16-बिट कोड में, शून्य लाभ है। LEA एक अंकगणितीय निर्देश है जो सीपीयू की क्षमता को संबोधित मोड को डिकोड / कंप्यूट करने के लिए उजागर करता है।
imul eax, edx, 1यह गणना नहीं करता है: यह सिर्फ edx को eax पर कॉपी करता है। लेकिन वास्तव में यह 3 चक्र विलंबता के साथ आपके डेटा को गुणक के माध्यम से चलाता है। या कि rorx eax, edx, 0सिर्फ प्रतियां (शून्य से घुमाएं)।
सभी सामान्य "गणना" निर्देश जैसे गुणा, अनन्य जोड़ने या स्थिति के झंडे को सेट करें जैसे शून्य, साइन। यदि आप एक जटिल पते का उपयोग करते हैं, AX xor:= mem[0x333 +BX + 8*CX] तो झंडे एक्सोर ऑपरेशन के अनुसार सेट किए जाते हैं।
अब आप कई बार पते का उपयोग करना चाह सकते हैं। एक रजिस्टर में इस तरह के अतिरिक्त को लोड करना कभी भी स्टेटस फ्लैग सेट करने का इरादा नहीं करता है और सौभाग्य से यह नहीं है। वाक्यांश "लोड प्रभावी पता" प्रोग्रामर को इससे अवगत कराता है। यह वह जगह है जहाँ अजीब अभिव्यक्ति से आता है।
यह स्पष्ट है कि एक बार प्रोसेसर अपनी सामग्री को संसाधित करने के लिए जटिल पते का उपयोग करने में सक्षम है, यह अन्य उद्देश्यों के लिए गणना करने में सक्षम है। वास्तव में इसका उपयोग x <- 3*x+1एक निर्देश में परिवर्तन करने के लिए किया जा सकता है । यह असेंबली प्रोग्रामिंग में एक सामान्य नियम है: निर्देशों का उपयोग करें हालांकि यह आपकी नाव को हिलाता है।
केवल एक चीज जो मायने रखती है, वह है कि निर्देश द्वारा सन्निहित विशेष परिवर्तन आपके लिए उपयोगी है।
जमीनी स्तर
MOV, X| T| AX'| R| BX|
तथा
LEA, AX'| [BX]
AX पर समान प्रभाव है लेकिन स्टेटस फ्लैग पर नहीं। (यह सियासिस संकेतन है।)
call lbl lbl: pop raxतकनीकी रूप से "काम" जैसी चीजों के मूल्य के रूप में प्राप्त करता है rip, लेकिन आप शाखा की भविष्यवाणी को बहुत दुखी करेंगे। निर्देशों का उपयोग करें जो आप चाहते हैं, लेकिन अगर आप कुछ मुश्किल करते हैं तो आश्चर्यचकित न हों और इसके परिणाम आपके सामने नहीं हैं
यदि कोई पहले ही उल्लेख कर चुका हो, तो मुझे माफ़ कर देना, लेकिन x86 के दिनों में जब मेमोरी सेगमेंटेशन अभी भी प्रासंगिक था, तो आपको इन दोनों निर्देशों से समान परिणाम नहीं मिल सकते हैं:
LEA AX, DS:[0x1234]
तथा
LEA AX, CS:[0x1234]
seg:offजोड़ी का "ऑफसेट" हिस्सा है । LEA खंड आधार से प्रभावित नहीं है; उन दोनों निर्देशों को (अक्षम रूप से) 0x1234AX में डाला जाएगा। x86 दुर्भाग्य से एक रजिस्टर या रजिस्टर-जोड़ी में पूर्ण रैखिक पते (प्रभावी + खंड आधार) की गणना करने का एक आसान तरीका नहीं है।