메모리 구조
어셈블리어
mov : Source에서 Destination으로 데이터를 복사한다.
add : Destination에 Source의 값을 더해서 Destination에 저장한다.
sub : Destination에 Source의 값을 빼서 Destination에 저장한다.
push : 스택에 값을 넣는다. ESP의 값이 4만큼 줄어들고 이 위치에 새로운 값이 채워진다.
pop : ESP 레지스터가 가리키고 있는 위치의 스택 공간에서 4byte 만큼을 Destination 피연산자에 복사하고 ESP 레지스터의 값에 4를 더한다.
레지스터
eax : 산술, 논리연산을 수행한 값이 저장되며 함수의 리턴값이 저장됨.
eip : 다음에 실행하여야 할 명령어가 존재하는 메모리 주소가 저장됨.
ebp : 스택의 시작지점 주소가 저장된다. (스택의 처음 부분)
esp : 스택의 마지막부분을 저장하며, push, pop 에따라 4씩 값이 변한다.
32비트 환경에서 실험해보기로 하겠다.
디버깅해볼 간단한 코드이다.
변수 a 와 b를 선언하고, a 에 1을 넣고
b에는 a에서 2를 뺀 값을 넣고 0을 리턴한다.
gdb로 디버깅하여 어셈코드를 봐보도록 하겠다.
처음에 push ebp 를 하고, mov esp, ebp 를 한다.
이게 함수 프롤로그라는 것으로, 함수가 시작할때 하는 행위이다.
해설하자면, push ebp는 Main이 끝나고 돌아갈 곳의 주소를 스택에 저장하는 것이다.
(이때 푸쉬에 의해 esp가 4만큼 움직인다.)
그다음, ebp 에 esp의 값을 저장한다.(ebp를 esp로 옮긴다.)
------------------------
ret
------------------------
sfp (저장된 ebp)
------------------------ << esp & ebp
그후, sbu $0x10 $esp 에 의해 10바이트의 공간이 할당된다.
(변수 a 와 b를 위한 공간이다.)
---------------------------
ret
----------------------------
sfp(4byte)
----------------------------- ebp
b(4byte)
------------------------------
a(4byte)
------------------------------
dummy(2byte)
------------------------------- esp
movl a , b 는 b 에 a를 넣으란 뜻이다.
고로 movl 0x1 -0x8(%ebp)는
ebp 에서 8바이트밑에 1을 넣으라는것인데,
코드에서 1를 넣는것은 a이므로 -0x8(%ebp) 는 a가 된다.
이후 mov -0x8(%ebp), %eax 로
eax 레지스터에 a의 값을 넣는다.
그후 sub 0x2, %eax로, 1-2 를 수행하여 -1을 얻는다.
그후 mov %eax, -0x4(%ebp)로 ebp의 4바이트밑에있는 b에 -1을 넣는다.
이후 return 0을 수행하기 위해 리턴값을 저장하는 레지스터인 eax에 0을 넣는다.
(mov 0x0, %eax)
이렇게 코드가 어셈으로 끝났다.
이후의 명령은 leave와 ret은 각각 이런 명령이다.
leave = mov ebp, esp + pop ebp
ret = pop eip + jmp eip
그러니까, 스택의 끝을 뜻하는 esp를 ebp로 옮기고, pop ebp 하여 저장해 둔 sfp를
ebp에 담고 ret 명령을 실행한다.
ret은 다음 명령을 뜻하든 eip를 팝하고 eip 에는 ret의 주소가 들어간다., 그 주소로 점프하여 실행한다.
이렇게 함수가 끝나고 원래 시작된 주소로 돌아간다.
'C' 카테고리의 다른 글
구조체 struct (0) | 2016.05.05 |
---|---|
링크드리스트, 스택, 큐 (0) | 2016.04.21 |
문자치환 (0) | 2016.04.10 |
swap! (0) | 2016.04.10 |
배열에 데이터들을 입력하여 특정값을 특정값으로 치환하기. (0) | 2016.04.07 |