[TIL/크래프톤 정글9기] 19일 차(3.4 정보 접근 (Accessing Information))

blueprint·2025년 5월 29일

크래프톤정글9기

목록 보기
17/55

원문 번역

3.4 정보 접근 (Accessing Information)

x86-64 중앙 처리 장치(CPU)는 64비트 값을 저장하는 16개의 범용 레지스터 세트를 포함. 이러한 레지스터는 정수 데이터뿐만 아니라 포인터를 저장하는 데 사용.

x86-64 레지스터 구조

레지스터 명명 규칙의 역사적 발전

16개 레지스터의 이름은 모두 %r로 시작하지만, 명령 세트의 역사적 발전으로 인해 여러 다른 명명 규칙을 따름:

  1. 원래 8086 (16비트): 8개의 16비트 레지스터 (%ax ~ %bp)

    • 각각 특정 목적을 가지고 있어 용도를 반영하는 이름이 부여
  2. IA32 확장 (32비트): 레지스터를 32비트로 확장 (%eax ~ %ebp)

  3. x86-64 확장 (64비트):

    • 원래 8개 레지스터를 64비트로 확장 (%rax ~ %rbp)
    • 8개의 새로운 레지스터 추가 (%r8 ~ %r15)

레지스터 크기별 접근

16개 레지스터의 하위 바이트에 저장된 다양한 크기의 데이터에 대해 명령이 작동:

크기접근 범위예시 레지스터
바이트 (8비트)최하위 1바이트%al, %bl, %cl, %dl
워드 (16비트)최하위 2바이트%ax, %bx, %cx, %dx
더블워드 (32비트)최하위 4바이트%eax, %ebx, %ecx, %edx
쿼드워드 (64비트)전체 레지스터%rax, %rbx, %rcx, %rdx

레지스터 목록과 용도

Figure 3.2: 정수 레지스터. 모든 16개 레지스터의 하위 부분은 바이트, 워드(16비트), 더블워드(32비트), 쿼드워드(64비트) 수량으로 접근 가능.

레지스터 사용 규칙

대상 레지스터에 대한 두 가지 규칙

8바이트보다 적게 생성하는 명령의 경우:

  1. 1바이트 또는 2바이트 수량 생성: 나머지 바이트는 변경되지 않음
  2. 4바이트 수량 생성: 레지스터의 상위 4바이트는 0으로 설정됨
    • 이 규칙은 IA32에서 x86-64로 확장의 일부로 채택됨

특별한 레지스터

스택 포인터 (%rsp)
  • 용도: 런타임 스택의 끝 위치를 나타냄
  • 특징: 일부 명령이 이 레지스터를 특별히 읽고 씀
  • 고유성: 16개 레지스터 중 가장 고유한 역할
기타 15개 레지스터
  • 유연성: 사용에 더 많은 유연성을 가짐
  • 특별 사용: 소수의 명령이 특정 레지스터를 특별히 사용
  • 표준 규칙: 표준 프로그래밍 관례가 다음을 관리하는 레지스터 사용을 제어:
    • 스택 관리
    • 함수 인수 전달
    • 함수에서 값 반환
    • 지역 및 임시 데이터 저장

3.4.1 피연산자 지정자 (Operand Specifiers)

대부분의 명령은 연산 수행에 사용할 소스 값과 결과를 배치할 대상 위치를 지정하는 하나 이상의 피연산자를 가짐.

피연산자 형태

x86-64는 여러 피연산자 형태를 지원:

  • 소스 값: 상수, 레지스터, 메모리에서 읽기 가능
  • 결과: 레지스터 또는 메모리에 저장 가능

세 가지 피연산자 타입

Figure 3.3: 피연산자 형태. 피연산자는 즉시값(상수), 레지스터 값, 또는 메모리의 값을 나타낼 수 있습니다. 스케일링 팩터 s는 1, 2, 4, 또는 8이어야 합니다.

각 타입별 상세 설명

1. 즉시값 (Immediate)

  • 용도: 상수 값
  • ATT 형식: '$' 다음에 표준 C 표기법을 사용한 정수
  • 예시: $-577, $0x1F
  • 특징:
    • 명령마다 다른 즉시값 범위 허용
    • 어셈블러가 자동으로 값을 인코딩하는 가장 컴팩트한 방법 선택

2. 레지스터 (Register)

  • 용도: 레지스터의 내용
  • 범위: 16개 레지스터의 8, 4, 2, 또는 1바이트 하위 부분
  • 비트 대응: 64, 32, 16, 또는 8비트 피연산자에 각각 대응
  • 표기법:
    • ra: 임의의 레지스터 a
    • R[ra]: 레지스터 식별자로 인덱싱된 배열 R로 본 레지스터 세트에서의 값

3. 메모리 참조 (Memory Reference)

  • 용도: 계산된 주소에 따른 메모리 위치 접근
  • 유효 주소: 종종 유효 주소(effective address)라고 불림
  • 메모리 뷰: 큰 바이트 배열로 봄
  • 표기법: Mb[Addr] - 주소 Addr에서 시작하는 b바이트 값에 대한 참조
  • 단순화: 일반적으로 하첨자 b는 생략

메모리 주소 지정 모드

가장 일반적인 형태

구문: Imm(rb,ri,s)

구성 요소:

  • Imm: 즉시값 오프셋
  • rb: 베이스 레지스터 (64비트 레지스터여야 함)
  • ri: 인덱스 레지스터 (64비트 레지스터여야 함)
  • s: 스케일 팩터 (1, 2, 4, 또는 8이어야 함)

유효 주소 계산: Imm + R[rb] + R[ri] × s

특별한 경우들

  • 배열 요소 참조: 일반적인 형태가 자주 사용됨
  • 다른 형태들: 일부 구성 요소가 생략된 일반 형태의 특별한 경우
  • 복잡한 주소 지정 모드: 배열과 구조체 요소 참조 시 유용

사용 예시 및 응용

  • 배열 요소 접근: 복잡한 주소 지정 모드가 특히 유용
  • 구조체 필드 접근: 다양한 주소 지정 형태 활용
  • 효율적인 메모리 접근: 하드웨어 지원으로 빠른 주소 계산

간단 정리

  • x86-64 구조에서는 총 16개의 64비트 일반 목적 레지스터가 있으며, 이들은 정수 데이터나 포인터 저장에 사용
  • 원래 16비트 구조였던 %ax ~ %sp 레지스터들이 IA32 (32비트)로 확장되면서 %eax ~ %esp로 바뀌었고, 이후 x86-64 (64비트)로 오면서 %rax ~ %rsp로 확장
  • 추가적으로 %r8 ~ %r15까지 8개의 새로운 레지스터가 도입
  • 이들 레지스터는 각각 하위 바이트 (1, 2, 4, 8 바이트) 단위로 접근 가능
  • %rsp는 특별히 런타임 스택의 끝을 가리키며, 스택 조작 시 사용된다. 나머지 레지스터는 용도에 따라 유연하게 사용

Operand (피연산자) 정리

어셈블리 명령어의 피연산자는 소스(Source) 와 목적지(Destination) 로 나뉘며, 피연산자의 종류는 크게 3가지:
Immediate (즉시 값):
$ 기호로 표기됨 (예: $0x1F)
상수 값을 의미

movq $10, %rax   # 64비트 레지스터 rax에 정수 10을 저장
  • $10 → 즉시 값
  • %rax → 목적지 레지스터
  • → 즉시 값을 레지스터에 넣는 가장 단순한 형태

Register (레지스터 참조):
예: %rax, %eax, %al 등
64, 32, 16, 8 비트 레지스터의 하위 일부분도 접근 가능

movq %rax, %rbx   # rax의 값을 rbx로 복사
addq %rcx, %rdx   # rcx의 값을 rdx에 더함
  • %rax, %rbx, %rcx, %rdx 모두 레지스터 피연산자
  • 여기에는 메모리 접근이 없음. CPU 내부에서만 동작

Memory (메모리 참조):

  • 메모리 주소에 접근하는 피연산자
  • 다양한 주소 계산 방식(Addressing Mode)을 사용
    가장 일반적인 형태: Imm(rb, ri, s)
    → 실제 주소는 Imm + R[rb] + R[ri] * s 로 계산됨
    (여기서 s는 1, 2, 4, 8 중 하나)

(1) 단순 메모리 주소 (Indirect)

movq (%rax), %rbx   # rax가 가리키는 메모리 주소의 값을 rbx로
  • (%rax) → 메모리 참조 (간접 주소)
  • rbx는 목적지 레지스터

(2) 베이스 + 오프셋 (Base + Displacement)

movl 8(%rbp), %eax   # rbp 기준으로 오프셋 8 바이트 떨어진 값 가져오기

로컬 변수나 함수 인자 접근에 많이 쓰이는 형식

(3) 인덱스 + 스케일 (Indexed)

movl (%rdi,%rax,4), %ecx   # ecx = *(rdi + rax*4)

배열 접근할 때 유용. 예: arr[rax]

(4) 전체형태: Imm(rb, ri, s)

movl 12(%rdi,%rsi,4), %eax   # eax = *(rdi + rsi*4 + 12)

배열 내 구조체 필드처럼 복잡한 위치에 접근할 때 사용
예를 들어: structs[i].field 같은 접근

✨ 참고
mov %rax, %rbx 라면, %rax가 소스, %rbx가 목적지
즉, %rax의 값을 %rbx에 "복사"

0개의 댓글