Intel x86 Assembly Language & Microarchitecture
호출 규칙
수색…
비고
자원
개요 / 비교 : Agner Fog의 훌륭한 전화 회의 가이드 . 또한 x86 ABI (wikipedia) : x86-64 Windows 및 System V (Linux)를 포함한 함수 호출 규칙.
SystemV x86-64 ABI (공식 표준) . Windows를 제외한 모든 OS에서 사용됩니다. ( 이 github 위키 페이지 는 HJ Lu의 최신 버전으로 32 비트, 64 비트 및 x32에 대한 링크가 있으며 ABI 관리자 / 참여자를위한 공식 포럼 링크도 있습니다.) clang / gcc sign / zero는 서면으로 된 ABI가 그것을 필요로하지 않더라도 32 비트 . Clang이 생성 한 코드는이 코드에 의존합니다.
SystemV 32 비트 (i386) Linux 및 Unix에서 사용되는 ABI (공식 표준) . ( 이전 버전 ).
OS X 32 비트 x86 호출 규칙, 다른 것들에 대한 링크 포함 . 64 비트 호출 규칙은 System V입니다. Apple의 사이트는 FreeBSD pdf에 링크되어 있습니다.
Windows
__vectorcall: 32 비트 및 64 비트 버전 문서화Windows 32 비트
__stdcall: Win32 API 함수를 호출하는 데 사용됩니다. 이 페이지는 다른 호출 규칙 (예 :__cdecl)과 연결됩니다.Windows64가 x86-64의 다른 모든 OS와는 다른 호출 규칙을 사용하는 이유는 무엇입니까? : 몇 가지 흥미로운 역사, 특히. SysV ABI의 경우 우편 목록 보관소는 공개되어 AMD가 첫 번째 실리콘을 공개하기 전에 되돌아갑니다.
32 비트 cdecl
cdecl은 많은 POSIX 운영 체제에서 사용되는 호출 규칙과 매우 유사한 Windows 32 비트 함수 호출 규칙입니다 ( i386 System V ABI에 설명되어 있음 ). 차이점 중 하나는 작은 구조체를 반환하는 것입니다.
매개 변수
매개 변수는 스택에서 전달됩니다. 첫 번째 인수는 호출시 스택의 가장 낮은 주소 (마지막으로 푸시되므로 함수에 대한 입력시 리턴 주소 바로 위에 있습니다)와 함께 전달됩니다. 호출자는 호출 후 스택에서 매개 변수를 다시 팝핑해야합니다.
반환 값
스칼라 반환 유형의 경우 반환 값은 EAX 또는 64 비트 정수의 경우 EDX : EAX에 배치됩니다. 부동 소수점 유형은 st0 (x87)로 반환됩니다. 구조체와 같은 더 큰 유형을 반환하는 것은 암시 적 첫 번째 매개 변수로 전달되는 포인터를 사용하여 참조에 의해 수행됩니다. 이 포인터는 EAX에서 반환되므로 호출자는 전달 된 내용을 기억할 필요가 없습니다.
저장된 레지스터 및 Clobbered 레지스터
호출자가 호출로 변경되지 않은 레지스터에 의존 할 수 있도록 EBX, EDI, ESI, EBP 및 ESP (및 FP / SSE 반올림 모드 설정)가 호출 수신자에 의해 보존되어야합니다.
다른 모든 레지스터 (EAX, ECX, EDX, FLAGS (DF 제외), x87 및 벡터 레지스터)는 수신자가 자유롭게 수정할 수 있습니다. 호출자가 함수 호출 전후에 값을 보존하려면 저장 된 레지스터 또는 스택 중 하나에 값을 저장해야합니다.
64 비트 시스템 V
이것은 많은 POSIX 운영 체제에서 64 비트 응용 프로그램에 대한 기본 호출 규칙입니다.
매개 변수
처음 8 개의 스칼라 매개 변수는 RDI, RSI, RDX, RCX, R8, R9, R10, R11 순서로 전달됩니다. 처음 8 개를 지나친 매개 변수는 스택에 배치되며 이전 매개 변수는 스택 맨 위에 가깝습니다. 호출자는 더 이상 필요하지 않으면 호출 후에 스택에서이 값을 팝핑 할 책임이 있습니다.
반환 값
스칼라 반환 유형의 경우 반환 값은 RAX에 저장됩니다. 구조체와 같은 더 큰 유형을 반환하는 것은 개념적으로 함수의 서명을 변경하여 반환 값을 배치 할 위치에 대한 포인터 인 매개 변수 목록의 시작 부분에 매개 변수를 추가하는 방법으로 수행됩니다.
저장된 레지스터 및 Clobbered 레지스터
RBP, RBX 및 R12-R15는 수신자에 의해 보존됩니다. 다른 모든 레지스터는 호출 수신자에 의해 수정 될 수 있으며 호출자는 나중에 해당 값을 사용하려는 경우 레지스터의 값 자체 (예 : 스택)를 보존해야합니다.
32 비트 stdcall
stdcall은 32 비트 Windows API 호출에 사용됩니다.
매개 변수
매개 변수는 스택에서 전달되고 첫 번째 매개 변수는 스택 맨 위에 가깝습니다. 피 호출자가 반환하기 전에 스택에서이 값을 팝합니다.
반환 값
스칼라 반환 값은 EAX에 저장됩니다.
저장된 레지스터 및 Clobbered 레지스터
EAX, ECX 및 EDX는 수신자가 자유롭게 수정할 수 있으므로 원할 경우 발신자가 저장해야합니다. EBX, ESI, EDI 및 EBP는 수정 된 경우 호출 수신자가 저장해야하며 반환시 원래 값으로 복원해야합니다.
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 비트 값은 littel 엔디안 규칙 2를 준수하면서 두 번의 푸시를 사용하여 스택에 전달되고 먼저 상위 32 비트를 누른 다음 하위 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 에서 반환되어 결국 전체 eax 합니다.
16 비트 정수가 AX 에서 반환되어 결국 전체 eax 합니다.
32 비트 정수가 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 CPU는 긴 모드가 아닐 때만 2 또는 4 바이트를 누를 수 있습니다.
2 낮은 주소에서 낮은 DWORD
32 비트, cdecl - 부동 소수점 처리
매개 변수 (float, double)
부동 소수점 크기는 32 비트이며 자연스럽게 스택에 전달됩니다.
Doubles는 64 비트 크기이며 Little Endian 규칙 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
매개 변수 (long double)
long double은 80 비트 2 와이드이며, 스택에서 TBYTE는 스택을 4 바이트에 정렬되도록 유지하기 위해 두 개의 32 비트 푸시 및 한 개의 16 비트 푸시 (4 + 4 + 2 = 10)로 저장할 수 있습니다. 12 바이트, 따라서 3 개의 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 10 바이트의 TBYTE로 알려져 있습니다.
3 임의의 확장자와 함께 전체 폭 누름을 사용하면 더 높은 WORD가 사용되지 않습니다.
4 어느 쪽이 TBYE 와이드인가, 정수와는 반대로, FP는 항상보다 정밀하게 반환된다는 점에주의하십시오.
64 비트 Windows
매개 변수
처음 4 개 매개 변수는 (순서대로) RCX, RDX, R8 및 R9로 전달됩니다. XMM0 ~ XMM3은 부동 소수점 매개 변수를 전달하는 데 사용됩니다.
추가 매개 변수가 스택에 전달됩니다.
64 비트보다 큰 매개 변수는 주소로 전달됩니다.
유출 공간
함수가 4 개 미만의 매개 변수를 사용하더라도 호출자는 항상 스택에 4 개의 QWORD 크기 매개 변수를위한 공간을 제공합니다. 피 호출자는 어떤 용도로든 자유롭게 사용할 수 있습니다. 다른 호출에 의해 유포 될 경우 매개 변수를 복사하는 것이 일반적입니다.
반환 값
스칼라 반환 유형의 경우 반환 값은 RAX에 저장됩니다. 반환 유형이 64 비트보다 큰 경우 (예 : 구조체의 경우) RAX가 포인터입니다.
저장된 레지스터 및 Clobbered 레지스터
매개 변수 전달 (RCX, RDX, R8, R9 및 XMM0 ~ XMM3), RAX, R10, R11, XMM4 및 XMM5에 사용되는 모든 레지스터는 수신자가 흘릴 수 있습니다. 다른 모든 레지스터는 호출자가 보존해야합니다 (예 : 스택에 있음).
스택 정렬
스택은 16 바이트 정렬로 유지되어야합니다. "call"명령은 8 바이트 리턴 주소를 푸시하기 때문에 모든 리프가 아닌 함수는 16 바이트 정렬을 복원하기 위해 16n + 8 형식의 값으로 스택을 조정합니다.
통화 후 스택을 청소하는 것은 발신자 작업입니다.
출처 : 호출 규칙의 역사, 파트 5 : amd64 Raymond Chen
32 비트, cdecl - 구조체 다루기
심
구조체의 멤버는 일반적으로 자연 경계에 정렬되도록 패딩되어 있습니다.
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 비트 cdecl 을 참조하십시오.
매개 변수 (값 전달)
값으로 전달되면 구조체는 원래 메모리 레이아웃을 고려하여 스택에 완전히 복사됩니다 ( 즉 , 첫 번째 멤버가 낮은 주소에 있음).
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 는 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 레지스터에 반환됩니다. (이 동작은 Linux를 타겟팅하는 GCC에서 관찰되었습니다)
cdecl의 Windows 버전은 System V ABI의 호출 규칙과 다릅니다. "사소한"구조체는 구조체가 아닌 비 배열 유형 (최대 32 비트 크기)의 멤버를 최대 두 명까지 포함 할 수 있습니다. 이 값들은 64 비트 정수처럼 eax 와 edx 에서 반환됩니다. 이 동작은 Win32를 대상으로하는 MSVC 및 Clang에서 관찰되었습니다.