SWjungle/#컴퓨터시스템

[컴퓨터 시스템 - 7장] 링커

장영 2023. 9. 7. 15:03

링커


📌링킹

  • 여러개의 코드와 데이터를 모아서 이를 연결하여, 메모리에 로드될 수 있고 실행될 수 있는 한 개의 파일로 만드는 작업이다.

 

💡링커의 링킹 과정

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

  • 이렇게 만들어진 object file들을 링커가 링킹해서 실행파일로 만듭니다.
  • 이 object파일은 재배치 가능한 목적 파일

📌정적연결

  • unix_ld와 같은 정적 링커들은 재배치 가능한 목적파일들과 명령줄 인자들을 입력으로 받아서 로드될 수 있고 실행될 수 있는 완전히 링크된 실행가능 목적파일을 출력으로 생성한다. 입력인 재배치가능 목적파일을은 다양한 코드와 데이터 섹션들로 이뤄져 있다.
  • 인스트럭션들은 한 개의 섹션에 초기회된 섹션은 다른섹션에, 초기화 되지 않은 변수들은 또 다른 섹션에 들어있다.
  • 실행 파일을 만들기위해 링커는 다음의 두가지 주요 작업을 수행해야함
    • ※ unix ld : 계열 운영 체제에서 사용되는 프로그램으로, 소스 코드에서 컴파일된 객체 파일을 하나의 실행 파일로 결합하고 메모리에 로드하는 역할
-컴파일러가 컴파일을 하게 되면 object 파일 (.o) 가 생성되는데 이 오브젝트 파일 안에 심볼테이블이라는 것이 존재한다.
-심볼 테이블 안에는 그 오브젝트 파일에 정의되어 있던 심볼들의 이름, 타입, 심볼의 위치 등이 구조체 배열의 형태로 저장되어 있다.
-심볼 테이블이라는 것은 구조체 배열이라고 생각하자.각각의 구조체 하나하나에 심볼 하나에 대한 이름, 타입, 위치 등 정보가 들어 있는       것이고 이 심볼테이블은 오브젝트 파일 안에 있다.

💡1. 심볼해석

  • 심볼이라는것은 함수의 이름, 변수의 이름 등등을 의미함. 심볼을 해결한다는 뜻은 여러개의 오브젝트 파일에 같은 이름의 함수 또는 같음 이름의 변수 명이 정의 되어 있을때 어떤 파일의 어떤 함수의 것을 사용해야 하는가를 결정하는 일을 의미한다.

👉🏻예시) 심볼 해석

  • 링커가 오브젝트 파일들을 한곳에서 모아서 수행파일을 만드는데, 심볼을 정의하거나 , 참조할 수 있다.

  • 맨 위의 swap()은 심볼을 정의한 것이며, swap()은 심볼을 정의한 것이며, swap();은 심볼을 참조한 것이고, 마지막은 심볼을 정의하면서 동시에 x라는 심볼을 참조하고 있다.
  • 심볼 정의라는 것은 오브젝트 파일속에 있는 심볼 테이블의 한 엔트리이다.

💡2.재배치 

  1. 링커가 컴파일러가 생성한 오브젝트 파일들을 모아서 하나의 수행파일을 만들때, 각 오브젝트 파일에 있는 데이터의 주소나 코드의 메모리 참조주소가 링커에 의해 합쳐진 수행파일에서의 주소가 다르기 때문에, 그것을 알맞게 수정 해줘야함 

쉽게 말해서 A.o 파일과 B.o 파일안에 f()라는 함수가 동시에 정의 되어 있다고 해보자. 이때, 링커가 두개의 오브젝트 파일을 합쳐서 하나의 수행파일로 만들때 두개의 f()중에 어떤 f()를 사용해야 할지 결정해야 한다.

 

👉🏻예시) 재배치

  • 왼쪽 .o파일이 다른 .o파일들과 합쳐져서 오른쪽 그림의 수행파일을 만드는 과정을 보여주는 그림이다.
char c1 = 'c';

char *p=&c1;
main {
f(c1);
}
  • 이런 코드가 어셈블리어로 바꼈을때 왼쪽 .o파일 처럼 나오게 된다. 그런데 링커가 .o파일들을 합치고 나면 오른쪽 그림처럼 변수(c)의 주소나 코드상의 주소(load R1,(100))를 바꿔줘야한다. 이것이 링커가 하는 가장 중요한 역할중 하나인 재배치이다.
  • 수행파일로 합쳐지기전 .o파일은 char 'c'를 담은 변수인 c1의 주소가 100번지이다. 또한, c1의 주소를 포인터변수 p에 담고 있고, p는 200번지에 할당 되어 있다. 200번지를 참조하면 c1의 주소(100)가 들어 있는데, 이 100이라는 값을 주소 상수라고 한다.
    • ※ 주소 상수 : 주소값 그자체, 변하지 않는 상수라는 뜻

  • 베이스 레지스터를 사용한 재배치 방법이다. 다시 말해 R5라는 레지스터에 합쳐지고 난 뒤,  .o파일의 시작 주소를 넣어 놓고 합쳐지기 전 .o 파일에 있던 주소를 R5의 값을 더해서 코드 및 데이터를 재배치 하는 방식이다.
  • 하지만, 이 방법을 사용하게 됐을때 주소 상수 값은 여전히 재배치가 필요하나 이것은 링커가 해주지 못한다.
  • 쉽게 말하면, 주소상수 값은 p가 갖고 있는 주소값을 의미하는데, 원래 합쳐지기전 .o파일에서 이 주소값은 100번지를 의미했다. 하지만 합쳐지고 나서 이 .o파일이 200번지 부터 시작하므로 사실상 100번지 -> 300번지로 주소 상수 값을 수정해 주어야 하지만, 이것을 자동으로 링커가 해주지 못한다는 뜻

📌넓은 의미의 컴파일 과정

  1. 전처리기(cpp)
  2. 좁은 의미의 컴파일러 (cc1)
  3. 어셈블러( high level 언어를 low level언어로)
  • 다시 말해 c언어로 작성된 코드를 컴파일 하게 되면 전처리기가 #incldue 또는 #define과 같은 구문을 처리해서 그 부분을 코드로 대체 시킨다. 그 다음 진짜 c언어를 어셈블리어로 컴파일 한다. 어셈블러가 한번더 어셈블리어를 0과 1로 이루어진 기계어로 번역하게 된다. 최종적으로 어셈블러에 의해 오브젝트 파일이 만들어 진다고 보면 된다.
  • 이렇게 3가지 과정을 거치면 컴파일이 완료된것이다.
  • 좁은 의미의 컴파일  = high level -> low level
  • 넓은 의미의 컴파일 = high level -> 기계어

 

📌오브젝트 파일 유형

  • 재배치 가능 오브젝트 파일 : 정적 링킹 시에 다른 재배치 가능 오브젝트 파일들과 결합되어 하나의 실행 파일을 만들어내는 데 사용되는 오브젝트 파일을 의미
  • 실행 가능 오브젝트 파일 : 간단하게 실행 파일이라고 부르며, 코드와 데이터가 바로 메모리에 로드되어 실행될 수 있는 오브젝트 파일을 의미한다.
  • 공유 오브젝트 파일 : 로드 타임이나 런타임에 동적으로 메모리에 로드되어 링킹 될 수 있는 재배치 가능 오브젝트 파일

📌Elf 포맷을 따르는 재배치 가능 오브젝트 파일의 내부 구조

 

 

 

  • ELF헤더 :시스템의 워드 사이즈나 바이트 오더링과 같은 시스템의 속성 정보가 저장된다. 그리고 링커가 이 파일을 읽어서 분석할 때 알아야 하는 정보들도 저장된다. 예를 들어, ELF 헤더의 크기, 오브젝트 파일의 유형(EX. 재배치 가능 오브젝트 파일, 실행 파일, 공유 오브젝트 파일), 타겟 머신의 유형(EX. x86-64), 섹션 헤더 테이블의 파일 오프셋, 섹션 헤더 테이블에 존재하는 엔트리의 개수와 각 엔트리의 크기 등이 이곳에 저장된다.
  • .text : 컴파일된 프로그램의 명령어에 해당하는 기계어 코드들이 저장된다.
  • .rodata : 문자열 등의 상수나 switch 점프 테이블과 같은 읽기 전용 값들이 저장된다
  • data : 0이 아닌 값으로 초기화되는 전역 변수 및 static 변수들이 저장된다. 참고로 non-static 지역 변수는 런타임 시에 스택 영역에서 저장 및 관리되므로, 오브젝트 파일에는 저장되지 않는다.
  • .bss : 0으로 초기화되거나 초기화가 되지 않는 전역 변수 및 static 변수
  • .symtab : 이 모듈에서 정의하거나 참조하는 모든 심볼(함수, 전역 변수, static 변수 등)들의 정보
  • .rel.text : 링킹 시 재배치가 필요한 .text 섹션 내 메모리 로케이션들의 정보
  • .rel.data : 링킹 시 재배치가 필요한 .data 섹션 내 메모리 로케이션들의 정보
  • .debug : 디버깅을 위한 정보
  • .line : 원본 C 소스 파일의 라인들과 .text 섹션에 존재하는 기계어 코드들의 맵핑 정보
  • .strtab : 심볼 이름들에 해당하는 문자열들과 섹션 헤더 테이블에 존재하는 섹션 이름들에 해당하는 문자열들
  • 섹션 헤더 테이블  : 각 섹션의 크기와 위치 정보

 

📌실행 가능한 오브젝트 파일

 

  •  전반적으로 재배치 가능 오브젝트 파일과 유사한 구조를 가지고 있음을 볼 수 있다. ELF 헤더는 해당 오브젝트 파일의 전반적인 포맷 정보를 저장하며, 프로그램을 실행할 때 실행해야 하는 첫 번째 명령어의 주소인 Entry Point도 이곳에 저장된다. .text, .rodata, .data 섹션은 재배치 가능 오브젝트 파일과 거의 유사하다. 다만 이러한 섹션들이 링커에 의해 최종적인 런타임 가상 주소로 재배치가 이뤄졌다는 것만 다르다. .init 섹션은 _init이라는 이름의 작은 함수를 하나 정의하는데, 이는 프로그램의 초기화 코드에 의해 호출되는 함수이다. 한편 실행 파일은 Fully Linked, 즉 이미 재배치가 수행된 완전한 실행 파일이기 때문에 .rel.text 섹션과 .rel.data 섹션은 존재하지 않는다.
  • 전반적으로 재배치 가능 오브젝트 파일과 유사한 구조를 가지고 있음을 볼 수 있다. ELF 헤더는 해당 오브젝트 파일의 전반적인 포맷 정보를 저장하며, 프로그램을 실행할 때 실행해야 하는 첫 번째 명령어의 주소인 Entry Point도 이곳에 저장된다. .text, .rodata, .data 섹션은 재배치 가능 오브젝트 파일과 거의 유사하다. 다만 이러한 섹션들이 링커에 의해 최종적인 런타임 가상 주소로 재배치가 이뤄졌다는 것만 다르다. .init 섹션은 _init이라는 이름의 작은 함수를 하나 정의하는데, 이는 프로그램의 초기화 코드에 의해 호출되는 함수이다. 한편 실행 파일은 Fully Linked, 즉 이미 재배치가 수행된 완전한 실행 파일이기 때문에 .rel.text 섹션과 .rel.data 섹션은 존재하지 않는다.

  • 코드 세그먼트는 읽기 및 실행 권한이 부여되고, 메모리 상에서 가상 주소 0x400000에서 시작하며, 메모리에서 차지하는 총사이즈는 0x69c 바이트이며, 실행 파일의 0x000 ~ 0x69c 바이트 부분으로 초기화된다는 것을 알 수 있다. ELF 헤더부터 시작하여 .rodata 섹션까지가 이 세그먼트에 해당한다.
  • 데이터 세그먼트는 읽기 및 쓰기 권한이 부여되고, 메모리 상에서 가상 주소 0x600df8에서 시작하며, 메모리에서 차지하는 총사이즈는 0x230 바이트이며, 실행 파일의 0xdf8 ~ 0x228 바이트 부분으로 초기화된다는 것을 알 수 있다. 나머지 8바이트는 .bss 섹션에 해당하는 8바이트이며, 런타임 시에 모두 0으로 초기화된다.

'SWjungle > #컴퓨터시스템' 카테고리의 다른 글

Unix I/O  (1) 2023.09.17
메모리 정렬과 패딩  (2) 2023.09.09
[컴퓨터 시스템 - 3장] 프로시저 / 배열의 할당과 접근  (5) 2023.09.06