Skip to main content

[C++] 컴파일 과정



전처리 단계 #

  • 소스 파일에 있는 문자들을 해석한다.
  • \ 문자를 해석한다.
  • 소스 파일을 다음과 같이 분리한다.
    • 주석(comment) → 공백 문자 하나로 변경
    • 공백 문자
    • 전처리 토큰(Preprocessing token) → 컴파일러 토큰의 근간이 된다.
      • 헤더 이름
      • 식별자
      • 문자, 문자열 리터럴
      • 연산자들(+, ##)
  • 전처리기 실행
    • #include 에 지정된 파일의 내용을 복사한다.
    • #define 에 정의된 매크로를 사용해서 코드를 치환한다.
    • #if, #ifndef 와 같은 구문들을 실행해서 코드를 치환한다.
    • #pragma 와 같은 컴파일러 명령문들을 해석한다.
  • 소스 코드 문자 셋에서 실행 문자 셋(Execution character set)의 문자들로 변경한다.
  • 인접한 문자열을 합친다.



컴파일 단계 #

  • 전처리된 소스 코드 파일(*.i)을 어셈블리어 파일(*.s)로 변환하는 과정이다.

  • 해석 유닛 생성(Translation Unit)
    • 전처리기 토큰들이 컴파일 토큰으로 변환이 되고, 컴파일 토큰들은 컴파일러에 의해 해석되어서 TU가 생성된다.
    • TU는 각 소스 파일 별로 하나씩 존재하게 된다.
  • 유일 정의 규칙(One Definition Rule)
    • 각 TU에 존재하는 모든 변수, 함수, 클래스, enum, 템플릿 등의 정의(Definition)는 유일 해야 하고 inline 이 아닌 모든 함수의 변수들의 정의는 전체 프로그램에서 유일해야 한다.
  • 네임 맹글링(Name mangling)
    • 함수, 변수에게 규칙에 맞는 새 이름을 생성해서 링커가 구분할 수 있게 해주는 것이다.
    • 예를 들어, 함수를 오버로딩해서 같은 이름을 가지는 함수가 여러 개일 경우 함수 이름만으로는 구분이 어려워진다. 따라서 컴파일러마다의 규칙을 통해 고유한 새 이름을 지어주는 것이다.



어셈블 단계 #

  • 이후 어셈블러가 목적 파일(*.o) 파일을 생성한다.
  • 이 목적 파일을 재배치 가능한 목적 파일(Relocatable object file) 이라고 한다.

오브젝트 파일 포멧 #

이름 설명
오브젝트 파일 헤더
(Object File Header)
오브젝트 파일의 기초 정보를 가지고 있는 헤더
텍스트 섹션
(Text Section)
.text
기계어로 변환된 코드가 들어 있는 부분
데이터 섹션
(Data Section)
.rodata(읽기 전용 값), .data(초기화된 전역변수), .bss(초기화되지 않은 전역변수)
데이터 영역 변수들(전역 변수, 정적 변수)이 들어 있는 부분
심볼 테이블 섹션
(Symbol Table Section)
.symtab
소스 코드에서 참조되는 심볼(변수, 함수 등)들의 이름과 주소가 정의되어 있는 부분
해당 오브젝트 파일의 심볼 정보만 가지고 있어야 하기 때문에
다른 파일에서 참조되고 있는 심볼 정보의 경우 심볼 테이블에 저장할 수 없다.
재배치 정보 섹션
(Relocation Information Section)
.rel.text, .rel.data
링킹 전까지 심볼의 위치를 확정할 수 없으므로 심볼의 위치가 확정 나면 바꿔야 할 내용을 적어놓은 부분
디버깅 정보 섹션
(Debugging Information Secion)
.debug, .line
디버깅에 필요한 정보가 있는 부분



링킹 단계 #

  • 컴파일러가 생성한 목적 파일들외부 라이브러리 파일들을 모아서 하나의 실행 파일로 만든다.

  • 심볼 해석(Symbol Resolution)
    • 여러 파일에서 같은 이름의 심볼을 사용할 수 있기에, 같은 이름을 쓰는 모든 심볼에 대해 오로지 하나의 정의에 대응되는지를 확인한다.
    • 예를 들어, 여러 파일에 같은 전역 변수인 int count 가 있으면 에러를 발생시킨다.
  • 메모리 재배치(Relocation)
    • 목적 파일에 정의된 심볼들의 위치를 확정시킨다.
    • 목적 파일의 재배치 테이블을 참고해서 심볼들이 최종적으로 메모리에 올바르게 배치되도록 한다.

링크 방식 #

  • 라이브러리(Library)란?
    • 프로그램이 동작하기 위해 필요한 외부 목적 코드들이다.
    • 예를 들어, iostream 헤더파일을 include 했다면, 이 프로그램이 실행하기 위해서는 iostream 라이브러리가 있어야 하겠다.
  • 바인딩(Binding)이란?
    • 하나를 다른 것으로 매핑시키는 것을 의미한다.
    • 프로그램 내에서 변수, 배열, 라벨, 절차 등의 명칭, 즉 식별자(identifier)가 그 대상인 메모리 주소, 데이터형 또는 실제값으로 배정되는 것이다.
  • 정적 라이브러리(Static Library)
    • 필요한 라이브러리들이 (링킹 과정에서) 완성된 프로그램 안에 포함된다.
    • 즉, 링크 타임에 바인딩된다.
  • 동적 라이브러리(Dynamic Library)
    • 여러 개의 프로그램에서 똑같은 라이브러리가 필요하다고 모두 포함하면 프로그램 크기가 너무 커질 것이다. 메모리에 하나만 올려 놓고 공유하면 어떨까?
    • 프로그램이 동적 라이브러리의 주소만 갖고 있다가, 런타임에 해당 주소에 가서 참조하는 방식이다.
    • 즉, 런타임 타임에 바인딩된다.
  • OS 메모리 관리 관련 포스팅



References & Further information #