Fundamental of CS/: : Computer Architecture

x86-64 CPU 레지스터(Register) 종류, 32bit / 64bit 비교

Jay.P Morgan 2024. 5. 31. 11:27

 

  1. 레지스터란?

 

  CPU의 빠른 데이터 처리를 돕기 위해 사용되는 임시저장공간으로, 처리중인 데이터나 처리 결과를 담는다.
  레지스터의 종류에는 범용 레지스터, 세그먼트 레지스터, 포인터 레지스터, 인덱스 레지스터, 플래그 레지스터가 있다.
 

※ 레지스터의 크기

 
  32bit, 64bit 운영체제에서 32bit, 64bit 는 레지스터 및 데이터 경로의 크기 를 의미한다.
  위 예시에서 AH 는 8bit 운영체제와 호환되는 레지스터라고 이해하면 된다. 
 
  운영체제의 발전에 따라, 수행해야할 기능이 많아지면서
  많은 정보를 다룰 수 있도록 새로운 레지스터가 추가되고, 크기도 점점 커졌다.
 
※ E 는 Extended 의 약자. CPU의 아키텍쳐에 따라 레지스터의 종류가 다를 수 있다.

 

 

 

  2. 범용 레지스터

 

  범용 레지스터는 연산 결과의 임시 저장, 산술 및 논리 연산, 주소 색인 등 다양한 용도로 사용되는 다목적 레지스터이다.
종류는 EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP, EIP 가 있다.

  하지만 이는 관례적으로 사용되는 용도별로 나눠놓은 것으로, 범용 레지스터라는 이름과 같이 프로그래머의 의도, 또는 규약(stdcall, Thiscall...) 따라 다르게 사용될 수 있다.

 

 

  EAX/RAX (Accumulator Register, 누산기 레지스터)

산술, 논리 연산을 담당하는 레지스터로, 함수의 반환값이 이 레지스터에 저장된다.

 

  EAX 레지스터의 종류
      RAX: 64비트 (x86-64 아키텍처에서의 확장된 EAX)
      EAX: 32비트 누산기 레지스터
      AX: 16비트의 EAX 레지스터 하위 부분
      AH: AX의 상위 8비트
      AL: AX의 하위 8비트

 

  EBX/RBX (Base Register, 베이스 레지스터)

  메모리 주소를 저장하기 위해 사용되는 레지스터.
  종종 배열이나 문자열과 같은 데이터 구조에 접근하기 위한 기준 포인터로 사용된다.

  EBX 레지스터의 종류
      RBX: 64비트 (x86-64 아키텍처에서의 확장된 EBX)
      EBX: 32비트 베이스 레지스터
      BX: 16비트의 EBX 레지스터 하위 부분
      BH: BX의 상위 8비트
      BL: BX의 하위 8비트

 

  ECX/RCX (Count Register, 카운트 레지스터)

  반복 작업에서 카운터 역할을 수행하는 레지스터이다.
  loop 명령어 사용시 레지스터의 값을 하나씩 감소시키며, 0이 될때까지 반복 작업을 수행한다.

  ECX 레지스터의 종류
      RCX: 64비트 (x86-64 아키텍처에서의 확장된 ECX)
      ECX: 32비트 카운트 레지스터
      CX: 16비트의 ECX 레지스터 하위 부분
      CH: CX의 상위 8비트
      CL: CX의 하위 8비트

 

  EDX/RDX (Data Register, 데이터 레지스터)

  EAX 레지스터와 함께 사용하여 큰 수를 연산을 하거나, 그 결과를 저장할 수 있는 레지스터이다.
  64bit 더블워드 연산을 수행할 때에도 사용 가능하다. (div, mul)

  EDX 레지스터의 종류
      RDX: 64비트 (x86-64 아키텍처에서의 확장된 EDX)
      EDX: 32비트 데이터 레지스터
      DX: 16비트의 EDX 레지스터 하위 부분
      DH: DX의 상위 8비트
      DL: DX의 하위 8비트

 

 

 

  3. 인덱스 레지스터

  메모리 내의 데이터 접근 및 조작에 특화된 레지스터로, SI, DI 는 x86 아키텍쳐에서 범용 레지스터로 분류되기도 한다.

 

  ESI/RSI (Source Index, 소스 인덱스 레지스터)

  데이터 복사, 문자열 연산, 입력/출력 처리 등의 작업에서 소스 데이터의 주소를 가리키는 데 사용된다.

  ESI 레지스터의 종류
      RSI: 64비트 (x86-64 아키텍처에서의 확장된 ESI)
      ESI: 32비트 소스 인덱스 레지스터
      SI: 16비트의 ESI 레지스터 하위 부분

 

  EDI/RDI (Destination Index, 목적지 인덱스 레지스터)

  데이터 복사, 문자열 처리, 배열조작 등의 작업에서 목적지 데이터의 메모리 주소를 가리키는데 사용된다.

  EDI 레지스터의 종류
      RDI: 64비트 (x86-64 아키텍처에서의 확장된 EDI)
      EDI: 32비트 목적지 인덱스 레지스터
      DI: 16비트의 EDI 레지스터 하위 부분

 

 

 

  4. 포인터 레지스터

  스택과 프로그램의 실행 흐름 관리에 사용되는 레지스터로, 메모리 주소가 저장된다.

 

  EBP/RBP (Base Pointer, 베이스 포인터 레지스터)

  함수내의 지역 변수와 인자에 일관되고, 쉽게 접근하기 위해 사용되는 포인터 역할 레지스터이다.
  스택 내에서 접근할 부분의 메모리 주소를 저장한다.

  EBP 레지스터의 종류
      RBP: 64비트 (x86-64 아키텍처에서의 확장된 EBP).
      EBP: 32비트 베이스 포인터 레지스터.
      BP: 16비트의 EBP 레지스터 하위 부분.

 

  ESP/RSP (Stack Pointer, 스택 포인터 레지스터)

  프로그램의 스택 메모리 내에서, 현재 스택 최상단 주소를 저장하는 레지스터이다.
  함수 호출, 지역 변수 관리, 함수 내 데이터 저장 및 복구 등의 작업에서 필수적으로 사용된다.

  ESP 레지스터의 종류
      RSP: 64비트 (x86-64 아키텍처에서의 확장된 ESP)
      ESP: 32비트 스택 포인터 레지스터.
      SP: 16비트의 ESP 레지스터 하위 부분.

 

 

  EIP/RIP (Instruction Pointer, 명령 포인터)

  다음에 실행될 명령의 메모리 주소를 저장한다. 

  EIP 레지스터의 종류
      RIP: 64비트 (x86-64 아키텍처)
      EIP: 32비트

 

 

 

  5. 세그먼트 레지스터

  메모리를 다른 세그먼트로 나누어 관리하고, 각 세그먼트에 대한 기준 주소를 저장한다.

  세그먼트를 이용함으로써, 물리 메모리를 효율적으로 사용하고 프로그램 간 메모리 격리를 가능하게 한다.

 

      CS (Code Segment, 코드 세그먼트): 현재 프로그램의 코드가 포함된 세그먼트의 주소를 저장하는 레지스터

      DS (Data Segment, 데이터 세그먼트): 데이터가 포함된 세그먼트의 주소를 저장하는 레지스터

      SS (Stack Segment, 스택 세그먼트): 스택이 포함된 세그먼트의 주소를 저장하는 레지스터

      ES, FS, GS: 추가적인 데이터 세그먼트 주소를 저장하는 레지스터

 

 

 

  6. 플래그 레지스터

  연산의 결과를 나타내는 플래그들을 포함하고, 특정 프로세서 연산을 제어한다.

      RFLAGS: 64비트 (x86-64 아키텍처)

      EFLAGS: 32비트

 

1. Zero Flag(ZF)
목적: 작업 결과가 0일 경우 설정한다.
사용: JZ(Jump-if-zero) 또는 JNZ(Jump-if-not-zero)와 같은 조건부 분기 지시에 일반적으로 사용된다.

2. Sign Flag(SF)
목적: 작업 결과가 음수일 경우 설정한다.
사용: 부호화된 산술 연산에서 결과의 부호를 나타낸다.

3. Carry Flag(CF)
목적: 산술 연산에서 가장 중요한 비트의 수행이 발생할 경우 설정한다(부호가 없는 연산에서 유용함).
사용: 최소 비트에서 다음 비트로의 오버플로를 나타내기 위해 다중 정밀도 산술에서 사용된다.

4. Overflow Flag(OF)
목적: 산술 연산으로 인해 부호화된 오버플로가 발생할 경우 설정한다. 즉, 결과가 너무 커서 지정된 비트 수로 표시할 수 없다.
사용: 서명된 산술 연산에 중요하다.

5. Parity Flag(PF)
목적: 결과에 설정된 비트 수가 짝수인지 설정한다.
사용: 오류 검사 및 단순 패리티 검사에 자주 사용된다.

6. Auxiliary Carry Flag(AC)
목적: BCD(Binary Coded Decimal) 산술에서 사용되는 결과의 하위 절반부터 수행되는 수행이 있는지 설정한다.
용도: BCD 계산에서 특화된 산술 연산 및 특정 유형의 보정에 유용하다.

7. Interrupt Enable/Disable Flag(IF)
목적: 하드웨어 인터럽트를 활성화하거나 비활성화하는 데 사용된다.
사용: CPU가 인터럽트 처리를 제어할 수 있다.

8. Direction Flag(DF)
목적: 문자열 작업에서 처리 방향(증가 또는 감소)을 제어하기 위해 사용된다.
사용: 문자열 및 메모리 작업에서 데이터 블록을 통한 이동 방향을 제어한다.

 

 

 

  7. CPU별 레지스터 종류

 

8-bit
(예: Intel 8080, 
Zilog Z80)
A (Accumulator), B, C, D, E, H, L, SP (Stack Pointer), PC (Program Counter)
16-bit
(예: Intel 8086/8088)
AX, BX, CX, DX, SI, DI, BP, SP, CS, DS, ES, SS, IP (Instruction Pointer), Flags
32-bit
(예: Intel 80386)
EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP, CS, DS, ES, SS, FS, GS, EIP (Extended IP), EFlags
64-bit
(예: x86-64 아키텍처)
RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, CS, DS, ES, SS, FS, GS, RIP (Register IP), RFlags, R8-R15

 

 

 

  8. 레지스터 사용법 변화 (x86 → x64)

  x86에서 x64가 되면서 구조 뿐만 아니라 스택에도 변화가 생겼다

 

 

  8.1 레지스터 이름 및 크기 변화

 

  EAX(4바이트)  →  RAX(8바이트)

  EBX(4바이트)    RBX(8바이트)

  EBP(4바이트)    RBP(8바이트)

 

앞에 e가 붙으면 x86의 레지스터(32bit)로 4byte, r이 붙으면 x64의 레지스터(64bit)로 8byte이다

x86에서 x64가 되면서 레지스터 이름과 크기가 변화했기 때문인데, 그렇기 때문에 e~가 사용되지 않는 건 아니다. eax는  rax 레지스터의 하위 4바이트를 의미하는 이름이라고 알면 된다

 

 

  8.2 새로운 레지스터 추가

 

  x86에서는 없던 레지스터 R8, R9 ~ R15가 추가되었다

  이 중 R12~15비휘발성 레지스터(Non-Volatile Register)로, 자식 함수를 호출하고 리턴된 후에도 호출 전과 값이 동일하게 유지되는 레지스터이다. 모든 함수에서는 비휘발성 레지스터를 사용하기 전에 스택에 백업받고, 리턴하기 전에 복원해 주는 작업을 한다. 이 작업을 함수 프롤로그, 에필로그라고 한다

 

 

  8.3 스택 프레임 포인터 EBP 용도 변화

 

  x86에서는 스택 베이스 포인터 EBP와 스택 포인터 ESP를 이용해 각 프레임 별로 사용 중인 스택 영역을 확인했다.

  하지만 x64에서는 RBP 레지스터 더이상 스택 프레임 포인터로 사용되지 않고 일반적인 목적으로 사용된다. 즉, x64에서는 더 이상 push ebp 후 mov ebp, esp 하는 함수 프롤로그를 볼 수 없다

 

 

 

  9. 함수 호출규약 변화 (x86 → x64)

 

  9.1 인자 전달 방법의 변화

 

x86은 _cdecl이나 _stdcall 같은 호출 규약을 사용하기 때문에 대부분의 함수는 호출 시 인자값이 스택을 통해 전달되었다(x86은 스택 기반 인자 전달)

 

x64에서는 fastcall 호출 규약이 사용되어 레지스터 기반 인자 전달 방식이 되었다

(1) 처음 1번째~3번째 인자까지는 각각 RCX, RDX, R8, R9 네 개의 레지스터에 담겨서 전달된다 (부동소수점이라면 XMM0~)

(2) 5번째 이후의 인자부터는 x86과 동일하게 스택에 저장되어 전달된다

 

 

 

  10. 스택 구성의 차 (x86 → x64)

 

  10.1 x64에서는 함수 실행 중 스택 사이즈 변경 없음

 

  x86에서는 함수의 프롤로그 (push ebp 후 mov ebp, esp)가 끝나고 나면 함수가 리턴할 때까지는 ebp가 바뀌지 않고 esp는 자식 함수 호출 과정에서 매개변수 push할 때 등 수시로 바뀐다

 

  x64에서는 함수의 프롤로그가 끝나고 나면 함수가 리턴될 때까지 rsp가 바뀌지 않는다. 다른 말로, 함수의 프롤로그를 하며 해당 함수에서 필요한 모든 스택 공간에 한꺼번에 확보된다는 의미이다. 예를 들어 함수의 인자값이 6개면 함수가 호출되면서 6개 분의 스택 공간이 한 번에 확보된다

 

 

  10.2 x64에서는 스택 포인터를 기준으로 인자와 지역 변수를 참조

 

x86에서는 스택 베이스 포인터 ebp를 기준으로 인자 및 지역 변수를 참조했다

→ ebp+xx 는 인자값 (ebp+8h는 첫번째 인자값, ebp+ch는 두번째 인자값..)

 ebp-xx 는 지역변수

mov dword ptr [ebp-4], 1Eh //로컬변수->[ebp-4] (다음 로컬변수 [ebp-8])
mov eax, dword ptr [ebp+8] //인자1->[ebp+8]
add eax, dword ptr [ebp+0Ch] //인자2->[ebp+Ch]

 

x64에서는 스택 포인터 rsp를 기준으로 인자 및 지역 변수를 참조한다

 rsp+xx (xx>현재 스택 사이즈) 는 인자값

 rsp+xx (xx<현재 스택 사이즈) 는 지역변수

mov dword ptr [rsp], 1Eh //로컬변수->[rsp] (다음 로컬변수 [rsp+4])
mov eax, dword ptr [rsp+28h] //인자1->[rsp+28h]
add eax, dword ptr [rsp+20h] //인자2->[rsp+20h]