Sök…


Anmärkningar

Resurser

Översikter / jämförelser: Agner Fogs trevliga samtalshandbok . Dessutom x86 ABI: er (wikipedia) : samtalskonventioner för funktioner, inklusive x86-64 Windows och System V (Linux).


32-bitars cdecl

cdecl är en Windows 32-bitars funktionskonvention som är mycket lik den samtalskonvention som används på många POSIX-operativsystem (dokumenterad i i386 System V ABI ). En av skillnaderna är att returnera små strukturer.

parametrar

Parametrar överförs på bunten, med det första argumentet på den lägsta adressen på bunten vid samtalstidpunkten (tryckt senast, så det är precis ovanför returadressen vid posten till funktionen). Den som ringer är ansvarig för att poppa parametrar tillbaka från bunten efter samtalet.

Returvärde

För skalära returtyper placeras returvärdet i EAX eller EDX: EAX för 64 bitars heltal. Flytande punkttyper returneras i st0 (x87). Återvända större typer som strukturer görs genom referens, med en pekare som en implicit första parameter. (Denna pekare returneras i EAX, så den som ringer behöver inte komma ihåg vad den passerade).

Sparade och klädda register

EBX, EDI, ESI, EBP och ESP (och FP / SSE avrundningsläge) måste bevaras av callee, så att den som ringer kan lita på att de register som inte har ändrats av ett samtal.

Alla andra register (EAX, ECX, EDX, FLAGS (andra än DF), x87 och vektorregister) kan fritt modifieras av callee; Om en samtalare vill bevara ett värde före och efter funktionssamtalet måste det spara värdet någon annanstans (t.ex. i ett av de sparade register eller på stacken).

64-bitars system V

Detta är standardsamtalskonventionen för 64-bitars applikationer på många POSIX-operativsystem.

parametrar

De första åtta skalparametrarna skickas i (i ordning) RDI, RSI, RDX, RCX, R8, R9, R10, R11. Parametrar förbi de första åtta placeras på bunten, med tidigare parametrar närmare toppen av bunten. Den som ringer är ansvarig för att dessa värden släpps från bunten efter samtalet om det inte längre behövs.

Returvärde

För skala returtyper placeras returvärdet i RAX. Återvända större typer som strukturer görs genom att konceptuellt ändra signatur för funktionen för att lägga till en parameter i början av parameterlistan som är en pekare till en plats där returvärdet ska placeras.

Sparade och klädda register

RBP, RBX och R12 – R15 bevaras av callee. Alla andra register kan modifieras av callee, och den som ringer måste bevara ett registervärde själv (t.ex. på stacken) om den vill använda det värdet senare.

32-bitars stdcall

stdcall används för 32-bitars Windows API-samtal.

parametrar

Parametrar överförs på bunten, med den första parametern närmast toppen av bunten. Callee kommer att dyka upp dessa värden från stacken innan de återvänder.

Returvärde

Skala returvärden placeras i EAX.

Sparade och klädda register

EAX, ECX och EDX kan fritt modifieras av callee och måste sparas av den som ringer om så önskas. EBX, ESI, EDI och EBP måste sparas av callee om de modifieras och återställs till sina ursprungliga värden vid retur.

32-bitars cdecl - Hantering av heltal

Som parametrar (8, 16, 32 bitar)

8, 16, 32 bitars heltal passeras alltid, på bunten, som 32 bitars hela breddvärden 1 .
Ingen förlängning, signerad eller noll, behövs.
Callee använder bara den nedre delen av värdena för hela bredden.

//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

Som parametrar (64 bitar)

64 bitar värden överförs på bunten med hjälp av två tryck, respekterande littel endian konvention 2 , tryck först de högre 32 bitarna sedan de lägre.

//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

Som returvärde

Åtta bitar heltal återlämnas i AL , så småningom clobbering hela eax .
16 bitar heltal återlämnas i AX , så småningom clobbering hela eax .
32 bitars heltal returneras i EAX .
64 bitars heltal returneras i EDX:EAX , där EAX håller de lägre 32 bitarna och EDX de övre.

//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 Detta håller stapeln i linje med 4 byte, den naturliga ordstorleken. En x86-CPU kan också bara trycka på 2 eller 4 byte när den inte är i långt läge.

2 Sänk DWORD på lägre adress

32-bitars cdecl - Hantering av flytande punkt

Som parametrar (float, double)

Floats är 32 bitar i storlek, de förs naturligt på bunten.
Dubblarna är 64 bitar i storlek, de passeras på bunten, respekterar Little Endian-konvention 1 , först skjuter de övre 32 bitarna och än de nedre.

//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

Som parametrar (lång dubbel)

Långa dubblar är 80 bitar 2 breda, medan på stacken en TBYTE skulle kunna lagras med två 32 bitar tryck och en 16 bitars push (för 4 + 4 + 2 = 10), för att hålla stacken i linje med 4 byte, slutar den uppta 12 byte, så att man använder tre 32 bitar tryck.
Med respekt för Little Endian-konventionen skjuts bitarna 79-64 först 3 , sedan bitarna 63-32 följt av bitarna 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

Som returvärde

Ett flytande punktvärde, oavsett storlek, returneras i 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 Sänk DWORD på lägre adress.

2 Känd som TBYTE, från Ten Bytes.

3 Med hjälp av en full breddspush med någon förlängning används inte högre WORD.

4 Vilket är TBYE-brett, observera att i motsats till heltalen returneras FP alltid med mer precision som det krävs.

64-bitars Windows

parametrar

De första fyra parametrarna skickas i (i ordning) RCX, RDX, R8 och R9. XMM0 till XMM3 används för att skicka parametrar för flytande punkt.

Eventuella ytterligare parametrar överförs på stacken.

Parametrar större än 64 bit skickas via adress.

Spill utrymme

Även om funktionen använder mindre än 4 parametrar ger den som ringer alltid utrymme för 4 QWORD-storlekar på bunten. Det är gratis att använda dem för alla ändamål, det är vanligt att kopiera parametrarna där om de skulle spillas av ett annat samtal.

Returvärde

För skala returtyper placeras returvärdet i RAX. Om returtypen är större än 64 bitar (t.ex. för strukturer) är RAX en pekare till det.

Sparade och klädda register

Alla register som används vid parameterpassering (RCX, RDX, R8, R9 och XMM0 till XMM3), RAX, R10, R11, XMM4 och XMM5 kan spillas av callee. Alla andra register måste bevaras av den som ringer (t.ex. på stacken).

Stapla inriktningen

Bunten måste hållas i linje med 16 byte. Eftersom "samtal" -instruktionen trycker på en 8-byte-returadress, betyder detta att varje icke-bladfunktion kommer att justera stapeln med ett värde på formen 16n + 8 för att återställa 16-byte-justering.
Det är uppringarnas jobb att rengöra stacken efter ett samtal.


Källa: Historik om samtalskonventioner, del 5: amd64 Raymond Chen

32-bitars cdecl - Hantering av strukturer

Stoppning

Kom ihåg att medlemmar i en struktur vanligtvis är vadderade för att säkerställa att de är i linje med sin naturliga gräns:

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)
};

Som parametrar (pass by reference)

När det skickas som referens skickas en pekare till strukturen i minnet som det första argumentet på stacken. Detta motsvarar passering av ett heltäckande (32-bitars) heltalsvärde; se 32-bitars cdecl för detaljer.

Som parametrar (pass by value)

När de överförs av värdet kopieras strukturer helt på bunten med respekt för den ursprungliga minneslayouten ( dvs. den första medlemmen kommer att vara på den lägre adressen).

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

Som returvärde

Såvida de inte är triviala 1 , kopieras strukturer till en anropslevererad buffert innan de återvänder. Detta motsvarar att ha en dold första parameter struct S *retval (där struct S är typen av strukt).

Funktionen måste återgå med denna pekare till returvärdet i eax ; Den som ringer får bero på att eax håller pekaren till returvärdet, som den tryckte rätt före call .

struct S
{
    unsigned char a, b, c;
};

struct S foo();         // compiled as struct S* foo(struct S* _out)

Den dolda parametern läggs inte till i parameterräkningen för staplingsrening, eftersom den måste hanteras av callee.

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

I exemplet ovan sparas strukturen högst upp i bunten.

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 En "trivial" struktur är en som endast innehåller en medlem av en icke-struktur, icke-array-typ (upp till 32 bitar i storlek). För sådana strukturer returneras värdet på denna medlem helt enkelt i eax registret. (Detta beteende har observerats med GCC-inriktning på Linux)

Windows-versionen av cdecl skiljer sig från System V ABI: s samtalskonvention: En "trivial" struktur tillåter att innehålla upp till två medlemmar av en icke-struktur, icke-array-typ (upp till 32 bitar i storlek). Dessa värden returneras i eax och edx , precis som ett 64-bitars heltal skulle vara. (Detta beteende har observerats för MSVC och Clang som riktar sig till Win32.)



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow