WIPIVERSE

호출 규약

호출 규약(영어: calling convention)은 컴퓨터 과학에서 서브루틴이나 함수가 호출자(caller)로부터 매개변수를 전달받고, 연산 결과를 반환하는 방법에 대한 구현 수준(저수준)의 방식이다. 이는 호출자와 피호출자(callee) 간의 계약으로, 함수 호출 시 매개변수를 어디에(레지스터 또는 스택) 어떻게 배치할지, 반환 값을 어떻게 전달할지, 함수 종료 후 스택을 누가 정리할지 등을 규정한다. 호출 규약은 일반적으로 응용 프로그램 이진 인터페이스(ABI)의 일부로 간주된다.

개요

프로그램이 함수를 호출할 때는 CPU가 해당 함수를 올바르게 처리할 수 있도록 일련의 규칙이 필요하다. 호출 규약이 정의하는 주요 사항은 다음과 같다.

  • 매개변수 전달 위치: 매개변수를 레지스터에 저장할지, 콜 스택(call stack)에 푸시할지, 또는 둘을 혼합하여 사용할지 결정한다.
  • 매개변수 전달 순서: 인자를 왼쪽에서 오른쪽 순서로 전달할지, 오른쪽에서 왼쪽 순서로 전달할지 결정한다.
  • 스택 정리 주체: 함수 호출이 끝난 후 스택에 쌓인 매개변수 공간을 호출자가 정리할지(caller cleanup), 피호출자가 정리할지(callee cleanup) 결정한다.
  • 반환 값 전달 방식: 함수의 결과 값을 레지스터에 저장하여 반환할지, 스택이나 힙을 통해 반환할지 결정한다.
  • 레지스터 보존 규칙: 함수 호출 전후에 값이 유지되어야 하는 레지스터(callee-saved/preserved)와 호출 후 값이 변경될 수 있는 레지스터(caller-saved/volatile)를 구분한다.
  • 가변 인자 함수 처리: 인자 개수가 가변적인 함수(예: printf)에 대해 인수를 어떻게 전달할지 정의한다.

주요 호출 규약의 종류

x86 (32비트) 아키텍처

cdecl (C Declaration)

  • C 언어의 기본 호출 규약이다.
  • 인자는 오른쪽에서 왼쪽 순서로 스택에 푸시된다.
  • 스택 정리는 호출자(caller)가 수행한다.
  • 가변 인자 함수를 지원할 수 있다.
  • 반환 값은 EAX 레지스터에 저장된다.

stdcall (Standard Call)

  • 마이크로소프트 Win32 API의 표준 호출 규약이다.
  • 인자는 오른쪽에서 왼쪽 순서로 스택에 푸시된다.
  • 스택 정리는 피호출자(callee)가 수행한다.
  • 가변 인자 함수를 지원하지 않는다.
  • cdecl에 비해 코드 크기가 작아지는 장점이 있다.

fastcall

  • 처음 몇 개의 인자를 레지스터(ECX, EDX)로 전달하고 나머지는 스택으로 전달한다.
  • 레지스터를 활용하여 호출 속도를 높인 것이 특징이다.
  • 컴파일러 및 플랫폼에 따라 세부 구현이 다를 수 있다.

thiscall

  • C++ 멤버 함수 호출에 사용되는 규약이다.
  • this 포인터를 ECX 레지스터에 전달하고, 나머지 인자는 스택에 전달한다.

x86-64 (64비트) 아키텍처

Microsoft x64 호출 규약 (Windows)

  • 처음 네 개의 정수 인자는 RCX, RDX, R8, R9 레지스터에 전달된다.
  • 부동소수점 인자는 XMM0~XMM3 레지스터에 전달된다.
  • 추가 인자는 오른쪽에서 왼쪽 순서로 스택에 전달된다.
  • 호출자는 스택에 반드시 32바이트의 "shadow space"를 할당해야 한다.
  • 스택 정리는 호출자가 수행한다.

System V AMD64 ABI (Unix/Linux)

  • 처음 여섯 개의 정수 인자는 RDI, RSI, RDX, RCX, R8, R9 레지스터에 전달된다.
  • 부동소수점 인자는 XMM0~XMM7 레지스터에 전달된다.
  • 추가 인자는 스택에 전달된다.
  • 스택 정리는 호출자가 수행한다.
  • 리눅스, macOS, BSD 등 대부분의 Unix 계열 운영체제에서 사용된다.

ARM 아키텍처

ARM (A32, 32비트)

  • r0~r3 레지스터가 인자 전달 및 반환 값에 사용된다.
  • 추가 인자는 스택에 전달된다.
  • r4~r11은 피호출자에 의해 보존되어야 하는 레지스터이다.
  • r13은 스택 포인터(SP), r14는 링크 레지스터(LR), r15는 프로그램 카운터(PC)로 사용된다.

ARM (A64, AArch64, 64비트)

  • x0~x7 레지스터가 인자 전달 및 반환 값에 사용된다.
  • 추가 인자는 스택에 전달된다.
  • x19~x28은 피호출자 저장 레지스터이다.
  • 부동소수점 레지스터 v0~v7이 인자 전달에 사용된다.

RISC-V

  • 가능한 경우 항상 레지스터로 인수를 전달한다.
  • 부동소수점 유무에 따라 두 가지 방식의 호출 규약을 정의한다.

호출 규약의 중요성

올바른 호출 규약의 사용은 전체 프로그램의 정확하고 신뢰할 수 있는 실행을 위해 필수적이다. 서로 다른 호출 규약을 사용하는 함수가 혼용될 경우 스택 불일치, 레지스터 손상, 잘못된 반환 값 등의 오류가 발생할 수 있다. 이러한 이유로 컴파일러는 함수 선언에 호출 규약을 지정하는 키워드(예: __cdecl, __stdcall, __fastcall)를 제공하며, 프로그래머가 명시적으로 선택할 수 있도록 한다.

같이 보기

  • 응용 프로그램 이진 인터페이스(ABI)
  • 응용 프로그램 프로그래밍 인터페이스(API)
  • 콜 스택
  • 스택 프레임
  • 레지스터
둘러보기

더 찾아볼 만한 주제

    전체 문서 보기