2015-01-10 4 views
1

스택에 대한 메모리 페이지가 얼마나 정확하게 할당되고 할당되는지 이해하려고합니다.ESP 또는 RSP 레지스터를 뺄 때 어떤 예외가 생성 될 수 있습니까? (stack stacking)

#include <string.h> 

int main() 
{ 
    char a; 

    memset((&a - 4444444), 0, 3333333); 

    return 0; 
} 

(& T 구문 AT) 어셈블리 코드의 다음 단편 GCC에 의해 생성된다 :

는 I 분명 (x86_64에 리눅스) 세그먼트 오류의 원인은 다음의 개념 증명 C 코드를 작성 C 프로그램 위에서 :

subq $16, %rsp 
leaq -1(%rbp), %rax 
subq $4444444, %rax 
movl $3333333, %edx 
movl $0, %esi 
movq %rax, %rdi 
call memset 

내가 subq $5555555, %rsp를 추가 할 경우 수동으로 memset를 호출하기 전에 :

subq $16, %rsp 
leaq -1(%rbp), %rax 
subq $4444444, %rax 
movl $3333333, %edx 
movl $0, %esi 
movq %rax, %rdi 
subq $5555555, %rsp /* added manually */ 
call memset 

rsp 레지스터를 뺀 후 스택의 가상 메모리 페이지가 할당되어 일부 하드웨어 예외가 발생하고 할당 된 예외 핸들러가 호출 되었기 때문에 세그먼트 오류가 사라집니다 (물론 커널 공간에서 호출 됨).

여기서 memset을 호출하면 "부 페이지 오류"예외가 발생한다는 것을 알고 있습니다. 하지만 이는 물리적 메모리 페이지를 할당하는 것과는 다른 이야기입니다.

제 질문은 subq $5555555, %rsp을 호출 할 때 어떤 예외가 생성 되었습니까? 나는 그것이 "스택 결함"예외가 될 것이라고 제안하지만 정확한 증거를 찾지 못했습니다.

+0

x86에도 이러한 레지스터가 없습니다 ... 아마도 잘못된 아키텍처 태그를 사용 했습니까? –

+0

@Ben Voigt 저는'인텔 ® 코어 ™ i3-2328M CPU @ 2.20GHz × 4'를 가지고 있으므로 x86 레지스터입니다. –

+1

RSP는 x86 레지스터가 아닙니다 ... x86에서는 사용할 수없는 긴 모드에서만 CPU에서 사용할 수 있습니다. –

답변

2

나는 그것을 알아 냈다. 우선, rsp 레지스터를 빼는 것은 아무런 효과가 없습니다. 둘째, 맵핑되지 않은 스택 영역에 쓸 때 "부 페이지 오류"예외 핸들러가 커널 공간에서 호출됩니다. 그런 다음이 페이지 결함 핸들러는 그것이 적법한 지 또는 비 합법적인지를 점검합니다. 나는 페이지 결함 처리기가 스레드의 현재 스택 포인터와 비교한다고 생각한다 (우리의 경우 값은 rsp 레지스터에 저장되어있다). 프로세스가 쓰려고하는 주소가 현재 스택 포인터보다 높으면 페이지 폴트 처리기가 프로세스의 가상 주소 공간을 확장하고이 가상 페이지를 물리적 페이지에 매핑합니다. 그렇지 않으면 처리기가 SIGSEGV를 프로세스에 보냅니다.

난 GDB와은/proc/[PID]/맵을 사용하여 다음 조각을 검사 :

subq $1500016, %rsp가 스택 주소 범위를 호출
subq $1500016, %rsp 
movq %fs:40, %rax 
movq %rax, -8(%rbp) 
xorl %eax, %eax 
movb $44, -1500016(%rbp) 
movb $55, -1100016(%rbp) 
movb $66, -600016(%rbp) 

는 변경되지 않는다. 그러나 첫번째 쓰기가 movb $44, -1500016(%rbp)에 의하여 일어날 때, 나가 위에 설명한대로 스택 주소 범위는 확장된다.

0

해당 행에 예외는 없습니다.

그러나 스택 포인터가 유효하지 않으므로 memset의 프롤로그 코드는 스택에 레지스터를 저장하여 레지스터를 보존하려고하면 액세스 위반이 발생합니다.

대부분의 환경에서는 커밋 할 추가 스택 페이지를 트리거하는 단일 보호 페이지 만 있습니다. 이 경우 스택을 확장하여 액세스 위반이 처리되지 않고 프로그램이 중단됩니다.

OS가 실제로 레지스터 저장 중 발생하는 액세스 위반을 처리하는 경우 스택의 모든 중간 페이지를 커밋하고 작업을 다시 시도합니다 (PUSH 명령어). 그런 다음 중재 페이지가 루프에 의해 memset 안에 성공적으로 기록됩니다.

물론 빼기로 인해 RSP이 스택 증가를 위해 예약 된 주소 공간 외부를 가리키는 경우 모든 베팅은 해제됩니다. 다른 스레드의 스택을 늘릴 수도 있습니다.

+0

내 주제를 업데이트했습니다. 제 질문에 대한 제 대답을 확인하십시오. –

+0

설명이 실제 동작과 일치하지 않습니다. 'subq $ 555555, rsp' 라인으로 모든 것이 잘됩니다. 당신의 대답이 정확하다면 그건 효과가 없을 것입니다 ... – glglgl