앞선 글에서 프로그램은 텍스트 파일로 작성되고, 텍스트 파일은 각 1바이트 크기의 아스키 코드로 구성된다는 사실을 살펴봤습니다. c 코드 파일이 있다고 할 때, 이것을 컴퓨터가 이해할 수 있는 일련의 명령어로 바꾸기 위해서 기계어로 해석하는 과정이 인코딩입니다. 사람이 이해할 수 있는 데이터에서 이해할 수 없는 데이터(기계어)로 바꾸는 과정이기 때문에 인코딩이라고 부르는 거죠.
아래와 같은 c 파일이 있다고 해봅시다. 이 프로그램에는 main()이 없군요.
root@a0fd357a556d:/test# cat mstore.c
long mult2(long, long);
void multstore(long x, long y, long *dest) {
long t = mult2(x, y);
*dest = t;
}
이 코드로부터 gcc -Og -S mstore.c 명령어를 통해 어셈블리 코드를 얻을 수 있습니다. 만약 arm 아키텍처 노트북에서 이 과정을 거치면 아래와 같이 arm 기반 어셈블리 코드가 생성됩니다.
.arch armv8-a
.file "mstore.c"
.text
.align 2
.global multstore
.type multstore, %function
multstore:
.LFB0:
// ... 생략
이 과정을 compile이라고 합니다. 여전히 기계는 이 코드를 읽을 수 없지만(어셈블리 코드도 아스키 코드의 텍스트 파일이니까요), 이제 이 코드는 이진수 기계어와 1대1 대응이 됩니다.
이 어셈블리 코드를 executable object file로 인코딩하는 과정을 asmble이라고 합니다.
이 오브젝트 파일은 텍스트 편집기로는 더 이상 보기 어렵고(아스키 코드 파일이 아니어서 텍스트 에디터에 렌더링 되지 않음), objdump 명령어를 통해 파일의 각 세그먼트를 확인하는 방식으로 읽을 수 있습니다.
root@a0fd357a556d:/test# objdump -d mstore.o
mstore.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <multstore>:
0: f3 0f 1e fa endbr64
4: 53 push %rbx
5: 48 89 d3 mov %rdx,%rbx
8: e8 00 00 00 00 call d <multstore+0xd>
d: 48 89 03 mov %rax,(%rbx)
10: 5b pop %rbx
11: c3 ret
위에서 text 섹션이 우리가 알고 있는 c프로그램의 코드 부분입니다. 이 외에도 각종 링크 정보나 가상주소 매핑을 나타내는 섹션들이 있죠.
이까지 했다면 대부분의 작업은 끝났습니다. 하지만 만약 이렇게 인코딩한 프로그램이 다른 모듈에서 참조된다면 컴퓨터는 이 파일을 어떻게 가져올 수 있을까요? 우리가 컴파일한 코드는 main함수가 없었기 때문에 무조건 다른 프로그램에 참조될텐데 말이죠. 아래와 같이 두 개의 C파일이 있다고 생각해봅시다. 하나는 위에서 봤던 코드처럼 메인 메서드가 없는 프로그램이고, 나머지 하나는 메인이 있는 프로그램입니다.
long mult2(long, long);
void multstore(long x, long y, long *dest) {
long t = mult2(x, y);
*dest = t;
}
root@a0fd357a556d:/test# cat main.c
#include <stdio.h>
void multstore(long, long, long *);
int main(){
long d;
multstore(2, 3, &d);
printf("2 * 3 --> %ld/n", d);
return 0;
}
long mult2(long a, long b) {
long s = a * b;
return s;
}
이때, 이 두 파일을 링크시키기 위해 linux> gcc -Og -o prog main.c mstore.c명령어를 사용하여 오브젝트 파일을 생성할 수 있습니다. 흥미로운점은, 이렇게 링킹까지 끝난 실행 파일을 보면 용량이 부쩍 증가한다는 점입니다.
root@a0fd357a556d:/test# ls -l
total 32
# C파일
-rw-r--r-- 1 root root 205 Apr 3 09:34 main.c
-rw-r--r-- 1 root root 107 Apr 3 08:52 mstore.c
# 링킹이 끝난 오브젝트 파일
-rwxr-xr-x 1 root root 16112 Apr 3 09:34 prog # <-- 프린트 때문에 더 커진 용량 확인 가능.
이는 기존 우리가 작성했던 프로그램 뿐만 아니라 운영체제와의 상호작용을 시작하고 끝내는 과정을 위한 데이터까지 링커가 포함시켰기 때문입니다.
이렇게 해서 텍스트로 작성한 C 프로그램으로부터 프로그램 인코딩이 완료되고, 운영체제가 실행할 수 있는 Executable Object File이 생성됩니다.