2016-10-31 2 views
2

스택 작동 방식을 이해하는 데 문제가 있습니다. 먼저 내 작은 코드 :RSP가 스택 맨 위에 있지 않습니까?

void func1 (int z) { 
    int i = 1; 
} 

int main () { 
    func1 (89); 
    return 0; 
} 

내가 사용하고 : 우분투 16.04 64 비트, GCC 버전 5.4.0, GDB 버전 7.11.1.

컴파일러가 스택에서 함수 인수를 푸시하는 방법을 확인하기 위해 GDB를 사용하여 디버깅했습니다. 나는 어디 RSP 포인트의 시점에서 스택을 검토 할 때는

,이 얻을 :

(gdb) p &i 
$4 = (int *) 0x7fffffffdf14 
:

나는 새로운 창조 변수의 주소를 출력
(gdb) x/10xw $rsp 
0x7fffffffdf20: 0xffffdf30 0x00007fff 0x00400525 0x00000000 
0x7fffffffdf30: 0x00400530 0x00000000 0xf7a2e830 0x00007fff 
0x7fffffffdf40: 0x00000000 0x00000000 

,이 얻을

함수에 전달 된 변수의 주소를 출력하면 다음과 같이 표시됩니다.

(gdb) p &z 
$5 = (int *) 0x7fffffffdf0c 

스택의 수가 적어집니다.

그래서 나는이 명령을 호출 할 때 RSP이 항상 스택 맨을 가리킨다 고 생각했습니다. x/10xw $rsp 함수에서 모든 변수를 볼 수는 있지만 거기에서 볼 수는 없습니다.

이 명령의 첫 번째 주소는 변수 z의 주소보다 길다. 그 때문에 나는 RSP 점이 스택 맨 위에 있지 않다고 추측했습니다.

내게 궁금한 것은 i의 주소가 z의 주소보다 높다는 것입니다. i은 나중에 z보다 스택에 푸시되었으므로 i은 내 의견으로는 z보다 낮은 주소 여야합니다.

누군가가 왜 그런지 설명 할 수 있기를 바랍니다.

편집 : 답변을 찾았습니다! 컴파일러에서 최적화되었습니다. func1()에서 RSP 레지스터는 필요하지 않기 때문에 스택의 "위쪽"을 가리키고 있지 않았습니다. func1()에 다른 함수가 호출 된 경우에만 필요했습니다. 그래서 컴파일러는 이것을보고 RSP 레지스터를 감소시키지 않았습니다. func1()에서 어떤 함수 호출에

여기 ismy 어셈블러 코드 :

0x00000000004004d6 <+0>:  push rbp 
    0x00000000004004d7 <+1>:  mov rbp,rsp 
    0x00000000004004de <+8>:  mov DWORD PTR [rbp-0x14],edi 
    0x00000000004004e1 <+11>: mov DWORD PTR [rbp-0x4],0x1 
    0x00000000004004e8 <+18>: mov eax,0x0 
    0x00000000004004f3 <+29>: leave 
    0x00000000004004f4 <+30>: ret 

그래서 당신은 RSP을 감소시키는에 대한 SUB 전화를 볼 수 있습니다.

이제 함수 호출에 func1()의 코드는 :
0x00000000004004d6 <+0>:  push rbp 
    0x00000000004004d7 <+1>:  mov rbp,rsp 
    0x00000000004004da <+4>:  sub rsp,0x20 
    0x00000000004004de <+8>:  mov DWORD PTR [rbp-0x14],edi 
    0x00000000004004e1 <+11>: mov DWORD PTR [rbp-0x4],0x1 
    0x00000000004004e8 <+18>: mov eax,0x0 
    0x00000000004004ed <+23>: call 0x4004f5 <func2> 
    0x00000000004004f2 <+28>: nop 
    0x00000000004004f3 <+29>: leave 
    0x00000000004004f4 <+30>: ret  

그래서 당신은 RSP을 감소시키는에 대한 SUB 전화를 볼 수 있습니다. 따라서 RSP은 "위쪽"을 가리킬 수 있습니다.

+2

Linux 용 64 비트 호출 규칙은 처음 6 개의 인수에 대해 레지스터를 사용합니다. 스택이 필요 없습니다. –

+0

레지스터에 값이 저장됩니까? 당신은 내가 그것에 관해 읽을 수있는 링크 또는 무언가를 안다? –

+1

이것은 좋은 스레드입니다. http://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-on-x86-64 –

답변

1

x86의 규칙은 스택이 감소하는 주소로 "아래쪽으로"자랍니다.

스택의 "상단"은 단순히 뭔가가 인 가장 가까운 위치 인 것입니다.; 주소의 상대 값을 기반으로하지 않습니다. 스택은 주소 공간에서 "상향"또는 "하향"으로 성장할 수 있습니다. 링크 된 목록과 같은 일부 구현에서는 주소가 순차적 일 필요조차 없습니다.

This page은 다이어그램과 함께 공정한 설명이 있습니다.

+0

괜찮습니다. 이는 모두 유효한 호출임을 의미합니다. mov DWORD PTR [rbp-0x14], edi 및 mov DWORD PTR [rbp + 0x14], edi. ? 그러나 새로운 스택 프레임이 생성되면 어떻게 될까요? 나는 RSP의 가치가 미사용 주소를 사용할 수있는 방향으로 사용한다고 생각했다. 내가 아직도 많이 읽어야 할 것 같아. –

+0

@FelixWe : 내가 내 대답에 링크 된 페이지를 살펴 보라. 그것은 당신의 질문에 대답하는 데 도움이 될 것이다. –

+0

나는 스스로에게 질문을 대답 할 수 있었다. 이 기사는 나를 올바른 방향으로 지적했다. 내 질문에 대한 답변을 추가했습니다. –

관련 문제