x64 스택 사용
RSP의 현재 주소를 초과하는 모든 메모리는 휘발성으로 간주됩니다. OS 또는 디버거는 사용자 디버그 세션 또는 인터럽트 처리기 중에 이 메모리를 덮어쓸 수 있습니다. 따라서 스택 프레임에 대한 값을 읽거나 쓰기 전에 항상 RSP를 설정해야 합니다.
이 섹션에서는 로컬 변수에 대한 스택 공간 할당 및 alloca 내장 함수에 대해 설명합니다.
스택 할당
함수의 프롤로그는 로컬 변수, 저장된 레지스터, 스택 매개 변수 및 레지스터 매개 변수에 대한 스택 공간을 할당합니다.
매개 변수 영역은 항상 스택의 맨 아래에 있습니다(alloca
가 사용되는 경우에도 마찬가지). 그러므로 모든 함수를 호출하는 동안 항상 반환 주소에 인접하게 됩니다. 4개 이상의 항목을 포함하지만 호출될 수 있는 함수에 필요한 모든 매개 변수를 저장할 수 있는 공간이 항상 충분합니다. 매개 변수 자체가 스택으로 열리지 않는 경우에도 항상 레지스터 매개 변수에 공간이 할당됩니다. 호출 수신자는 모든 매개 변수에 대한 공간이 할당되도록 보장을 받습니다. 호출된 함수가 인수 목록(va_list) 또는 개별 인수의 주소를 사용해야 하는 경우 인접한 영역을 사용할 수 있도록 레지스터 인수에 홈 주소가 필요합니다. 또한 이 영역은 썽크 실행 중 또는 디버깅 옵션으로 레지스터 인수를 저장하는 편리한 위치를 제공합니다. 예를 들어, 프롤로그 코드의 홈 주소에 저장되면 디버깅 중에 인수를 쉽게 찾을 수 있습니다. 호출된 함수에 4개 미만의 매개 변수가 있는 경우에도 이러한 4개 스택 위치는 호출된 함수가 유효하게 소유하며, 매개 변수 레지스터 값을 저장하는 것 외에 다른 용도로 호출된 함수가 사용할 수 있습니다. 따라서 호출자는 함수 호출에서 이 스택 영역에 정보를 저장하지 않을 수 있습니다.
함수에서 공간을 동적으로 할당하는 경우(alloca
), 비휘발성 레지스터를 프레임 포인터로 사용하여 스택의 고정 부분을 표시하고 프롤로그에서 레지스터를 저장하고 초기화해야 합니다. alloca
를 사용하는 경우 동일한 호출 수신자에 대한 동일한 호출자의 호출에 레지스터 매개 변수에 대한 다른 홈 주소가 있을 수 있습니다.
스택은 항상 16바이트 맞춤 상태로 유지되지만, 반환 주소가 푸시된 후와 같이 프롤로그 내부에서 지정된 경우와 함수 형식에 특정 프레임 함수 클래스용으로 표시된 경우는 예외입니다.
다음은 함수 A가 리프가 아닌 함수 B를 호출하는 스택 레이아웃의 예입니다. 함수 A의 프롤로그는 스택의 맨 아래에 있는 B에 필요한 모든 레지스터 및 스택 매개 변수에 대한 공간을 이미 할당했습니다. 호출은 반환 주소를 푸시하고 B의 프롤로그는 로컬 변수 및 비휘발성 레지스터에 대한 공간과 함수를 호출하는 데 필요한 공간을 할당합니다. B가 alloca
를 사용하는 경우 로컬 변수/비휘발성 레지스터 저장 영역과 매개 변수 스택 영역 사이에 공간이 할당됩니다.
함수 B가 다른 함수를 호출하면 반환 주소가 RCX의 홈 주소 바로 아래에 푸시됩니다.
동적 매개 변수 스택 영역 생성
프레임 포인터를 사용하는 경우 매개 변수 스택 영역을 동적으로 만들기 위한 옵션이 있습니다. 현재 x64 컴파일러에서는 이 작업이 수행되지 않습니다.
함수 유형
기본적으로 두 가지 형식의 함수가 있습니다. 스택 프레임이 필요한 함수를 프레임 함수라고 합니다. 스택 프레임이 필요하지 않은 함수를 리프 함수라고 합니다.
프레임 함수는 스택 공간을 할당하거나, 다른 함수를 호출하거나, 비휘발성 레지스터를 저장하거나, 예외 처리를 사용하는 함수입니다. 또한 함수 테이블 항목이 필요합니다. 프레임 함수에는 프롤로그 및 에필로그가 필요합니다. 프레임 함수는 스택 공간을 동적으로 할당하고 프레임 포인터를 사용할 수 있습니다. 프레임 함수는 이 호출 표준의 전체 기능을 사용할 수 있습니다.
프레임 함수가 다른 함수를 호출하지 않는 경우에는 스택을 맞출 필요가 없습니다(스택 할당 섹션 참조).
리프 함수는 함수 테이블 항목이 필요하지 않은 함수입니다. RSP를 비롯한 모든 비휘발성 레지스터를 변경할 수 없습니다. 이는 함수를 호출하거나 스택 공간을 할당할 수 없음을 의미합니다. 함수 실행되는 동안 스택을 맞추지 않은 상태로 둘 수 있습니다.
malloc 맞춤
malloc은 기본 맞춤을 사용하고 할당된 메모리 양에 맞을 수 있는 개체를 저장하는 데 적절하게 맞춘 메모리를 반환하도록 보장됩니다. 기본 맞춤은 맞춤 사양이 없는 구현에서 지원하는 가장 큰 맞춤보다 작거나 같은 맞춤입니다. (Visual C++에서 이 맞춤은 8바이트 단위에 필요한 double
맞춤입니다. 64비트 플랫폼을 대상으로 하는 코드에서는 16바이트입니다.) 예를 들어 4 바이트 할당은 4 바이트 또는 더 작은 개체를 지원하는 경계에 맞춰집니다.
Visual C++에서는 확장 맞춤을 사용하는 형식(과다 정렬된 형식이라고도 함)이 허용됩니다. 이 맞춤은. 예를 들어 SSE 형식 __m128 및 __m256
그리고 __declspec(align( n ))
(여기서 n
은 8보다 큼)을 사용하여 선언된 형식은 확장 맞춤을 사용합니다. 확장 맞춤이 필요한 개체에 적합한 경계에서의 메모리 맞춤은 malloc
에서 보장하지 않습니다. 과다 정렬된 형식에 메모리를 할당하려면 _aligned_malloc 및 관련 함수를 사용합니다.
alloca
_alloca는 16바이트로 맞춰야 하며, 프레임 포인터를 사용하는 데 추가적으로 필요합니다.
스택 할당에 설명된 대로 할당된 스택에는 이후 호출된 함수의 매개 변수에 대한 공백이 포함되어야 합니다.