본문 바로가기

무언가 만들기 위한 지식/C,C++,Embedded C

[ALL] 컴파일(compile) 과정


프로그래밍을 하면서 초급을 어느정도 지나고 메모리맵(Memory Map)을 이해할 정도가 되면,
그 사이 과정에서 컴파일 과정 에 대해 흥미를 갖게 된다.
(사실 이 컴파일 과정 자체도 기초중의 하나지만 처음부터 접근하기는 어려웠다. 뭐 지금은 당연한거라고 생각하지만.. - 필자의 경우)

C언어는 고급언어로 프로그래머가 작성하기는 쉽지만 CPU가 그대로 이해할 수는 없다. 일련의 컴파일 과정을 거쳐서 CPU가 이해할 수 있는 기계어로 번역이 되어야 한다. 그 과정을 컴파일 과정이라고 하고, 그 과정을 이해하여야 최적화 등에 이용을 할 수 있다.

최적화에서 사용하는 중간 산출물인 역어셈(disassemble)된 코드를 주로 보게 된다. 이는 기계코드의 바로 전 단계로  C언어보다 하층에 존재한다. 이는 어느정도 프로그래머가 이해가 가능하기 때문에 이를 이용하여 최적화에 이용한다.
(기계어의 경우, 사람이 보고 판단하기란... 엄청난 시간이 걸릴뿐더로 효율도 없고, 거의 불가능하다.)

ARM 내부에서 C언어가 어떤식으로 기계어로 번역되는지도 역어셈으로 확인하면서 해야, Register가 어떤식으로 사용되고, 함수에서 인자가 어떤식으로 전달되는지 등을 확인할 수 있다. 특히 ARM에서 모드변경을 하려면 Assembler Level에서 제어해야하기 때문에, 필수적으로 어셈블러는 할 줄 알아야하고, 적어도 보고 이해는 할 수 있어야 한다.
필자의 경우 잘하는건 아니고, 계속 보다보니까 익숙해졌다고나 할까. 흠

간단하게 Compile이 되는 과정은 다음 그림과 같다.


처음의 소스 파일은 우리가 작성하는 ".c"파일이다. 실행파일을 만들기 위해 가장 먼저 실행되는 것은 전처리기(Preprocessor)이다. 전처리기를 통해서 ".i"파일이 생성되고, 컴파일러에서 하드웨어 종속적인 어셈블리코드(".s")를 생성하게 된다. 이후 어셈블러(Assembler)에 의하여 어셈블리어가 오브젝트 파일을 생성하고, 이 오브젝트 파일들이 링킹(Linking)과 재배치(Relocation)과정을 거쳐 실행 파일이 생성된다.

GNU C 컴파일러 gcc로 실제 그 과정 파일들을 보면 다음과 같다.

<temp.c 라는 기본 source File>


<gcc의 옵션중 "--save-temp"를 사용하면 중간 산출물 ".i",".s",".o"파일을 모두 볼 수 있다.>


<temp.i의 내용을 확인한 결과, 전처리기로 여러 작업이 쓰여져있고 가장 마지막에 코드내용이 들어가 있다.>


<temp.s의 내용을 확인해본 결과이다, 조금은 익숙한 어셈블러이다.>

어셈블러에 의해 오브젝트파일은 아직 실행이 불가능한 재배치 가능파일이나, 공유 오브젝트 파일이다.
오브젝트 파일은 헤더와 섹션으로 구성되는데 헤더에는 파일구성에 대한 정보를 가지고 있고,
섹션에는 실행코드나 데이터를 저장하거나 링킹에 필요한 제어정보가 있다.

오브젝트파일에도 PE(윈도우-Portable Executable), COFF, ELF(유닉스,리눅스,솔라리스-Executable and Linking Format)등 다양하게 존재한다.
오브젝트 파일의 구성은 C프로그램이 메모리를 어떻게 사용하는지 이해하는데 필수적이라고 한다.
아직 필자가 오브젝트 파일의 세세한 구성요소에 대해 공부해보지는 않았지만, 기본적인 메모리맵과 같다. text, data,bss,rodata등 익숙한 섹션으로 나뉘어져 있다.

링킹과 로케이트과정은 오브젝트 파일들을 하나로 뭉치고 심볼들을 해석하며, 메모리에 로드시 각종 주소정보를 생성하고 실행파일을 실행하게 된다.

※ 전체적인 컴파일 과정에서 더 이상 들어가면, 너무 방대해져서
추후, 이 오브젝트 파일 및 링킹과 재배치에 대해 포스팅 할 예정이다.
(OS수업을 듣는 친구중 ELF 분석하던걸 본적이 있는데, 한번 찾아봐야겠다.)