x64 프롤로그 및 에필로그x64 prolog and epilog

스택 공간을 할당하거나, 다른 함수를 호출하거나, 비휘발성 레지스터를 저장하거나, 예외 처리를 사용하는 모든 함수는 해당 함수 테이블 항목에 연결된 해제 데이터에 주소 제한이 설명된 프롤로그가 있어야 합니다.Every function that allocates stack space, calls other functions, saves nonvolatile registers, or uses exception handling must have a prolog whose address limits are described in the unwind data associated with the respective function table entry. 자세한 내용은 x64 예외 처리를 참조하세요.For more information, see x64 exception handling. 프롤로그는 홈 주소에 인수 레지스터를 저장하고(필요한 경우), 스택에 비휘발성 레지스터를 푸시하고, 로컬 및 임시 개체에 대해 스택의 고정 부분을 할당하고, 필요에 따라 프레임 포인터를 설정합니다.The prolog saves argument registers in their home addresses if necessary, pushes nonvolatile registers on the stack, allocates the fixed part of the stack for locals and temporaries, and optionally establishes a frame pointer. 연결된 해제 데이터는 프롤로그의 동작을 설명하고 프롤로그 코드의 효과를 취소하는 데 필요한 정보를 제공해야 합니다.The associated unwind data must describe the action of the prolog and must provide the information necessary to undo the effect of the prolog code.

스택의 고정 할당이 두 페이지를 넘는 경우(즉 4096바이트보다 큰 경우) 스택 할당이 두 페이지 이상의 가상 메모리에 걸쳐 있을 수 있으므로 할당하기 전에 확인해야 합니다.If the fixed allocation in the stack is more than one page (that is, greater than 4096 bytes), then it's possible that the stack allocation could span more than one virtual memory page and, therefore, the allocation must be checked before it's allocated. 프롤로그에서 호출할 수 있고 어떤 인수 레지스터도 제거하지 않는 특수 루틴은 이러한 용도로 제공됩니다.A special routine that's callable from the prolog and which doesn't destroy any of the argument registers is provided for this purpose.

비휘발성 레지스터를 저장하는 기본 방법은 고정 스택 할당 전에 스택으로 이동하는 것입니다.The preferred method for saving nonvolatile registers is to move them onto the stack before the fixed stack allocation. 비휘발성 레지스터를 저장하기 전에 고정 스택 할당을 수행하는 경우 저장된 레지스터 영역을 처리하려면 대개 32비트 치환이 필요합니다.If the fixed stack allocation is performed before the nonvolatile registers are saved, then most probably a 32-bit displacement is required to address the saved register area. (레지스터의 푸시는 이동만큼 빠르다고 하며, 푸시 간의 묵시적 종속성에도 불구하고 예측 가능한 미래를 위해 그대로 유지되어야 합니다.) 비휘발성 레지스터는 순서에 관계없이 저장할 수 있습니다.(Reportedly, pushes of registers are as fast as moves and should remain so for the foreseeable future in spite of the implied dependency between pushes.) Nonvolatile registers can be saved in any order. 그러나 프롤로그에서 비휘발성 레지스터를 처음 사용하는 경우에는 순서에 따라 저장해야 합니다.However, the first use of a nonvolatile register in the prolog must be to save it.

프롤로그 코드Prolog code

일반적인 프롤로그에 대한 코드는 다음과 같을 수 있습니다.The code for a typical prolog might be:

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

이 프롤로그는 인수 레지스터 RCX를 홈 위치에 저장하고, 비휘발성 레지스터 R13-R15를 저장하고, 스택 프레임의 고정 부분을 할당하고, 128바이트를 고정 할당 영역으로 가리키는 프레임 포인터를 설정합니다.This prolog stores the argument register RCX in its home location, saves nonvolatile registers R13-R15, allocates the fixed part of the stack frame, and establishes a frame pointer that points 128 bytes into the fixed allocation area. 오프셋을 사용하면 1바이트 오프셋으로 더 많은 고정 할당 영역에 주소를 지정할 수 있습니다.Using an offset allows more of the fixed allocation area to be addressed with one-byte offsets.

고정 할당 크기가 메모리의 한 페이지이거나 이보다 큰 경우에는 RSP를 수정하기 전에 도우미 함수를 호출해야 합니다.If the fixed allocation size is greater than or equal to one page of memory, then a helper function must be called before modifying RSP. 이 도우미 __chkstk는 할당될 스택 범위를 검색하여 스택이 적절히 확장되게 합니다.This helper, __chkstk, probes the to-be-allocated stack range to ensure that the stack is extended properly. 이 경우 이전 프롤로그 예제는 다음과 같습니다.In that case, the previous prolog example would instead be:

    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 및 조건 코드 이외의 레지스터는 수정하지 않습니다.The __chkstk helper will not modify any registers other than R10, R11, and the condition codes. 특히 RAX를 변경되지 않은 상태로 반환하고 모든 비휘발성 레지스터 및 인수 전달 레지스터를 수정되지 않은 상태로 둡니다.In particular, it will return RAX unchanged and leave all nonvolatile registers and argument-passing registers unmodified.

에필로그 코드Epilog code

에필로그 코드는 매번 함수로 종료될 때마다 존재합니다.Epilog code exists at each exit to a function. 일반적으로 프롤로그는 하나뿐이지만 에필로그는 여러 개일 수 있습니다.Whereas there is normally only one prolog, there can be many epilogs. 에필로그 코드는 스택을 고정 할당 크기로 자르고(필요한 경우), 고정 스택 할당을 취소하고, 스택에서 저장된 값을 팝하여 비휘발성 레지스터를 복원하고, 반환합니다.Epilog code trims the stack to its fixed allocation size (if necessary), deallocates the fixed stack allocation, restores nonvolatile registers by popping their saved values from the stack, and returns.

에필로그 코드는 예외 및 인터럽트를 통해 안정적으로 해제하기 위해 해제 코드에 대한 엄격한 규칙 집합을 따라야 합니다.The epilog code must follow a strict set of rules for the unwind code to reliably unwind through exceptions and interrupts. 이러한 규칙으로 인해 각 에필로그를 설명하는 데 추가 데이터가 없어도 되므로 필요한 해제 데이터의 양이 줄어듭니다.These rules reduce the amount of unwind data required, because no extra data is needed to describe each epilog. 대신에 해제 코드는 에필로그를 식별하기 위해 코드 스트림을 통한 정방향 스캔으로 에필로그가 실행되고 있음을 확인할 수 있습니다.Instead, the unwind code can determine that an epilog is being executed by scanning forward through a code stream to identify an epilog.

함수에서 프레임 포인터를 사용하지 않는 경우 에필로그는 먼저 스택의 고정 부분 할당을 취소해야 합니다. 이어서 비휘발성 레지스터가 팝되고 컨트롤이 호출 함수로 반환됩니다.If no frame pointer is used in the function, then the epilog must first deallocate the fixed part of the stack, the nonvolatile registers are popped, and control is returned to the calling function. 예를 들면 다음과 같습니다.For example,

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

함수에서 프레임 포인터를 사용하는 경우 에필로그를 실행하기 전에 스택을 고정된 할당으로 잘라내야 합니다.If a frame pointer is used in the function, then the stack must be trimmed to its fixed allocation prior to the execution of the epilog. 이 작업은 기술적으로 에필로그의 일부가 아닙니다.This action is technically not part of the epilog. 예를 들어 다음 에필로그를 사용하여 이전에 사용한 프롤로그를 실행 취소할 수 있습니다.For example, the following epilog could be used to undo the prolog previously used:

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

실제로 프레임 포인터를 사용하는 경우 두 단계로 RSP를 조정해야 할 유가 없으므로 다음 에필로그가 대신 사용됩니다.In practice, when a frame pointer is used, there is no good reason to adjust RSP in two steps, so the following epilog would be used instead:

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

이러한 형식은 에필로그에 유일하게 유효한 것입니다.These forms are the only legal ones for an epilog. 에필로그는 add RSP,constant 또는 lea RSP,constant[FPReg]에 이어 나오는 일련의 8바이트 레지스터 팝 0개 이상과 return 또는 jmp로 구성되어야 합니다.It must consist of either an add RSP,constant or lea RSP,constant[FPReg], followed by a series of zero or more 8-byte register pops and a return or a jmp. (jmp 문의 하위 집합만 에필로그에서 허용됩니다.(Only a subset of jmp statements are allowable in the epilog. 이 하위 집합은 단독으로 jmp 문의 클래스입니다. 이 문에는 ModRM mod 필드 값이 00인 ModRM 메모리 참조가 있습니다.The subset is exclusively the class of jmp statements with ModRM memory references where ModRM mod field value is 00. ModRM mod 필드 값이 01 또는 10인 에필로그에서 jmp 문을 사용하는 것은 금지됩니다.The use of jmp statements in the epilog with ModRM mod field value 01 or 10 is prohibited. 허용되는 ModRM 참조에 대한 자세한 내용은 AMD x86-64 아키텍처 프로그래머의 수동 볼륨 3: 범용 및 시스템 지침에 있는 표 A-15를 참조하세요.) 다른 코드는 표시되지 않습니다.See Table A-15 in the AMD x86-64 Architecture Programmer's Manual Volume 3: General Purpose and System Instructions, for more info on the allowable ModRM references.) No other code can appear. 특히 반환 값 로드를 포함해 에필로그 내에서는 아무 것도 예약할 수 없습니다.In particular, nothing can be scheduled within an epilog, including loading of a return value.

프레임 포인터를 사용하지 않는 경우 에필로그는 add RSP,constant를 사용하여 스택의 고정 부분을 할당 취소해야 합니다.When a frame pointer is not used, the epilog must use add RSP,constant to deallocate the fixed part of the stack. 에필로그는 lea RSP,constant[RSP]를 대신 사용해서는 안 됩니다.It may not use lea RSP,constant[RSP] instead. 이러한 제한이 있으므로 해제 코드가 에필로그를 검색할 때 인식할 패턴의 수가 더 적습니다.This restriction exists so the unwind code has fewer patterns to recognize when searching for epilogs.

이러한 규칙을 따르면 에필로그가 현재 실행되고 있는지를 해제 코드가 확인하고 에필로그의 나머지 부분 실행을 시뮬레이션하여 호출하는 함수의 컨텍스트를 다시 만들 수 있습니다.Following these rules allows the unwind code to determine that an epilog is currently being executed and to simulate execution of the remainder of the epilog to allow recreating the context of the calling function.

참조See also

x64 소프트웨어 규칙x64 Software Conventions