Instruction Pointer??

dandb3·2023년 5월 1일
0

이것저것 TMI

목록 보기
9/17
  • Instruction Pointer (일반적으로 Program Counter)
    • 다음 실행될 명령어를 가리키고 있다.
    • 매 명령어 실행 시 마다 특정 크기만큼 증가한다.

까지만 알고 있었는데, 정확히 증가하는 시점.? 이 갑자기 궁금해짐.
얘는 왜 궁금해졌나?

  • 사실 이야기 할 것들이 많은데, 이유를 다 설명하기에는 너무 장황해져서 생략함.

  • 편의상 rip라고 설명함. (x64에서의 레지스터)

  • 명령어가 실행될 때 마다 특정 크기만큼 증가하는데, 그 증가하는 크기는 현재 실행중인 명령어의 크기에 해당한다. 예를 들면, MOV rax, rbx라고 하면 MOV 명령어는 5bytes 이므로 rip가 5씩 늘어난다.
    -> 그러므로 rip의 값을 증가시켜주는 하드웨어가 따로 존재한다.

  • OK, 평소에 rip가 어떤 식으로 증가하면서 코드가 실행되는지 알겠다. 그러면 jmp의 경우는?

  • 올바른 코드는 아니겠지만, 일단 예제코드로 보자.

    • jmp instruction : 해당 주소로 rip를 옮겨서 실행 흐름을 옮김.
    • 그런데 위에서는 명령어가 실행될 때 마다 rip값이 명령어의 크기만큼 증가한다고 했었다.
    • 그러면 jmp했으니 rip 값이 0x40057a로 바뀌었는데, rip값이 jmp 명령어의 크기인 2바이트만큼 증가해야 되네? 그러면 rip값은 0x40057c가 되네? ??????????
    • 이상한 결과가 나오게 된다.
    • 해결할 수 있는 방법 : 실제 작동시에 rip가 증가하고, 그 후에 instruction이 실행된다면 해결할 수 있다(?는 아니고 사실 이게 올바른 작동 순서임.)
    • jmp 명령어의 경우, 0xeb 0x03이 오게 되는데, 이것이 의미하는 것은
      0xeb : jmp
      0x03 : 현재 rip로 부터 0x03만큼의 위치로 떨어진 곳으로 rip를 이동시켜라.
    • 그런데, 위 코드에서는 현재 rip값이 0x400575인데 오른쪽 어셈블리어를 보니 <loop+10>으로 jmp하라는 말이 적혀있다.
    • 분명 0x03만큼 떨어진 곳으로 jmp하라고 했는데 +5인 지점으로 가게되는 것이 사실은 jmp 명령어의 크기가 2바이트 였고, 명령어 실행 전에 rip 값이 2가 증가한 상태인 +7이 우선 적용된 후에 jmp 명령어로 인해 3만큼 이동하게 된 것이다.
    • 아래의 jle를 보아도 마찬가지인데, 0xf8 = -8에 해당하고, 현재 코드는 +13에 위치해 있으므로 +13 +2 -8 = 7이 되어서 마찬가지로 성립한다.
  • 정리 : rip의 증가는 명령 실행 전에 일어나게 된다.

  • 위의 경우에는 offset이 1바이트만으로 표현이 가능한 경우의 jmp에 해당하고, "jmp short 주소"의 형태로도 사용이 가능하다.

  • offset이 1바이트보다 더 큰 경우가 궁금해서 온라인 어셈블러로 코드를 쳐 봤더니, 이 경우에도 절대 주소로 jmp가 아닌 offset을 통해서 jmp를 하더라.

    • 보면은 알겠지만 jmp 0에 해당하는 코드이고, 현재 instruction의 위치는 0x1b0에 해당한다.
    • 앞선 방법 그대로 적용시키면
      1. rip : 1b0 + 5byte => 1b5가 됨.
      2. offset을 통해서 jmp를 수행하는데, 4b fe ff ff => -1b5가 된다. (little endian)
      3. 결국 0x1b5 - 0x1b5 = 0인 결과가 나온다.
  • 사실 jmp에 대해서 찾아보게 된 이유가 ret의 세부 동작이 궁금해서 그랬던 것이긴 한데, 일반적으로 검색하면 나오는 말들이 "ret는 pop rip; jmp rip와 동일하다"는 것이었다.

  • 그런데 생각해 보면 jmp rip는 어차피 rip 값으로 rip값을 이동시키는 거니까 안 붙여줘도 되지 않나? 왜 굳이 jmp rip를 넣어주는 거지? 라는 의문이 들기 시작함.

  • chat gpt한테 물어보니까 pop rip는 특수연산이라서 스택을 정리를 안 해준다고 하는데, 이거는 말이 안 되는 것 같고, 그냥 pop rip만 하면 보기에 안 이쁘니까 실제로 jmp한다는 의미로써 jmp rip라고 추가적으로 적어놓은 것 같기도..?

  • pop rax; jmp rax; 해도 되잖아 이제보니깐..

  • 그러면 도대체가 왜 ret, call, jmp같은 명령어가 존재하는가? 그냥 일반 연산으로 하면 되잖아.

    • 정확히는 모르겠지만, 안전상 직접적으로 rip를 다루는 행위는 금지하는 것 같다.
    • 가독성? 측면에서도 더 좋기도 한듯.
  • 일단 먼저 정리하고 시작해야 될 것은, rip는 특수 목적 레지스터이므로 ADD, MOV 등과 같은 일반적인 연산이 허용되지 않는다. (but, x86에서는 가능..!)

  • 정리 : 일단 자세한거는 컴퓨터구조 공부하면서 알아보는 것으로 하고, 일단 지금은 ret의 역할 : 스택의 제일 위의 값을 pop하고, 그 pop한 값으로 rip를 옮겨준다. 정도로 이해하면 될 듯. 애초에 얘 자체가 하나의 기계어라서 그냥 하나의 동작으로 보는 것일 수도 있음. 당연히 call의 경우에는 rip값을 push해주고 그 다음에 jmp하는 거겠지.

profile
공부 내용 저장소

0개의 댓글