Intel x86 Assembly Language & Microarchitecture
कॉलिंग कन्वेंशन
खोज…
टिप्पणियों
साधन
ओवरव्यू / तुलना: एग्नर फॉग का अच्छा कॉलिंग कन्वेंशन गाइड । इसके अलावा, x86 एबीआई (विकिपीडिया) : x86-64 विंडोज और सिस्टम वी (लिनक्स) सहित कार्यों के लिए सम्मेलनों को बुला रहा है।
SystemV x86-64 ABI (आधिकारिक मानक) । सभी OS लेकिन विंडोज द्वारा उपयोग किया जाता है। ( यह जीथुब विकी पेज , जिसे एचजे लू द्वारा अद्यतित रखा गया है, इसमें 32 बिट, 64 बिट और एक्स 32 के लिंक हैं। एबीआई मेंटेनर्स / योगदानकर्ताओं के लिए आधिकारिक फोरम के लिंक भी हैं।) यह भी ध्यान दें कि क्लैग / जीसीसी साइन / जीरो संकीर्ण संकीर्ण आकृतियों को बढ़ाता है। 32 बिट , भले ही लिखित रूप में एबीआई को इसकी आवश्यकता नहीं है। क्लैंग-जनरेटेड कोड इस पर निर्भर करता है।
SystemV 32bit (i386) ABI (आधिकारिक मानक) , लिनक्स और यूनिक्स द्वारा उपयोग किया जाता है। ( पुराना संस्करण )।
ओएस एक्स 32 बिट x86 कॉलिंग कन्वेंशन, दूसरों के लिंक के साथ । 64 वाँ कॉलिंग कन्वेंशन, सिस्टम V है। Apple की साइट इसके लिए एक FreeBSD पीडीएफ से जुड़ी है।
विंडोज x86-64
__fastcallकॉलिंग कन्वेंशनविंडोज
__vectorcall: 32 बिट और 64 बिट संस्करणों का दस्तावेज हैWindows 32bit
__stdcall: जिसका उपयोग Win32 API फ़ंक्शन को कॉल करने के लिए किया जाता है। वह पेज अन्य कॉलिंग कन्वेंशन डॉक्स (जैसे__cdecl) से लिंक करता है।Windows86 x86-64 पर अन्य सभी ओएस से अलग कॉलिंग कन्वेंशन का उपयोग क्यों करता है? : कुछ दिलचस्प इतिहास, esp। SysV ABI के लिए जहां मेलिंग सूची अभिलेखागार सार्वजनिक हैं और AMD के पहले सिलिकॉन के रिलीज से पहले वापस चले जाते हैं।
32-बिट सीडीसीएल
cdecl एक Windows 32-बिट समारोह बुला सम्मेलन जो बहुत कई POSIX ऑपरेटिंग सिस्टम (में दस्तावेज पर इस्तेमाल किया बुला सम्मेलन के समान है i386 सिस्टम वी एबीआई )। मतभेदों में से एक छोटी संरचनाओं को वापस करने में है।
पैरामीटर
पैरामीटर को स्टैक पर पास किया जाता है, कॉल के समय स्टैक पर सबसे कम पते पर पहले तर्क के साथ (अंतिम धक्का दिया जाता है, इसलिए यह फ़ंक्शन में प्रवेश पर वापसी पते के ठीक ऊपर है)। कॉल के बाद स्टैक से वापस मापदंडों को पॉप करने के लिए कॉलर जिम्मेदार है।
प्रतिलाभ की मात्रा
स्केलर रिटर्न प्रकारों के लिए, वापसी मूल्य EAX, या EDX: EAX में 64 बिट पूर्णांक के लिए रखा जाता है। फ्लोटिंग-पॉइंट प्रकार st0 (x87) में लौटाए जाते हैं। संरचनाओं जैसे बड़े प्रकारों को वापस करना संदर्भ द्वारा किया जाता है, एक सूचक के साथ एक अंतर्निहित पहले पैरामीटर के रूप में पारित किया जाता है। (यह सूचक EAX में वापस आ गया है, इसलिए फोन करने वाले को यह याद नहीं रखना चाहिए कि यह क्या हुआ)।
सेव्ड और क्लोबर्ड रजिस्टर्स
EBX, EDI, ESI, EBP, और ESP (और FP / SSE राउंडिंग मोड सेटिंग्स) को कैली द्वारा संरक्षित किया जाना चाहिए, जैसे कि कॉलर उन रजिस्टरों पर भरोसा कर सकता है जो कॉल द्वारा नहीं बदले जा रहे हैं।
अन्य सभी रजिस्टर (EAX, ECX, EDX, FLAGS (DF के अलावा अन्य), x87 और वेक्टर रजिस्टर) कैली द्वारा स्वतंत्र रूप से संशोधित किए जा सकते हैं; यदि कॉल करने वाले व्यक्ति फ़ंक्शन कॉल से पहले और बाद में एक मान को संरक्षित करना चाहता है, तो उसे मूल्य को कहीं और सहेजना होगा (जैसे कि सहेजे गए रजिस्टरों में या स्टैक पर)।
64-बिट सिस्टम वी
यह कई POSIX ऑपरेटिंग सिस्टम पर 64-बिट अनुप्रयोगों के लिए डिफ़ॉल्ट कॉलिंग कन्वेंशन है।
पैरामीटर
पहले आठ स्केलर पैरामीटर RDI, RSI, RDX, RCX, R8, R9, R10, R11 में (क्रम में) पारित किए जाते हैं। पहले आठ के पिछले पैरामीटर को स्टैक पर रखा जाता है, जिसमें पहले पैरामीटर स्टैक के शीर्ष के करीब होते हैं। यदि कॉल की आवश्यकता नहीं है, तो कॉल के बाद स्टैक से इन मानों को पॉप करने के लिए कॉलर जिम्मेदार है।
प्रतिलाभ की मात्रा
स्केलर रिटर्न प्रकारों के लिए, वापसी मूल्य RAX में रखा गया है। बड़े प्रकार की संरचनाओं को वापस करना, पैरामीटर सूची की शुरुआत में एक पैरामीटर को जोड़ने के लिए फ़ंक्शन के हस्ताक्षर को वैचारिक रूप से बदलकर किया जाता है जो कि उस स्थान के लिए एक पॉइंटर है जिसमें रिटर्न वैल्यू डालनी है।
सेव्ड और क्लोबर्ड रजिस्टर्स
RBP, RBX और R12-R15 कैलीले द्वारा संरक्षित हैं। अन्य सभी रजिस्टरों को कैली द्वारा संशोधित किया जा सकता है, और कॉल करने वाले को रजिस्टर के मूल्य को स्वयं संरक्षित करना चाहिए (जैसे स्टैक पर) यदि वह बाद में उस मूल्य का उपयोग करना चाहता है।
32-बिट stdcall
stdcall का उपयोग 32-बिट विंडोज एपीआई कॉल के लिए किया जाता है।
पैरामीटर
पैरामीटर को स्टैक पर पारित किया जाता है, स्टैक के शीर्ष के सबसे पहले पैरामीटर के साथ। लौटने से पहले स्टैक से इन मूल्यों को पॉप करेगा कैली।
प्रतिलाभ की मात्रा
स्केलर रिटर्न मान EAX में रखे गए हैं।
सेव्ड और क्लोबर्ड रजिस्टर्स
EAX, ECX और EDX को कैली द्वारा स्वतंत्र रूप से संशोधित किया जा सकता है, और यदि वांछित हो तो कॉलर द्वारा बचाया जाना चाहिए। ईबीएक्स, ईएसआई, ईडीआई और ईबीपी को कैली द्वारा सहेजा जाना चाहिए, यदि संशोधित और वापसी पर उनके मूल मूल्यों को बहाल किया जाए।
32-बिट, cdecl - इंटिजर्स के साथ व्यवहार करना
मापदंडों के रूप में (8, 16, 32 बिट्स)
8, 16, 32 बिट्स पूर्णांक हमेशा स्टैक पर, पूर्ण चौड़ाई 32 बिट्स मान 1 के रूप में पारित किए जाते हैं।
कोई एक्सटेंशन, हस्ताक्षरित या शून्य नहीं, की आवश्यकता है।
कैलली पूरी चौड़ाई मूल्यों के निचले हिस्से का उपयोग करेगा।
//C prototype of the callee
void __attribute__((cdecl)) foo(char a, short b, int c, long d);
foo(-1, 2, -3, 4);
;Call to foo in assembly
push DWORD 4 ;d, long is 32 bits, nothing special here
push DWORD 0fffffffdh ;c, int is 32 bits, nothing special here
push DWORD 0badb0002h ;b, short is 16 bits, higher WORD can be any value
push DWORD 0badbadffh ;a, char is 8 bits, higher three bytes can be any value
call foo
add esp, 10h ;Clean up the stack
मापदंडों के रूप में (64 बिट्स)
64 बिट्स मान को दो पुश का उपयोग करके स्टैक पर पारित किया जाता है, लिटेल एंडियन कन्वेंशन 2 का सम्मान करते हुए, पहले 32 बिट्स को और फिर निचले वाले को धक्का दिया जाता है।
//C prototype of the callee
void __attribute__((cdecl)) foo(char a, short b, int c, long d);
foo(0x0123456789abcdefLL);
;Call to foo in assembly
push DWORD 89abcdefh ;Higher DWORD of 0123456789abcdef
push DWORD 01234567h ;Lower DWORD of 0123456789abcdef
call foo
add esp, 08h
वापसी मूल्य के रूप में
8 बिट पूर्णांक में लौटा दिया जाता है AL , अंत में पूरे clobbering eax ।
16 बिट पूर्णांक में लौटा दिए जाते हैं AX , अंत में पूरे clobbering eax ।
32 बिट पूर्णांक EAX में दिए गए हैं।
EDX:EAX में 64 बिट पूर्णांक दिए गए हैं EDX:EAX , जहां EAX निचले 32 बिट्स और EDX ऊपरी वाले रखता है।
//C
char foo() { return -1; }
;Assembly
mov al, 0ffh
ret
//C
unsigned short foo() { return 2; }
;Assembly
mov ax, 2
ret
//C
int foo() { return -3; }
;Assembly
mov eax, 0fffffffdh
ret
//C
int foo() { return 4; }
;Assembly
xor edx, edx ;EDX = 0
mov eax, 4 ;EAX = 4
ret
1 यह 4 बाइट्स, प्राकृतिक शब्द आकार पर स्टैक को संरेखित रखता है। इसके अलावा एक x86 सीपीयू केवल 2 या 4 बाइट्स को धक्का दे सकता है जब लंबे मोड में नहीं होता है।
2 निचले पते पर DWORD कम करें
32-बिट, सीडीसीएल - फ्लोटिंग पॉइंट से निपटना
मापदंडों के रूप में (फ्लोट, डबल)
फ्लोट्स आकार में 32 बिट्स हैं, वे स्टैक पर स्वाभाविक रूप से पारित हो जाते हैं।
डबल्स आकार में 64 बिट्स हैं, उन्हें स्टैक पर पारित किया जाता है, लिटिल एंडियन सम्मेलन 1 का सम्मान करते हुए, पहले ऊपरी 32 बिट्स और निचले लोगों की तुलना में धक्का दिया जाता है।
//C prototype of callee
double foo(double a, float b);
foo(3.1457, 0.241);
;Assembly call
;3.1457 is 0x40092A64C2F837B5ULL
;0.241 is 0x3e76c8b4
push DWORD 3e76c8b4h ;b, is 32 bits, nothing special here
push DWORD 0c2f837b5h ;a, is 64 bits, Higher part of 3.1457
push DWORD 40092a64h ;a, is 64 bits, Lower part of 3.1457
call foo
add esp, 0ch
;Call, using the FPU
;ST(0) = a, ST(1) = b
sub esp, 0ch
fstp QWORD PTR [esp] ;Storing a as a QWORD on the stack
fstp DWORD PTR [esp+08h] ;Storing b as a DWORD on the stack
call foo
add esp, 0ch
मापदंडों के रूप में (लंबा दोहरा)
लंबे डबल्स 80 बिट्स 2 चौड़े होते हैं, जबकि स्टैक पर एक टीबीवाईटीई को दो 32 बिट्स पुश और एक 16 बिट पुश (4 + 4 + 2 = 10 के लिए) के साथ संग्रहीत किया जा सकता है, स्टैक को 4 बाइट्स पर संरेखित करने के लिए, यह कब्जा कर लेता है 12 बाइट्स, इस प्रकार तीन 32 बिट्स पुश का उपयोग करते हैं।
लिटिल एंडियन सम्मेलन का सम्मान करते हुए, बिट्स 79-64 को पहले 3 , फिर बिट्स 63-32 को बिट्स 31-0 के बाद धकेल दिया जाता है।
//C prototype of the callee
void __attribute__((cdecl)) foo(long double a);
foo(3.1457);
;Call to foo in assembly
;3.1457 is 0x4000c9532617c1bda800
push DWORD 4000h ;Bits 79-64, as 32 bits push
push DWORD 0c9532617h ;Bits 63-32
push DWORD 0c1bda800h ;Bits 31-0
call foo
add esp, 0ch
;Call to foo, using the FPU
;ST(0) = a
sub esp, 0ch
fstp TBYTE PTR [esp] ;Store a as ten byte on the stack
call foo
add esp, 0ch
वापसी मूल्य के रूप में
एक अस्थायी बिंदु मान, इसका आकार जो भी हो, ST(0) 4 में वापस आ जाता है।
//C
float one() { return 1; }
;Assembly
fld1 ;ST(0) = 1
ret
//C
double zero() { return 0; }
;Assembly
fldz ;ST(0) = 0
ret
//C
long double pi() { return PI; }
;Assembly
fldpi ;ST(0) = PI
ret
1 कम पते पर DWORD कम करें।
2 टीबीवाईटीई के रूप में जाना जाता है, दस बाइट्स से।
3 किसी भी एक्सटेंशन के साथ पूरी चौड़ाई के पुश का उपयोग करने से उच्च WORD का उपयोग नहीं किया जाता है।
4 जो टीबीवाई व्यापक है, ध्यान दें कि पूर्णांकों के विपरीत, एफपी को हमेशा अधिक सटीकता के साथ लौटाया जाता है, जिसकी आवश्यकता होती है।
64-बिट विंडोज
पैरामीटर
पहले 4 पैरामीटर आरसीएक्स, आरडीएक्स, आर 8 और आर 9 में (क्रम में) पारित किए जाते हैं। XMM0 से XMM3 को फ्लोटिंग पॉइंट पैरामीटर पास करने के लिए उपयोग किया जाता है।
स्टैक पर किसी भी अन्य मापदंडों को पारित किया जाता है।
64 बिट से बड़े पैरामीटर पते द्वारा पारित किए जाते हैं।
स्पिल स्पेस
भले ही फ़ंक्शन 4 पैरामीटर से कम का उपयोग करता है, कॉलर हमेशा स्टैक पर 4 QWORD आकार के मापदंडों के लिए स्थान प्रदान करता है। कैली किसी भी उद्देश्य के लिए उनका उपयोग करने के लिए स्वतंत्र है, अगर वे किसी अन्य कॉल के द्वारा छीने जाएंगे तो वहां मापदंडों की नकल करना आम है।
प्रतिलाभ की मात्रा
स्केलर रिटर्न प्रकारों के लिए, वापसी मूल्य RAX में रखा गया है। यदि रिटर्न प्रकार 64 बिट्स (जैसे संरचनाओं के लिए) से बड़ा है, तो RAX उसी का एक संकेतक है।
सेव्ड और क्लोबर्ड रजिस्टर्स
पैरामीटर पासिंग (RCX, RDX, R8, R9 और XMM0 to XMM3) में इस्तेमाल होने वाले सभी रजिस्टर, RAX, R10, R11, XMM4 और XMM5 कैली द्वारा देखे जा सकते हैं। अन्य सभी रजिस्टरों को कॉलर द्वारा संरक्षित किया जाना चाहिए (जैसे स्टैक पर)।
स्टैक अलाइनमेंट
स्टैक को 16-बाइट संरेखित रखा जाना चाहिए। चूंकि "कॉल" निर्देश 8-बाइट रिटर्न पते को धक्का देता है, इसका मतलब है कि प्रत्येक गैर-पत्ता फ़ंक्शन 16-बाइट संरेखण को पुनर्स्थापित करने के लिए फॉर्म 16n + 8 के मान से स्टैक को समायोजित करने जा रहा है।
कॉल के बाद स्टैक को साफ करना कॉलर्स का काम है।
स्रोत: सम्मेलनों को बुलाने का इतिहास, भाग 5: amd64 रेमंड चेन
32-बिट, सीडीसीएल - स्ट्रक्चर से निपटना
गद्दी
याद रखें, एक संरचना के सदस्य आमतौर पर यह सुनिश्चित करने के लिए गद्देदार होते हैं कि वे अपनी प्राकृतिक सीमा पर संरेखित हैं:
struct t
{
int a, b, c, d; // a is at offset 0, b at 4, c at 8, d at 0ch
char e; // e is at 10h
short f; // f is at 12h (naturally aligned)
long g; // g is at 14h
char h; // h is at 18h
long i; // i is at 1ch (naturally aligned)
};
पैरामीटर के रूप में (संदर्भ द्वारा पास)
जब संदर्भ द्वारा पारित किया जाता है, तो मेमोरी में संरचना के लिए एक सूचक स्टैक पर पहले तर्क के रूप में पारित किया जाता है। यह एक प्राकृतिक-आकार (32-बिट) पूर्णांक मान को पारित करने के बराबर है; विशिष्टताओं के लिए 32-बिट सीडीसीएल देखें।
पैरामीटर के रूप में (मान से पास)
जब मूल्य से पारित किया जाता है, तो स्ट्रक्चर्स को स्टैक पर पूरी तरह से कॉपी किया जाता है, मूल मेमोरी लेआउट का सम्मान करता है ( यानी , पहला सदस्य निचले पते पर होगा)।
int __attribute__((cdecl)) foo(struct t a);
struct t s = {0, -1, 2, -3, -4, 5, -6, 7, -8};
foo(s);
; Assembly call
push DWORD 0fffffff8h ; i (-8)
push DWORD 0badbad07h ; h (7), pushed as DWORD to naturally align i, upper bytes can be garbage
push DWORD 0fffffffah ; g (-6)
push WORD 5 ; f (5)
push WORD 033fch ; e (-4), pushed as WORD to naturally align f, upper byte can be garbage
push DWORD 0fffffffdh ; d (-3)
push DWORD 2 ; c (2)
push DWORD 0ffffffffh ; b (-1)
push DWORD 0 ; a (0)
call foo
add esp, 20h
वापसी मूल्य के रूप में
जब तक वे तुच्छ 1 नहीं होते हैं, तब तक लौटने से पहले स्ट्रक्चर्स को कॉलर-सप्लाई किए गए बफर में कॉपी किया जाता है। यह एक छिपे हुए पहले पैरामीटर struct S *retval (जहां struct S का प्रकार है) के बराबर है।
फ़ंक्शन को इस सूचक के साथ eax में वापसी मूल्य पर लौटना चाहिए; कॉल करने वाले को रिटर्न मान के सूचक को पकड़े हुए eax पर निर्भर होने की अनुमति है, जिसे उसने call से ठीक पहले धक्का दिया था।
struct S
{
unsigned char a, b, c;
};
struct S foo(); // compiled as struct S* foo(struct S* _out)
स्टैक क्लीन-अप के प्रयोजनों के लिए पैरामीटर गणना में छिपे हुए पैरामीटर को नहीं जोड़ा गया है, क्योंकि इसे कैली द्वारा नियंत्रित किया जाना चाहिए।
sub esp, 04h ; allocate space for the struct
; call to foo
push esp ; pointer to the output buffer
call foo
add esp, 00h ; still as no parameters have been passed
ऊपर के उदाहरण में, संरचना को स्टैक के शीर्ष पर सहेजा जाएगा।
struct S foo()
{
struct S s;
s.a = 1; s.b = -2; s.c = 3;
return s;
}
; Assembly code
push ebx
mov eax, DWORD PTR [esp+08h] ; access hidden parameter, it is a pointer to a buffer
mov ebx, 03fe01h ; struct value, can be held in a register
mov DWORD [eax], ebx ; copy the structure into the output buffer
pop ebx
ret 04h ; remove the hidden parameter from the stack
; EAX = pointer to the output buffer
1 एक "तुच्छ" संरचना वह है जिसमें गैर-संरचना, गैर-सरणी प्रकार (आकार में 32 बिट तक) का केवल एक सदस्य होता है। इस तरह की संरचना के लिए, उस सदस्य का मूल्य केवल eax रजिस्टर में वापस आ जाता है। (यह व्यवहार GCC लक्ष्यीकरण लिनक्स के साथ देखा गया है)
सीडीसीएल का विंडोज संस्करण सिस्टम वी एबीआई के कॉलिंग कन्वेंशन से अलग है: एक "तुच्छ" संरचना को गैर-संरचना वाले, गैर-सरणी प्रकार (आकार में 32 बिट तक) के दो सदस्यों को शामिल करने की अनुमति है। इन मूल्यों में लौटा दिए जाते हैं eax और edx बस की तरह एक 64-बिट पूर्णांक किया जाएगा। (यह व्यवहार MSVC और Clang Win32 को लक्षित करने के लिए देखा गया है।)