Little Jay

[CS] C Compliation System(C언어 컴파일 과정) - Computer System 3rd Edition by Bryant 본문

Univ/System Programming

[CS] C Compliation System(C언어 컴파일 과정) - Computer System 3rd Edition by Bryant

Jay, Lee 2022. 5. 3. 00:24

Source File

소스파일, 혹은 소스 프로그램이라고도 하는데 이는 본질적으로 text파일이다. 

main.c 라는 파일이 있다고 하자. 이 파일은 byte의 sequence를 담고 있는 파일이다.

각각의 바이트는 character에 해당하는 interger value값을 가지고 있다.

ASCII(American Standartd Code for Information Interchange)은 text character을 나타내기 위한 규약으로서 대부분의 현대 컴퓨터 시스템들은 이를 따르고 있다. ASCII Table은 byte sized interger value를 unique하게 나타낸다. 

아래는 Standard ASCII code의 Table이다.

Linux환경에서 터미널에 gcc -o hello hello.c 라고 치면 hello라는 파일이 생성이 된다.

우리는 간단하게 Visual Studio, VS Code, CLion 등 다양한 컴파일러를 통해 디버깅하고 컴파일 했지만 Linux환경에서는 다르다. 이런 방식으로 hello 파일은 일련의 과정을 통해 만들어진다.

hello.c라는 text file이 우리가 실행할 수 있는 Executable Object가 되는 것이다. 

여기에는 총 4개의 단계가 있는데,  본 포스팅에서는 이 과정에 대해서 알아보겠다. 

1. Preprocessing Phase

먼저 hello.c라는 text파일이 Pre-Processor, 즉 전처리기를 만나게 된다. 전처리기는 # Character로 시작하는 전처리 문장을 처리해준다. 예를 들어서 C에 #include <stdio.h>라는 C Standard I/O관련한 헤더파일을 맨 상단에 적어놓고 시작하는 경우가 많다. 전처리기는 /usr/include 에 있는 이 헤더파일을 실행중인 코드에 넣게 된다. 혹은 #define ~~~ 을 통해서 Macro를 지정해줄 수 있다. 이런 것을 전처리기가 처리해준다. 

gcc -E 옵션을 주면 전처리 기를 수행할 수 있고 -o 옵션을 주면 object file을 생성할 수 있다.

아래의 예시는 우분투를 사용하는 온라인 ide로서 필자는 여기서 코드를 수행했다. 

temp.c를 -E 옵션을 주어서 실행을 하게되면 .i 확장자를 가진 파일이 하나 나오게 된다.

이 파일은 텍스트 파일로서 전처리문만 수행된 텍스트를 수행한 결과이다. 

800줄이 넘어가는 코드인데 stdio.h만 include 해주어 그렇지 다른 헤더파일까지 include 했으면 코드의 길이는 더욱 길어졌을 것이다. 

2. Compiler

컴파일러는 위에서 생성된 temp.i 이라는 text file을 해석해서 temp.s라는 text file로 변환시킨다. 

확장자가 .s로 붙은 이 파일은 assembly language 프로그램이다. 

앞선 포스팅들에서는 x86-64 ISA에서 사용되는 Assembly Language를 살펴보았다. 

Assembly Language의 개념을 잠깐 복기하자면

Machine Language를 인간이 읽을 수 있는 표현으로 나타낸 것이다.

예를 들어 movq $0x0, %rax 라고 하면 이는 binary로 1011100000000 정도로 나타낼 수 있다.

다시 돌아와 결국 Compiler의 역할은 우리가 작성한 Source Code를 컴퓨터가 읽을 수 있는 Assebly Language로 변환시켜주는 역할을 한다고 이해하면 될 것이다. 

gcc -S temp.c 라고 적으면 Assembly를 볼 수 있는 .s 파일이 생성된다. 

3. Assembly Phase

이제 Assembler가 temp.s 파일을 실제 Machine Language Instruction으로 변환해준다. 

이때 나오는 결과가 .o 확장자를 가진 Relocatable Object File이다. 

이때 Assembler가 만든 .o라는 Object File은 Binary File이다.

실제 Binary File은 인간이 읽을 수 없는 형태로 나온다.

그래서 이를 Vim이나 text파일로 열어서 봐도 글씨가 다 깨져서 나올 것이다.

따라서 -c 옵션을 주고 .o 파일을 생성 후 objdump -d temp.o 라는 명령어를 통해 그 내용을 볼 수 있다. 

4. Linking Phase

바로 이전포스팅에서 Linking에 대해서 다루었으므로 간단하게 설명하고 넘어가겠다. 

Linking은 결과적으로 Executable Object File을 생성하는 것이다. 

gcc -o temp temp.c 하면 temp라는 실행가능한 파일이 나오고 ./temp를 통해 실행하면 드디어 우리가 원하던 코드의 결과가 출력될 것이다. 

 

Wrap Up

지금까지 Assembly, Linking 등 다양한 것을 배웠다. 

이 과정들을 왜 배우는지는 이 파트를 이해하기 위한 빌드업이었다. 

어느 언어를 배우더라고 그 코어는 C로 이루어져있다고 들었다.

사실 나는 자바스크립트를 많이 쓰는 Front-End 직무를 희망하는 컴공학도이다.

그러나 전공자라면 어떻게 code가 컴파일 되는지는 알아야하지 않을까?

이 포스팅들이 전공자 혹은 비전공자들에게 CS 지식을 쌓는데 도움이 되었으면 좋겠다.

 

Reference

Randal E. Bryant, & David R. O’Hallaron. (2016). Computer Systems A Programmer’s Perspective Third edition. Carnegie Mellon University: Pearson.

Digital Systems Principles and Application, 12th ed, Neal S. Widmer, Gregory L. Moss, Ronald J. Tocci, Pearson, 2018

Comments