애들한테 esp, ebp ,스택프레임이 어떻게 구성되는지 쉽게 설명해주려고 만들었던 예제다
[완전 기초 소스]
main()
{
int a = 10;
printf("%d\n", a);
}
[디버깅]
(gdb) disas main
Dump of assembler code for function main:
0x80483c8 <main>: push %ebp// SFP값이 된다 브레이크를 걸고 들어가보면 SFP 에 들어있는 값을 확인할 수 있다.
0x80483c9 <main+1>: mov %esp,%ebp // esp를 ebp의 위치로 옮긴다. 현재상태는 esp, ebp 같은 값을 가리키게된다 .SFP 의 윗부분을 가리키게 되겟지
0x80483cb <main+3>: sub $0x4,%esp // int 변수 한개를 선언했다 4바이트가 필요하므로 esp에서 4를 빼줌으로써 esp가 a 를 가리키게 되겟다. 그림을 그리고싶은데... 귀찮다.
0x80483ce <main+6>: movl $0xa,0xfffffffc(%ebp) //ebp에 변수의 값을 넣어주게된다 계산기 두드려보면0xfffffffc는 -4값이라는 것을 알수 있다 . ebp-4이므로 ebp가 현재 가리키고있는 위치에서 -4를 빼준 위치를 가리키게 되고 그곳에는 0xa(10)이라는 값이 들어가게된다 .즉 int a=10 에서 int a 는 esp로 10이라는 값은 ebp로
0x80483d5 <main+13>: mov 0xfffffffc(%ebp),%eax//ebp-4의값(현재는 당연히 0xa값이 되겟다) 을 사용하기 위해서 eax옮겻다
0x80483d8 <main+16>: push %eax // 스택에 집어넣어주게된다 10
0x80483d9 <main+17>: push $0x8048440 // %d 형식변수
0x80483de <main+22>: call 0x8048308 <printf> // printf() call
0x80483e3 <main+27>: add $0x8,%esp // 다쓴 다음 다시 값을 회수해 간다.
0x80483e6 <main+30>: leave // mov ebp,esp pop ebp
0x80483e7 <main+31>: ret // pop eip
[다른 함수를 call 하는 기초적인 소스 ]
sub()
{
int c;
int d;
}
main()
{
int a;
int b;
sub();
}
마찬가지로 굉장히 쉬운 소스이긴한데, sub라는 함수를 따로 만들어줫다. 어떤식으로 되는지 까보도록하자
(gdb) disas main
Dump of assembler code for function main:
0x80483a0 <main>: push %ebp // SFP 부분
0x80483a1 <main+1>: mov %esp,%ebp // esp를 ebp 가 위치하는 곳으로옮긴다 마찬가지로 현재상황은 SFP 의 윗부분을 가르키게 될것이다. 맨날 함수 프롤로그 에필로그 그러고댕겻는데 이제와서 다시 생각해보니 SFP를 넣어주고 SFP 의 윗부분을 esp ,ebp가 가르키게 하는과정까지가 함수의 '프롤로그'이다.
0x80483a3 <main+3>: sub $0x8,%esp // 여기서는 main 안에 두개의 integer변수를 선언해주엇으므로 8byte가 필요로 되므로 8byte를 뺴준다
0x80483a6 <main+6>: call 0x8048398 <sub> //함수 call call()을 보고 오자
0x80483ab <main+11>: leave //mov ebp,esp pop ebp 함수의 에필로그
0x80483ac <main+12>: ret // pop ei p
0x80483ad <main+13>: nop
0x80483ae <main+14>: nop
0x80483af <main+15>: nop
End of assembler dump.
(gdb) disas sub
Dump of assembler code for function sub:
0x8048398 <sub>: push %ebp // 마찬가지로 SFP 를 넣어주게된다
0x8048399 <sub+1>: mov %esp,%ebp // SFP 위를 esp,ebp가 가리키게해주고
0x804839b <sub+3>: sub $0x8,%esp // 두개의 integer 변수르 선언해준다
0x804839e <sub+6>: leave // mov ebp,esp pop ebp
0x804839f <sub+7>: ret
End of assembler dump.
(gdb)
+) 근데여기서 한가지 의문점이 생겻엇다 ex1 을 봐보면 add 명령으로 스택을 정리해주는데, ex2의경우는 그런게 전혀 보이지 않는다 .. 멘붕이왔는데 알고보니 calling convetion때문이라고한다(매번 도움주시는 chop형님에게 감사드립니다.. )
printf와 같은 경우 일반적으로 파라메터가 가변적이다. 그래서 호출하는 쪽에서 스택을 정리하게 되는거고 , 반대로 파라메터가 고정일 경우에는 호출된 함수에서 정리를 하고 나오게 된다. 따라서 위와 같은 상황이 발생하게 된다
+) cdecl 의 경우 printf 같은거고, stdcall이 sub 같은 명령이다 ( 따라서 cdecl의 경우 함수가 가변적이므로 호출하는 쪽에서 스택을 정리하게 되고, stdcall의 경우 파라메터가 고정이므로 호출되는 함수쪽에서 정리를 하고나오게 되시겟다)
+)시스템 시작한지 7개월가량 되가는데 이걸 이제야 알앗다니 정말한심하다.. 더열심히해야겟다.
'과거의 컴퓨터 공부 > C 디버깅' 카테고리의 다른 글
fflush() (0) | 2014.09.20 |
---|---|
gets() v.s scanf() (0) | 2014.09.20 |
if else (0) | 2014.09.19 |
for() (0) | 2014.09.19 |
main함수 옆에 파라메터 넣어보기 (0) | 2014.09.18 |