#CSAPP #til
오늘은 리눅스 환경에서 C 프로그램의 인코딩 과정을 작성해 봅니다.
long mult2(long, long);
void multstore(long x, long y, long *dest){
long t = mult2(x, y);
*dest = t;
}```
>linux> gcc -Og -S mstore.c
GCC, 컴파일러를 실행하여 위 C파일을 어셈블리 파일을 만듭니다.
```asm
multstore:
.LFB0:
.cfi_startproc
endbr64
pushq %rbx
.cfi_def_cfa_offset 16
.cfi_offset 3, -16
movq %rdx, %rbx
call mult2@PLT
movq %rax, (%rbx)
popq %rbx
.cfi_def_cfa_offset 8
ret
.cfi_endproc
만들어진 어셈블리 파일을 까보면 왼쪽은 명령어, 오른쪽은 오퍼랜드들 입니다.
다음은 GCC에게 C코드를 컴파일, 어셈블하도록 명령해 목적 파일을 만들어 봅시다.
linux> gcc -Og -c mstore.c
이렇게 생성된 목적 파일 mstore.o
에는 나열된 어셈블리 인스트럭션에 대응되는 16진수 데이터가 내장되어 있습니다.
여기서 알 수 있는 건 컴퓨터에 의해 실행된 프로그램은 단순히 일련의 인스트럭션(명령)을 인코딩한 바이트 라는 점입니다.
이제 이 목적 파일을 역어셈블 해보겠습니다.
linux> objdump -d mstore.o
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 callq d <multstore+0xd>
d: 48 89 03 mov %rax,(%rbx)
10: 5b pop %rbx
11: c3 retq
이전에 보았던 어셈블러 파일과는 다릅니다.
기계어 코드의 몇몇 특징과 역어셈블러 된 표현에 대한 것
이제 실제 실행 가능한 코드를 생성하기 위해 링커를 목적코드에 실행합니다. 여기서 한 개의 파일은 main
함수를 포함해야 합니다.
#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
링커를 통해 실행가능 프로그램인 prog 생성.
파일 크기가 늘어나는데 그 이유는 아래와 같습니다.
linux> objdump -d prog
이 프로그램을 다시 역어셈블 합니다.
00000000000011d5 <multstore>:
11d5: f3 0f 1e fa endbr64
11d9: 53 push %rbx
11da: 48 89 d3 mov %rdx,%rbx
11dd: e8 e7 ff ff ff callq 11c9 <mult2>
11e2: 48 89 03 mov %rax,(%rbx)
11e5: 5b pop %rbx
11e6: c3 retq
11e7: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
11ee: 00 00
이 어셈블러는 mstore.o
를 역어셈블해서 생성한 것과 거의 동일하지만 두가지 차이점이 있습니다.
링커의 임무는 함수들을 위한 실행 코드의 위치들과 함수 호출을 일치 시키는 것 입니다.