소스 코드 파일부터 실행 파일까지 컴파일러 과정 알아보기
CS

소스 코드 파일부터 실행 파일까지 컴파일러 과정 알아보기

반응형

여러분들은 개발을 진행하면서 단순히 소스코드를 작성하게 되고, 개발한 소스코드를 토대로 실행파일이 생성이 됩니다.

어떻게 우리가 작성한 소스코드가 실행파일이 되는지 알아가보겠습니다.

 

인간이 인식할 수 있는 단어로 코드를 작성하는 것 == 소스 파일 (source file)

이 소스 파일을 컴파일러에게 전달하면 실행 파일 형태가 됩니다.

소스 코드 -> 컴파일러 -> 실행파일

컴파일러?

특정 프로그래밍 언어로 쓰여있는 문서를 다른 프로그래밍 언어로 옮기는 언어 번역 프로그램입니다. (번역기)

컴파일러는 고급 프로그래밍 언어를 저급 프로그래밍(CPU가 인식할 수 있는) 언어로 바꿔주는 역할을 하며, 컴파일된 이후의 코드를 목적코드(object code)라고 합니다.

 

왜 컴파일러를 사용할까?

간단한 코드 예시

int a = 1;
int b = 2;

while(a < b)
{
	b = b - 1;
}

 

인간의 관점

a 변수에 1을 할당합니다;
b 변수에 2를 할당합니다;
a < b 이면 b가 1씩 줄어듭니다;
더 이상 a < b가 성립하지 않을 때까지의 앞의 문장을 반복합니다;

 

CPU는 이런 추상적인 표현을 직접 이해할 수 없기 때문에 컴파일러의 도움을 받아야 합니다.

우리가 작성한 소스코드는 컴파일러를 통해 CPU가 인식할 수 있는 언어로 변환이 되어 우리가 작성한 소스코드대로 동작이 진행됩니다.

(CPU는 단세포 생물처럼 매우 원시적이며 멍청하지만, 이 멍청함을 상쇄하고도 남을 정도고 속도가 매우 빠릅니다.)

 

컴파일러 과정

첫 번째, 어휘 분석

컴파일러는 먼저 각 항목을 잘게 쪼개고, 각 항목이 가지고 있는 추가 정보를 묶어서 관리합니다.

컴파일러의 첫 번째 작업은 소스 코드를 돌아다니면서 모든 토큰을 찾아냅니다.

T_Keyword	int
T_Identifier	a
T_Assign	= 
T_Int	1
T_Semicolon	:
T_Keyword	int
T_Identifier	b
T_Assign	=
T_Int	2

위에서 작성한 소스코드를 예시로 이런 식으로 각 항목을 잘게 쪼갭니다.

1. 키워드 

2. int 키워드라는 추가 정보를 포함

이렇게 각 항목에 추가로 정보를 결헙한 것을 전문용어로 토큰(token)이라고 합니다.

 

각각의 줄은 하나의 토큰을 의미하며, T로 시작하는 왼쪽 열은 토큰을 의미하며, 오른쪽 열은 각각의 토큰이 가지는 값을 나타냅니다.

이러한 소스 코드에서 토큰을 추출하는 과정을 어휘 분석이라고 합니다.

 

두 번째, 구문 분석

컴파일러는 구문에 따라 한 글자도 놓치지 않고 해석(parsing) 작업을 진행합니다.

예를 들어서 컴파일러가 while 키워드의 토큰을 찾으면 다음 토큰이 (라는 것을 알 수 있는데, 다음 토큰이 while 키워드에 필요한 토큰이 아니라면 컴파일러는 문법 오류(syntax error)를 보고 합니다.

 

컴파일러는 구문에 따라 해석을 하며 트리를 생성하고, 이 트리를 생성하는 전체 과정을 구문 분석이라고 합니다.

 

세 번째, 의미 분석

구문 분석을 통해 구문 트리가 생성되고 나면 구문 트리에 이상이 없는지 확인합니다.

즉, 정수 값에 문자열을 더하면 안 되고, 비교 기호의 좌우에 있는 값 형식이 다른 경우를 찾으며 이 단계를 통과하면 작성한 소스코드에 이상이 없기 때문에 컴파일 오류가 없다는 것이 증명됩니다.

이 과정을 의미 분석이라고 합니다.

 

네 번째, 중간 코드 생성

의미 분석이 끝나면 컴파일러는 구문 트리를 탐색한 결과를 바탕으로 좀 더 다듬어진 형태인 중간 코드(Intermediate Representation Code, IR Code)를 생성합니다.

 

중간 표현이라고도 불리며, 소스 코드를 표현하기 위해 컴파일러나 가상 머신에 의해 내부적으로 사용되는 데이터 구조 또는 코듭

a = 1
b = 2
goto B
A: b = b - 1
B: if a < b goto A

 

다섯 번째, 중간 코드를 어셈블리어 코드로 변환

movl $0x1, -0x4(%rbp) // a = 1
movl $0x2, -0x8(%rbp) // b = 2
jmp B // b로 점프
A: subl $0x1, -0x8(%rbp) // b = b - 1
B: mov -0x4(%rbp),%eax
cmp -0x8(%rbp),%eax // a < b ?
jl A	// a < b 이면 A로 점프

 

여섯 번째, 기계 명령어로 변환

마지막으로 컴파일러는 이 어셈블리어 코드를 기계 명령어로 변환합니다.

이런 방식으로 컴파일러는 인간이 작성한 소스코드라고 부르는 문자열을 CPU가 실행할 수 있는 기계 명령어로 변환합니다.

 

그렇다면 지금 까지 과정이 끝나면 실행 파일이 알아서 생성이 되는 걸까요?

-> 추가적으로 대상 파일을 병합하는 링크(link) 작업이 필요합니다.

 

모든 소스 파일에는 각각의 대상 파일이 존재하고, 프로젝트가 복잡해져서 소스 파일이 세 개 있다고 가정하면 대상 파일은 모두 세 개가 됩니다. 하지만 우리가 원하는 건 하나의 실행 파일이고 이 대상 파일 세 개를 하나의 실행 파일로 합쳐주는 작업을 링크 라고 합니다.

이 링크를 담당하는 프로그램을 링커(linker)라고 합니다.

 

 

참고

컴퓨터 밑바닥의 비밀

반응형

'CS' 카테고리의 다른 글

실행 파일을 생성하는 링커  (0) 2024.07.27