Addressing Mode (AArch64)

 

Addressing Mode (AArch64)

AArch64 아키텍처 A64 명령셋에서 사용하는 주소 인덱싱 모드를 알아본다.

  • Simple (또는Base Register Only)
    • Base 레지스터에 담긴 주소를 참조한다.
  • Offset (또는 Base Plus Offset)
    • Base 레지스터에 오프셋을 더한 주소로 참조한다.
    • Base 레지스터는 인덱스의 변화가 없다.
  • Pre-Indexed
    • Base 레지스터에 인덱스를 증가시킨 후 Base 레지스터의 주소를 참조한다.
  • Post-Indexed
    • Base 레지스터의 주소를 참조한 후 Base 레지스터에 인덱스를 증가
  • Literal (또는 PC-Relative)
    • 컴파일러 타임에 <label>까지의 offset 바이트 값을 <imm> 값으로 변환하여 명령을 엔코딩한다.
      • offset 바이트 값과 <imm> 값은 1:1로 변환할 수도 있지만, 각 명령마다 다르다.
    • 그 후 런타임에 PC + offset 주소를 참조한다.
  • Immediate Addressing
    • 상수로 입력한 직접(immediate) 절대 주소는 지원하지 않는다.
    • 예) ldr x1, #0x1234567800000000 <- 불가능

 

다음 표는 정수 값을 가리키는 주소를 사용하여 참조하는 어셈블리 코드를 주소 인덱스 모드별로 예를 보여준다.

  • 읽어오는 값은 int 형이므로 목적지 레지스터로 32비트 Wt 레지스터를 사용하였고, 베이스 레지스터로 64비트 X1 레지스터를 사용하였다.

 

주소 이동(branch)

Immediate

  • 상수로 입력한 직접(immediate) 주소는 이동 뿐만이 아니라 어떠한 참조라도 AArch64 A64에서 사용할 수 없다.
  • 예) b #0x1234567800000000
    • Error: immediate out of range at operand 1 — `b 0x1234567800000000′
    • 4바이트 명령에 8바이트 주소를 담아낼 공간이 없어 주소를 직접 지정하여 이동하는 방법은 지원하지 않는다.

 

Simple

  • 레지스터에 담긴 주소로의 이동은 가능하다.
  • 예) br x0

 

Literal (or PC-Relative)

  • 각 명령이 지원하는 범위(Range) 이내에 위치한 <Label>을 참고할 수 있다.
  • 예) b my_label
    • = b #offset

 

다음 그림은 컴파일 타임에 branch 코드의 주소와 사용한 my_label이 위치한 주소와의 차이 값 offset을 산출하여 사용하는 모습을 보여준다.

 

AArch64 아키텍처에서의 A64 인코딩

AArch64 아키텍처에서 코드 재배치에 대해 정확히 이해하려면 명령(instruction) 사이즈가 고정되지 않은 CISC 구조와 고정되어 동일한 RISC 구조에 대한 이해와 인코딩 방법에 대해 알아야 한다.

 

AArch64 A64 명령 세트의 경우 4바이트 고정 길이 명령을 사용하고 다음과 같은 형식을 사용한다.

  • 명령어, <오퍼랜드1>, <오퍼랜드2>, …
    • 예) add w0, w1, #123, lsl12

 

4 바이트 고정길이를 사용하는 인코딩을 사용하고, 인코딩에 사용되는 각 필드들은 다음과 같다.

  •  op(operation)
    • bits[28:25]으로 시작한다.
  • operand
    • 각 명령에 사용되는 인자들로 레지스터(register)나 상수(immediate)등이 있다.
  • Rt 또는 Rd
    • 첫 번째 오퍼랜드 레지스터이다. 타겟(target) 또는 목적지(destination)을 의미하는  레지스터로 64비트 레지스터인 경우 Xt와 32비트 레지스터인 경우 Wt를 사용한다.
  • Rn, Rm
    • 두 번째, 세 번째 오퍼랜드로 사용되는 레지스터이다.

 

Addressing 모드와 관련되어 대표적으로 자주 거론되는 명령들에 대한 자세한 인코딩 방법을 알아본다.

  • 이동 명령
    • B
    • BL
    • BR
    • BLR
  • 대입 명령
    • MOV
  • 로드 & 스토어 명령
    • LDR
    • STR
  • 주소 참조 명령 (PC+relative)
    • ADR
    • ADRP

 

B 명령

  • <label> 주소로 이동(branch) 한다.
  • 컴파일러 타임에 현재 주소로부터 <label> 위치까지의 offset을 계산하여 4로 나눈 값을 imm26에 위치하게 만들어준다.
  • 이와 같은 주소 인덱싱 방법을 Literal Addressing이라고 한다.
  • 런타임에 PC(Program Counter) + immediate offset * 4 주소로 이동한다.

 

BL 명령

  • <label> 주소로 서브루틴 콜을 수행한다. B 명령과 거의 같은 엔코딩 포맷을 사용하고, op만 1이다.

 

BR 명령

  • 레지스터에 담긴 주소로 이동(Branch) 한다.

 

BLR 명령

  • 레지스터에 담긴 주소로 서브루틴 콜을 수행한다. BR 명령과 거의 같은 엔코딩 포맷을 사용하고, op만 01이다.

 

MOV 명령

  1. Rd에 상수값을 대입한다.
  2. Rd에 Rm을 대입한다.
  3. Rd에 Rn + 상수값을 대입한다.
  4. Rd에 상수값을 shift 만큼 좌측 시프트한다.
  5. Rd에 상수값을 대입한다.

 

LDR 명령

  1. Rt에 Rn 주소가 가리키는 값을 읽어온 후, Rn 주소를 상수값만큼 증가시킨다.
  2. Rn 주소를 상수값만큼 증가시킨 후, Rn 주소가 가리키는 값을 Rt에 읽어온다.
  3. 1번과 유사하지만 +방향만 지원하며, 조금 더 큰 범위까지 지원한다.
  4. Rt에 <label> 주소가 가리키는 값을 읽어온다. <label> 주소는 +- 256K 범위내에서 지원한다.
    1. 컴파일 타임에 <lable> 까지의 offset을 산출하여 imm12에 대입한다.
    2. 주의: ldr <Xt>, =<label> 과 다르다.

 

주의: 다음 3 가지 명령은 각각 다른 결과를 가져온다.

  • ldr x0, label
    • label 주소에 있는 8바이트 값을 로드하여 x0에 담는다.
  • ldr x0, =label
    • 실제 존재하는 명령이 아니라 컴파일러가 사용하는 pesudo-instruction이다.
    • 코드 인근에 8바이트 label 주소를 담을 영역을 만든다. 이 8 바이트 값에는 컴파일 타임에 label 주소 값을 담아둔다. 그리고 코드에서는 ldr x0, <8바이트 주소>에 해당하는 4바이트 코드를 만들어 사용한다. 결국 컴파일 타임에 생성된 label 주소를 로드하여 알아오는 코드를 만들어낸다.
  • adr x0, label
    • 런타임에 읽어들인 label 주소를 x0에 담는다.

 

다음 샘플 코드를 통해 각각의 결과를 확인해본다.

./test.S

.text
.globl _start
.align 2

_start:
        adrp x0, msg            // 1. 아래 명령과 같이 사용하여 msg의 런타임 주소를 알아온다.
        add x0, x0, :lo12:msg   //  

        adr x1, msg             // 2. msg의 런타임 주소를 알아온다.   

        ldr x2, msg             // 3. msg 주소에 있는 8바이트 값을 로드한다.

        ldr x3, =msg            // 3. 컴파일 타임에 계산된 msg의 주소를 알아온다.

        /* sys_exit 코드 */
        mov x0, 123
        mov x8, 93
        svc #0

.data

msg:
        .quad   10

 

gdb 디버거를 통해 실행된 결과를 레지스터 값으로 확인한다.

$ gdb ./test
(gdb) 
┌──Register group: general─────────────────────────────────────────────────────────────────────────────────────────────┐
│x0             0x4100d8 4260056                            x1             0x4100d8 4260056                            │
│x2             0xa      10                                 x3             0x4100d8 4260056                            │
│x4             0x0      0                                  x5             0x0      0                                  │
│                                                                                                                      |
| (...생략...)                                                                                                         |
│                                                                                                                      |
│x28            0x0      0                                  x29            0x0      0                                  │
│x30            0x0      0                                  sp             0x7ffffff4d0     0x7ffffff4d0               │
│pc             0x4000c4 0x4000c4 <_start+20>               cpsr           0x200000 [ EL=0 SS ]                        │
│fpsr           0x0      0                                  fpcr           0x0      0                                  │   
   ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
B+ │0x4000b0 <_start>       adrp   x0, 0x410000                                                                        │
   │0x4000b4 <_start+4>     add    x0, x0, #0xd8                                                                       │   
   │0x4000b8 <_start+8>     adr    x1, 0x4100d8                                                                        │   
   │0x4000bc <_start+12>    ldr    x2, 0x4100d8                                                                        │
   │0x4000c0 <_start+16>    ldr    x3, 0x4000d0 <_start+32>                                                            │
  >│0x4000c4 <_start+20>    mov    x0, #0x7b                       // #123                                             │
   │0x4000c8 <_start+24>    mov    x8, #0x5d                       // #93                                              │
   │0x4000cc <_start+28>    svc    #0x0                                                                                │
   │0x4000d0 <_start+32>    .inst  0x004100d8 ; undefined                                                              │
   │0x4000d4 <_start+36>    .inst  0x00000000 ; undefined                                                              |
   │                                                                                                                   |
   | (...생략...)                                                                                                      |
   │                                                                                                                   |   
   │0x4100d8                .inst  0x0000000a ; undefined                                                              │
   │0x4100dc                .inst  0x00000000 ; undefined                                                              |

 

STR 명령

  1. Rt 값을 Rn 주소가 가리키는 위치에 기록한 후, Rn 주소를 상수값만큼 증가시킨다.
  2. Rn 주소를 상수값만큼 증가시킨 후, Rn 주소가 가리키는 위치에 Rt 값을 기록한다.
  3. 1번과 유사하지만 +방향만 지원하며, 조금 더 큰 범위까지 지원한다.
  4. Rt 값을 <label> 위치에 기록한다. <label> 주소는 +- 256K 범위내에서 지원한다.
    1. 컴파일 타임에 <lable> 까지의 offset을 산출하여 imm12에 대입한다.

 

ADR 명령

  • 런타임에 현재 동작중인 PC를 기준으로 <label>이 위치한 주소를 읽어온다.
    • <label> 까지의 범위는 최대 +-1M로 제한된다.
    • 컴파일 타임에 <label>까지의 offset을 계산하여 #imm(immhi:immlo)에 사용한다.
    • 컴파일 타임에 생성되는 주소를 사용하지 않으므로 MMU가 off된 상태에서 <label> 물리 주소를 알아올 수 있다.

 

ADRP 명령

  • 런타임에 현재 동작중인 PC를 기준으로 <label>이 위치한 주소 페이지(4K 단위)를 읽어온다.
    • <label> 까지의 범위는 최대 +-4G로 제한된다.
    • 컴파일 타임에 <label>까지의 offset을 계산하여 이를 4K로 나눈 값을 #imm(immhi:immlo)에 사용한다.
    • ADR과 유사하게 동작하지만 조금 더 큰 범위를 지원하고, 4K 미만의 주소를 추가로 얻으려면 다음과 같은 명령을 추가로 사용해야 한다.
      • add X0, X0, :lo12:<label>

 

참고

댓글 남기기