이 글은 ARM 어셈블리를 처음 배우는 사람을 위한 메모리 접근 입문 튜토리얼입니다. ARMv4 아키텍처 기준으로 ldr(load)과 str(store) 명령어를 사용하여 CPU가 메모리의 데이터를 읽고 쓰는 가장 기본적인 방식을 다룹니다.

특히 아래 내용을 순서대로 학습할 수 있습니다.

  • 왜 연산 전에 메모리에서 레지스터로 값을 읽어와야 하는지
  • ldrstr 명령어의 구조와 역할
  • 대괄호([])가 의미하는 주소 접근 방식
  • QEMUGDB를 사용해 레지스터와 메모리 상태를 직접 확인하는 방법

핵심 포인트:
ldrstr은 모든 ARM 프로그램의 기본이 되는 레지스터–메모리 데이터 이동의 출발점입니다. 이 글을 통해 “CPU가 실제로 데이터를 어떻게 읽고 저장하는가?”를 직접 이해할 수 있습니다.

CPU ↔ Memory data flow with LDR/STR Shows LDR reading from Memory to Register and STR writing from Register to Memory. CPU Registers r0 r1 r2 Memory 0x1000 0x1004 0x1008 STR LDR
LDR은 메모리 → 레지스터, STR은 레지스터 → 메모리로 데이터가 이동한다.

메모리에 접근해야 하는 이유

ARM CPU의 데이터 연산 명령어(mov, add 등)은 메모리에 직접 접근하지 않고 항상 레지스터에 저장된 값으로만 계산을 수행합니다. 따라서 메모리에 저장된 값은 연산 전에 반드시 레지스터로 읽어와야 합니다.

ARMv4에서 제공하는 일반 목적 레지스터는 총 16개(r0 ~ r15) 입니다. 하지만 프로그램이 사용하는 모든 데이터를 16개의 레지스터 안에 담는 것은 불가능합니다. 그래서 필요한 데이터만 레지스터로 불러와 연산하고, 그 결과를 다시 메모리에 저장하는 방식으로 동작합니다.

Registers vs Memory roles Shows 16 general-purpose registers contrasted with large memory as main storage. Registers (16) r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 Main Memory (Data store) Large, holds program data Registers used for compute
레지스터는 연산을 위한 임시 저장소, 메모리는 주 데이터 저장소다.

메모리에 접근하는 어셈블리 명령어

메모리에 접근하기 위한 명령어는 다음 두 가지입니다:

  • ldr : 메모리에서 레지스터로 데이터를 읽어온다.
  • str : 레지스터에서 메모리로 데이터를 저장한다.

두 명령어의 기본 문법은 다음과 같습니다.

ldr Rd, [Rn]
str Rd, [Rn]

여기서,

  • Rd : 데이터를 읽거나 저장할 대상 레지스터(destination register)
  • Rn : 접근할 메모리 주소(base register)

ldr은 메모리 주소 Rn에 저장된 값을 레지스터 Rd로 읽어오고, str은 레지스터 Rd의 값을 메모리 주소 Rn이 가리키는 위치에 저장합니다.

대괄호와 베이스 레지스터의 의미

대괄호([]) 안의 값은 메모리 주소를 나타내고, [주소] 형태는 그 주소에 저장된 값을 의미합니다.

예를 들어,

ldr r0, [r1]

이 명령은 r1이 가리키는 주소의 메모리 값을 읽어와 r0에 저장한다” 는 뜻입니다.

Base register addressing Shows r1 holding an address 0x1000 and [r1] meaning value at memory[0x1000]. r1 0x00001000 Memory[0x1000] 0x000004D2 [r1] ≡ *(uint32_t*)0x1000
r1이 주소 0x1000을 담고 있으면, [r1]은 그 주소의 메모리 값이다.

CPU가 주소를 통해 데이터를 읽고 쓰는 과정

CPU는 베이스 레지스터(Rn)에 저장된 값을 주소(address)로 해석합니다. 그 주소가 가리키는 메모리 위치를 찾아, 그 위치의 데이터를 읽거나 씁니다.

즉,

  • ldr은 주소를 해석해 데이터를 읽는 동작,
  • str은 주소를 해석해 데이터를 쓰는 동작입니다.
CPU ↔ Memory data flow with LDR/STR Shows LDR reading from Memory to Register and STR writing from Register to Memory. CPU Registers r0 r1 r2 Memory 0x1000 0x1004 0x1008 STR LDR
LDR moves data Memory → Register; STR moves data Register → Memory.

실습 예제

아래 예제는 ldrstr의 기본 동작을 보여줍니다. QEMUversatilepb 보드와 gdb-multiarch를 이용하면, 레지스터와 메모리의 상태를 직접 확인할 수 있습니다.

컴파일과 실습방법은 이전 포스팅을 참고해주세요.

  .text
  .global _start
_start:
  mov r0, #3         @ r0 10진수 3 저장
  ldr r1, =0x1000    @ r1 메모리 주소 0x1000 저장
  str r0, [r1]       @ [0x1000] = 3

이 코드를 실행하면 CPU는 r1에 저장된 주소(0x1000)를 해석하고, 그 주소의 메모리 위치에 r0의 값(3)을 저장합니다.

다음은 메모리에서 값을 읽어오는 예제입니다.

  .text
  .global _start
_start:
  ldr r1, =0x1000    @ r1 0x1000 주소를 저장
  ldr r0, [r1]       @ r0 [0x1000] 값을 읽어온다

만약 메모리 0x1000에 앞서 3이 저장되어 있다면, 이 명령 실행 후 r0에는 3이 저장될 것 입니다.


이 포스팅에서는 ldrstr을 통해 CPU가 메모리에 접근하고 데이터를 주고받는 가장 기본적인 원리를 살펴봤습니다. 다음 포스팅에서는 오프셋(offset)을 이용한 더 유연한 메모리 접근 방식을 다뤄보겠습니다.