다음을 통해 공유


프롤로그 및 에필로그

스택 공간을 할당하거나, 다른 함수를 호출하거나, 비volatile 레지스터를 저장하거나, 예외 처리를 사용하는 모든 함수에는 프롤로그가 있어야 합니다. 프롤로그의 주소 제한은 개별 함수 테이블 항목에 연결된 해제 데이터에 설명되어 있습니다. 자세한 내용은 예외 처리(x64)를 참조하십시오.프롤로그에서는 필요한 경우 인수 레지스터를 홈 주소에 저장하고, 비volatile 레지스터를 스택에 푸시하고, 스택의 고정 부분을 지역 변수 및 임시 변수용으로 할당하고, 선택적으로 프레임 포인터를 설정합니다.연결된 해제 데이터에서는 프롤로그의 동작을 설명하고 프롤로그 코드가 미치는 영향을 취소하는 데 필요한 정보를 제공해야 합니다.

스택에서 고정 할당이 1페이지(4096바이트)를 넘으면 스택 할당의 범위가 두 개 이상의 가상 메모리 페이지에 이를 수 있으므로 할당을 확인한 다음 실제로 할당해야 합니다.이러한 확인을 위해 프롤로그에서 호출할 수 있으며 인수 레지스터를 소멸시키지 않는 특별한 루틴이 제공됩니다.

비volatile 레지스터를 저장하려면 고정 스택을 할당하기 전에 스택에 이동하는 것이 좋습니다.비volatile 레지스터를 저장하기 전에 고정 스택을 할당한 경우 레지스터가 저장된 영역의 주소를 나타내기 위해 32비트 치환이 필요할 수 있습니다. 보고된 바에 따르면, push 간에 암시적인 종속성이 있음에도 불구하고 레지스터의 push와 mov의 속도는 거의 같으며 근본적인 변화가 없는 한 앞으로도 그러할 것입니다.비volatile 레지스터를 저장하는 순서에는 제한이 없습니다.그러나 프롤로그에서 비volatile 레지스터를 사용하는 데 있어 첫 번째 할 일은 이를 저장하는 것입니다.

일반적인 프롤로그의 코드는 다음과 같습니다.

mov       [RSP + 8], RCX
push   R15
push   R14
push   R13
sub      RSP, fixed-allocation-size
lea      R13, 128[RSP]
...

이 프롤로그에서는 인수 레지스터 RCX를 홈 주소에 저장하고, 비volatile 레지스터 R13-R15를 저장하고, 스택 프레임의 고정 부분을 할당하고, 고정 할당 영역의 시작 주소에서 128바이트 뒤를 가리키는 프레임 포인터를 설정합니다.오프셋을 사용하면 1바이트 오프셋으로 더 많은 고정 할당 영역의 주소를 나타낼 수 있습니다.

고정 할당 크기가 메모리의 한 페이지 이상인 경우 RSP를 수정하기 전에 도우미 함수를 호출해야 합니다.도우미 함수인 __chkstk는 스택이 제대로 확장될 수 있도록 할당될 스택 범위를 검사합니다.이 경우 이전 프롤로그 예제는 다음과 같이 변경됩니다.

mov       [RSP + 8], RCX
push   R15
push   R14
push   R13
mov      RAX,  fixed-allocation-size
call   __chkstk
sub      RSP, RAX
lea      R13, 128[RSP]
...

__chkstk 도우미 함수에서는 R10, R11 및 조건 코드 레지스터만 수정합니다.특히 RAX가 변경되지 않고 반환되며 비volatile 레지스터와 인수 전달 레지스터가 모두 수정되지 않습니다.

에필로그 코드는 함수의 모든 종료 위치에 있습니다.일반적으로 프롤로그는 하나뿐이지만 에필로그는 여러 개가 있을 수 있습니다.에필로그 코드에서는 필요한 경우 스택을 고정 할당 크기로 트리밍하고, 고정 스택의 할당을 해제하고, 비volatile 레지스터의 저장된 값을 스택에서 팝하여 복구한 다음 제어를 반환합니다.

해제 코드에서 예외 및 인터럽트를 안정적으로 처리하며 해제하려면 에필로그 코드에서 몇 가지 규칙을 엄격하게 지켜야 합니다.이렇게 하면 각 에필로그를 설명하는 데 추가적인 데이터가 필요하지 않으므로 해제 데이터의 양이 줄어듭니다.해제 코드에서는 추가적인 데이터를 사용하는 대신 코드 스트림에서 앞으로 스캔하여 에필로그를 식별하는 방법으로 에필로그가 실행 중인지 확인할 수 있습니다.

함수에서 프레임 포인터를 사용하지 않는 경우 에필로그는 스택 고정 부분의 할당을 먼저 해제한 다음 비volatile 레지스터를 팝하고 호출하는 함수로 제어를 반환해야 합니다.다음 예제를 참조하십시오.

add      RSP, fixed-allocation-size
pop      R13
pop      R14
pop      R15
ret

함수에서 프레임 포인터를 사용하는 경우에는 에필로그를 실행하기 전에 스택을 고정 할당으로 트리밍해야 합니다.이 작업은 기술적으로는 에필로그의 일부가 아닙니다.예를 들어, 이전에 사용한 프롤로그를 취소하는 데 다음 에필로그를 사용할 수 있습니다.

lea      RSP, -128[R13]
; epilogue proper starts here
add      RSP, fixed-allocation-size
pop      R13
pop      R14
pop      R15
ret

프레임 포인터가 사용되는 경우 실제로는 RSP를 두 단계에 걸쳐 조정할 필요가 없으므로 다음과 같은 에필로그를 대신 사용합니다.

lea      RSP, fixed-allocation-size – 128[R13]
pop      R13
pop      R14
pop      R15
ret

에필로그에 사용할 수 있는 형태는 이들 뿐입니다.에필로그는 add RSP,constant 또는 lea RSP,constant[FPReg]를 사용한 다음 0개 이상의 8바이트 레지스터를 팝한 후 ret나 jmp가 오는 형태로 구성되어야 합니다.에필로그에서는 jmp 문 중 일부만 사용할 수 있습니다.ModRM mod 필드 값이 00인 ModRM 메모리 참조를 사용하는 jmp 문만 사용할 수 있습니다.ModRM mod 필드 값이 01이나 10인 jmp는 에필로그에서 사용할 수 없습니다.사용할 수 있는 ModRM 참조에 대한 자세한 내용은 AMD x86-64 Architecture Programmer’s Manual Volume 3: General Purpose and System Instructions에서 표 A-15를 참조하십시오.다른 코드는 사용할 수 없습니다.특히 에필로그 안에서는 반환 값을 로드하는 등 어떠한 작업도 예약할 수 없습니다.

프레임 포인터가 사용되지 않는 경우 에필로그에서는 add RSP,constant를 사용하여 스택 고정 부분의 할당을 해제해야 합니다.lea RSP,constant[RSP]를 대신 사용해서는 안 됩니다.이러한 제한은 해제 코드가 에필로그를 검색할 때 인식해야 하는 패턴의 수가 적어지도록 하기 위한 것입니다.

이들 규칙을 따르면 해제 코드에서 에필로그가 현재 실행 중임을 확인할 수 있고, 나머지 에필로그 부분의 실행을 시뮬레이션하여 호출하는 함수의 컨텍스트를 다시 만들 수 있습니다.

참고 항목

참조

x64 소프트웨어 규칙