1.함수의 호출


함수를 호출할때 어떻게 스택에 어떻게 PUSH하고 어떻게 POP할까?


함수 호출은 크게 다음과 같이 나뉘어 진다.

1.함수가 사용할 파라메터를 스택에 넣고 함수 시작지점으로 점프(함수 호출)한다.

2.함수 내에서 사용할 스택프레임을 설정한다. (프롤로그)

3.함수의 내용을 수행한다.

4.수행을 마치고 처음 호출한 지점으로 돌아가기 위해 스택을 복원한다. (에필로그)

호출한 지점의 다음 라인으로 점프한다.

이때 2번 과정을 프롤로그(prolog) 라고 부르며, 4번 과정을 에필로그(epilog) 라고 부른다. 보면 알겠지만 스택프레임의 설정과 복원과 관계가 있다.


그렇다면 함수 프롤로그와 에필로그가 어떤식으로 될까?

(by 어셈블리어)



2.함수 프롤로그 & 에필로그


함수의 프롤로그와 에필로그는 함수호출 규약에 따라 조금씩 다르다.



대표적으로 cdecl 을 보면 다음과 같다.


-cdecl 


특징

  • 스택에 파라메터 삽입 순서 : right → left
  • 스택의 정리를 호출한 함수(caller)에서 수행한다. 따라서 가변인자를 사용할 수 있다.
  • Name Mangling : 함수 이름 앞에 _ 추가 
    ex) _Foo
  • C / C++ 언어의 기본 함수 호출규약

int main() { foo(); /* foo 함수 호출 call foo (128102Dh) add esp, 8 ; (6) <<스택을 정리하는 부분>> */ return 0; }

위 코드를 보면 역순으로 파라메터를 push 하고 함수호출을 한 후, 이어지는 다음 라인에서 스택을 정리함을 알 수 있다. 즉 caller 가 직접 스택을 정리한다.


int __cdecl foo( int a, int b ) { /* caller 의 ebp 를 저장하고 callee (foo) 의 ebp 를 확보 push ebp mov ebp, esp push ecx ; local 변수 c 의 자리 확보 */  

int c;

  1. caller 의 ebp 를 저장하고 callee 의 ebp 를 새로 확보한다. 이 새로운 ebp 를 이용하여 foo 함수 내에서 파라메터 및 local 변수로의 접근을 시도할 것이다. 그리고 local 변수에 사용할 스택을 할당하는데 여기서는 4바이트 변수 하나이므로 단순히 ecx 를 push 함으로써 이를 수행한다. 하지만 local 변수들이 8바이트 (혹은 그 이상) 일 경우 “sub esp, 8” 과 같이 스택을 할당하게 된다.

} /* foo 함수 종료 후 caller 의 ebp, esp 복구 mov esp, ebp pop ebp ret */

  1. caller 의 ebp 를 저장하고 callee 의 ebp 를 새로 확보한다. 이 새로운 ebp 를 이용하여 foo 함수 내에서 파라메터 및 local 변수로의 접근을 시도할 것이다. 그리고 local 변수에 사용할 스택을 할당하는데 여기서는 4바이트 변수 하나이므로 단순히 ecx 를 push 함으로써 이를 수행한다. 하지만 local 변수들이 8바이트 (혹은 그 이상) 일 경우 “sub esp, 8” 과 같이 스택을 할당하게 된다.





함수 프롤로그


/* caller 의 ebp 를 저장하고 callee (foo) 의 ebp 를 확보 push ebp mov ebp, esp push ecx ; local 변수 c 의 자리 확보 */  


베이스포인터(ebp)를 스택에 저장하고 현재 스택포인터(esp)를 베이스포인터(ebp)에다가 저장함.




함수 에필로그


} /* foo 함수 종료 후 caller 의 ebp, esp 복구 mov esp, ebp pop ebp ret */


현재 스택 포인터(esp)를 베이스포인터(ebp)로 복구한 후 베이스 포인터(ebp)를 복구해주고 ret를 통해 다음에 가야에 adress로 점프한다. 





참고 : hhttp://z3moon.com/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D/%ED%98%B8%EC%B6%9C%EA%B7%9C%EC%95%BD


WRITTEN BY
Who1sth1s

,