이번에는 C언어의 컴파일 과정을 알아보도록 하겠습니다. 우리가 작성한 C 소스 코드를 컴파일러가 컴파일을 하여 어떻게 실행파일을 만들어 내는 지에 대해서 알아보겠습니다. 기본적인 컴파일러의 동작을 이해함으로써 C 소스를 구성하고 있는 요소들이 어떤 영향을 주는 지 약간의 이해를 할 수 있을 것입니다. 우리가 작성한 소스가 hello.c라고 했을 때, 우리는 이를 실행파일로 만들기 위해서 C컴파일러를 이용하여 컴파일을 하게 됩니다.

 

$ gcc -o hello hello.c

$ ls 
hello  hello.c

위와 같이 hello.c를 컴파일하면 오류가 없다면 hello 라는 실행 파일을 만들게 됩니다. 짧은 소스인 hello.c의 경우 1초 내외의 짧은 시간에 간단히 실행파일을 만들어 내는 것을 볼 수 있습니다. 그러나, C 컴파일러가 하는 일은 간단하지 않습니다. gcc -o hello hello.c라는 간단한 명령에 대해서 C 컴파일러가 어떤 일을 하는 지 알아보도록 하겠습니다.

 


 

1. 프로그램 소스 코드 작성

Sample). hello.c

/*
* Hello World를 출력하는 프로그램입니다.
*/

#include <stdio.h>

int main(int argc, char **argv)
{
    printf("Hello World!\n");

    return 0;
}

이 부분은 우리들이 작성하는 부분입니다. 아주 Simple하지만, 세상에서 가장 유명한 프로그램입니다. 이 hello.c를 Input으로 C 컴파일러가 동작하는 순서를 진행하도록 하겠습니다.

 


 

2. Preprocessing (또는 Precompile)

hello.c에서 #include 부분과 또는 #으로 시작하는 부분에 대해서는 C컴파일러가 쉽게 인식할 수 있도록 C언어 소스를 재정리합니다. /* ~ */와 같은 Comment는 불필요한 부분이라 컴파일 시에 걸리적 거리기 때문에 제거를 하고 #include는 header file의 내용을 읽어서 필요한 내용을 복사해두고 #define문과 같은 내용은 치환 작업 등을 합니다. 그 결과 gcc 컴파일러는 hello.i 라는 파일을 내부적으로 생성합니다.  

$ gcc -E hello.c

명령어를 실행하면 아래와 파일과 같은 내용을 화면으로 출력되는 것을 확인할 수 있습니다.

 

Sample). hello.i

# 1 "hello.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "hello.c"

# 1 "/usr/include/stdio.h" 1 3 4
# 28 "/usr/include/stdio.h" 3 4

... 중략 ...

# 211 "/usr/lib/gcc/x86_64-redhat-linux/4.4.7/include/stddef.h" 3 4
typedef long unsigned int size_t;

... 중략 ...

extern int printf (__const char *__restrict __format, ...);

... 중략 ...

int main(int argc, char **argv)
{
    printf("Hello World!\n");

    return 0;
}

위와 같이 printf( )함수의 선언부를 위쪽에 표시하게 됩니다. 참고로 hello.i 파일에서 #으로 시작하는 라인은 컴파일러가 무시(Comment 부분)하는 라인입니다. #부분을 통해서 자료가 어디서 가지고 왔는 지를 알 수 있습니다. 만약에 컴파일 시에 여러개의 header file을 include하면서 전역 변수나 함수 들의 prototype이 중복되는 것이 발생하면 어느 header랑 중복이 발생하였는 지 확인하기 위한 출처 정보입니다. 그러나 컴파일러는 라인 단위로 읽어 들이면서 #이 있는 라인은 버립니다.

 


 

3. Compile (Assembler 코드 생성)

전처리가 끝난 소스코드(hello.i)를 기반으로 assembler 소스 코드를 생성합니다. 즉, C언어 컴파일러의 중요한 목적은 바로 Assembler 코드를 생성하는 것입니다. 이후 assembler는 CPU 제조회사에 맞게 작성되기 때문입니다. 다만 Assembler의 표준 문법은 GAS(GNU Assembler), MASM(Microsoft Macro Assembler), NASM(Netwide Assembler) 등이 있어서 C언어 처럼 문법 체계가 하나가 아닙니다. LINUX는 GNU기반이라 기본적으로 GAS assebler를 사용합니다.

 

Sample). hello.s

        .file   "hello.c"
        .section        .rodata
.LC0:
        .string "Hello World!"
        .text
.globl main
        .type   main, @function
main:
.LFB0:
        .cfii_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        
... 이하 생략 ...

 


 

4. Assenble (Object 파일 생성)

어셈블러 소스(hello.s)를 바탕으로 목적 파일(Object) 파일을 생성하는 데, Object 파일부터는 사람일 볼 수 있는 형태가 아닌 기계어 코드가 됩니다.

 

Sample). hello.o

^?ELF^B^A^A^@^@^@^@^@^@^@^@^@^@^@^A^@>^ ..... 생략 .....

 


 

5. Link (실행 파일 생성)

object 파일은 직접 실행할 수 없는 파일입니다. 이를 실행 파일로 만들기 위해서는 Link라는 작업을 하게 됩니다. C언어는 분할 컴파일이 가능합니다. 프로그램을 하나의 파일로 만들 필요가 없다는 의미입니다. 즉, A라는 기능을 하는 파일과 B라는 기능을 하는 파일을 따로 만들어서 컴파일을 해서 링크 작업을 통하여 여러 파일의 기능을 합쳐서 실행 파일을 만들 수 있다는 의미입니다. 이와 같이 여러개로 흩어져 있는 object 파일을 합쳐서 하나의 실행 파일을 만드는 것을 Link한다 또는 Linking이라고 하고 이렇게 합치는 작업을 하는 프로그램을 linker라고 합니다. C언어의 표준 라이브러리들도 여러 개의 object 파일들을 모아서 하나의 파일로 만들어 둡니다. 확장자가 .a인 archive 파일과 확장자가 .so 파일인 shared object 파일이 있습니다. 

실행 파일은 hello.o와 표준 라이브러리인 .a 또는 .so 파일을 합쳐서 hello 실행파일이 만들어 집니다.

 

Sample) hello

^?ELF^B^A^A^@^@^@^@^@^@^@^@^@^@^@^A^@>^ ..... 생략 .....

 


C언어의 컴파일 과정을 다시 정리하면,

 

    1). 소스 코딩 →  hello.c 

    2). hello.c → preprocess (전처리)  →  hello.i 

    3). hello.i →  컴파일 →  hello.s

    4). hello.s →  Assemble hello.o

    5). hello.o + library →  Link hello

 

위와 같은 복잡한 과정을 거치면서 실행 파일이 만들어집니다.

 

이와 관련 gcc 컴파일 option을 알아보겠습니다.

$ gcc -E hello.c

preprocessing까지만 작업을 하고 멈춥니다. preprocessing한 결과는 화면으로 출력됩니다.
$ gcc -S hello.c

Assembler 소스 코드 생성까지만 하고 작업을 멈춥니다.
결과로 hello.s 파일이 생성됩니다.
$ gcc -c hello.c

Object 파일만 하고 멈춥니다. 주로 main()함수가 포함되지 않은 소스에서는 여기까지 진행합니다.
결과: hello.o 파일이 생성됩니다.
$ gcc -o hello hello.c

실행 파일까지 생성합니다.
결과: hello.o hello 파일이 생성됩니다.
$ gcc --save-temps -o hello hello.c

실행 파일을 생성하는 전과정에서 발생하는 중간 소스 파일을 생성합니다.
결과: hello.i hello.s hello.o hello 파일이 생성됩니다.

 


 

이상으로 C 컴파일러가 컴파일을 하는 과정을 알아봤습니다. 이 부분은 전산 전공자들은 대부분 알고 있을 것 같습니다만, 알아두면 좋을 것 같습니다. 처음 시작하시는 분은 지금은 이해가 안되실 수 있겠지만, 나중에 이런 것도 있었다는 정도만 알고계셨다가 이해가 되실 때에 다시 한번 보시는 것도 좋을 듯 합니다.

 

 

 

 

C Programming Language 문법

1. C 프로그래밍 언어는? 2. C언어 개발 환경 (실습 환경) 3. C언어의 컴파일 과정 4. C 소스 파일 구성 5. 주석문(Comment) 6. 식별자 명명 규칙 7. C 프로그래밍의 시작 - 함수 8. 변수와 상수 (정수형) 9. 변..

www.it-note.kr

 

'C언어 > 문법' 카테고리의 다른 글

6. 식별자 명명 규칙  (2) 2019.10.22
5. 주석문(Comment)  (0) 2019.10.22
4. C 소스 파일 구성  (0) 2019.10.20
3. C언어의 컴파일 과정  (0) 2019.10.20
2. C언어 개발 환경 (실습 환경)  (0) 2019.10.13
1. C 프로그래밍 언어는?  (2) 2019.10.13
블로그 이미지

사용자 자연&사람

행복한 개발자 programmer since 1995.

Tag ,

댓글을 달아 주세요