프로그램과 컴파일
CPU가 수행 할 명령들을 프로그래밍 언어(C, C++, Python 등)으로 작성한 것을 소스코드라고 하는데, 이것을 컴퓨터가 이해할 수 있는 기계어의 형식으로 번역하는 것을 컴파일이라고 한다. 컴파일러로는 대표적으로 GCC, Clang, MSVC 등이 있다.
컴파일은 꼭 필요한가?
모든 프로그래밍 언어가 컴파일이 필요한 것은 아닌데, Python, Javascript 등의 언어는 컴파일이 필요하지 않다. 이러한 언어들은 사용자의 입력, 혹은 사용자가 작성한 스크립트를 즉시 번역하여 CPU에 전달한다. 이러한 동작은 통역과 비슷하기 때문에 인터프리팅(Interpreting)이라고 한다.
컴파일 과정(리눅스/gcc/C언어)
c언어로 작성된 코드는 일반적으로 전처리(Preprocess), 컴파일(Compile), 어셈블(Assemble), 링크(Link)의 과정을 거쳐 바이너리로 번역된다. 아래의 예제 코드를 사용하여 각 단계를 알아보도록 하자.

1. 전처리
컴파일러가 소스코드를 어셈블리어로 컴파일하기 전에, 필요한 형식으로 가공하는 과정.
주석 제거 -> 매크로 치환(#define) -> 파일 병합(여러 개의 소스와 헤더 파일로 이루어져 있는데 따로 컴파일하여 합치는 경우도 있고, 전처리 과정에서 합치는 경우도 있음)
의 순서로 이루어진다.

2. 컴파일
C로 작성된 소스코드를 어셈블리어로 번역하는 것이다. 이 과정에서 소스코드의 문법을 검사하는데 코드에 문법적인 오류가 있다면 컴파일을 멈추고 에러를 출력함. 또한 컴파일러는 코드를 번역할 때, 몇몇 조건을 만족하면 최적화 기술을 적용하여 효율적인 어셈블리 코드를 생성해준다. gcc에서는 -o -o0 -o1 -o2 -o3 -o2 들의 옵션을 사용하여 최적화를 적용할 수 있다.

3. 어셈블
컴파일로 생성된 어셈블리어 코드를 ELF형식의 목적파일(Object file)로 변환하는 과정.(ELF는 리눅스의 실행파일 형식으로, 윈도우에서 어셈블한다면 목적 파일은 PE형식을 갖게 된다.)
목적파일로 변환되고 난다면 기계어로 번역되므로 더 이상 사람이 해석하기 힘들어진다.
4. 링크
여러 목적파일들을 연결하여 실행 가능한 바이너리로 만드는 과정.

위 코드에서 printf 함수를 호출하지만, printf 함수의 정의는 hello-world.c에 없으며, libc라는 공유 라이브러리에 존재한다. libc는 gcc의 기본 라이브러리 경로에 있는데, 링커는 바이너리가 printf를 호출하면 libc의 함수가 실행될 수 있도록 연결해준다. 링크를 거치고 나면 실행할 수 있는 프로그램이 완성된다.
디스어셈블과 디컴파일
디스어셈블
바이너리를 분석하려면 바이너리를 읽을 수 있어야 한다. 하지만 컴파일된 프로그램의 코드는 기계어로 작성되어 있어서 이해하기 매우 어렵다. 그래서 이를 어셈블리어로 재번역하고자 하였고, 이것은 어셈블의 역과정이므로 디스어셈블이라고 한다.

디컴파일
규모가 큰 바이너리의 동작을 어셈블리 코드만으로 이해하기는 어려웠기 때문에 어셈블리어보다 고급 언어로 바이너리를 번역하는 디컴파일러를 개발했다.
하지만 어셈블리어와 기계어는 거의 일대일로 대응되어서 오차없는 디스어셈블러를 개발할 수 있었지만, 고급 언어와 어셈블리어 사이에는 이런 대응관계가 없다. 또한 코드를 작성할 때 사용했던 변수나 함수의 이름 등은 컴파일 과정에서 전부 사라지고, 코드의 몇몇 부분은 최적화와 같은 이유로 컴파일러에 의해 완전히 변형되기도 한다. 이러한 어려움으로 인해 디컴파일러는 일반적으로 바이너리의 소스코드와 동일한 코드를 생성하지는 못하지만 이런 오차가 바이너리의 동작을 왜곡하지는 않으며, 디스어셈블러를사용하는 것 보다 압도적으로 분석 효율을 높여주기 때문에 디컴파일러를 사용하는 것이 유리하다.
정리
- 프로그램: 컴퓨터가 실행해야 할 명령어의 집합, 바이너리라고도 불림
- 전처리: 소스코드가 컴파일에 필요한 형식으로 가공되는 과정
- 컴파일: 소스코드를 어셈블리어로 번역하는 과정
- 어셈블: 어셈블리 코드를 기계어로 번역하고, 실행 가능한 형식으로 변형하는 과정
- 링크: 여러 개의 목적 파일을 하나로 묶고, 필요한 라이브러리와 연결해주는 과정
- 디스어셈블: 바이너리를 어셈블리어로 변역하는 과정
- 디스컴파일: 바이너리를 고급언어로 번역하는 과정
'Computer Science > OS' 카테고리의 다른 글
| 컴퓨터 구조 (0) | 2025.02.04 |
|---|---|
| Windows Memory Layout (0) | 2023.03.27 |