मेरे लिए, यह सिर्फ एक कायरतापूर्ण 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 बिट (ताकि हैं ycoord
struct में ऑफसेट 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
LEA
LEA EAX, [ EBX + N * EBX ]
N
LEA EAX, [ EAX + 1 ]
INC EAX
EFLAGS
CMP
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
और अन्य मेमोरी संदर्भ पता) के लिए पोर्ट है , इसका मतलब है कि LEA
ALU में अंकगणितीय ऑपरेशन और अन्य सामान्य अंकगणितीय ऑपरेशन समानांतर में किए जा सकते हैं। कोर।
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 केवल चार में से दो हैं जो अन्य निर्देशों को निष्पादित कर सकते हैं, इसलिए दावा किए गए कोई समानता लाभ नहीं है। lea
lea
lea
lea
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 खंड आधार से प्रभावित नहीं है; उन दोनों निर्देशों को (अक्षम रूप से) 0x1234
AX में डाला जाएगा। x86 दुर्भाग्य से एक रजिस्टर या रजिस्टर-जोड़ी में पूर्ण रैखिक पते (प्रभावी + खंड आधार) की गणना करने का एक आसान तरीका नहीं है।