- 복귀 주소 (Return Address)

 프로시저가 해당 기능을 끝마친 후에 이 프로시저를 호출한 바로 다음 코드의 내용이 실행되어질 수 있도록 해당 정보를 가지고 있음

 프로시저가 호출되기 바로 직전에 저장해 놓고 호출한 프로시저가 끝마치게 되면 이 내용을 사용하고, 해당 프로시저가 끝나게 되면 복귀 주소 내용 역시 필요 없어짐

 프로시저 안에서 또 다른 프로시저의 호출 또는 재귀 호출과 같은 형태의 흐름이 가능함 (복귀 주소로 돌아오기 전에 또 다른 복귀 주소가 중첩하여 여러 개 존재할 수 있음)

 => 이러한 복귀 주소의 특성을 고려하여 컴퓨터 시스템에서는 스택이라는 메모리 공간을 정의해놓고, 복귀 주소 정보를 저장하고 있다.

 

- 스택 로컬 변수 저장

 프로시저 내에서 사용하는 로컬 변수는 그 프로시저가 호출되어 실행될 때까지만 유효함

 즉, 프로시저가 호출될 때에만 그 프로시저에서 사용할 로컬 변수의 메모리 공간이 필요하고, 프로시저가 복귀하게 되면 이 영역에서 사용한 로컬 변수들 역시 쓸모없는 데이터가 되어버리기 때문에, 복귀 주소처럼 스택 구조에 저장

 스택 구조에서 쓰레기 값 영역에 대해 다음 번 프로세서가 덮어쓰기 형태로 사용하기 때문에 효율적으로 스택 메모리를 사용할 수 있음

 

- 스택 파라미터 전달

 프로그램에서 어떠한 데이터를 프로시저에게 전달하고자 할 때 전달하고자 하는 데이터들을 어디엔가 저장하여 놓거나, 데이터가 저장된 위치를 발견하여 프로시저 내에서 사용하는 단계, 혹은 프로시저가 복귀 되어질 때 전달받은 데이터를 다시 해제하는 방법이 있음

 프로시저로의 데이터 전달 역시 전달하고자 하는 프로시저 내에서만 사용하면 되기 때문에 스택 영역에 저장

 파라미터가 있는 프로시저의 경우 프로시저가 호출되기 전에 전달하고자 하는 파라미터들을 스택에 저장하여 놓은 후 프로시저로 제어 흐름을 바꿈

 프로시저 내에서 중간에 스택의 내용을 변경하는 코드가 있는 경우를 해결하기 위해 스택이 변경되는 경우까지 고려하여 그 이후의 코드에 대해 스택의 위치를 새로 계산하여 올바른 파라미터를 가리킬 수 있도록 하고 있으며, 스택 프레임이라는 것을 구성하여 해결하고 있음

 * 스택 프레임

   호출된 프로시저의 복귀 주소를 기준으로 고정된 변위 값을 통하여 파라미터를 다루는 것

   파라미터 참조 기준을 push나 pop 등에 의해 스택 포인터의 값이 변경될 수 있는 스택 포인터(ESP) 기준이 아니라, 명령어에 의하여 영향을 받지 않는 EBP 레지스터를 기준으로 하여 문제를 해결함

   스택 프레임 구성은 로컬 변수 참조에도 편리함을 제공

 ESP, EBP 어셈블리 예제)

push ebp // 이전 스택의 베이스 주소를 저장

mov ebp, esp // 현재 스택의 꼭대기를 새로운 스택의 베이스로 설정 (새로운 스택 시작)

보통 EBP는 데이터 접근을 위한 기준 주소를 저장, ESP는 최상위 주소를 유지하는  용도로 사용

- 파라미터 전달 방법 (Calling Conventions)

 콜링 컨벤션 : 스택을 이용하여 파라미터를 전달할 때 스택에 파라미터를 어떤 순서로 넣을 것인지, 전달된 파라미터를 어느 곳에서 해제할 것인가에 따른 여러가지 방법

 * __cdecl

   C 또는 C++ 프로그램에서 파라미터 전달 시 디폴트로 사용하는 방식으로, 파라미터 전달은 오른쪽에서 왼쪽 방향으로 이루어지며 프로시저를 호출한 쪽에서 파라미터에 대한 해제까지 책임짐

 * __stdcall

   Windows API의 프로시저에서 사용하는 콜링 컨벤션 방식으로, 오픈쪽에서 왼쪽 방향으로 스택에 저장하며 파라미터의 해제는 프로시저가 복귀 되기 전에 이루어짐

   함수의 독립성이 뛰어나다는 장점이 있음 (프로시저를 부르기 전에 스택에 파라미터를 쌓아놓고 그 프로시저를 부르기만 하면 그 함수가 리턴되어진 후 호출한 프로시저에 대해 신경쓰지 않아도 되며, 스택을 해제하는 코드가 호출한 프로시저 안에 있으므로 여러 곳에서 호출되더라도 스택을 해제하는 코드는 하나만 존재하므로 코드의 크기가 줄어듬)

 * __fastcall

   처음 두 개의 파라미터는 스택을 사용하지 않고 ecx와 edx 레지스터를 사용하며, 그 이상의 파라미터에 대해서만 오른쪽에서 왼쪽 방향으로 스택에 저장하며, 스택 제거는 __stdcall과 동일함

 

- Windows에서의 스택 구조

 어떤 프로그램이 함수를 부르거나 로컬 변수를 저장하기 위해서는 스택 공간이 필요하며, 윈도우즈에서는 이러한 공간을 스레드가 생성되는 단계에서 함께 만들며, 스레드 생성시 디폴터로 1MB의 메모리를 예약하여 놓고 이 중 1개의 페이지인 4KB만을 실제 메모리와 매핑시켜 놓음으로써 사용 가능하게 하고 또 하나의 메모리는 가드 페이지라는 형태로 만들어 둠

 기본적인 1MB 메모리에서 상위 첫 페이지의 4KB 영역은 Committed 영역으로 프로그램에서 바로 스택 영역으로 사용되어질 수 있는 영역이며, 그 아래 한 페이지인 4KB 영역은 가드 페이지로 윈도우즈에 의해 페이지 보호가 이루어지고 있으며, 그 외의 영역은 단지 예약만 해놓은 상태로 실제적인 물리적 메모리가 할당되지 않은 상태임 (예약 해놓음으로써 이 프로세스 공간에 대한 메모리 할당을 스택 용도 외로 사용하지 못하게 함)

 

- 함수에서의 레지스터 사용

 컴퓨터에서 수행되는 대부분의 연산은 레지스터를 이용해 이루어지는데, 레지스터의 개수는 제한적이기 때문에 함수 내에서의 또 다른 함수 호출 시 레지스터간의 충돌을 고려해야 함

 Windows의 컴파일러들은 이러한 문제를 피하기 위해 Caller-save/Callee-save 레지스터와 같은 규칙을 사용하여 함수들간의 레지스터 사용의 충돌을 피함

 * Caller-save 레지스터 (scratch 레지스터 혹은 volatile 레지스터)

   함수 내에서 어떠한 임시적 저장 없이 마음대로 쓸 수 있는 레지스터로, 호출한 이전 함수에 어떠한 영향도 미치지 않는 레지스터들로 정의

   ex) RAX, RCS, RDX, R8~R11, ST(0)~ST(7), XMM0~XMM5, High half of XMM6~XMM15

 * Callee-save 레지스터

   호출한 함수에 영향을 미칠 수 있기 때문에 해당 레지스터를 사용하기 전에 반드시 저장을 해놓고 사용해야 하는 레지스터로, 해당 함수가 끝났을 때 반드시 이전의 값으로 돌려주어 이 함수를 호출한 함수에 어떠한 영향을 미쳐선 안 되는 레지스터들로 정의

   ex) RBX, RSI, RDI, RBP, R12~R15, XMM6~XMM15

 

- Name Mangling (Name Decoration)

 함수를 선언하거나 전역 변수 등을 선언하였을 때, 사용되어진 이름들은 실제 컴파일 단계에서 일정한 규칙을 가지고 그 이름을 변경하는데 이를 네임 맹글링 혹은 네임 데코레이션이라 부름

 링커(Linker)가 다른 범위에 있는 같은 이름의 함수와 변수들에 대하여 구별할 수 있도록 함으로써 해당 함수 전체의 모듈에서 동일한 방법으로 사용되어지는지 체크할 수 있기 때문에 컴파일러 입장에서 중요한 작업이며, Namespace 안에 있는 변수의 이름에 대해서도 그 Namespace와 합해진 형태의 네임 맹글링을 만들게 됨

 

 

'시스템' 카테고리의 다른 글

dummy 크기 정확히 파악하기 - 출처 http://beist.org  (0) 2016.05.01
코어덤프  (0) 2016.05.01
리눅스 gdb 명령어 (계속 추가)  (0) 2016.02.28
공유메모리  (0) 2016.02.18
시스템 $0x80  (0) 2016.02.11

+ Recent posts