크래프톤 정글 (컴퓨터 시스템: CSAPP)/3장 프로그램의 기계수준 표현

컴퓨터 시스템 : CSAPP 3장 정리 - 3.2장 프로그램의 인코딩 Part. 2

고웅 2025. 4. 5. 09:22

🧪 3.2.2 Code Examples — 코드 예제

💡 C 코드로 함수 만들기

먼저 아래와 같은 C 코드를 가정해 보자. 파일 이름은 mstore.c라고 한다.

long mult2(long, long);

void multstore(long x, long y, long *dest) {
    long t = mult2(x, y);
    *dest = t;
}

이 함수는 x와 y를 mult2 함수에 넘겨서 곱한 결과를 dest가 가리키는 곳에 저장한다.

🛠️ 어셈블리 코드로 보기

이 C 코드를 어셈블리로 바꾸려면 이렇게 명령어를 입력한다:

gcc -Og -S mstore.c
multstore:
    pushq %rbx           ; %rbx 저장 (함수 호출 시 보존해야 함)
    movq %rdx, %rbx      ; dest를 %rbx로 복사
    call mult2           ; mult2(x, y) 호출 (%rdi, %rsi가 인자 전달)
    movq %rax, (%rbx)    ; 결과(%rax)를 dest가 가리키는 메모리에 저장
    popq %rbx            ; 저장했던 %rbx 복원
    ret                  ; 함수 종료

각 줄은 다음과 같은 일을 한다:

  1. pushq %rbx: rbx 레지스터의 내용을 스택에 저장한다.
  2. movq %rdx, %rbx: dest의 값을 rbx에 복사한다.
  3. call mult2: mult2 함수를 호출해. 결과는 rax에 들어있음.
  4. movq %rax, (%rbx): rax에 있는 값을 *dest에 저장한다.
  5. popq %rbx: 저장해 둔 rbx 값을 스택에서 복원한다.
  6. ret: 함수 종료

🔍 기계어로 보기

그런데 이 어셈블리도 결국은 숫자(byte)로 바뀌어서 저장된다. mstore.o라는 오브젝트 파일을 만들면, 이 함수는 14바이트 길이의 코드로 바뀌게 된다:

53 48 89 d3 e8 00 00 00 00 48 89 03 5b c3

이 숫자들이 실제 컴퓨터가 실행하는 진짜 기계어다.


🔧 레지스터란?

📦 레지스터는 컴퓨터의 초고속 메모리!

CPU 안에는 데이터를 저장하는 특별한 장소들이 있다. 이걸 레지스터(register)라고 부른다. 컴퓨터가 계산을 하거나 명령을 수행할 때, 데이터를 바로 꺼내 쓰려면 빠른 접근이 가능한 이 레지스터를 사용한다.


🏷️ 대표적인 x86-64 레지스터들

이름 설명
%rax 결과값을 저장하는 기본 레지스터. 함수 반환값도 여기에 담김
%rdx, %rsi, %rdi 함수 인자를 전달할 때 사용
%rbx, %rcx, %rsp, %rbp 등 다양한 계산이나 메모리 접근에 사용됨

각 레지스터는 특정 역할이 있고, 어떤 레지스터는 호출 전/후에 보존해야 하는 규칙도 있다.


🎯 레지스터의 예시와 의미

%rdi, %rsi, %rdx:

이들은 보통 함수의 입력 인자를 담는 데 사용된다.

  • %rdi: 첫 번째 인자
  • %rsi: 두 번째 인자
  • %rdx: 세 번째 인자

예를 들어, multstore(2, 3, &d)를 호출하면,

  • x=2는 %rdi
  • y=3는 %rsi
  • &d는 %rdx에 담긴다

main.c

#include <stdio.h>

void mulstore(long, long, long *);


int main() {

    long d;
    mulstore(2,3,&d);
    printf("2 * 3 --> %ld\n", d);
    return 0;
}

long mult2(long a, long b) {
    long s = a * b;
    return s;
}

🔧 어셈블리 코드 (objdump -d 결과)

0000000000400540 <multstore>:
400540: 53                 push   %rbx
400541: 48 89 d3           mov    %rdx,%rbx
400544: e8 42 00 00 00     callq  40058b <mult2>
400549: 48 89 03           mov    %rax,(%rbx)
40054c: 5b                 pop    %rbx
40054d: c3                 retq

📦 한 줄씩 분석하기

🔹 push %rbx

→ rbx 상자에 있는 값을 스택에 저장해 둔다.
(나중에 다시 쓰기 위해 잠깐 보관)


🔹 mov %rdx, %rbx

→ rdx에는 &d라는 주소가 있었다.
그걸 rbx라는 상자에 복사한다.

즉, “결과 저장할 장소 기억해 둬”


🔹 callq 40058b <mult2>

→ mult2(2, 3) 함수를 호출한다
그 결과는 rax에 들어온다.


🔹 mov %rax, (%rbx)

→ rax에 있던 곱한 결과를, rbx가 가리키는 메모리 위치에 저장한다.

즉, “결과를 d에 저장한다”


🔹 pop %rbx

→ 아까 push로 저장했던 값을 다시 rbx로 되돌려준다.


🔹 retq

→ 함수 실행을 끝내고 다시 main으로 돌아간다.


📞 callq는 “전화 걸기” 같은 것이다.

C에서 mult2(2, 3) 이렇게 함수를 부르면,
어셈블리에서는 callq 명령어를 써서 그 함수에 점프한다.

callq 40058b <mult2>

이건 "주소가 40058b인 곳으로 가서, 거기에 있는 mult2 함수를 실행해!"라는 뜻이다.


📍 그럼 주소는 무엇인가?

컴퓨터는 함수가 어디에 있는지 숫자로 기억한다.

  • 40058b ← 함수 mult2가 시작되는 정확한 위치(주소)이다..
  • callq는 그 주소로 이동해서 거기부터 명령어를 읽기 시작한다.

📦 마치 “요리 책의 58쪽에 가서 요리법을 읽어!” 하는 것처럼, 주소는 “이 코드가 있는 곳”을 나타내는 책갈피이다.


🔙 함수 끝나면 어떻게 돌아오는가?

callq는 똑똑해서 함수 부르기 전에 돌아올 주소도 기억해 둔다.

  1. callq가 실행되면,
    • “나 이 함수 끝나면 여기로 돌아와야 해!” 하고
    • 현재 위치를 스택에 저장해 둔다
  2. 그리고 함수 주소로 이동해서 실행한다.
  3. 함수 안에서 마지막에 retq를 호출하면
    • 아까 기억해 둔 주소로 돌아간다

3.2.3 형식에 대한 설명

💾 어셈블리 파일은 어떤 모습일까?

C 프로그램을 컴파일할 때 -S 옵션을 주면 어셈블리 코드 파일(.s)이 생성된다.

예를 들어

gcc -Og -S mstore.c

그러면 mstore.s라는 파일이 생기는데, 안을 보면 다음과 같이 생겼다:

.file "010-mstore.c"
.text
.globl multstore
.type multstore, @function
multstore:
  pushq %rbx
  movq %rdx, %rbx
  call mult2
  movq %rax, (%rbx)
  popq %rbx
  ret
.size multstore, .-multstore
.ident "GCC: (Ubuntu 4.8.1...)"
.section .note.GNU-stack,"",@progbits

 

  • .file, .text, .globl, .type, .ident, .section 같은 것들은 명령이 아니라 지시문이다.
  • 컴퓨터(어셈블러나 링커)는 이걸 보고 코드를 어떻게 배치할지 결정한다.
  • 하지만 우리 인간 입장에서는 이 지시문은 너무 많고 복잡해서, 읽기 어렵다

💡 그래서! 이 책에서는 이렇게 바꿔서 보여준다
주석 달린 친절한 버전:

void multstore(long x, long y, long *dest)
// x in %rdi, y in %rsi, dest in %rdx

1 multstore:
2   pushq %rbx         // %rbx 값 보관
3   movq %rdx, %rbx    // dest 복사
4   call mult2         // mult2(x, y) 호출
5   movq %rax, (%rbx)  // 결과 저장
6   popq %rbx          // %rbx 복구
7   ret                // 복귀

 

🧾 요점 정리

구분 의미
.로 시작하는 줄 어셈블리 지시문 (사람은 무시해도 됨)
어셈블리 명령어 진짜 동작하는 부분 (mov, call, ret 등)
주석 설명 코드를 이해하기 쉽게 도와주는 글