반응형

애들한테 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
,