빌드/컴파일 및 실행 과정

그림자왕국·2020년 9월 30일
1

CS

목록 보기
6/8

간략한 과정


  1. Pre Processer가 전처리기로 코드를 수정한 뒤 헤더파일과 cpp를 묶어 기본 단위인 translation unit(번역유닛)를 생성한다. tu는 전처리를 거친 파일들로 구성된 컴파일의 기본 단위이다.
    (번역 단위 : 컴파일을 통해 하나의 목적 파일(object file)을 만드는 바탕이 되는 include한 파일들까지 포함한 소스 코드를 일컫는다.) ※ 번역 : 소스의 언어를 기계어로 옮기는 과정

  2. Compiler가 translation unit을 언어의 명세를 이용하여 어셈블리 언어로 변환한다.
    (이 때는 운영체제나 라이브러리 등이 개입하지 않는 독립적인 상태다.)

  3. Assembiler가 어셈블리 언어를 가지고 이진 객체 파일(Binary Object File)을 만들어 운영체제 등에 종속된 형태가 된다. (운영체제에서 실행시키기 위해)

    오브젝트 파일은 코드 섹션/데이터 섹션으로 나뉘어져 있다.

    또한 소스 코드에서 사용하는 심볼(식별자) 정보를 ABI에 기록한다.
    (외부 소스에 있는 함수/데이터를 호출하기 위해선 식별자의 주소와 같은 정보가 필요하다.)


ABI(Application Binary Interface) :

ABI는 운영체제에서 실행되는 프로그램이 따라야 하는 포맷이며, 주어진 CPU를 사용하여 운영체제에서 프로그램이 실행될 때 필요한 정보를 기술하는 방법이다.

ABI는 언어의 명세와 상관없이 동작하기 때문에(바이너리 상태라서) 중요한 것은 객체 파일에 어떤 정보가 포함되느냐 하는 점이다. (라이브러리, 헤더 등)

(4). Linker가 헤더 파일의 printf 함수 등을 찾아서 호출한 프로그램에 끼워 넣는 역할을 한다.
이렇게 링커는 프로그램이 참조하는 심볼(함수)들을 해결하고 프로그램이 실행될 수 있도록 준비하는 역할을 담당한다. 또한 여러 객체 파일들을 링킹하여 하나의 실행 프로그램(.exe)을 생성한다.

링커가 만드는 실행 파일(.exe)는 모든 오브젝트 파일을 하나의 실행 파일로 합쳐지는 게 아니라 
각 오브젝트(소스) 마다 실제 사용되는 부분만 걸러서 실행 파일에 포함된다.
(각 오브젝트 파일에서 실제로 사용되는 코드/데이터 섹션만 골라서 실행파일에 재배치한다.)


심볼과 재배치 정보 :

앞서 말했듯이 코드에는 printf라는 함수의 몸체가 없으므로 어셈블러가 객체 파일을 만들면서 이 파일에는 printf 선언부가 없지만 참조가 요구되는 정보를 끼워 넣는데 이 정보는 대게 심볼 정보와 재배치 정보를 통해 해결한다.

어셈블러가 객체 파일을 만들면서 하는 작업 중 하나가 이런 연결이 필요한 심볼 정보를 만들고 관리하는 것인데 코드에서 사용하지만, 정의되지 않거나 코드에서 정의된 심볼들을 따로 모아서 심볼 테이블을 만들어 ABI에 기록한다.

그리고 재배치 정보는 심볼 정보를 사용해야 하지만 심볼이 정의되지 않았거나 값이 명확하지 않을 때 나중에 링커에게 업데이트하도록 요청하기 위한 정보라고 할 수 있으며, 링커는 이 둘을 활용하여 변경이 필요한 곳의 값을 올바른 값(주소)으로 변경해준다.

객체 파일의 foo와 bar 함수는 해당 함수의 정확한 주소를 모르기에 (다른 파일에 선언부가 있기에) 어셈블러는 ABI 해당 위치에 각각 foo와 bar 함수의 주소를 기록해 달라고 재배치 정보를 포함시킨다. 이런 재배치 정보가 포함된 곳이 재배치 섹션이며 이 재배치 섹션은 값을 업데이트할 때 어떤 심볼을 참조해야 하는지에 관한 정보 역시 포함된다. 값(심볼)은 링커에 의해 갱신되고 이제 프로그램은 정상적으로 foo와 bar 함수의 올바른 주소값을 사용하여 함수를 호출할 수 있다.

즉, 링커는 현재 정확한 주소가 필요한 재배치 섹션의 값(심볼)을 알맞는 심볼로 참조하도록 섹션의 값을 갱신해준다.


(5). Loader가 완성된 프로그램(.exe)을 실행시키기 위해 보조기억장치로부터 주기억장치에 프로그램을 적재하는 역할을 한다. 로더 안에서는 다시 4가지의 프로세스를 수행한다.

1. 할당(Allocation) : 프로그램을 실행시키기 위해 주기억장치의 공간을 확보
2. 연결(Linking) : 동적 라이브러리나 외부심볼을 참조할 때 이 주소값들을 연결한다.
(정적 라이브러리를 사용시에만 링커가 같이 묶는다.) 
3. 재배치(relocation) : 보조기억장치에서 불러온 프로그램을 실제 주기억장치에 맞추어
재배치하여 상대주소를 절대주소로 변경한다. 
4. 적재(Loading) : 실행 프로그램을 할당된 주기억공간에 실제로 옮긴다.
profile
언리얼 엔진 매니아입니다.

0개의 댓글