• 카테고리

    질문 & 답변
  • 세부 분야

    게임 프로그래밍

  • 해결 여부

    해결됨

스택프레임 공간을 찜하는 이유가 궁금합니다.

21.02.02 15:45 작성 조회수 243

3

MultiplyBy 함수의 어셈블리 코드를 보면 아래와 같이

sub esp, 0CCh

; 기타 코드들

add esp, 0CCh

먼저 sp에 sub를 해서 0xCC개 주소만큼의 공간을 찜해두고, 리턴 전에 sp에 add를 해서 현재 bp와 같아지도록 공간을 정리합니다. 그런데 코드에선 딱히 그 큰 공간의 빈 스택프레임을 활용하지는 않는 것 같습니다. 

이렇게 찜을 하는 이유가 뭔가요? 

추가 질문1 : 코드 중간에 ebx, esi, edi를 push하고 리턴하기 전에 pop하는 이유는 무엇인가요? 중간에 해당 레지스터를 사용하는 모습은 없는데 굳이 푸시했다 팝하는 이유가 있을까요? 중간에 call한 Debugger프로시저?에서 사용하는 부분인가요?

추가질문2: 위 함수의 반환값은 eax에 담아서 메인함수로 전해지는데요. 원래 모든 함수에서 반환값을 (반환값이 있다면) 레지스터에 담아서 호출한 함수에 전달하나요?

추가질문3: 제 컴파일러에서 함수가 매개변수를 전달받을 때 값을 스택에 push한 후 dword ptr [ebp+0x08]처럼 메모리를 통해서 접근하지 않고, 값을 바로 레지스터에 담아와서 지역변수처럼 현재 함수의 스택프레임에 넣어 사용하는 코드를 만들었습니다. 이것도 정상적인 과정인가요?

..쓰다보니 질문이 너무 길어졌네요..ㅜ

답변 2

·

답변을 작성해보세요.

4

먼저 sp에 sub를 해서 0xCC개 주소만큼의 공간을 찜해두고, 리턴 전에 sp에 add를 해서 현재 bp와 같아지도록 공간을 정리합니다. 그런데 코드에선 딱히 그 큰 공간의 빈 스택프레임을 활용하지는 않는 것 같습니다. 
이렇게 찜을 하는 이유가 뭔가요? 

-> 사실 그건 컴파일러 마음입니다.
어차피 아주 작은 수를 sp에 더하나, 큰 수를 더하나
성능 관점에서 보면 100% 동일합니다.
심지어 스택 메모리는 잠시 사용하고 반납하는 형태로 동작하기 때문에
메모리 측면에서도 아무런 차이가 없습니다.

일반적으로 할당된 스택 메모리는 내부적으로 사용하는 로컬 변수 저장 용도로 사용하지만,
컴파일러 옵션에 따라 이런 저런 추가 정보를 넣어서 디버깅 용도로도 활용됩니다.
대표적으로 Canary를 넣는 것을 고려할 수 있는데요.
예전에 인부들이 금광 채굴을 할 때,
유독가스 누출이 있는지를 탐지하기 위해 Canary(
카나리아)라는 새를 동행했다고 합니다.
이 새는 유독가스에 민감해서 가스가 누출되면 바로 죽었는데,
그걸로 사람들이 대피를 할 수 있었다고 합니다.
마찬가지로 스택에도 의미없는 0xCC 같은 정보들로 쫙 채운 다음에,
함수 호출이 끝날 때 혹시라도 Stack Overflow등으로 인해
해당 메모리가 변조 되었는지를 체크하는 등의 안전 기법들이 디버그 모드에서는 작동합니다.

추가 질문1 : 코드 중간에 ebx, esi, edi를 push하고 리턴하기 전에 pop하는 이유는 무엇인가요? 중간에 해당 레지스터를 사용하는 모습은 없는데 굳이 푸시했다 팝하는 이유가 있을까요? 중간에 call한 Debugger프로시저?에서 사용하는 부분인가요?

기본적으로 함수를 호출하기 전의 그 상태를 '그대로' 유지하기 위함입니다.
함수 내부에서 호출하는 call 어딘가에서 ebx, esi, edi를 수정하고 있을 수도 있고,
아니면 컴파일러가 조금 비효율적으로 만든 것일 수도 있겠습니다.

추가질문2: 위 함수의 반환값은 eax에 담아서 메인함수로 전해지는데요. 원래 모든 함수에서 반환값을 (반환값이 있다면) 레지스터에 담아서 호출한 함수에 전달하나요?

상황에 따라 다릅니다. 사실 그것도 컴파일러 마음이고 뭔가 표준에서 정해주는 것은 아닙니다.
아주 작은 값 (bool, int 등)이라면 레지스터를 통해 전달되겠지만,

100바이트 구조체를 레지스터를 통해 전달하긴 힘들겠죠.
그런 경우에는 스택 메모리에 임시적으로 값을 복사해서 전달하는 형태로 동작합니다.

추가질문3: 제 컴파일러에서 함수가 매개변수를 전달받을 때 값을 스택에 push한 후 dword ptr [ebp+0x08]처럼 메모리를 통해서 접근하지 않고, 값을 바로 레지스터에 담아와서 지역변수처럼 현재 함수의 스택프레임에 넣어 사용하는 코드를 만들었습니다. 이것도 정상적인 과정인가요?

물론 그것도 정상입니다.
사실 x86에서는 스택에서 넘기는게 일반적이지만
x64에서는 레지스터를 통해 넘기는 상황이 빈번합니다.

 

1

우와.. 감사합니다!