============================== Assembly =================================


7 =>    0x0000000000400548 <+20> : callq   0x400526 <my_func>      일 때,


                                                                       call : 함수 호출시 사용하는 니모닉(mnemonic)

                                                                       call = push + jmp

                                                                               push 는 복귀 주소를 스택에 저장함

                                                                               jmp 는 call 뒤에 오는 주소값으로 이동시킴


<call 전>

(gdb) p $rsp

$1 = 0x7fffffffdd20                              


<call 이후>

(gdb) p $rsp

$2 = 0x7fffffffdd18                       // 주소가 바뀌어 있다 !!  8Byte 할당 받음..

(gdb) x $rsp

$3 = ffdd18 : 0x0040054d             // 들어있는 값을 확인 - 함수가 끝난 후, 복귀할주소 저장되어 있음


============================== Computer =================================


7. callq 니모닉 처리

0x7fffffffdd18

 

0x7fffffffdd30


 숫자 '3'

 

  숫자 '3'

   

 rsp 레지스터

 

 rbp 레지스터

 eax 레지스터

 

 edi 레지스터

   



 

 Stack

 메모리 주소


rbp> 

0x00400570

 


 

0x7fffffffdd38
 

0x7fffffffdd30 

 

숫자 3

 


0x7fffffffdd28 

 



 



<이전 rsp 위치


0x7fffffffdd20 

 

 

0x0040054d

 

<rsp
 

0x7fffffffdd18
 

 0x00400570

 

   바뀔 rsp

<(-0x8(%rsp))



0x7fffffffdd10 


함수가 끝난 후 복귀해야 할 주소 : 0x0040054d 는 현재, rsp가 가리키는 스택에 들어있음

함수가 끝나면 Jump니모닉을 실행할 터이고.. 그럼 저 주소로 Jump 함..

그런데.. 가만보니.. my_func 함수가 실행되는 동시에.. 스택프레임이 다시 생성되고 있음 !! 


(gdb) x $rsp -0x8

$4 = ffdd10 : 0x00400570  





============================ 함수 안으로 들어가서.. ===========================


  int my_func(int n){

      return n* 2;

   }

 


  0x0000000000400526 <+0>  : push   %rbp

  0x0000000000400527 <+1>  : mov    %rsp, %rbp

  0x000000000040052a <+4>  : mov    %edi, -0x4(%rbp)

  0x000000000040052d <+7>  : mov    -0x4(%rbp), %eax

  0x0000000000400530 <+10> : add    %eax, %eax

  0x0000000000400532 <+12> : pop    %rbp

  0x0000000000400533 <+13> : retq






============================== Assembly =================================


8 =>     0x0000000000400526 <+0>  : push    %rbp    는, 위에서 callq 호출시 실행되었고


(gdb) p $rsp

$4 = 0x7fffffffdd10                   // 스택 프레임 생성 : 공간할당 -> rbp 주소 따라가면 있는 데이터 밀어넣기 





============================== Assembly =================================


9 =>     0x0000000000400527 <+1>  : mov    %rsp, %rbp    일 때,

                                                 rsp의 값을 rbp에 넣어라

(gdb) p $rsp

$5 = 0x7fffffffdd10                      // 8Byte 공간할당 됨, 돌아갈 주소의 포인터를 갖고 있음 

(gdb) p $rbp

$6 = 0x7fffffffdd10                      // rsp == rbp ,  동일한 상황이 됨

(gdb) x $rsp

0x7ffdd10 : 0xffdd30                    // 이전 rbp 위치를 가리키고 있다 -ㅁ- ;;


============================== Computer =================================


9. 스택프레임 생성 - rbp 설정

0x7fffffffdd10

 

0x7fffffffdd10


 숫자 '3'

 

  숫자 '3'

   

 rsp 레지스터

 

 rbp 레지스터

 eax 레지스터

 

 edi 레지스터

   



 

 Stack

 메모리 주소



이전 rbp위치>

0x00400570

 

 

0x7fffffffdd38

0x7fffffffdd30 

 

숫자 3

 


0x7fffffffdd28 

 



 





0x7fffffffdd20 

 

 

0x0040054d

 


 

0x7fffffffdd18

 
 
rbp>

 0x00400570

 
 
<rsp


 
0x7fffffffdd10 
    






============================== Assembly =================================


10 =>     0x000000000040052a <+4>  : mov    %edi, -0x4(%rbp)    일 때,

                                                 edi 의 값을 rbp-0x4 위치에 넣어라

(gdb) p $rsp

$7 = 0x7fffffffdd10                      

(gdb) p $rbp

$8 = 0x7fffffffdd10                     

(gdb) x $rbp -0x4

0x7ffdd0c : 0x00000003                        // edi 레지스터의 값을 스택에 넣었음을 확인


============================== Computer =================================


10. (의미없는 수행) edi 레지스터의 데이터를 스택에 넣어라

0x7fffffffdd10

 

0x7fffffffdd10


 숫자 '3'

 

 숫자 '3'

   

 rsp 레지스터

 

 rbp 레지스터

 eax 레지스터

 

 edi 레지스터

   



 

 Stack

 메모리 주소




0x00400570

 

 

0x7fffffffdd38

0x7fffffffdd30 

 

숫자 3

 


0x7fffffffdd28 

 



 





0x7fffffffdd20 

 

 

0x0040054d

 


 

0x7fffffffdd18

 
 
rbp>

 0x00400570

 
 
<rsp



0x7fffffffdd10 


 
-0x4(%rbp)>

 숫자 '3'

 

 


0x7fffffffdd0c






============================== Assembly =================================


11 =>     0x000000000040052d <+7>  : mov    -0x4(%rbp), %eax   일 때,

                                                   rbp-0x4 의 값을 eax 레지스터에 넣어라

(gdb) p $eax

$9 = 3                                  // eax 레지스터에 이미 3 들어 있는데.. 또 3을 넣다니.. 의미없는 수행과정      


============================== Computer =================================


11. (의미없는 수행) rbp-0x4의 값을 eax 레지스터에 넣어라

0x7fffffffdd10

 

0x7fffffffdd10


 숫자 '3'

 

 숫자 '3'

   

 rsp 레지스터

 

 rbp 레지스터

 eax 레지스터

 

 edi 레지스터

   



 

 Stack

 메모리 주소




0x00400570

 

 

0x7fffffffdd38

0x7fffffffdd30 

 

숫자 3

 


0x7fffffffdd28 

 



 





0x7fffffffdd20 

 

 

0x0040054d

 


 

0x7fffffffdd18

 
 
rbp>

 0x00400570

 
 
<rsp



0x7fffffffdd10 


 

-0x4(%rbp)>

 숫자 '3'

 


 

0x7fffffffdd0c







============================== Assembly =================================


12 =>      0x0000000000400530 <+10> : add    %eax, %eax   일 때,

                                                    eax += eax 

(gdb) p $eax

$10 = 6                                  // 3 += 3 이므로, eax 레지스터의 값이 6으로 갱신됨


============================== Computer =================================


12. 3 더하기 3은~?

0x7fffffffdd10

 

0x7fffffffdd10


 숫자 '6'

 

 숫자 '3'

   

 rsp 레지스터

 

 rbp 레지스터

 eax 레지스터

 

 edi 레지스터

   







============================== Assembly =================================


13 =>       0x0000000000400532 <+12> : pop    %rbp   일 때,

                                                    1. rbp 는 : 이전 rbp위치 에 담겨있는 정보 - 돌아갈 주소로 감 

 2. rsp 는 : +0x8 됨.. 할당 전의 주소로 복귀

(gdb) p $rbp

$11 = 0x7fffffffdd30  


(gdb) p $rsp

$12 = 0x7fffffffdd18      


(gdb) x $rbp

0x7fffffffdd30 = 0x00400570


(gdb) x $rsp

0x7fffffffdd18 = 0x0040054d    


(gdb) p $rip

$12 = 0x400533  <my_func+13>


============================== Computer =================================


13. pop 니모닉 실행

0x7fffffffdd18

 

0x7fffffffdd30


 숫자 '6'

 

 숫자 '3'

   

 rsp 레지스터

 

 rbp 레지스터

 eax 레지스터

 

 edi 레지스터

   



 

 Stack

 메모리 주소



rbp>

0x00400570

 

 

0x7fffffffdd38

0x7fffffffdd30 

 

숫자 3

 


0x7fffffffdd28 

 



 





0x7fffffffdd20 

 

 

0x0040054d

 

<rsp

 

0x7fffffffdd18

 
 
이전 rbp위치>

 0x00400570

 
 
<이전 rsp위치



0x7fffffffdd10 


 

-0x4(%rbp)>

 숫자 '3'

 


 

0x7fffffffdd0c







============================== Assembly =================================


14 =>    0x0000000000400533 <+13> : retq    시전 과 동시에, <my_func>함수를 빠져나옴

                                                                          0x000000000040054d <+25> 할 차례라고 가리키고 있음



============================== Computer =================================


14. 

0x7fffffffdd18

 

0x7fffffffdd30


 숫자 '6'

 

  숫자 '3'

   

 rsp 레지스터

 

 rbp 레지스터

 eax 레지스터

 

 edi 레지스터

   



 

 Stack

 메모리 주소


rbp> 

0x00400570

 


 

0x7fffffffdd38
 

0x7fffffffdd30 

 

숫자 3

 


0x7fffffffdd28 

 



 





0x7fffffffdd20 

 

 

0x0040054d

 

<rsp
 

0x7fffffffdd18
 


 

   








이 정도만 보아도.. 그동안 배운 자료구조, 운영체제, C언어, 어셈블리, 컴퓨터구조.. 등 많은 과목이 스쳐지나가면서

퍼즐이 맞춰지듯 이해가 된다.. 신기하다..

확인하기~!


============================== Assembly =================================


1. =>  0x0000000000400534 <+0>  : push    %rbp       일 때,


                                                    push 명령어 뒤에 있는 데이터를 밀어넣는다

                                                    push 는 Stack 의 최상위에 을 밀어넣는다

                                                    push 는 sp 레지스터에 데이터를 넣은 주소를 밀어넣는다

                                                    그리고 sp 레지스터는 포인터의 크기만큼 증가한다

                                 [포인터의 크기 - 64 bit 의 경우 8 byte / 32 bit 의 경우 4 byte]


(gdb) p/x $rbp                                  // rbp 또는 ?bp : Stack의 베이스를 나타냄

$1 = 0x400570

(gdb) p/x $rsp

$2 = 0x7fffffffdd38                             // rsp 또는 ?sp : Stack의 최상위를 나타냄


============================== Computer =================================


1. Stack prame 생성(공간할당)

0x7fffffffdd38

 

0x400570 

=>

0x7fffffffdd30

 

 0x400570 

   

 rsp 레지스터

 

 rbp 레지스터

 rsp 레지스터

 

 rbp 레스터

   


                   

 

 Stack

 

메모리 주소

rbp >


 


 


<rsp

0x7fffffffdd38

0x7fffffffdd30 


sp 레지스터의 크기가 8 byte 증가한다 (수치상으로는 감소했음.. 밑으로 자란다 생각하면 됨)

sp 레지스터에 메모리주소를 밀어넣는다

                                                         

                                                  

  

 Stack

 

메모리 주소

  rbp >


 

0x400570



<rsp



0x7fffffffdd30 


rbp의 데이터를 Stack의 최상위 에 밀어넣는다                                 






============================== Assembly =================================


2. =>  0x0000000000400535 <+1>  : mov    %rsp, %rbp     일 때,


                                                  rsp의 값을 rbp에 넣어라

                                                  [mov %a %b] 좌측의 레지스터 정보를 우측 레지스터로 복사한다

                                                                    복사 명령어 이므로, 이동이 X안된다X

(gdb) p $rsp

$3 = 0x400570

(gdb) p $rbp

$4 = 0x400570                             // bp와 sp가 같아짐을 확인


============================== Computer =================================


2. Stack prame 생성(rbp 셋팅 하여, stack 사용할 수 있도록 하는 과정)

0x7fffffffdd30

 

0x400570 

=>

 0x7fffffffdd30

 

0x7fffffffdd30

   

 rsp 레지스터

 

 rbp 레지스터

 rsp 레지스터

 

 rbp 레스터

   



 

 Stack

 메모리 주소

이전 rbp위치>


rbp> 

0x400570

 


 <rsp

0x7fffffffdd38

0x7fffffffdd30 


rsp 의 정보를  rbp 에 넣음

(그러면 rbp 는 이전주소보다 8Byte 확장된 주소공간을 가리키고 있겠네요~!)






============================== Assembly =================================


3. =>      0x0000000000400538 <+4>  : sub     $0x10, %rsp     일 때,


                                                  rsp 에서 0x10 을 빼서 rsp에 넣을 것..

                                                  [sub $0x10 %rsp] subtract의 약자로 뺄셈을 의미한다

                                                                         rsp 에서 0x10 을 뺄셈하겠다는 의미

(gdb) p $rsp

$5 = 0x7fffffffdd20

(gdb) p $rbp

$6 = 0x7fffffffdd30


============================== Computer =================================


3. main함수 사용하기 위한 (임시적인)공간만 할당

0x7fffffffdd30

 

0x7fffffffdd30

=>

 0x7fffffffdd20

 

0x7fffffffdd30

   

 rsp 레지스터

 

 rbp 레지스터

 rsp 레지스터

 

 rbp 레스터

   



 

 Stack

 메모리 주소


rbp> 

0x400570

 


 <이전 rsp위치

0x7fffffffdd38

0x7fffffffdd30 

 

 일단 공간만 할당(쓰레기값)



 <rsp

 

0x7fffffffdd20 


rsp 의 주소값에서 0x10 (즉, 16byte) 를 빼서, 다시 rsp에 넣음으로써, 16 byte의 공간을 확보한다

(rbp는 그대로 유지하고 있군요.. 역시 베이스포인터 답습니다.)






============================== Assembly =================================


4. =>      0x000000000040053c <+8>  : movl    $0x3, -0x8(%rbp)     일 때,


                                                  숫자 3을 .. rbp 로 부터 8byte 공간을 할당한 후, 그 자리에 넣을것..

(gdb) x $rbp-0x8

$7 = 0xffdd28 : 0x00000003                   // 숫자 3이 들어가 있음 확인


============================== Computer =================================


4. 임시공간 -0x8만큼 할당하여 숫자 3을 스택에 넣는다 - 다음 연산을 위한 준비작업

0x7fffffffdd20

 

0x7fffffffdd30

   
   

 rsp 레지스터

 

 rbp 레지스터

      



 

 Stack

 메모리 주소


rbp> 

0x400570

 


 

0x7fffffffdd38

0x7fffffffdd30 

 

 숫자 3

 


0x7fffffffdd28 

 




  <rsp


0x7fffffffdd20 


rbp 의 주소값에서 -0x8만큼 (즉, 8byte) 를 할당한 공간에, 숫자 3이라는 값을 넣음

(여기서 우리는, 변수 선언시 사용하는 이름따위.. 컴퓨터는 관심없고.. 위치정보를 기억하고..

꺼내쓸때는 그 주소를 따라가서.. 있는.. 값을 꺼내씀을... 알 수 있습니다)





============================== Assembly =================================


5. =>        0x0000000000400543 <+15> : mov    -0x8(%rbp), %eax     일 때,


                                             rbp 로부터 8byte 아래의 공간에 저장되어있는 값을 -> eax 레지스터에 넣기

                                              rax 또는 ?ax : 주로 산술 연산에 활용하며 모든 return 값은 ax 레지스터에 저장됨

(gdb) p $eax

$8 = 3                                  // eax 레지스터에 숫자 '3' 복사됨을 확인


============================== Computer =================================


5. 연산을 위해 eax 레지스터에 복사

0x7fffffffdd20

 

0x7fffffffdd30


 숫자 '3'

     

 rsp 레지스터

 

 rbp 레지스터

 eax 레지스터

 
   



 

 Stack

 메모리 주소


rbp> 

0x400570

 


 

0x7fffffffdd38

0x7fffffffdd30 

 

 숫자 3 

 


0x7fffffffdd28 

 




  <rsp


0x7fffffffdd20 


rbp 로 부터 8 byte 아래의 임의공간에 저장되어 있는 값을 -> eax 레지스터에 넣는다

(연산을 위해 연산레지스터로 값을 보내는군요)






============================== Assembly =================================


6. =>        0x0000000000400546 <+18> : mov    %eax, %edi    일 때,


                                             eax 레지스터에 있는 값을 -> edi 레지스터에 복사하기

                                              edi : temp 와 같이 임시저장 레지스터

(gdb) p $edi

$9 = 3                                  // edi 레지스터에 숫자 '3' 복사됨을 확인


============================== Computer =================================


6. 연산을 위해 edi 레지스터에 임시저장

0x7fffffffdd20

 

0x7fffffffdd30


 숫자 '3'

 

  숫자 '3'

   

 rsp 레지스터

 

 rbp 레지스터

 eax 레지스터

 

 edi 레지스터

   



 

 Stack

 메모리 주소


rbp> 

0x400570

 


 

0x7fffffffdd38

0x7fffffffdd30 

 

 숫자 3 

(언제지워질지 모르는값)

 


0x7fffffffdd28 

 




  <rsp


0x7fffffffdd20 


eax 레지스터의 값을 -> edi 레지스터에 복사하여 넣는다 (연산을 위해 임시저장하는 공간)

(연산 레지스터 말고도.. 임시저장하는 레지스터도 있군요..)


스압주의...

x32 (32비트 컴퓨터) -- 레지스터가 4 Byte 씩 실행되는 구조이다

x64 (64비트 컴퓨터) -- 레지스터가 8 Byte 씩 실행되는 구조이다  -- 요즘 거의 사용하는 형태



기계어(어셈블리) 디버깅 과정을 통해

해당 명령어 (mov, sub 등..) 수행하면서

메모리 할당을 어떻게 받고, 레지스터의 각 역할사용 등을 확인해 볼 수 있다.



 #include <stdio.h>                               


   int my_func(int n){

      return n* 2;

   }


   int main(void){

      int n = 3, res;

      res = my_func(n);

      printf("res = %d\n", res);


      return 0;

   }


  0x0000000000400534 <+0>  : push    %rbp

  0x0000000000400535 <+1>  : mov    %rsp, %rbp

  0x0000000000400538 <+4>  : sub     $0x10, %rsp

  0x000000000040053c <+8>  : movl    $0x3, -0x8(%rbp)

  0x0000000000400543 <+15> : mov    -0x8(%rbp), %eax

  0x0000000000400546 <+18> : mov    %eax, %edi

  0x0000000000400548 <+20> : callq   0x400526 <my_func>

  0x000000000040054d <+25> : mov    %eax, -0x4(%rbp)

  0x0000000000400550 <+31> : mov    -0x4(%rbp), %eax

  0x0000000000400553 <+31> : mov    %eax, %esi

  0x0000000000400555 <+33> : mov    $0x4005f4, %edi

  0x000000000040055a <+38> : mov    $0x0, %eax

  0x000000000040055f <+43> : callq    0x400400 <printf@plt>

  0x0000000000400564 <+48> : mov    $0x0, %eax

  0x0000000000400569 <+53> : leaveq

  0x000000000040056a <+54> : retq





아래 사항을 확인해보기 위한.. 리눅스 기본 Setting



1.  vi [파일이름.c]  >>>  insert 키를 누르고  >>>  왼쪽 위 의 코드 작성하기


2.  작성을 완료하였다면, gdb 를 사용할 수 있도록 컴파일하기

    gcc -g -o [파일의별칭아무거나] [파일이름.c]


3.  컴파일이 성공하였다면, gdb 소환

    gdb [파일의별칭설정한것]


4.  gdb 소환에 성공하였다면, breakpoint 을 main 으로 잡기

    b main


5.  실행하기

    r


6.  디버깅 전체보기

    disas


7.  그러면 현재 화살표가 가리키고 있는 곳이 breakpoint 로 잡혀있는 것을 확인할 수 있다.

    디버깅을 첫줄부터 시도해보기 위해... 

    맨 윗줄의 0x0000000000400534 긁어서 >>> 오른클릭 >>> 복사


8.  breakpoint 추가하기 / 주의할 점은.. *을 붙인다는 것

    b *0x0000000000400534


9.  breakpoint 가 잘 잡혔는지 확인해보기

    info b

    그럼 잡혀있는 것이 2개 뜨지?


10. 잘 잡힌것을 확인했다면, 다시 실행하기(재시작)

     r


11. 다시 실행 후 현재 화살표가 가리키고 있는 곳이 첫 줄임을 확인하자

     disas


12. 여기까지 잘 따라왔다면.. 디버깅을 한 줄씩 해보면서, 

     실제 각 레지스터의 역할과 데이터가 메모리상에서 어떻게 처리되는지를 확인해 볼 수 있다.

     어셈블리어 단위로 1 줄 진행하기 : si

     C언어 단위로 1 줄 진행하기 : s




=================== 셋 팅 완 료 ===================


[참고] '%'는 레지스터 사용시 활용, '$'는 상수값 사용시 활용

[참고] p/x $레지스터이름 : 


+ Recent posts