'과거의 컴퓨터 공부/C 디버깅'에 해당하는 글 7건

반응형

+)설명이 굉장히 잘되어있어서 도움이 많이되었다(디버깅할떄도 아래링크의 예제를 사용하였다)

 http://channelofchaos.tistory.com/55


[exec() source]

#include<sys/types.h>

#include<unistd.h>


main()

{

printf("executing ls\n");

execl("/bin/ls", "ls", "-1", (char*)0);

perror("execl failed to run ls");

exit(1);

}


[exec() debugging]

(gdb) disas main

Dump of assembler code for function main:

0x8048470 <main>: push   %ebp

0x8048471 <main+1>: mov    %esp,%ebp // 프롤로그 

0x8048473 <main+3>: push   $0x8048510// printf()'s string 

0x8048478 <main+8>: call   0x8048394 <printf>// printf() call 

0x804847d <main+13>: add    $0x4,%esp // calling convention 

0x8048480 <main+16>: push   $0x0  //char* 0 

0x8048482 <main+18>: push   $0x804851e // -1 

0x8048487 <main+23>: push   $0x8048521 //ls 

0x804848c <main+28>: push   $0x8048524// /bin/ls 

0x8048491 <main+33>: call   0x8048344 <execl>//execl() call 

0x8048496 <main+38>: add    $0x10,%esp// calling convention 

0x8048499 <main+41>: push   $0x804852c // perror's string

0x804849e <main+46>: call   0x8048364 <perror> //perror's call 

0x80484a3 <main+51>: add    $0x4,%esp//calling convention 

0x80484a6 <main+54>: push   $0x1 // exit's parameter 

0x80484a8 <main+56>: call   0x80483a4 <exit> // exit() call 

0x80484ad <main+61>: add    $0x4,%esp //calling convention 

0x80484b0 <main+64>: leave  // 에필로그 

0x80484b1 <main+65>: ret    


exec()는 딱히 특이사항이 보이지 않는다.

==========================================================================================================================================================================


[fork() source]#include <sys/types.h>

#include <unistd.h>


main()

{

pid_t pid;

pid=fork();

switch(pid)

{

case -1:

printf("fork failed");

break;

case 0:

execl("/bin/ls", "ls", "-1", (char*)0);

printf("exec failed");

break;

default:

wait((int *)0);

printf("ls complete\n");

exit(0);

}


}


[fork() debugging ] 

(gdb) disas main

Dump of assembler code for function main:

0x80484a0 <main>: push   %ebp

0x80484a1 <main+1>: mov    %esp,%ebp // 프롤로그 

0x80484a3 <main+3>: sub    $0x4,%esp // pid 

0x80484a6 <main+6>: call   0x8048388 <fork> // fork () call

0x80484ab <main+11>: mov    %eax,%eax  // NOP ,switch()를 사용하기 이전에 pid를 초기화 해준다 

0x80484ad <main+13>: mov    %eax,0xfffffffc(%ebp) //ebp - 4 에 eax값(case's parameter) 

0x80484b0 <main+16>: mov    0xfffffffc(%ebp),%eax

0x80484b3 <main+19>: cmp    $0xffffffff,%eax // eax 와 -1 을 비교 , case 문 

0x80484b6 <main+22>: je     0x80484c0 <main+32> // 같을경우 main +32로 이동된다 

0x80484b8 <main+24>: test   %eax,%eax  //  NULL check. cmp는 한값에서 다른 한값을 빼고, test 는 두 값을 and 한다 대신 이 두명령은 계산한 값을 저장하지 않는다. flag 레지스터에만 그 흔적이 남는다 .여기서는 case 0 에서 파라미터 0인가를 체크하는 부분이다 

0x80484ba <main+26>: je     0x80484d0 <main +48> // 따라서 같으면 case 0: 으로 jmp 

0x80484bc <main+28>: jmp    0x80484f8 <main+88>// 아닐경우 default 문으로 점프한다 

0x80484be <main+30>: mov    %esi,%esi // NOP 

0x80484c0 <main+32>: push   $0x8048580 // fork failed 문 

0x80484c5 <main+37>: call   0x80483b8 <printf> // printf () call 

0x80484ca <main+42>: add    $0x4,%esp//calling convention 

0x80484cd <main+45>: jmp    0x8048520 <main+128> //에필로그 

0x80484cf <main+47>: nop     

0x80484d0 <main+48>: push   $0x0 // (char*) 0 

0x80484d2 <main+50>: push   $0x804858c// -1 

0x80484d7 <main+55>: push   $0x804858f // ls 

0x80484dc <main+60>: push   $0x8048592 /// /bin/ls

---Type <return> to continue, or q <return> to quit---

0x80484e1 <main+65>: call   0x8048368 <execl> //execl call

0x80484e6 <main+70>: add    $0x10,%esp//calling convention 

0x80484e9 <main+73>: push   $0x804859a//printf's string ]

0x80484ee <main+78>: call   0x80483b8 <printf>// printf() call 

0x80484f3 <main+83>: add    $0x4,%esp// calling convention

0x80484f6 <main+86>: jmp    0x8048520 <main+128>  // 에필로그로 jmp 

0x80484f8 <main+88>: push   $0x0// wait's parameter 

0x80484fa <main+90>: call   0x80483d8 <wait> // wait()call 

0x80484ff <main+95>: add    $0x4,%esp//calling convention 

0x8048502 <main+98>: push   $0x80485a6// printf() 's string 

0x8048507 <main+103>: call   0x80483b8 <printf> // printf() call 

0x804850c <main+108>: add    $0x4,%esp // calling convention 

0x804850f <main+111>: push   $0x0 //exit's parameter

0x8048511 <main+113>: call   0x80483c8 <exit> //exit call 

0x8048516 <main+118>: add    $0x4,%esp //calling convention 

0x8048519 <main+121>: lea    0x0(%esi,1),%esi //nop 

0x8048520 <main+128>: leave // 에필로그   

0x8048521 <main+129>: ret    

0x8048522 <main+130>: nop    



0x80484ab <main+11>: mov    %eax,%eax  // NOP 

0x80484be <main+30>: mov    %esi,%esi // NOP 

0x80484cf <main+47>: nop 

반응형

'과거의 컴퓨터 공부 > 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
,
반응형

참조 : http://msdn.microsoft.com/ko-kr/library/9yky46tz.aspx


[source]

// crt_fflush.c #include <stdio.h> int main( void ) { int integer; char string[81]; // Read each word as a string. printf( "Enter a sentence of four words with scanf: " ); for( integer = 0; integer < 4; integer++ ) { scanf( "%s", string, sizeof(string) ); printf( "%s\n", string ); } // You must flush the input buffer before using gets. // fflush on input stream is an extension to the C standard fflush( stdin ); printf( "Enter the same sentence with gets: " ); gets( string); printf( "%s\n", string ); }


//MS에서 제공한 test source를 사용햇는데 conio 헤더가 레뎃6.2버전에서는 

존재하지 않아서 약간수정하고 gets_s와 scanf_s를 각각 gets 와 scanf로 

수정해준후 진행하였다


[디버깅]

(gdb) disas main Dump of assembler code for function main: 0x8048490 <main>: push %ebp 0x8048491 <main+1>: mov %esp,%ebp // SFP 

// | SFP | RET    ↑esp,ebp

0x8048493 <main+3>: sub $0x58,%esp //

// | stack [88] | SFP | RET

↑esp,ebp

스택은 보통 4의배수로 할당받기 때문에 이렇게 85byte 가아닌 88byte를

할당된다

0x8048496 <main+6>: push $0x80485a0 //printf()'s string 0x804849b <main+11>: call 0x80483c8 <printf>// printf() call 0x80484a0 <main+16>: add $0x4,%esp // calling convention 0x80484a3 <main+19>: movl $0x0,0xfffffffc(%ebp)//ebp -4에 0 을

| stack [81]|integer= 0 | SFP | RET 

↑ebp,esp

0x80484aa <main+26>: lea 0x0(%esi),%esi// NOP 0x80484b0 <main+32>: cmpl $0x3,0xfffffffc(%ebp)//ebp -4 부분과

// | stack [81]| integer=0 | SFP | RET 

↑ebp,esp

0x80484b4 <main+36>: jle 0x80484b8 <main+40>// 같거나 작으면 

//main+40으로 jmp 0x80484b6 <main+38>: jmp 0x80484e1 <main+81>//크면 main+81로

// jmp 0x80484b8 <main+40>: push $0x51 //81바이트 push 0x80484ba <main+42>: lea 0xffffffa8(%ebp),%eax//ebp-88을eax로

0x80484bd <main+45>: push %eax // eax에 인자를 집어넣고 0x80484be <main+46>: push $0x80485cc // 형식포멧 집어넣어주고 0x80484c3 <main+51>: call 0x8048398 <scanf> // scanf() call 0x80484c8 <main+56>: add $0xc,%esp//calling conevntion 0x80484cb <main+59>: lea 0xffffffa8(%ebp),%eax // input값 0x80484ce <main+62>: push %eax // push 0x80484cf <main+63>: push $0x80485cf //string 0x80484d4 <main+68>: call 0x80483c8 <printf> // printf() call 0x80484d9 <main+73>: add $0x8,%esp // calling convention ---Type <return> to continue, or q <return> to quit--- 0x80484dc <main+76>: incl 0xfffffffc(%ebp)//long형으로

//???? 0x80484df <main+79>: jmp 0x80484b0 <main+32>// for()로 jmp 0x80484e1 <main+81>: mov 0x80496f0,%eax// &stdin 0x80484e6 <main+86>: push %eax // push 0x80484e7 <main+87>: call 0x8048378 <fflush>//fflush() call 0x80484ec <main+92>: add $0x4,%esp//calling convention 0x80484ef <main+95>: push $0x80485e0 //printf()'s string 0x80484f4 <main+100>: call 0x80483c8 <printf>// printf() call 0x80484f9 <main+105>: add $0x4,%esp//calling convention 0x80484fc <main+108>: lea 0xffffffa8(%ebp),%eax //string을 입력받음 0x80484ff <main+111>: push %eax //push 0x8048500 <main+112>: call 0x8048388 <gets> //gets() call 0x8048505 <main+117>: add $0x4,%esp // calling convention 0x8048508 <main+120>: lea 0xffffffa8(%ebp),%eax//string 0x804850b <main+123>: push %eax// push 0x804850c <main+124>: push $0x80485cf //printf()'s string 0x8048511 <main+129>: call 0x80483c8 <printf>// printf() call 0x8048516 <main+134>: add $0x8,%esp //calling convention 0x8048519 <main+137>: leave //에필로그 0x804851a <main+138>: ret


반응형

'과거의 컴퓨터 공부 > C 디버깅' 카테고리의 다른 글

exec() v.s fork ()  (0) 2014.09.22
gets() v.s scanf()  (0) 2014.09.20
if else  (0) 2014.09.19
for()  (0) 2014.09.19
main함수 옆에 파라메터 넣어보기  (0) 2014.09.18
,
반응형
gets() 함수
- 1줄을 입력할 때는 gets() 함수를 사용한다.
- 줄 바꿈(엔터)키를 입력할때까지 1줄을 그대로 읽어들인다.
- 읽어들인 문자열은 줄 바꿈 하는 곳에서 \0이 붙여진 배열에 저장됩니다.
참고( http://lechocolat.egloos.com/viewer/441439) 

[gets source ] 

#include<stdio.h>

int main(void)
{
char str[100];
puts("input:\n");
gets(str);
printf("output : %s.\n", str);
return 0;
}


[디버깅] 

(gdb) disas main

Dump of assembler code for function main:

0x8048430 <main>: push   %ebp

0x8048431 <main+1>: mov    %esp,%ebp //프롤로그 

 |SFP | RET | 

↑ESP,EBP

0x8048433 <main+3>: sub    $0x64,%esp// esp -100

| BUF[100]|SFP | RET |

↑ESP         ↑EBP

0x8048436 <main+6>: push   $0x80484c0 // PUTS의 문자열 INPUT : 

0x804843b <main+11>: call   0x8048340 <puts> // PUTS() 

0x8048440 <main+16>: add    $0x4,%esp // CALLING CONVENTION

0x8048443 <main+19>: lea    0xffffff9c(%ebp),%eax // INPUT 값을 집어넣어주기위해 EAX 에다가 //EBP-100(buf[100] 앞부분) 을 집어넣어준다  

| BUF[100]|SFP | RET |

↑ESP,EAX  ↑EBP

0x8048446 <main+22>: push   %eax // 옮겻으니 집어넣어줘야 되겟고 

0x8048447 <main+23>: call   0x8048330 <gets>//gets() call 

0x804844c <main+28>: add    $0x4,%esp// CALLING CONVENTION 

0x804844f <main+31>: lea    0xffffff9c(%ebp),%eax// input값을 집어넣어준다 . 

0x8048452 <main+34>: push   %eax // input value 

0x8048453 <main+35>: push   $0x80484c8// 문자열 

0x8048458 <main+40>: call   0x8048370 <printf> // printf() call 

0x804845d <main+45>: add    $0x8,%esp // 문자열과 input 값을 집어넣기위해 사용한 str형식문자열을 반환 , 마찬가지로calling convention 

0x8048460 <main+48>: xor    %eax,%eax // eax 초기화 

0x8048462 <main+50>: jmp    0x8048464 <main+52> // 딱히 jump 가 나올필요가 없다고 생각을하는데 왜 여기에 jmp문이존재할까? 

0x8048464 <main+52>: leave   // 에필로그 

0x8048465 <main+53>: ret 

------------------------------------------------------------------------------------

V.S

------------------------------------------------------------------------------------

[scanf() 소스] int main(void)

{

char str[100];

puts("input:\n");

scanf("%s",&str); //간단하게 GETS()에서 SCANF()로 바꿔주었다 SCANF 는 GETS()와는달리 스페이스바가 입력되기전까지 입력받는다 HELLO WORLD 하면 HELLO 이후는 짤려짐 

printf("output : %s.\n", str);

return 0;

}


[디버깅 ] 

(gdb) disas main

Dump of assembler code for function main:

0x8048430 <main>: push   %ebp

0x8048431 <main+1>: mov    %esp,%ebp // 프롤로그 

0x8048433 <main+3>: sub    $0x64,%esp // esp -100 

|buf [100]  | SFP  | RET

↑ESP         ↑EBP

0x8048436 <main+6>: push   $0x80484d0 // PUTS'S STRING 

0x804843b <main+11>: call   0x8048330 <puts> // PUTS CALL 

0x8048440 <main+16>: add    $0x4,%esp //CALLING CONVENTION 

0x8048443 <main+19>: lea    0xffffff9c(%ebp),%eax // EBP -100 (BUF[100]부분을 EAX에 넣어준다 ) 

0x8048446 <main+22>: push   %eax // PUSH 

0x8048447 <main+23>: push   $0x80484d8// 형식문자열 

0x804844c <main+28>: call   0x8048340 <scanf> //SCANF CALL 

0x8048451 <main+33>: add    $0x8,%esp//CALLING CONVENTION 

0x8048454 <main+36>: lea    0xffffff9c(%ebp),%eax // 입력받은것을 다시 EAX로 

0x8048457 <main+39>: push   %eax// PUSH 

0x8048458 <main+40>: push   $0x80484db // STR 

0x804845d <main+45>: call   0x8048370 <printf> // PRINTF() CALL 

0x8048462 <main+50>: add    $0x8,%esp //CALLING CONVENTION 

0x8048465 <main+53>: xor    %eax,%eax // EAX 초기화 

0x8048467 <main+55>: jmp    0x8048470 <main+64> // 여기도 마찬가지로 JMP 문이생긴다 

0x8048469 <main+57>: lea    0x0(%esi,1),%esi // SCANF는 GETS()와는 달리 이런 부분이 생겻는데왜생겻지 ; 

0x8048470 <main+64>: leave   //에필로그 

0x8048471 <main+65>: ret    

반응형

'과거의 컴퓨터 공부 > C 디버깅' 카테고리의 다른 글

exec() v.s fork ()  (0) 2014.09.22
fflush()  (0) 2014.09.20
if else  (0) 2014.09.19
for()  (0) 2014.09.19
main함수 옆에 파라메터 넣어보기  (0) 2014.09.18
,
반응형

[if source ]

#include <stdio.h>


int main(){

int i;

printf("input :\n")


scanf("%d",&i);

if(i==7){

printf("Yes!!\n");

}

return 0;

}

else를 추가하지 않은상태다 

[debugging]

Dump of assembler code for function main:

0x8048400 <main>: push   %ebp

0x8048401 <main+1>: mov    %esp,%ebp//프롤로그 

|int i | SFP |RET | a==7

       ↑esp,ebp

0x8048403 <main+3>: sub    $0x4,%esp // int i를 로컬로 선언햇기때문에 이처럼4를 뺴줫다 

|int i           | SFP |RET |

↑esp          ↑ebp

0x8048406 <main+6>: push   $0x80484a0 // input: 이라는 문자열을 꺼내온다 

0x804840b <main+11>: call   0x804833c <printf> // printf() call  진행상황 : int i 가 선언되었고, printf("input:"\n");까지 나온상태 

0x8048410 <main+16>: add    $0x4,%esp // int  i에 값이들올것이므로 esp 가 다시 4 상승하게 된다 

|int i          | SFP |RET | a==7

                 ↑esp,ebp

0x8048413 <main+19>: lea    0xfffffffc(%ebp),%eax // ebp-4부분(ret)를 eax에 넣어준다 

|int i(scanf) | SFP         | RET | 

↑eax          ↑esp,ebp    

0x8048416 <main+22>: push   %eax // eax가 가리키는 부분을 집어넣는다 

0x8048417<main+23>: push   $0x80484a9 //scanf 의 %d 부분이될것이다 

0x804841c <main+28>: call   0x804830c <scanf> //scanf call //입력받는다 

0x8048421 <main+33>: add    $0x8,%esp// calling convention

0x8048424 <main+36>: cmpl   $0x7,0xfffffffc(%ebp) //0x7 과 ebp -4(input값)를 비교한다 

0x8048428 <main+40>: jne    0x8048437 <main+55> //비교햇을때와 같지않을경우  에필로그로 점프해서 종료한다 

0x804842a <main+42>: push   $0x80484ac // 문자열 Yes ! 

0x804842f <main+47>: call   0x804833c <printf>// printf () call 

0x8048434 <main+52>: add    $0x4,%esp//calling convetion 

0x8048437 <main+55>: xor    %eax,%eax //eax의 초기화 

0x8048439 <main+57>: jmp    0x8048440 <main+64> // 에필로로 점프 

0x804843b <main+59>: nop    

0x804843c <main+60>: lea    0x0(%esi,1),%esi // NOP보다 빠른 NOP 인데 if 문을사용하면 if 문이끝난뒤에 NOP와 lea 0x0 ... 이생기나보다 for 문같은경우는 앞뒤로 lea ... 이생겻는데 왜이런지는 다음주에 가서 물어봐야겟다 

0x8048440 <main+64>: leave   // 에필로그 

0x8048441 <main+65>: ret 


[if else source ]// 위의 소스에다가 추가하엿음

 int main(){

int i;

printf("input :\n");

scanf("%d",&i);

if(i==7){

printf("Yes!!\n");

}


else{

printf("noph!\n");}

return 0;

}

[debugging]

Dump of assembler code for function main:

0x8048400 <main>: push   %ebp

0x8048401 <main+1>: mov    %esp,%ebp

0x8048403 <main+3>: sub    $0x4,%esp

0x8048406 <main+6>: push   $0x80484b0

0x804840b <main+11>: call   0x804833c <printf>

0x8048410 <main+16>: add    $0x4,%esp

0x8048413 <main+19>: lea    0xfffffffc(%ebp),%eax

0x8048416 <main+22>: push   %eax

0x8048417 <main+23>: push   $0x80484b9

0x804841c <main+28>: call   0x804830c <scanf>

0x8048421 <main+33>: add    $0x8,%esp //calling convention 

0x8048424 <main+36>: cmpl   $0x7,0xfffffffc(%ebp)

0x8048428 <main+40>: jne    0x8048440 <main+64>

0x804842a <main+42>: push   $0x80484bc

0x804842f <main+47>: call   0x804833c <printf>

0x8048434 <main+52>: add    $0x4,%esp//calling convention

0x8048437 <main+55>: jmp    0x804844d <main+77>

0x8048439 <main+57>: lea    0x0(%esi,1),%esi

0x8048440 <main+64>: push   $0x80484c3

0x8048445 <main+69>: call   0x804833c <printf> // else문이 추가되었다 

0x804844a <main+74>: add    $0x4,%esp // calling convetion 

0x804844d <main+77>: xor    %eax,%eax

---Type <return> to continue, or q <return> to quit---

0x804844f <main+79>: jmp    0x8048451 <main+81>

반응형

'과거의 컴퓨터 공부 > C 디버깅' 카테고리의 다른 글

fflush()  (0) 2014.09.20
gets() v.s scanf()  (0) 2014.09.20
for()  (0) 2014.09.19
main함수 옆에 파라메터 넣어보기  (0) 2014.09.18
변수, printf 디버깅  (0) 2014.09.17
,
반응형

[source]

#include<stdio.h>


int main(int a)

{

  a=3;

for(a;a<10;a++){

printf("Hello\n");}

return 0;

}


[debugging]

0x80483d0 <main>: push   %ebp

0x80483d1 <main+1>: mov    %esp,%ebp//SFP|ret 

0x80483d3 <main+3>: movl   $0x3,0x8(%ebp) //SFP| RET | int a=3

0x80483da <main+10>: lea    0x0(%esi),%esi //NOP(cf: http://stackoverflow.com/questions/10505690/what-is-the-meaning-of-lea-0x0esi-esi) 

//많이 반복되는 부분앞에서는 전형적으로 NOP를 깔아줘야한다함.이러한 NOP들은 빠르게 fetch engine을 통해서 소비가된다(이러한  fetch engine들은 실행했을때가아닌 prefetch때에 완성되어있다)

0x80483e0 <main+16>: cmpl   $0x9,0x8(%ebp) //ebp + 8 (ret  바로 뒷부분) 의값과 0x9를 비교 

0x80483e4 <main+20>: jle    0x80483e8 <main+24>// 낮으면 main +24로 점프

0x80483e6 <main+22>: jmp    0x8048400 <main+48> // 크면 main + 48 로 점프 

0x80483e8 <main+24>: push   $0x8048460 // HELLO 

0x80483ed <main+29>: call   0x8048308 <printf> //printf() 

0x80483f2 <main+34>: add    $0x4,%esp // esp+4 (intger 크기, a가 또사용되니까 esp 

0x80483f5 <main+37>: incl   0x8(%ebp)// a값 증가 (incl =long increase ) 

0x80483f8 <main+40>: jmp    0x80483e0 <main+16>//for문으로 다시 점프 

0x80483fa <main+42>: lea    0x0(%esi),%esi// 이부분에 왜 NOP가 존재하는지는 아직 모르겟다 ; 

0x8048400 <main+48>: xor    %eax,%eax // eax 비우기 

0x8048402 <main+50>: jmp    0x8048404 <main+52> //완료됫을시 retn으로 

0x8048404 <main+52>: leave  

0x8048405 <main+53>: ret    



반응형

'과거의 컴퓨터 공부 > C 디버깅' 카테고리의 다른 글

fflush()  (0) 2014.09.20
gets() v.s scanf()  (0) 2014.09.20
if else  (0) 2014.09.19
main함수 옆에 파라메터 넣어보기  (0) 2014.09.18
변수, printf 디버깅  (0) 2014.09.17
,
반응형


[source]

#include <stdio.h>


int main(int a){

a=3;

}

int main 옆에 존재하는 파라메터는 엄연히 '전역변수다' 

확인해보자 

[디버깅]

(gdb) disas main

Dump of assembler code for function main:

0x8048398 <main>: push   %ebp

0x8048399 <main+1>: mov    %esp,%ebp

0x804839b <main+3>: movl   $0x3,0x8(%ebp)

0x80483a2 <main+10>: leave  

역시 전역변수 여서 그런지 main 스택상에 보이지 않는다 헌데 특별한점이있다면, 굵은부분으로 표시한 main+3  부분이다 . 잘보면 3을 ebp +8 부분에 집어넣어 주고 있다. 이부분은 어디일까? 


[break]

eip &0x8048398:     0xbffffb6c(esp)

eip &0x8048399 : SFP생성뒤 직전 ebp : 0xbffffb88(SFP)  esp: 0xbffffb68 (6c-4)

eip &0x804839b : ebp : 0xbffffb68 esp: 0xbffffb68 (같아진다) 

eip &0x8048a2 : ebp :0xbffffb68 esp :0xbffffb

eip &0x8048a


[스택의 상황 ]

push ebp (SFP생성)     |SFP |RET


mov esp ,ebp             |SFP(ESP,EBP) | RET


movl 0x3,0x8(%ebp)       |SFP(ESP)| RET | 3(int a)


이런식으로 된다. 따라서 intger a의 값은  환경변수|스택|데이터|코드 중 데이터영역중에 .bss영역에 들어갈것이다(왜냐하면 .data영역의 경우에는 전역변수가 초기화(값이 설정)됫을떄에 들어가게 되고.bss 는 초기화 되지않은 값이 들어간다 )



반응형

'과거의 컴퓨터 공부 > 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
변수, printf 디버깅  (0) 2014.09.17
,
반응형

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