x86 호출 규칙

x86 calling conventions

이 글은 x86 아키텍처 마이크로프로세서를 프로그래밍할 때 사용되는 호출 규약을 설명한다.

호출 규약은 호출된 코드의 인터페이스를 설명한다.

  • 원자(scalar) 매개변수 또는 복잡한 매개변수의 개별 부분이 할당되는 순서
  • 매개 변수 전달 방법(스택에 밀어넣기, 레지스터에 배치 또는 둘 다 혼합)
  • 호출된 함수가 호출자를 위해 보존해야 하는 레지스터(일명: callee-saved 레지스터 또는 비휘발성 레지스터)
  • 함수 호출에 대한 스택 준비 및 복원 작업을 호출자와 호출자 간에 구분하는 방법

이것은 프로그래밍 언어 유형에 크기 및 형식을 할당하는 것과 밀접하게 관련되어 있다.또 다른 밀접하게 연관된 주제는 이름망링으로, 코드의 기호 이름이 링커에 의해 사용되는 기호 이름에 매핑되는 방법을 결정한다.호출 규칙, 유형 표현 및 이름 망링은 모두 ABI(응용프로그램 이진 인터페이스)라고 알려진 것의 일부분이다.

다양한 컴파일러가 이러한 규약을 구현하는 방법에는 미묘한 차이가 있기 때문에, 다른 컴파일러가 컴파일러로 컴파일한 코드의 접점이 어려운 경우가 많다.반면에 API 표준으로 사용되는 규약(stdcall 등)은 매우 균일하게 구현된다.

역사적 배경

마이크로컴퓨터 이전에 기계 제조업체는 일반적으로 몇 가지 프로그래밍 언어대한 운영 체제와 컴파일러를 제공했다.각 플랫폼의 호출 규약은 제조자의 프로그래밍 도구에 의해 정의된 규약이었다.

Commodore Pet과 Apple II 이전의 초기 마이크로컴퓨터는 일반적으로 OS나 컴파일러 없이 제공되었다.IBM PC는 마이크로소프트의 선구자인 윈도용 디스크 운영체제(DOS)와 함께 나왔지만 컴파일러와 함께 나오지는 않았다.IBM PC 호환 기계의 유일한 하드웨어 표준은 인텔 프로세서(8086, 80386)와 IBM이 출하한 리터럴 하드웨어에 의해 정의되었다.하드웨어 확장 및 모든 소프트웨어 표준(BIOS 호출 규약을 위한 비용 절감)이 시장 경쟁에 개방되었다.

다수의 독립 소프트웨어 회사들은 운영 체제, 많은 프로그래밍 언어용 컴파일러, 응용 프로그램을 제공했다.많은 서로 다른 통화 계획들은 종종 상호 배타적인 회사들에 의해 서로 다른 요구사항, 역사적 관행, 그리고 프로그래머 창의성에 기초하여 실행되었다.

IBM과 호환되는 시장 이탈 이후 마이크로소프트 운영 체제와 프로그래밍 툴(규약이 다른)이 앞서고, 볼랜드Novell과 같은 2차 기업과 GCC와 같은 오픈소스 프로젝트는 여전히 자체 표준을 유지했다.벤더와 제품 간의 상호운용성을 위한 조항이 결국 채택되어 실행 가능한 규약을 선택하는 문제가 단순화되었다.[1]

호출자 정리

이러한 규칙에서 호출자는 스택에서 인수를 정리한다.

선언하다

cdecl(C 선언의 약자)은 마이크로소프트의 C 프로그래밍 언어 컴파일러에서 유래한 호출 규약으로, x86 아키텍처에 많은 C 컴파일러가 사용한다.[1]cdecl에서 서브루틴 인수는 스택에 전달된다.정수 값과 메모리 주소는 ST0 x87 레지스터의 부동 소수점 값인 EAX 레지스터에서 반환된다.레지스터 EAX, ECX, EDX는 호출자 저장, 나머지는 캘리 저장.새 함수를 호출할 때 x87 부동소수점 레지스터 ST0 ~ ST7은 비어 있어야 하며(도핑 또는 해제) 기능을 종료할 때 ST1 ~ ST7은 비어 있어야 한다.또한 값을 반환하는 데 사용하지 않을 때는 ST0이 비어 있어야 한다.

C 프로그래밍 언어의 맥락에서 함수 인수는 오른쪽에서 왼쪽 순서로 스택에 푸시된다. 즉, 마지막 인수가 먼저 푸시된다.

다음 C 소스 코드 조각을 고려하십시오.

인트로 고삐를 늦추다(인트로, 인트로, 인트로);  인트로 전화를 건 사람(공허하게 하다) {  돌아오다 고삐를 늦추다(1, 2, 3) + 5; } 

x86에서는 다음과 같은 조립 코드(Intel 구문)를 생성할 수 있다.

호출자:     ; 새로운 통화 틀을 만들다     ; (일부 컴파일러는 대신 '입력' 명령을 생성할 수 있음)     밀다    에브       ; 오래된 통화 프레임 저장     영화를 찍다     에브, esp  ; 새로운 통화 프레임 초기화     ; 통화 인수 밀어넣기, 역방향     ; (일부 컴파일러는 스택 포인터에서 필요한 공간을 뺄 수 있다.     ; 그런 다음 각 주장을 직접 쓰십시오. 아래를 참조하십시오.     ; 'Enter' 명령도 비슷한 것을 할 수 있다.)     ; sub esp, 12 : '입력' 지시는 우리를 위해 이것을 할 수 있다.     ; mov [ebp-4], 3 : 또는 mov [esp+8], 3     ; mov [ebp-8], 2 : 또는 mov [esp+4], 2     ; mov [ebp-12], 1 : 또는 mov [esp], 1     밀다    3     밀다    2     밀다    1     부르다    고삐를 늦추다    ; 서브루틴 'callee'를 호출한다.     덧셈을     esp, 12   ; 프레임에서 호출 인수 제거     덧셈을     이삭스, 5    ; 서브루틴 결과 수정                       ; (Eax는 우리 칼리어의 반환 값이다.                       ; 로컬 변수로 이동하지 않아도 됨)     ; 기존 통화 프레임을 복원     ; (일부 컴파일러는 대신 'leave' 명령을 생성할 수 있음)     영화를 찍다     esp, 에브  ; 대부분의 호출 규약은 ebp가 callee-callee가 되도록 명령한다.                       ; 즉, 그것은 탈주범에게 전화한 후에 보존된다.                       ; 그러므로 그것은 여전히 우리의 스택 프레임의 시작을 가리킨다.                       ; 우리는 확실히 해야한다.                       그러나 callee는 ebp를 수정하거나 복구하지는 않는다.                       그래서 우리는 확실히 해야한다.                       ; 이렇게 하는 호출 규칙을 사용한다.     펑펑 터지다     에브       ; 기존 통화 프레임을 복원     되받아치다               반환하다;반환하다;리턴 

함수 호출이 돌아온 후 호출자가 스택을 청소한다.

cdec 호출 규약은 일반적으로 x86 C 컴파일러의 기본 호출 규약이지만, 많은 컴파일러가 사용하는 호출 규약을 자동으로 변경할 수 있는 옵션을 제공한다.cdecl할 함수를 수동으로 정의하기 위해 일부에서는 다음 구문을 지원한다.

return_type __cdecl func_name(); 

변형

cdecl의 해석에는 약간의 차이가 있다.결과적으로, 서로 다른 운영 체제 플랫폼 및/또는 다른 컴파일러를 위해 컴파일된 x86 프로그램은 둘 다 "cdecl" 규칙을 사용하고 기본 환경에 호출하지 않더라도 호환되지 않을 수 있다.

값을 반환하는 방법과 관련하여, 일부 컴파일러는 레지스터 쌍 EAX에서 레지스터 길이가 2개 이하인 단순한 데이터 구조를 반환한다.EDX, 그리고 예외 취급자에 의한 특별한 처리가 필요한 더 큰 구조물과 클래스 객체(예: 정의된 생성자, 소멸자 또는 할당)는 기억으로 반환된다."In memory"를 전달하기 위해 호출자는 메모리를 할당하고 숨겨진 첫 번째 매개 변수로 포인터를 전달한다. callee는 메모리를 채우고 포인터를 반환하여 돌아올 때 숨겨진 포인터를 튀긴다.[2]

리눅스에서는 GCC가 규약을 호출하기 위한 사실상의 표준을 정한다.GCC 버전 4.5이므로 함수를 호출할 때 스택을 16바이트 경계로 정렬해야 한다(이전 버전은 4바이트 정렬만 필요함).[1][3]

cdecl 버전은 i386 시스템에 대한 시스템 V ABI에 설명되어 있다.[4]

syscall

이는 주장이 우에서 좌로 밀린다는 점에서 cdecl과 비슷하다.EAX, ECX, EDX는 보존되지 않는다.더블 워드로 된 파라미터 리스트의 크기는 AL로 전달된다.

Syscall은 32비트 OS/2 API의 표준 호출 규약이다.

옵트링크

논쟁은 오른쪽에서 왼쪽으로 밀린다.첫 번째(맨 왼쪽) 인수는 EAX, EDX, ECX에서 통과되며, ST0에서 ST3까지 최대 4개의 부동 소수 인수는 스택의 인수 목록에 저장되어 있다.결과는 EAX 또는 ST0으로 반환된다.레지스터 EBP, EBX, ESI, EDI가 보존되어 있다.

Optlink는 IBM VisualAge 컴파일러에 의해 사용된다.

칼리 청소

이러한 규칙에서, 칼리지는 스택에서 인수를 정리한다.이러한 규약을 활용하는 기능은 복귀 후 스택을 풀기 때문에 ASM 코드에서 쉽게 인식할 수 있다.x86년ret 명령은 발신자에게 반환한 후 해제할 스택 바이트 수를 지정하는 선택적 16비트 매개변수를 허용한다.이러한 코드는 다음과 같다.

되받아치다 12 

fastcall 또는 register라는 이름의 규약은 표준화되지 않았으며 컴파일러 공급업체에 따라 다르게 구현되었다.[1]일반적으로 호출에 필요한 메모리 액세스 수를 줄이고 따라서 일반적으로 더 빠르게 만드는 레지스터에서 하나 이상의 인수를 호출 규칙에 기반한 레지스터에 통과시킨다.

파스칼

볼랜드 파스칼 프로그래밍 언어의 호출 규약을 바탕으로 매개변수를 좌우 순서(cdel의 반대자)로 스택에 밀어넣고, 이를 스택에서 제거하는 것은 캘리(callee)가 담당한다.

결과를 반환하는 방법은 다음과 같다.

  • 순서형 값은 AL(8비트 값), AX(16비트 값), EAX(32비트 값) 또는 DX:AX(16비트 시스템의 32비트 값)로 반환된다.
  • 실제 값은 DX:BX로 반환된다.AX
  • 부동소수점(8087) 값은 ST0으로 반환된다.
  • 포인터들은 32비트 시스템의 EAX와 16비트 시스템의 AX에서 반환된다.
  • 문자열은 @Result 기호가 가리키는 임시 위치에서 반환된다.

이 호출 규약은 OS/2 1.x, 마이크로소프트 윈도 3.x, 볼랜드 델파이 버전 1.x 등 16비트 API에서 흔히 볼 수 있었다. 현대판 Windows API에서는 여전히 파스칼 규약처럼 callelee가 스택을 복원하고 있는 stdcall을 사용하지만, 매개변수는 현재 오른쪽에서 왼쪽으로 밀려 있다.

stdcall

stdcall[5] 호출 규약은 _cdecl 호출 규약에서와 같이 callee가 스택을 정리하는 책임을 지는 Pascal 호출 규약에 대한 변형이지만 매개변수는 _cdecl 호출 규약에서와 같이 오른쪽에서 왼쪽으로 스택으로 밀려난다.레지스터 EAX, ECX 및 EDX는 기능 내에서 사용하도록 지정된다.반환 값은 EAX 레지스터에 저장된다.

stdcall은 Microsoft Win32 APIOpen Watcom C++에 대한 표준 호출 규칙이다.

마이크로소프트 패스트콜

Microsoft __fastcall 규약(일명 _msfastcall)은 ECX와 EDX에 맞는 처음 두 개의 주장(평가된 왼쪽에서 오른쪽으로)을 통과시킨다.[6] 나머지 주장은 오른쪽에서 왼쪽으로 스택으로 밀려난다.컴파일러가 IA64 또는 AMD64를 컴파일할 때 __fastcall 키워드를 무시하고 대신 하나의 64비트 호출 규칙을 사용한다.

이것은 매우 일반적인 통화 규약이기 때문에 GCC, 클랑, ICC와 같은 다른 컴파일러들도 패스트콜을 지원한다.[7]

다음 C 조각을 고려하십시오.

_____((속달로 부르다)) 공허하게 하다 인쇄 번호(인트로 num1, 인트로 num2, 인트로 숫자3){  활자화하다("보낸 번호는: %d %d %d %d", num1, num2, 숫자3); }  인트로 본래의(){  인쇄 번호(1, 2, 3);  돌아오다 0; } 

x86 주함수의 분해는 (Intel 구문에서는):

주:  ; 스택 설정  밀다 에브  영화를 찍다 에브, esp  밀다 3 ; 즉시 3(세 번째 인수가 스택에 푸시됨)  영화를 찍다 edx, 0x2 ; 즉시 2(두 번째 인수)가 EDx 레지스터에 복사됨.  영화를 찍다 ecx, 0x1 ; 즉시 1(첫 번째 인수)을 ecx 레지스터에 복사한다.  부르다 인쇄 번호  영화를 찍다 이삭스, 0 ; return 0  떠나다  되감다 

처음 두 개의 인수는 왼쪽에서 오른쪽 순서로 통과되고, 세 번째 인수는 스택으로 밀린다.Callee에 의해 스택 정리가 수행되므로 스택 정리는 없다.Callee 함수의 분해는 다음과 같다.

인쇄 숫자:  ; 스택 설정  밀다 에브  영화를 찍다 에브, esp  후보선수 esp, 0x08  영화를 찍다 [ebp-0x04], ecx    ; x86에서 ecx = 첫 번째 인수.  영화를 찍다 [ebp-0x08], edx    ; arg2  밀다 [에브+0x08]        ; arg3를 눌러 쌓는다.  밀다 [ebp-0x08]        ; arg2가 밀린다.  밀다 [ebp-0x04]        ; arg1이 푸시됨  밀다 0x8065d67         ; "보낸 번호는 %d %d %d"  부르다 활자화하다  ; 스택 정리  덧셈을 esp, 0x10  끄떡없다  떠나다  되감다 0x04 

두 개의 인수가 레지스터를 통과하고 스택에서 하나의 파라미터만 푸시되었기 때문에, x86 시스템에서는 int가 4바이트 크기인 것처럼, retn 명령으로 푸시된 값을 지우고 있다.

마이크로소프트 벡터콜

비주얼 스튜디오 2013에서 마이크로소프트는 게임, 그래픽, 비디오/오디오, 코덱 개발자들의 효율성 우려에 대응하여 _vectorcall call convention을 도입했다.이 계획은 더 큰 벡터 유형(플로트, 더블, _m128, _m256)을 스택과는 반대로 레지스터에 전달할 수 있도록 한다.[8]

IA-32와 x64 코드의 경우 _vectorcall은 각각 __fastcall 및 원래의 x64 호출 규약과 유사하지만 SIMD 레지스터를 사용한 통과 벡터 인수를 지원하도록 확장한다.IA-32에서 정수 값은 평상시와 같이 통과하며, 첫 번째 6개의 SIMD(XMM/YMM0-5) 레지스터는 이들 사이에 나타나는 int 인수와 같이 실제 위치에 관계없이 왼쪽에서 오른쪽으로 순차적으로 최대 6개의 부동 소수점, 벡터 또는 HVA 값을 보유한다.그러나 x64에서는 원래의 x64 규약의 규칙이 여전히 적용되므로 XMM/YMM0-5는 첫 번째에서 여섯 번째까지가 될 때 부동 소수점, 벡터 또는 HVA 인수만 보유한다.[9]

__vectorcall은 동일한 6개의 레지스터를 사용하여 최대 4개의 동일한 벡터 유형만으로 구성된 복합형(구조물)인 통과 균일 벡터 집합(HVA) 값에 대한 지원을 추가한다.일단 벡터형 인수에 레지스터를 할당하면 사용하지 않는 레지스터는 왼쪽에서 오른쪽으로 HVA 인수에 할당된다.위치 지정 규칙은 여전히 적용된다.결과 벡터 유형과 HVA 값은 처음 4개의 XMM/YMM 레지스터를 사용하여 반환된다.[9]

clang 컴파일러와 Intel C++ 컴파일러도 벡터콜을 구현한다.[10]인텔 C++ 컴파일러는 __regcall이라는 비슷한 초기 규칙을 가지고 있었다. 또한 clang에 의해 지원된다.[11]

볼랜드 레지스터

왼쪽에서 오른쪽으로 인수를 평가하여 EAX, EDX, ECX를 통해 세 개의 인수를 전달한다.나머지 인수는 스택으로 밀리고, 또한 왼쪽에서 오른쪽으로 밀린다.[12]그것은 델파이 32비트 컴파일러의 디폴트 호출 규약으로, 여기서 레지스터로 알려져 있다.이 호출 규약은 엠바르카데로의 C++Builder에서도 사용되는데, 여기서 __fastcall이라고 한다.[13]이 컴파일러에서는 마이크로소프트의 fastcall_msfastcall로 사용할 수 있다.[14]

GCC와 클랑(Clang)은 유사한 통화 규약을 사용하여 사용할 수 있다.__stdcall…과 함께regparm함수 속성 또는-mregparm=3스위치. (스택 순서가 반전됨)또한 다음과 같은 방법으로 발신자 정리 변종도 제작할 수 있다.cdecl또는 SSE 레지스터를 사용하기 위해 이것을 확장한다.[15]acdecl-based 버전은 버전 2.6.20(2007년 2월 발표) 이후 i386의 Linux 커널에서 사용된다.[16]

왓컴 레지스터

Watcom__fastcall 키워드를 null로 가칭하는 것 외에는 지원하지 않는다.레지스터 호출 규약은 명령줄 스위치로 선택할 수 있다. (단, IDA는 통일성을 위해 __fastcall을 어쨌든 사용한다.)

EAX, EDX, EBX, ECX 순서로 최대 4개의 레지스터가 인수에 할당된다.인수는 왼쪽에서 오른쪽으로 레지스터에 할당된다.인수를 레지스터에 할당할 수 없는 경우(인수가 너무 크다고 말함) 인수와 이후의 모든 인수는 스택에 할당된다.스택에 할당된 인수는 오른쪽에서 왼쪽으로 푸시된다.이름들은 접미사가 붙은 밑줄을 추가함으로써 엉망이 된다.

변수 함수는 통화 규칙을 기반으로 Watcom 스택으로 되돌아간다.

왓콤 C/C++ 컴파일러도 사용자가 자신의 호출 규칙을 지정할 수 있는 #프라그마 보조 명령을 사용한다[17].매뉴얼대로 "이 방법이 필요할 것 같은 사용자는 극소수지만 필요하다면 생명의 은인이 될 수 있다"고 했다.

최고 속도 / 클라리온 / JPI

처음 4개의 정수 매개변수는 레지스터 eax, ebx, ecx 및 edx로 전달된다.부동 소수점 매개변수는 부동 소수점 스택에 전달된다 – 레지스터 st0, st1, st2, st3, st4, st5 및 st6.구조물 매개변수는 항상 스택에 전달된다.레지스터가 소진된 후 스택에 추가 파라미터가 전달된다.정수 값은 eax, 포인터(Edx), 부동 소수점 유형은 st0으로 반환된다.

세이프콜

Microsoft WindowsDelphiFree Pascal에서, 세이프콜 호출 규약은 COM(Component Object Model) 오류 처리를 캡슐화하므로, 발신자에게 예외가 유출되지 않고, COM/OLE에서 요구하는 대로 HRESULT 반환 값으로 보고된다.델파이 코드에서 세이프콜 기능을 호출하면 델파이도 자동으로 반송된 HRESULT를 확인하고 필요한 경우 예외를 제기한다.

세이프콜 호출 규약은 EAX에서 HResult(FS:[0] 대신)로서 발신자에게 예외가 다시 전달되는 것을 제외하고, 기능 결과는 최종 "아웃" 매개변수인 것처럼 스택에서 참조로 전달된다는 점을 제외하면 stdcall 호출 규약과 동일하다.델파이에서 델파이 함수를 호출할 때 이 호출 컨벤션은 다른 호출 컨벤션과 마찬가지로 나타나는데, EAX에서는 예외가 다시 전달되지만 호출자에 의해 적절한 예외로 자동 변환되기 때문이다.다른 언어로 작성된 COM 객체를 사용할 경우 HResults가 예외로 자동 제기되며 Get 기능에 대한 결과는 파라미터가 아닌 결과에 있다.세이프콜로 델파이에서 COM 객체를 만들 때, 예외는 정상으로 올릴 수 있지만 다른 언어에서는 HResults로 보일 것이기 때문에 HResults에 대해 걱정할 필요가 없다.

기능을 하다 함수_이름(a: DWORD): DWORD; 세이프콜; 

결과를 반환하고 일반 델파이 함수처럼 예외를 발생시키지만 값과 예외를 다음과 같이 전달한다.

기능을 하다 함수_이름(a: DWORD; 밖으로 결과: DWORD): HRESult; stdcall; 

호출자 또는 캘리 정리 중 하나

요번 부름

이 호출 규약은 C++ 비정적 회원 기능을 호출하는 데 사용된다.컴파일러에 따라, 그리고 함수가 변수 수의 인수를 사용하는지 여부에 따라 이 호출의 두 가지 기본 버전이 사용된다.

GCC 컴파일러의 경우 이 호출cdecl과 거의 동일하다.호출자가 스택을 정리하면 파라미터가 오른쪽에서 왼쪽으로 전달된다.차이점은 기능 프로토타입의 첫 번째 파라미터인 것처럼 마지막으로 스택에 밀어넣는 이 포인터를 추가하는 것이다.

Microsoft Visual C++ 컴파일러에서 포인터는 ECX에서 통과되며 이 컴파일러와 Windows API 함수에 대해 C에서 사용되는 stdcall 규칙을 미러링하여 스택을 정리하는 것이 바로 캘리입니다.함수가 변수 인수 수를 사용할 때 스택을 정리하는 것은 호출자(cf. cdel)이다.

호출 규칙은 Microsoft Visual C++ 2005 이상에서만 명시적으로 지정할 수 있다.다른 컴파일러에서 이 호출은 키워드가 아니다.(단, IDA와 같은 분해자는 반드시 명시해야 한다.그래서 IDA는 이를 위해 키워드 __the call을 사용한다.)

보존등기

호출 규약의 또 다른 부분은 서브루틴 호출 후에 등록부가 값을 유지할 수 있도록 보장되는 것이다.

호출자 저장(휘발성) 레지스터

대다수의 컴파일러가 준수하는 Intel ABI에 따르면 EAX, EDX 및 ECX는 절차나 기능 내에서 무료로 사용할 수 있으며 보존할[citation needed] 필요가 없다.

이름이 암시하듯이, 이러한 범용 레지스터는 보통 어떤 서브루틴으로도 덮어쓸 수 있는 임시(휘발성) 정보를 보유하고 있다.

따라서 서브루틴 호출 후 값을 복원하려면 각 레지스터를 스택에 밀어 넣는 것이 발신자의 책임이다.

캘리 저장(비휘발성) 레지스터

다른 레지스터는 통화 전반에 걸쳐 보존해야 하는 장수 값(비휘발성)을 보유하는 데 사용된다.

다시 말해, 발신자가 프로시저 통화를 할 때, 그러한 등록부는 캘리어가 돌아온 후 동일한 값을 가질 것으로 기대할 수 있다.

따라서, 발신자에게 돌아가기 전에 저장(처음에는 밀어넣기)과 복원(따라서 팝업을)을 하는 것이 칼리어의 책임이다.앞의 경우와 같이, 이 연습은 캘리어가 변경되는 레지스터에서만 수행되어야 한다.

x86-64 호출 규칙

x86-64 호출 규칙은 레지스터에서 더 많은 인수를 전달하기 위해 추가 레지스터 공간을 이용한다.또한 양립할 수 없는 통화 규약도 줄어들었다.공통적으로 두 가지가 있다.

Microsoft x64 호출 규칙

Microsoft x64 호출 규칙은[18][19] WindowsUEFI 사전 부트(x86-64의 롱 모드용)에서 준수된다.처음 네 개의 주장은 등록부에 올려져 있다.즉, 정수, 구조체 또는 포인터 인수의 경우 RCX, R8, R9, 부동 소수점 인수의 경우 XMM0, XM1, XM2, XM3를 의미한다.추가 인수는 스택(오른쪽에서 왼쪽으로)에 푸시된다.정수 반환 값(x86과 유사)은 64비트 이하인 경우 RAX로 반환된다.부동 소수점 반환 값은 XMM0으로 반환된다. 64비트 미만의 파라미터는 0으로 확장되지 않으며, 하이 비트는 0으로 설정되지 않는다.

정수와 일치하는 크기의 구조체와 유니언은 마치 정수인 것처럼 통과하여 반환한다.그렇지 않으면 인수로 사용할 때 포인터로 대체된다.큰 규모의 구조체 리턴이 필요할 때, 호출자가 제공한 공간에 대한 또 다른 포인터가 첫 번째 주장으로 제시되어 다른 모든 주장을 한 장소씩 오른쪽으로 이동시킨다.[20]

Windows 컨텍스트에서 x64 아키텍처에 대해 컴파일할 때(마이크로소프트 또는 비 Microsoft 도구 사용), stdcall, thiscall, cdecl 및 fastcall은 모두 이 규칙을 사용하도록 해결하십시오.

마이크로소프트 x64 통화 규약에서는 함수를 호출하기 직전(실제 사용된 파라미터 수에 관계없이) 스택에 32바이트의 "섀도 스페이스"를 할당하고 통화 후 스택을 터뜨리는 것이 호출자의 책임이다.그림자 공간은 RCX, RDX, R8, R9를 흘리기 위해 사용되지만,[21] 4개 미만의 매개변수를 가진 기능이라도 모든 기능에 사용할 수 있도록 해야 한다.

레지스터 RAX, RCX, RDX, R8, R9, R10, R11은 휘발성으로 간주된다(콜러 저장).[22]

레지스터 RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15는 비휘발성(칼리-저장)으로 간주된다.[22]

예를 들어, 5개의 정수 인수를 사용하는 함수는 레지스터에서 1~4위를 차지하며, 5번째는 그림자 공간 위에서 밀리게 된다.그래서 호출된 함수를 입력하면 스택은 반환 주소(오름차순)로 구성되고, 그 다음에 그림자 공간(32바이트)이 그 다음에 다섯 번째 매개변수가 된다.

In x86-64, Visual Studio 2008 stores floating point numbers in XMM6 and XMM7 (as well as XMM8 through XMM15); consequently, for x86-64, user-written assembly language routines must preserve XMM6 and XMM7 (as compared to x86 wherein user-written assembly language routines did not need to preserve XMM6 and XMM7).즉, x86에서 x86-64로 포팅될 때 함수 전후에 XMM6 및 XMM7을 저장/복원하도록 사용자가 작성한 어셈블리 언어 루틴을 업데이트해야 한다.

마이크로소프트는 비주얼 스튜디오 2013을 시작으로 x64 규약을 확장하는 _vectorcall call 규약을 도입했다.

시스템 V AMD64 ABI

시스템 V AMD64 ABI의 호출 규약은 Solaris, Linux, FreeBSD, macOS에서 따르며,[23] 유닉스 및 유닉스 유사 운영 체제 중에서 사실상의 표준이다.x86-64의 OpenVMS 호출 표준은 이전 버전과의 호환성을 위해 필요한 일부 확장이 포함된 시스템 V ABI를 기반으로 한다.[24]The first six integer or pointer arguments are passed in registers RDI, RSI, RDX, RCX, R8, R9 (R10 is used as a static chain pointer in case of nested functions[25]: 21 ), while XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6 and XMM7 are used for the first floating point arguments.[25]: 22 마이크로소프트 x64 호출 규약에서와 같이, 추가 인수는 스택에 전달된다.[25]: 22 최대 64비트 크기의 정수 반환 값은 RAX에 저장되며 최대 128비트 값은 RAX와 RDX에 저장된다. 부동 소수점 반환 값은 XMM0과 XMM1에 유사하게 저장된다.[25]: 25 더 넓은 YMM과 ZMM 레지스터는 XMM 대신 더 넓은 값을 통과하고 반환하는 데 사용된다.[25]: 26, 55

캘리어가 레지스터 RBX, RSP, RBP, R12–R15를 사용하고자 하는 경우, 콜리어는 발신자에게 제어권을 반환하기 전에 원래 값을 복원해야 한다.다른 모든 레지스터는 호출자가 값을 보존하려면 저장해야 한다.[25]: 16

리프 노드 기능(다른 기능을 호출하지 않는 기능)의 경우 128바이트 공간이 함수의 스택 포인터 바로 아래에 저장된다. 공간을 레드존이라고 부른다.이 구역은 신호 또는 인터럽트 핸들러에 의해 차단되지 않는다.따라서 컴파일러는 이 구역을 이용하여 지역 변수를 저장할 수 있다.컴파일러는 이 구역을 활용하여 기능 시작 시 일부 지침(RSP, RBP의 조정)을 생략할 수 있다.그러나 다른 기능들이 이 구역에 침입할 수 있다.따라서 이 구역은 리프 노드 기능에만 사용해야 한다. gcc그리고clang을 제공하다-mno-red-zone빨간색 영역 최적화를 사용하지 않도록 플래그 지정.

캘리어가 가변 함수인 경우 벡터 레지스터에서 함수에 전달되는 부동 소수점 인수의 수는 AL 레지스터에서 호출자가 제공해야 한다.[25]: 55

Microsoft 호출 규칙과 달리 섀도 공간은 제공되지 않으며, 기능 입력 시 반환 주소는 스택의 일곱 번째 정수 인수에 인접한다.

x86 통화 규칙 목록

이것은 x86 통화 규약 목록이다.[1]이는 주로 C/C++ 컴파일러(특히 아래 64비트 부분)를 대상으로 한 규약이며, 따라서 대부분 특수한 경우다.다른 언어는 구현 시 다른 형식과 규칙을 사용할 수 있다.

아르키텍처 이름 운영 체제, 컴파일러 매개변수 스택 정리 메모들
레지스터 적층순서
8086 선언하다 RTL(C) 호출자
파스칼 LTR(Pascal) 칼리
패스트콜(비회원) 마이크로소프트 AX, DX, BX LTR(Pascal) 칼리 BX에서 포인터를 반환하십시오.
fastcall (멤버 함수) 마이크로소프트 AX, DX LTR(Pascal) 칼리 this낮은 주소를 써서AX에서 포인터를 반환하십시오.
속달로 부르다 터보 C[26] AX, DX, BX LTR(Pascal) 칼리 this낮은 주소를 써서높은 주소 스택에 대한 포인터 반환
왓콤 AX, DX, BX, CX RTL(C) 칼리 SI에서 포인터를 반환하십시오.
IA-32 선언하다 Unix 유사(GCC) RTL(C) 호출자 구조체/클래스를 반환할 때 호출 코드는 공간을 할당하고 스택의 숨겨진 매개 변수를 통해 이 공간에 포인터를 전달한다.호출된 함수는 이 주소에 반환 값을 기록한다.

버그로 인해 16바이트 경계선에 정렬된 스택.

선언하다 마이크로소프트 RTL(C) 호출자 구조체/클래스를 반환할 때,
  • 일반 이전 데이터(POD) 반환 값 32비트 이하가 EAX 레지스터에 있음
  • 크기가 33–64비트인 POD 반환 값은 EAX를 통해 반환된다.EDX 레지스터.
  • 비-POD 반환 값 또는 64비트보다 큰 값은 호출 코드가 공간을 할당하고 스택의 숨겨진 매개 변수를 통해 이 공간에 포인터를 전달한다.호출된 함수는 이 주소에 반환 값을 기록한다.

4바이트 경계선에 정렬하여 쌓으십시오.

stdcall 마이크로소프트 RTL(C) 칼리 GCC의 지원도 받는다.
속달로 부르다 마이크로소프트 ECX, EDX RTL(C) 칼리 멤버 기능이 아닌 경우 스택에 포인터 반환GCC의 지원도 받는다.
등록하다 델피와 프리 파스칼 EAX, EDX, ECX LTR(Pascal) 칼리
요번 부름 Windows(Microsoft Visual C++) ECX RTL(C) 칼리 멤버 함수의 기본값.
벡터콜 Windows(Microsoft Visual C++) ECX, EDX, [XY]MM0–5 RTL(C) 칼리 빠른 호출에서 확장됨.ICC와 캉의 지원도 받는다.[9]
와트콤 컴파일러 EAX, EDX, EBX, ECX RTL(C) 칼리 ESI에서 포인터를 반환하십시오.
x86-64 Microsoft x64 호출 규칙[18] Windows(Microsoft Visual C++, GCC, Intel C++ Compiler, Delphi), UEFI RCX/XMM0, RDX/XM1, R8/XM2, R9/XM3 RTL(C) 호출자 16바이트 정렬.스택의 32바이트 그림자 공간.지정된 8개의 레지스터는 파라미터 1부터 4까지에만 사용할 수 있다.C++ 클래스의 경우 숨김this매개변수는 첫 번째 매개변수로, RCX로 전달된다.[27]
벡터콜 Windows(Microsoft Visual C++, Cang, ICC) RCX/[XY]MM0, RDX/[XY]MM1, R8/[XY]MM2, R9/[XY]M3 + [XY]MM4-5 RTL(C) 호출자 MS x64에서 확장됨.[9]
시스템 V AMD64 ABI[25] Solaris, Linux, BSD, macOS, OpenVMS(GCC, Intel C++ Compiler, Clang, Delphi) RDI, RSI, RDX, RCX, R8, R9, [XYZ]MM0-7 RTL(C) 호출자 16바이트 경계선에 정렬된 스택 아래 빨간색 영역 128바이트커널 인터페이스는 RDI, RSI, RDX, R10, R8 및 R9를 사용한다.C++에서는this첫 번째 매개 변수다.

참조

각주

  1. ^ a b c d e Agner Fog (2010-02-16). Calling conventions for different C++ compilers and operating systems (PDF).
  2. ^ de Boyne Pollard, Jonathan (2010). "The gen on function calling conventions". Frequently Given Answers.
  3. ^ "GCC Bugzilla – Bug 40838 - gcc shouldn't assume that the stack is aligned". 2009.
  4. ^ "SYSTEM V APPLICATION BINARY INTERFACE Intel 386 Architecture Processor Supplement Fourth Edition" (PDF).
  5. ^ "__stdcall (C++)". MSDN. Microsoft. Archived from the original on 2008-04-10. Retrieved 2019-02-13.
  6. ^ "__fastcall". MSDN. Retrieved 2013-09-26.
  7. ^ Ohse, Uwe. "gcc attribute overview: function fastcall". ohse.de. Retrieved 2010-09-27.
  8. ^ "Introducing 'Vector Calling Convention'". MSDN. 11 July 2013. Retrieved 2014-12-31.
  9. ^ a b c d "__vectorcall". MSDN. Retrieved 2014-12-31.
  10. ^ "Attributes in Clang: Calling Conventions". Clang Documentation. Retrieved 8 October 2019.
  11. ^ "_vectorcall and __regcall demystified". software.intel.com. 7 June 2017.
  12. ^ "Program Control: Register Convention". docwiki.embarcadero.com. 2010-06-01. Retrieved 2010-09-27.
  13. ^ "_fastcall, __fastcall". docwiki.embarcadero.com.
  14. ^ "__msfastcall". docwiki.embarcadero.com.
  15. ^ "x86 Function Attributes". Using the GNU Compiler Collection (GCC).
  16. ^ "i386: always enable regparm".
  17. ^ "Calling_Conventions: Specifying_Calling_Conventions_the_Watcom_Way". openwatcom.org. 2010-04-27. Retrieved 2018-08-31.
  18. ^ a b "x64 Software Conventions: Calling Conventions". msdn.microsoft.com. 2010. Retrieved 2010-09-27.
  19. ^ "x64 Architecture". msdn.microsoft.com.
  20. ^ "x64 Calling Convention: Return Values". docs.microsoft.com. Retrieved 2020-01-17.
  21. ^ "x64 Software Conventions - Stack Allocation". Microsoft. Retrieved 2010-03-31.
  22. ^ a b "Caller/Callee Saved Registers". Microsoft Docs. Microsoft.
  23. ^ "x86-64 Code Model". Mac Developer Library. Apple Inc. Archived from the original on 2016-03-10. Retrieved 2016-04-06. The x86-64 environment in OS X has only one code model for user-space code. It is most similar to the small PIC model defined by the x86-64 System V ABI.
  24. ^ "VSI OpenVMS Calling Standard" (PDF). vmssoftware.com. May 2020. Retrieved 2020-12-21.
  25. ^ a b c d e f g h Michael Matz; Jan Hubička; Andreas Jaeger; et al., eds. (2018-01-28). "System V Application Binary Interface: AMD64 Architecture Processor Supplement (With LP64 and ILP32 Programming Models) Version 1.0" (PDF). 1.0.
  26. ^ Borland C/C++ version 3.1 User Guide (PDF). Borland. 1992. pp. 158, 189–191.
  27. ^ "Register Usage". Microsoft Docs. Microsoft. Retrieved 15 September 2017.

기타 출처

추가 읽기