x86 belconventies - x86 calling conventions
In dit artikel worden de aanroepconventies beschreven die worden gebruikt bij het programmeren van microprocessors met x86- architectuur .
Aanroepconventies beschrijven de interface van aangeroepen code:
- De volgorde waarin atomaire (scalaire) parameters, of afzonderlijke delen van een complexe parameter, worden toegewezen
- Hoe parameters worden doorgegeven (op de stapel geduwd, in registers geplaatst of een combinatie van beide)
- Welke registers de aangeroepen functie moet bewaren voor de beller (ook bekend als: callee-saved registers of non-volatile registers)
- Hoe de taak van het voorbereiden van de stapel voor en het herstellen na een functieaanroep is verdeeld tussen de beller en de callee
Dit hangt nauw samen met de toewijzing van formaten en formaten aan programmeertaaltypen. Een ander nauw verwant onderwerp is het mangelen van namen , dat bepaalt hoe symboolnamen in de code worden toegewezen aan symboolnamen die door de linker worden gebruikt. Aanroepconventies, typerepresentaties en naammanipulatie maken allemaal deel uit van wat bekend staat als een binaire toepassingsinterface (ABI).
Er zijn vaak subtiele verschillen in hoe verschillende compilers deze conventies implementeren, dus het is vaak moeilijk om code te koppelen die door verschillende compilers is gecompileerd. Aan de andere kant zijn conventies die als API-standaard worden gebruikt (zoals stdcall) zeer uniform geïmplementeerd.
Historische achtergrond
Vóór microcomputers leverde de machinefabrikant over het algemeen een besturingssysteem en compilers voor verschillende programmeertalen . De aanroepconventie (s) voor elk platform waren die gedefinieerd door de programmeertools van de fabrikant.
Vroege microcomputers vóór de Commodore Pet en Apple II kwamen over het algemeen zonder besturingssysteem of compilers. De IBM PC kwam met Microsoft's voorloper van Windows, het Disk Operating System ( DOS ), maar het kwam niet met een compiler. De enige hardware standaard voor IBM PC-compatibele machines werd gedefinieerd door de Intel-processors (8086, 80.386) en de letterlijke hardware IBM verscheept. Hardware-uitbreidingen en alle softwarestandaarden (behalve een BIOS- aanroepconventie) werden opengesteld voor concurrentie op de markt.
Een groot aantal onafhankelijke softwarebedrijven bood besturingssystemen, compilers voor vele programmeertalen en toepassingen aan. Veel verschillende belschema's werden door de firma's geïmplementeerd, die elkaar vaak uitsloten, op basis van verschillende vereisten, historische praktijken en creativiteit van programmeurs.
Na de IBM-compatibele marktshake-out domineerden Microsoft- besturingssystemen en programmeertools (met verschillende conventies), terwijl tweederangsbedrijven zoals Borland en Novell en open-sourceprojecten zoals GCC nog steeds hun eigen normen handhaafden. Bepalingen voor interoperabiliteit tussen leveranciers en producten werden uiteindelijk aangenomen, waardoor het probleem van het kiezen van een levensvatbare conventie werd vereenvoudigd.
Beller opschonen
In deze conventies reinigt de aanroeper de argumenten van de stapel.
cdecl
De cdecl (wat staat voor C-declaratie ) is een aanroepconventie die afkomstig is van Microsoft's compiler voor de programmeertaal C en wordt gebruikt door veel C-compilers voor de x86-architectuur . In cdecl worden subroutine-argumenten doorgegeven aan de stack . Integer-waarden en geheugenadressen worden geretourneerd in het EAX- register , drijvende-kommawaarden in het ST0 x87- register. Registers EAX, ECX en EDX worden door de beller opgeslagen en de rest wordt door de beller opgeslagen. De x87 drijvende-kommaregisters ST0 tot ST7 moeten leeg zijn (gepopt of vrijgemaakt) bij het aanroepen van een nieuwe functie, en ST1 tot ST7 moeten leeg zijn bij het verlaten van een functie. ST0 moet ook leeg zijn als het niet wordt gebruikt voor het retourneren van een waarde.
In de context van de programmeertaal C worden functieargumenten in de volgorde van rechts naar links op de stapel geduwd, dwz het laatste argument wordt eerst geduwd.
Overweeg het volgende C-broncodefragment:
int callee(int, int, int);
int caller(void)
{
return callee(1, 2, 3) + 5;
}
Op x86 kan het de volgende assembly-code produceren ( Intel-syntaxis ):
caller:
; make new call frame
; (some compilers may produce an 'enter' instruction instead)
push ebp ; save old call frame
mov ebp, esp ; initialize new call frame
; push call arguments, in reverse
; (some compilers may subtract the required space from the stack pointer,
; then write each argument directly, see below.
; The 'enter' instruction can also do something similar)
; sub esp, 12 : 'enter' instruction could do this for us
; mov [ebp-4], 3 : or mov [esp+8], 3
; mov [ebp-8], 2 : or mov [esp+4], 2
; mov [ebp-12], 1 : or mov [esp], 1
push 3
push 2
push 1
call callee ; call subroutine 'callee'
add esp, 12 ; remove call arguments from frame
add eax, 5 ; modify subroutine result
; (eax is the return value of our callee,
; so we don't have to move it into a local variable)
; restore old call frame
; (some compilers may produce a 'leave' instruction instead)
mov esp, ebp ; most calling conventions dictate ebp be callee-saved,
; i.e. it's preserved after calling the callee.
; it therefore still points to the start of our stack frame.
; we do need to make sure
; callee doesn't modify (or restores) ebp, though,
; so we need to make sure
; it uses a calling convention which does this
pop ebp ; restore old call frame
ret ; return
De aanroeper maakt de stapel schoon nadat de functieaanroep is geretourneerd.
De cdecl-aanroepconventie is meestal de standaardaanroepconventie voor x86 C- compilers , hoewel veel compilers opties bieden om de gebruikte aanroepconventies automatisch te wijzigen. Om handmatig een functie te definiëren als cdecl, ondersteunen sommige de volgende syntaxis:
return_type __cdecl func_name();
variaties
Er zijn enkele variaties in de interpretatie van cdecl. Als gevolg hiervan kunnen x86-programma's die zijn gecompileerd voor verschillende besturingssysteemplatforms en/of door verschillende compilers incompatibel zijn, zelfs als ze allebei de "cdecl"-conventie gebruiken en niet naar de onderliggende omgeving verwijzen.
Met betrekking tot het retourneren van waarden, retourneren sommige compilers eenvoudige gegevensstructuren met een lengte van 2 registers of minder in het registerpaar EAX:EDX, en grotere structuren en klasseobjecten die een speciale behandeling vereisen door de uitzonderingshandler (bijv. een gedefinieerde constructor, destructor of toewijzing) worden in het geheugen geretourneerd. Om "in het geheugen" door te geven, wijst de beller geheugen toe en geeft er een aanwijzer aan als een verborgen eerste parameter; de aangeroepene vult het geheugen en retourneert de aanwijzer, waarbij de verborgen aanwijzer wordt weergegeven wanneer hij terugkeert.
In Linux , GCC zet de de facto standaard voor het bellen conventies. Sinds GCC-versie 4.5 moet de stapel worden uitgelijnd op een grens van 16 bytes bij het aanroepen van een functie (vorige versies vereisten alleen een uitlijning van 4 bytes).
Een versie van cdecl wordt beschreven in System V ABI voor i386-systemen.
syscall
Dit is vergelijkbaar met cdecl in die zin dat argumenten van rechts naar links worden geduwd. EAX, ECX en EDX worden niet bewaard. De grootte van de parameterlijst in dubbelwoorden wordt doorgegeven in AL.
Syscall is de standaard aanroepconventie voor 32 bit OS/2 API.
optlink
Argumenten worden van rechts naar links geschoven. De drie eerste (meest linkse) argumenten worden doorgegeven in EAX, EDX en ECX en maximaal vier drijvende-kommaargumenten worden doorgegeven in ST0 tot en met ST3, hoewel er ruimte voor is gereserveerd in de lijst met argumenten op de stapel. Resultaten worden geretourneerd in EAX of ST0. Registers EBP, EBX, ESI en EDI blijven behouden.
Optlink wordt gebruikt door de IBM VisualAge- compilers.
Callee opruimen
In deze conventies ruimt de aangeroepene de argumenten van de stapel op. Functies die deze conventies gebruiken, zijn gemakkelijk te herkennen in ASM-code omdat ze de stapel na terugkeer zullen afwikkelen. De x86 ret- instructie staat een optionele 16-bits parameter toe die het aantal stackbytes specificeert dat moet worden vrijgegeven na terugkeer naar de beller. Zo'n code ziet er als volgt uit:
ret 12
Conventies met de naam fastcall of register zijn niet gestandaardiseerd en zijn anders geïmplementeerd, afhankelijk van de compiler-leverancier. Gewoonlijk geven registergebaseerde aanroepconventies een of meer argumenten door in registers, waardoor het aantal geheugentoegangen dat nodig is voor de aanroep wordt verminderd en ze dus meestal sneller worden.
pascal
Gebaseerd op de oproepconventie van de Borland Pascal- programmeertaal, worden de parameters in de volgorde van links naar rechts op de stapel geduwd (in tegenstelling tot cdecl), en de aangeroepene is verantwoordelijk voor het verwijderen van de stapel.
Het retourneren van het resultaat werkt als volgt:
- Ordinale waarden worden geretourneerd in AL (8-bits waarden), AX (16-bits waarden), EAX (32-bits waarden) of DX:AX (32-bits waarden op 16-bits systemen).
- Werkelijke waarden worden geretourneerd in DX:BX:AX.
- Waarden met drijvende komma (8087) worden geretourneerd in ST0.
- Pointers worden geretourneerd in EAX op 32-bits systemen en in AX in 16-bits systemen.
- Tekenreeksen worden geretourneerd op een tijdelijke locatie die wordt aangeduid door het @Result-symbool.
Deze aanroepconventie was gebruikelijk in de volgende 16-bits API's: OS/2 1.x, Microsoft Windows 3.x en Borland Delphi versie 1.x. Moderne versies van de Windows API gebruiken stdcall , waarbij de aangeroepene nog steeds de stapel herstelt zoals in de Pascal-conventie, maar de parameters worden nu van rechts naar links geduwd.
stdcall
De stdcall-aanroepconventie is een variatie op de Pascal-aanroepconventie waarin de aangeroepene verantwoordelijk is voor het opschonen van de stapel, maar de parameters worden van rechts naar links op de stapel geduwd, zoals in de _cdecl-aanroepconventie. Registers EAX, ECX en EDX zijn bestemd voor gebruik binnen de functie. Retourwaarden worden opgeslagen in het EAX-register.
stdcall is de standaard aanroepconventie voor de Microsoft Win32 API en voor Open Watcom C++ .
Microsoft-sneloproep
Microsoft __fastcall- conventie (ook bekend als __msfastcall ) geeft de eerste twee argumenten door (van links naar rechts geëvalueerd) die in ECX en EDX passen. Resterende argumenten worden van rechts naar links op de stapel geschoven. Wanneer de compiler compileert voor IA64 of AMD64 , negeert het het __fastcall trefwoord en gebruikt het in plaats daarvan de ene 64-bits aanroepconventie .
Omdat dit een veel voorkomende aanroepconventie is, ondersteunen andere compilers zoals GCC, Clang en ICC ook fastcall.
Beschouw het volgende C-fragment:
__attribute__((fastcall)) void printnums(int num1, int num2, int num3){
printf("The numbers you sent are: %d %d %d", num1, num2, num3);
}
int main(){
printnums(1, 2, 3);
return 0;
}
x86-decompilatie van de hoofdfunctie ziet er als volgt uit (in Intel-syntaxis):
main:
; stack setup
push ebp
mov ebp, esp
push 3 ; immediate 3 (third argument is pushed to the stack)
mov edx, 0x2 ; immediate 2 (second argument) is copied to edx register.
mov ecx, 0x1 ; immediate 1 (first argument) is copied to ecx register.
call printnums
mov eax, 0 ; return 0
leave
retn
De eerste twee argumenten worden in de volgorde van links naar rechts doorgegeven en het derde argument wordt op de stapel geschoven. Er is geen stapelopruiming, aangezien de stapelopruiming wordt uitgevoerd door de aangeroepene. De demontage van de callee-functie is:
printnums:
; stack setup
push ebp
mov ebp, esp
sub esp, 0x08
mov [ebp-0x04], ecx ; in x86, ecx = first argument.
mov [ebp-0x08], edx ; arg2
push [ebp+0x08] ; arg3 is pushed to stack.
push [ebp-0x08] ; arg2 is pushed
push [ebp-0x04] ; arg1 is pushed
push 0x8065d67 ; "The numbers you sent are %d %d %d"
call printf
; stack cleanup
add esp, 0x10
nop
leave
retn 0x04
Omdat de twee argumenten door de registers zijn doorgegeven en er slechts één parameter in de stapel is geduwd, wordt de gepushte waarde gewist door de retn-instructie, aangezien int 4 bytes groot is in x86-systemen.
Microsoft vectorcall
In Visual Studio 2013 introduceerde Microsoft de __vectorcall-aanroepconventie als reactie op efficiëntieproblemen van game-, grafische, video/audio- en codec-ontwikkelaars. Het schema zorgt ervoor dat grotere vectortypen ( float , double , __m128 , __m256 ) in registers kunnen worden doorgegeven in plaats van op de stapel.
Voor IA-32- en x64-code is __vectorcall vergelijkbaar met respectievelijk __fastcall en de oorspronkelijke x64- aanroepconventies , maar breidt ze uit om het doorgeven van vectorargumenten te ondersteunen met behulp van SIMD- registers. In IA-32 worden de gehele waarden zoals gewoonlijk doorgegeven, en de eerste zes SIMD ( XMM / YMM 0-5) registers bevatten tot zes drijvende- komma- , vector- of HVA-waarden opeenvolgend van links naar rechts, ongeacht de werkelijke posities veroorzaakt door bijvoorbeeld een int-argument dat tussen hen verschijnt. In x64 is de regel van de oorspronkelijke x64-conventie echter nog steeds van toepassing, zodat XMM/YMM0-5 alleen drijvende-komma-, vector- of HVA-argumenten bevatten als ze toevallig de eerste tot en met de zesde zijn.
__vectorcall voegt ondersteuning toe voor het doorgeven van homogene vectoraggregaat (HVA)-waarden, dit zijn samengestelde typen (structs) die uitsluitend uit maximaal vier identieke vectortypen bestaan, waarbij dezelfde zes registers worden gebruikt. Zodra de registers zijn toegewezen voor argumenten van het vectortype, worden de ongebruikte registers van links naar rechts toegewezen aan HVA-argumenten. De positioneringsregels zijn nog steeds van toepassing. Resulterende vectortype- en HVA-waarden worden geretourneerd met behulp van de eerste vier XMM/YMM-registers.
De clang-compiler en de Intel C++-compiler implementeren ook vectorcall. Intel C++ compiler had een soortgelijke, eerdere conventie genaamd __regcall ; het wordt ook ondersteund door gerinkel.
Borland register
Door argumenten van links naar rechts te evalueren, geeft het drie argumenten door via EAX, EDX, ECX. Resterende argumenten worden op de stapel geschoven, ook van links naar rechts. Het is de standaard aanroepconventie van de 32-bits compiler van Delphi , waar het bekend staat als register . Deze aanroepconventie wordt ook gebruikt door C++Builder van Embarcadero, waar het __fastcall wordt genoemd . In deze compiler kan de fastcall van Microsoft worden gebruikt als __msfastcall .
GCC en Clang kunnen worden gemaakt om een vergelijkbare aanroepconventie te gebruiken door __stdcallhet regparmfunctieattribuut of de -mregparm=3schakelaar te gebruiken. (De stapelvolgorde is omgekeerd.) Het is ook mogelijk om een caller clean-up variant te maken door cdecldeze te gebruiken of uit te breiden om ook SSE registers te gebruiken. Een cdeclgebaseerde versie wordt gebruikt door de Linux-kernel op i386 sinds versie 2.6.20 (uitgebracht in februari 2007).
Watcom-register
Watcom ondersteunt het trefwoord __fastcall niet, behalve om het te aliasen naar null. De registeraanroepconventie kan worden geselecteerd met een opdrachtregelschakelaar. ( IDA gebruikt echter hoe dan ook __fastcall voor uniformiteit.)
Er worden maximaal 4 registers toegewezen aan argumenten in de volgorde EAX, EDX, EBX, ECX. Argumenten worden van links naar rechts aan registers toegewezen. Als een argument niet aan een register kan worden toegewezen (zeg dat het te groot is), worden het en alle volgende argumenten aan de stapel toegewezen. Argumenten die aan de stapel zijn toegewezen, worden van rechts naar links geschoven. Namen worden verminkt door het toevoegen van een achtervoegsel onderstrepingsteken.
Variadische functies vallen terug op de Watcom-stack-gebaseerde belconventie.
De Watcom C/C++-compiler gebruikt ook de #pragma aux- richtlijn waarmee de gebruiker zijn eigen belconventie kan specificeren. Zoals in de handleiding staat: "Zeer weinig gebruikers hebben deze methode waarschijnlijk nodig, maar als het nodig is, kan het levensreddend zijn".
TopSpeed / Clarion / JPI
De eerste vier integer-parameters worden doorgegeven in de registers eax, ebx, ecx en edx. Drijvende-kommaparameters worden doorgegeven aan de drijvende-kommastapel - registers st0, st1, st2, st3, st4, st5 en st6. Structuurparameters worden altijd doorgegeven aan de stapel. Aanvullende parameters worden doorgegeven aan de stapel nadat de registers zijn uitgeput. Integer-waarden worden geretourneerd in eax, pointers in edx en typen met drijvende komma in st0.
veilig bellen
In Delphi en Free Pascal op Microsoft Windows omvat de safecall-aanroepconventie COM ( Component Object Model ) foutafhandeling, dus uitzonderingen worden niet uitgelekt naar de beller, maar worden gerapporteerd in de HRESULT- retourwaarde, zoals vereist door COM/OLE. Bij het aanroepen van een safecall-functie vanuit Delphi-code, controleert Delphi ook automatisch het geretourneerde HRESULT en genereert indien nodig een uitzondering.
De safecall-aanroepconventie is hetzelfde als de stdcall-aanroepconventie, behalve dat uitzonderingen worden doorgegeven aan de aanroeper in EAX als een HResult (in plaats van in FS:[0]), terwijl het functieresultaat door verwijzing op de stapel wordt doorgegeven als hoewel het een laatste "uit" -parameter was. Bij het aanroepen van een Delphi-functie vanuit Delphi verschijnt deze aanroepconventie net als elke andere aanroepconventie, want hoewel uitzonderingen worden teruggegeven in EAX, worden ze automatisch terug geconverteerd naar de juiste uitzonderingen door de beller. Bij gebruik van COM-objecten die in andere talen zijn gemaakt, worden de HResults automatisch als uitzonderingen weergegeven en is het resultaat voor Get-functies in het resultaat in plaats van in een parameter. Bij het maken van COM-objecten in Delphi met safecall, hoeft u zich geen zorgen te maken over HR-resultaten, aangezien uitzonderingen normaal kunnen worden gemaakt, maar in andere talen als HR-resultaten worden gezien.
function function_name(a: DWORD): DWORD; safecall;
Retourneert een resultaat en verhoogt uitzonderingen zoals een normale Delphi-functie, maar geeft waarden en uitzonderingen door alsof het:
function function_name(a: DWORD; out Result: DWORD): HResult; stdcall;
Beller of gebelde opruimen
deze oproep
Deze aanroepconventie wordt gebruikt voor het aanroepen van C++ niet-statische lidfuncties. Er zijn twee primaire versies van deze aanroep die worden gebruikt, afhankelijk van de compiler en of de functie al dan niet een variabel aantal argumenten gebruikt.
Voor de GCC-compiler is deze aanroep bijna identiek aan cdecl : de aanroeper maakt de stapel schoon en de parameters worden van rechts naar links doorgegeven. Het verschil is de toevoeging van de this pointer , die als laatste op de stapel wordt geduwd, alsof het de eerste parameter in het functie-prototype is.
Op de Microsoft Visual C++-compiler wordt de this- pointer doorgegeven in ECX en is het de callee die de stapel opschoont, waarbij de stdcall- conventie wordt gespiegeld die in C voor deze compiler en in Windows API-functies wordt gebruikt. Wanneer functies een variabel aantal argumenten gebruiken, is het de aanroeper die de stapel opschoont (zie cdecl ).
De aanroepconventie van thiscall kan alleen expliciet worden opgegeven in Microsoft Visual C++ 2005 en later. Op elke andere compiler is deze oproep geen sleutelwoord. ( Demonteurs , zoals IDA , moeten het echter specificeren. IDA gebruikt hiervoor het trefwoord __thiscall .)
Register bewaring
Een ander onderdeel van een aanroepconventie is welke registers gegarandeerd hun waarden behouden na een subroutine-aanroep.
Door beller opgeslagen (vluchtige) registers
Volgens de Intel ABI waaraan de overgrote meerderheid van de compilers voldoet, moeten de EAX, EDX en ECX gratis zijn voor gebruik binnen een procedure of functie en hoeven ze niet te worden bewaard.
Zoals de naam al aangeeft, bevatten deze registers voor algemene doeleinden meestal tijdelijke (vluchtige) informatie, die door elke subroutine kan worden overschreven.
Daarom is het de verantwoordelijkheid van de aanroeper om elk van deze registers op de stapel te duwen, als hij zijn waarden wil herstellen na een subroutine-aanroep.
Door Callee opgeslagen (niet-vluchtige) registers
De andere registers worden gebruikt om waarden met een lange levensduur (niet-vluchtig) vast te houden, die voor alle oproepen moeten worden bewaard.
Met andere woorden, wanneer de beller een procedure-aanroep doet, kan hij verwachten dat die registers dezelfde waarde zullen behouden nadat de opgeroepene terugkeert.
Het is dus de verantwoordelijkheid van de gebelde om ze zowel op te slaan (in het begin te drukken) als te herstellen (dienovereenkomstig te knallen) voordat ze terugkeren naar de beller. Net als in het vorige geval moet deze praktijk alleen worden gedaan op registers die de opgeroepene verandert.
x86-64 belconventies
x86-64-aanroepconventies maken gebruik van de extra registerruimte om meer argumenten in registers door te geven. Ook is het aantal incompatibele belconventies verminderd. Er zijn er twee die algemeen worden gebruikt.
Microsoft x64-aanroepconventie
De Microsoft x64-aanroepconventie wordt gevolgd op Windows en pre-boot UEFI (voor lange modus op x86-64 ). De eerste vier argumenten worden op de registers geplaatst. Dat betekent RCX, RDX, R8, R9 voor integer-, struct- of pointer-argumenten (in die volgorde), en XMM0, XMM1, XMM2, XMM3 voor floating-point-argumenten. Extra argumenten worden op de stapel geschoven (van rechts naar links). Integer-retourwaarden (vergelijkbaar met x86) worden geretourneerd in RAX als 64 bits of minder. Retourwaarden met drijvende komma worden geretourneerd in XMM0. Parameters die minder dan 64 bits lang zijn, worden niet nul uitgebreid; de hoge bits worden niet op nul gezet.
Structuren en vakbonden met grootten die overeenkomen met gehele getallen, worden doorgegeven en geretourneerd alsof het gehele getallen zijn. Anders worden ze vervangen door een aanwijzer als ze als argument worden gebruikt. Wanneer een te grote structteruggave nodig is, wordt een andere aanwijzer naar een door de beller verstrekte spatie als eerste argument toegevoegd, waardoor alle andere argumenten één plaats naar rechts worden verschoven.
Bij het compileren voor de x64-architectuur in een Windows-context (met behulp van Microsoft- of niet-Microsoft-tools), lossen stdcall, thiscall, cdecl en fastcall allemaal op om deze conventie te gebruiken.
In de Microsoft x64-aanroepconventie is het de verantwoordelijkheid van de beller om 32 bytes "schaduwruimte" op de stapel toe te wijzen vlak voordat de functie wordt aangeroepen (ongeacht het daadwerkelijke aantal gebruikte parameters) en om de stapel na de aanroep te openen. De schaduwruimte wordt gebruikt om RCX, RDX, R8 en R9 te verspreiden, maar moet beschikbaar worden gemaakt voor alle functies, zelfs die met minder dan vier parameters.
De registers RAX, RCX, RDX, R8, R9, R10, R11 worden als vluchtig beschouwd (caller-saved).
De registers RBX, RBP, RDI, RSI, RSP, R12, R13, R14 en R15 worden als niet-vluchtig (calle-saved) beschouwd.
Een functie die bijvoorbeeld 5 integer-argumenten gebruikt, zal de eerste tot de vierde in registers nemen en de vijfde wordt bovenop de schaduwruimte geduwd. Dus wanneer de aangeroepen functie wordt ingevoerd, bestaat de stapel uit (in oplopende volgorde) het retouradres, gevolgd door de schaduwruimte (32 bytes) gevolgd door de vijfde parameter.
In x86-64 slaat Visual Studio 2008 getallen met drijvende komma op in XMM6 en XMM7 (evenals XMM8 tot en met XMM15); bijgevolg moeten voor x86-64 door de gebruiker geschreven assembler-routines XMM6 en XMM7 behouden (in vergelijking met x86 waarin door de gebruiker geschreven assembleertaal-routines XMM6 en XMM7 niet hoefden te behouden). Met andere woorden, door de gebruiker geschreven assembler-routines moeten worden bijgewerkt om XMM6 en XMM7 voor/na de functie op te slaan/te herstellen wanneer ze worden overgezet van x86 naar x86-64 .
Beginnend met Visual Studio 2013 introduceerde Microsoft de __vectorcall-aanroepconventie die de x64-conventie uitbreidt.
Systeem V AMD64 ABI
De aanroepconventie van de System V AMD64 ABI wordt gevolgd op Solaris , Linux , FreeBSD , macOS en is de de facto standaard onder Unix en Unix-achtige besturingssystemen. De OpenVMS Calling Standard op x86-64 is gebaseerd op System V ABI met enkele extensies die nodig zijn voor achterwaartse compatibiliteit. De eerste zes integer- of pointer-argumenten worden doorgegeven in registers RDI, RSI, RDX, RCX, R8, R9 (R10 wordt gebruikt als een statische ketenaanwijzer in het geval van geneste functies), terwijl XMM0, XMM1, XMM2, XMM3, XMM4, XMM5 , XMM6 en XMM7 worden gebruikt voor de eerste drijvende-kommaargumenten. Net als in de Microsoft x64-aanroepconventie worden aanvullende argumenten op de stapel doorgegeven. Integer-retourwaarden tot 64 bits worden opgeslagen in RAX, terwijl waarden tot 128 bits worden opgeslagen in RAX en RDX. Retourwaarden met drijvende komma worden op dezelfde manier opgeslagen in XMM0 en XMM1. De bredere YMM- en ZMM-registers worden gebruikt voor het doorgeven en retourneren van bredere waarden in plaats van XMM als ze bestaan.
Als de aangeroepene de registers RBX, RSP, RBP en R12-R15 wil gebruiken, moet hij hun oorspronkelijke waarden herstellen voordat hij de controle aan de beller teruggeeft. Alle andere registers moeten door de beller worden opgeslagen als deze hun waarden wil behouden.
Voor leaf-node-functies (functies die geen andere functie(s) aanroepen), wordt een ruimte van 128 bytes opgeslagen net onder de stapelaanwijzer van de functie. De ruimte wordt de rode zone genoemd . Deze zone wordt niet overvallen door signaal- of onderbrekingshandlers. Compilers kunnen deze zone dus gebruiken om lokale variabelen op te slaan. Compilers kunnen sommige instructies overslaan bij het starten van de functie (afstelling van RSP, RBP) door gebruik te maken van deze zone. Andere functies kunnen deze zone echter overschaduwen. Daarom moet deze zone alleen worden gebruikt voor functies van bladknooppunten. gccen clangbied de -mno-red-zonevlag aan om optimalisaties van de rode zone uit te schakelen.
Als de aangeroepene een variadische functie is , moet het aantal drijvende-kommaargumenten dat aan de functie in vectorregisters wordt doorgegeven, worden opgegeven door de aanroeper in het AL-register.
In tegenstelling tot de Microsoft-aanroepconventie is er geen schaduwruimte; bij het invoeren van een functie grenst het retouradres aan het zevende integerargument op de stapel.
Lijst met x86-oproepconventies
Dit is een lijst met x86-aanroepconventies. Dit zijn conventies die in de eerste plaats bedoeld zijn voor C/C++-compilers (vooral het 64-bits gedeelte hieronder), en dus grotendeels speciale gevallen. Andere talen kunnen andere formaten en conventies gebruiken in hun implementaties.
| architectuur | Naam | Besturingssysteem, compiler | Parameters: | Stapel opschonen | Opmerkingen: | |
|---|---|---|---|---|---|---|
| registreert | Stapelvolgorde | |||||
| 8086 | cdecl | RTL (C) | Beller | |||
| Pascal | LTR (Pascal) | Callee | ||||
| fastcall (niet-lid) | Microsoft | AX, DX, BX | LTR (Pascal) | Callee | Retourwijzer in BX. | |
| fastcall (lidfunctie) | Microsoft | AX, DX | LTR (Pascal) | Callee |
thisop stapel laag adres. Retouraanwijzer in AX.
|
|
| snel bellen | Turbo C | AX, DX, BX | LTR (Pascal) | Callee |
thisop stapel laag adres. Retouraanwijzer op stapel hoog adres.
|
|
| Watcom | AX, DX, BX, CX | RTL (C) | Callee | Retourwijzer in SI. | ||
| IA-32 | cdecl | Unix-achtig ( GCC ) | RTL (C) | Beller | Bij het retourneren van struct/class, wijst de aanroepende code ruimte toe en geeft een aanwijzer naar deze ruimte door via een verborgen parameter op de stapel. De aangeroepen functie schrijft de retourwaarde naar dit adres.
Stack uitgelijnd op 16-byte grens vanwege een bug. |
|
| cdecl | Microsoft | RTL (C) | Beller | Bij het retourneren van struct/klasse,
Stapel uitgelijnd op 4-byte grens. |
||
| stdcall | Microsoft | RTL (C) | Callee | Ook ondersteund door GCC. | ||
| snel bellen | Microsoft | ECX, EDX | RTL (C) | Callee | Retouraanwijzer op stapel als het geen lidfunctie is. Ook ondersteund door GCC. | |
| register | Delphi en Free Pascal | EAX, EDX, ECX | LTR (Pascal) | Callee | ||
| deze oproep | Windows ( Microsoft Visual C++ ) | ECX | RTL (C) | Callee | Standaard voor ledenfuncties. | |
| vectorcall | Windows ( Microsoft Visual C++ ) | ECX, EDX, [XY]MM0–5 | RTL (C) | Callee | Verlengd van fastcall. Ook ondersteund door ICC en Clang. | |
| Watcom-compiler | EAX, EDX, EBX, ECX | RTL (C) | Callee | Retourwijzer in ESI. | ||
| x86-64 | Microsoft x64-aanroepconventie | Windows ( Microsoft Visual C++ , GCC , Intel C++ Compiler , Delphi ), UEFI | RCX/XMM0, RDX/XMM1, R8/XMM2, R9/XMM3 | RTL (C) | Beller | Stapel uitgelijnd op 16 bytes. 32 bytes schaduwruimte op stapel. De opgegeven 8 registers kunnen alleen worden gebruikt voor parameters 1 tot en met 4. Voor C++-klassen is de verborgen thisparameter de eerste parameter en wordt deze doorgegeven in RCX.
|
| vectorcall | Windows ( Microsoft Visual C++ , Clang, ICC) | RCX/[XY]MM0, RDX/[XY]MM1, R8/[XY]MM2, R9/[XY]MM3 + [XY]MM4–5 | RTL (C) | Beller | Uitgebreid vanaf MS x64. | |
| Systeem V AMD64 ABI | Solaris , Linux , BSD , macOS , OpenVMS ( GCC , Intel C++-compiler , Clang , Delphi ) | RDI, RSI, RDX, RCX, R8, R9, [XYZ]MM0–7 | RTL (C) | Beller | Stapel uitgelijnd op 16 bytes grens. 128 bytes rode zone onder stapel. De kernelinterface gebruikt RDI, RSI, RDX, R10, R8 en R9. In C++ thisis dit de eerste parameter.
|
|
Referenties
voetnoten
Andere bronnen
-
"SYSTEEM V APPLICATIE BINAIR INTERFACE Intel386 Architecture Processor Supplement" (PDF) (4e ed.). De operatie van Santa Cruz, Inc. 1997/03/19. Cite journaal vereist
|journal=( hulp ) - Nemanja Trifunovic (2001-07-22). Sean Ewington (red.). "Calling Conventies Demystified" . Het codeproject .
- Stephen J. Friedl. "Intel x86 Function-call Conventies - Assembly View" . Steve Friedl's Unixwiz.net technische tips .
- "Visual Studio 2010 - Visual C++ Calling Convention" . MSDN-bibliotheek . Microsoft. 2010. 2010.
- Andreas Jonsson (2005-02-13). "Oproepconventies op het x86-platform" .
- Raymond Chen (2004-01-02). "De geschiedenis van het aanroepen van conventies, deel 1" . Het oude nieuwe ding .
- Raymond Chen (2004/01/07). "De geschiedenis van het aanroepen van conventies, deel 2" . Het oude nieuwe ding .
- Raymond Chen (2004-01-08). "De geschiedenis van het aanroepen van conventies, deel 3" . Het oude nieuwe ding .
- Raymond Chen (2004/01/13). "De geschiedenis van belconventies, deel 4: ia64" . Het oude nieuwe ding .
- Raymond Chen (2004-01-14). "De geschiedenis van het aanroepen van conventies, deel 5; amd64" . Het oude nieuwe ding .
Verder lezen
- Jonathan de Boyne Pollard (2010). "The gen on functie aanroepende conventies" . Vaak gegeven antwoorden .
- Kip R. Irvine (2011). "Geavanceerde procedures (hoofdstuk 8)". Assemblertaal voor x86-processors (6e ed.). Prentenzaal. ISBN 978-0-13-602212-1.
- Borland C/C++ versie 3.1 Gebruikershandleiding (PDF) . Borland. 1992. blz. 158, 189-191.
- Thomas Lauer (1995). "De nieuwe __stdcall-oproepvolgorde". Overzetten naar Win32: een gids om uw toepassingen klaar te maken voor de 32-bits toekomst van Windows . springer. ISBN 978-0-387-94572-9.