본문 바로가기
컴퓨터 공학/시스템 프로그래밍

링커 (1부)

by 조엘 2020. 8. 29.

안녕하세요 파피몬입니다! 오늘은 링커에 대해 공부해봤습니다!

Computer Systems A Programmer's Perspective (3rd Edition)의 내용을 공부한 것을 토대로한 포스팅입니다. 
오개념이 있다면 알려주세요! 
1부, 2부로 나눠져있습니다 :)

 

<개요>

링커: 여러개의 코드와 데이터를 모아서 이를 연결해, 메모리에 로드 될 수 있고, 실행될 수 있는 한 개의 file을 만들어 내는 역할을 수행한다!

 

대부분의 컴파일 시스템은 사용자를 대신해 언어 전처리기, 컴파일러, 어셈블러, 링커를 필요에 따라 호출하게 만들어졌다. 이를 컴파일러 드라이버라고 한다. ex) GCC 드라이버

 

컴파일을 모두 마친 실행 파일을 prog라고 한다면, 우리는 쉘에 ./prog 라는 명령어를 통해 해당 파일을 실행하게 된다. 이는 쉘이 운영체제 내의 로더를 호출시키고, 로더는 실행파일 prog의 코드와 데이터를 메모리로 복사 하고, 제어를 시작 부분으로 전환한다!

 

<목적 파일>

목적 파일은 3가지 형태가 있다. 

  1. Relocatable Object File: 재배치 가능 목적 파일로, 다른 재배치 가능 목적 파일과 결합하게 된다. 

  2. Executable Object File: 실행 가능한 목적 파일로, 메모리에 직접 복사가 가능하고 실행이 가능하다. 

  3. Shared Object File: 로드타임 또는 런타임시 동적으로 링크되고, 메모리에 로드될 수 있다. 

 

컴파일러와 어셈블러를 통해 Relocatable Object File을 만들고, 링커는 Executable Object File을 만든다!

 

<정적 연결>

정적 링커는 Relocatable Object File을 Executable Object File로 변환한다. 

두 가지 작업을 수행하게 된다!

  1단계. 심볼 해석: Relocatable Object File의 심볼 참조들을 정확히 하나의 심볼-정의로 연결!

  2단계. 재배치: 심볼 정의와 알맞은 메모리 위치로의 배치!

 

Relocatable Object File이 어찌 생겼는지, 정적 링커의 심볼 해석과 재배치가 어찌 작동하는 지 알아보자!

 

<Relocatable Object File> 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

<심볼과 심볼 테이블>

각 Relocatable Object File X는 X에 의해서 정의되고 참조되는 심볼들에 대한 정보를 포함하는 심볼 테이블을 갖고 있다. 심볼들의 종류는 다음과 같다. 

  1. 전역 심볼: 비정적 함수와 전역 변수에 대응 (X에 의해 정의됨, 타 파일에서 참조 가능)

  2. External 심볼: 비정적 함수와 전역 변수에 대응 (타 파일에서 정의됨, X에서 참조 가능)

  3. 지역 심볼: 정적 함수와 전역 변수에 대응 (X에서 정의, X에서만 참조가능, 타 파일에서 참조 불가능)

 

<심볼 해석>

링커는 입력 재배치 가능 목적 파일들의 심볼 테이블로부터 정확히 한 개의 심볼 정의에 각 참조를 연결시키게 된다. 

컴파일러는 현재 파일에서 어떠한 심볼이 정의가 되어 있지 않다면 다른 어딘가에 정의되어 있겠지 하고 skip한다!

링커에서는 심볼의 정의를 못 찾으면 에러가 발생한다!

 

여러 재배치 가능한 목적파일을 합치다 보면 링커 입장에서 동일한 이름을 가진 중복된 심볼을 마주할 수 있다! 이를 처리하는 규칙이 있다. 규칙을 보기전에, 강한 심볼/약한 심볼은 다음과 같다.  

 

  강한 심볼: 함수, 초기화 된 전역변수

  약한 심볼: 초기화 안 된 전역변수

 

이제 동일한 이름을 가진 심볼끼리의 충돌 처리 규칙을 살펴본다. 

  Case1. 강한 심볼 vs 강한 심볼 -- 허용되지 않음! 에러 발생!

  Case2. 강한 심볼 vs 약한 심볼 -- 강한 심볼 채택!

  Case3. 약한 심볼 vs 약한 심볼 -- 둘 중 아무거나 채택!

 

<정적 라이브러리와 링크하기>

연관된 함수들은 별도의 객체 모듈로 컴파일 해서 하나의 정적 라이브러리 파일로 패키지화 할 수 있다.

응용프로그램들은 명령줄에 한 개의 파일 이름만 명시해 라이브러리 내의 어떤 함수라도 사용할 수 있게 된다. 

링크 시 링커는 오직 프로그램이 참조하는 객체 모듈들만을 복사해, 디스크와 메모리 상의 실행파일 크기를 줄일 수 있다. 

 

정리하자면 link단계에서 라이브러리를 실행 가능한 목적 파일에 포함시키는 것!

 

그런데 printf() 같은 함수는 맨날 나오는데, 이걸 매 프로그램마다 메모리에 적재시키면 비효율적이지 않을까...? 라는 생각을 해보게 된다. 

 

<재배치>

심볼 해석 단계를 완료한 후, 코드 내의 심볼 참조는 정확히 한 개의 심볼 정의에 연결이 되었다!

이제 이 모듈들을 합치고, 각 심볼에 런타임 주소를 할당하는 재배치 과정을 살펴보자. 

여기서도 2단계를 거치게 된다. 

  1단계. 섹션과 심볼 정의를 재배치하기: 입력 모듈들 중 같은 종류의 섹션을 하나로 통일해 통합된 섹션으로 합치기!

  2단계. 섹션 내 심볼 참조 재배치하기: 링커는 코드와 데이터 섹션들 내의 모든 심볼 참조들을 수정해서 이들이 정확한 런타임 주소를 가리키도록 한다. 

 

<재배치 엔트리>

재배치 엔트리란 재배치 가능 목적 파일을 실행 가능한 목적 파일로 합칠 때, 링커에게 각 참조를 어찌 수정할 지 알려주는 것! 코드에 대한 재배치 엔트리는 .rel.text에, 데이터들에 대한 재배치 엔트리는 .rel.data에 저장이 된다!

 

<심볼 참조의 재배치>

PC-상대 참조의 재배치, 절대 참조의 재배치의 방식으로 실행 가능한 목적 파일이 생성된다. 여기서는 텍스트와 데이터가 각각 .text, .data 섹션에 정렬이 되었고, 직접 바이트를 메모리로 복사 할 수 있다. 

 

이렇게 실행가능한 목적파일을 만들었다!

 

2부에서 계속...

반응형

'컴퓨터 공학 > 시스템 프로그래밍' 카테고리의 다른 글

예외적인 제어흐름 (1부)  (0) 2020.08.30
링커 (2부)  (0) 2020.08.29
쉘(Shell)에 관하여  (0) 2020.05.10

댓글