Linking
linking : 수많은 코드/데이터를 ( 메모리로 로드되고 ,실행될 수 있는 ) 단일의 파일로 결합하는 것
linking time = compile time(static) + load time/run time(dynamic)
Static Linking (런타임 이전)
- 컴파일 시간에 수행된다.
- 재배치 가능한(컴파일된) object files을 수집하고, 하나의 연결된 실행가능 object 파일을 만든다.
정적링커들은 재배치 가능한 목적파일들과 인스트럭션들을 입력으로 받아들여 로드될 수 있고, 실행될 수 있는 완전히 링크된 실행 가능 목적파일을 출력으로 생성한다.
- 재배치 가능 목적파일 = 다양한 코드/데이터 섹션들 (각 섹션은 연속적인 바이트들이다)
- 인스트럭션들은 한 개의 섹션에, 초기화된 전역변수들은 다른 섹션에, 초기화되지 않은 변수들은 또 다른 섹션에 들어 있다.
섹션의 종류와 구성은 다음 글에서 작성해보겠다..!
정적 링킹 이전 과정 요약
1. 소스 코드 컴파일: 여러 개의 소스 코드 파일(.c)을 개별적으로 객체 파일(.o)로 컴파일한다.
2. 라이브러리 컴파일: 만약 외부 라이브러리를 사용한다면, 해당 라이브러리의 객체 파일 또는 정적 라이브러리(.a 또는 .lib)를 준비한다.
3. 링킹: 컴파일된 객체 파일들과 라이브러리 파일을 링커가 하나의 실행 파일로 결합한다. 이때, 필요한 모든 코드(라이브러리 함수 등)는 실행 파일에 포함된다.
Dynamic Linking (로드시간 / 런타임 )
- 런타임 때 수행된다.
- 필요한 라이브러리 파일을 실시간으로 로드하여 사용한다. ( 필요할 때까지 미룬다)
- 동적 라이브러리를 여러 프로세스가 공유할 수 있다.
즉 동적 라이브러리는 실행 파일이 아니라 독립적인 파일로 존재하며, 운영 체제는 이 라이브러리를 실행 중인 여러 프로세스 간에 공유할 수 있도록 **메모리 맵핑(Memory Mapping)**을 이용합니다. 구체적으로, 여러 프로세스가 동일한 동적 라이브러리 파일을 공유 메모리 영역에 로드하여 사용하게 됩니다.
$gcc -o hellos -Og hello.c -static // static linking
$gcc -o hellod -Og hello.c // default 로 dynamic linking 수행
$ls -l hellod hellos
/* hello.c
int main() {
printf("hello world");
printf("hello, again");
return 0;
}
정적 링킹과 동적링킹 비교
정적 링킹 (Static Linking) 동적 링킹 (Dynamic Linking)
파일 크기 | 실행 파일에 모든 코드 포함 → 크기 증가 | 실행 파일에는 최소한의 코드만 포함, 라이브러리는 외부에 있음 |
라이브러리 | 실행 파일에 라이브러리 포함 | 실행 시 외부 라이브러리 로드 |
업데이트 | 라이브러리 업데이트 시 재컴파일 필요 | 라이브러리만 업데이트 가능, 재컴파일 불필요 |
성능 | 라이브러리 코드가 미리 포함되어 있어 실행 시 빠를 수 있음 | 실행 시 라이브러리 로딩이 필요하므로 초기 로딩이 더 느릴 수 있음 |
배포 | 독립적인 실행 파일 배포 가능 | 라이브러리 파일을 따로 배포해야 하므로 종속성 문제 발생 가능 |
Static Linking Process
다음글에서 소개할 정적 링킹과정을 짧게 요약하자면
Step 1. Symbol resolution 심볼 해석
: 링커가 프로그램 내에서 사용된 **심볼(symbol)**을 해결하는 과정. 심볼이란, 변수나 함수 이름과 같은 식별자(식별하는 이름)를 의미한다. 각 심볼은 함수, 전역변수/정적변수에 대응된다. 심볼 해석을 하는 이유는, 심볼 참조를 정확하게 하나의 심볼 정의에 연결하는 것에 있다.
심볼 정의는 object file의 symbol 테이블에 저장되어 있다. ( an array of symbol entries)
Step 2. Relocation 재배치
: 재배치는 프로그램 내에서 각 심볼의 주소를 실제 메모리 상에서 사용할 수 있도록 조정하는 과정이다.
링킹 과정에서 링커는 서로 다른 객체 파일들 간에 정의된 변수나 함수들을 참조할 수 있도록 연결하는 작업을 한다. 컴파일러와 어셈블러는 주소 0번지에서 시작하는 코드와 데이터 섹션들을 생성하기에, 모든 참조들을 수정해서 정확한 메모리 위치를 가리키도록 해야 한다.
각 객체 파일은 별개의 메모리 공간에서 작업되므로, 링커는 최종 실행 파일이 메모리에서 제대로 실행될 수 있도록 심볼들이 참조하는 주소를 수정해야 한다.
어셈블러가 생성한 '재배치 엔트리' 에 따라 재배치 작업을 수행한다.
목적파일의 종류 (모듈) _ ELF FORMAT
1. 재배치 가능한 목적 파일 ( .o file) ← exactly one source(.c) file
: 실행 가능한 객체 파일을 만들기 위한 중간 형태의 코드와 데이터를 포함하는 파일이다. 완전한 실행 파일이 아닌, 최종 실행 파일로 변환되기 전에 필요한 일부 정보를 담고 있습니다. 컴파일 과정에서 생성되며 주소, 위치와 같은 일부 세부사항은 미완성 상태이다.(상대정보)
▶ ELF header = word size(1 word 32?64?) , byte orderint( little/big endian) , file type(.o, exec, .so) , machine type , etc
- offset 0에 위치한다.
- 파일에 대한 메타 정보를 제공한다.
$file main.o
$readelf -h main.o // ELF HEADER 보기
/*
magic number 4 byte
start of section headers + size of section headers * number of section headers = 실행파일 크기
▶ .text section - code
▶ .rodata section - read only data ex) jump tables... const
▶ .data section - 초기화된 전역변수
▶ .bss section - 초기화되지 않은 전역변수 Block Stroage Start
- 목적파일에 실제 공간을 차지하지는 않는다. 단순히 위치를 표시하는 것이다. 초기화되지 않은 변수들은 목적파일에서 실제 디스크 공간을 차지할 필요가 없기 때문에 공간효율성 측면에서 초기화 여부를 구분한다. 런타임에 이 변수들은 모두'0'으로 초기화된다!!
아래의 섹션들의 경우, 메모리에 로드되지 않는다. optional !!
▶ .symtab section
- -g 옵션을 이용해서 컴파일해야만 심볼테이블을 얻을 수 있는 것은 아니다. 기본적으로 모든 재배치가능 목적파일은 .
.symtab에 symbol table을 가지고 있다. 다만, 컴파일러 내부 심볼테이블과는 달리, 지역변수에 대한 엔트리를 갖고 있지 않다.
- 프로시저와 static 변수의 이름
- 섹션의 이름과 위치
재배치 정보는 실제 실행을 위해서가 아닌, 링커가 목적파일을 결합하거나, 라이브러리를 연결할 때 사용된다.
▶ .rel.text section
- .text section 에서 코드가 메모리 내 특정 주소로 로딩될 때 수정이 필요한 부분 재배치 정보
- 실행가능하도록 수정되어야 하는 인스트럭션들의 주소
- 지역함수를 호출하는 인스트럭션들은 다른 목적파일에서 정의된 함수를 참조하지 않기에 수정될 필요가 없다. 따라서 이러한 함수 호출들은 사용자가 링커에게 명시적으로 지시하기 전까지 재배치 정보에서 보통 제외된다.
why? 최종 실행 파일이 필요하지 않은 추가정보를 포함하지 않도록 하여 파일 크기를 최적화하고, 링커가 해당 정보를 처리하는 데만 사용되도록 하기 위해서 !!
▶ .rel.data
- .data section 전역변수의 재배치 정보
- 실행가능하도록 수정되어야 하는 pointer data의 주소
▶ .debug section (gcc -g)
- 디버깅에 대한 정보 -g 옵션으로 호출된 경우에만 생긴다.
- 프로그램 내 정의된 지역변수, typedef(타입정보), 프로그램과 최초 c소스 파일에서 정의되고 참조되는 전역변수들을 위한 엔트리를 갖는 디버깅 심볼 테이블
▶ section header table
- offsets : 각 섹션의 시작위치를 지정하는 값을 바이트 단위로 나타낸다.
- size : 각 섹션의 크기를 바이트 단위로 나타낸다.
- 그 외에도 section type - section name, section type, flags,,, 에 대한 정보
- section pointer들의 집합
Q. section header table 이 왜 필요한건데 ?
A. 링커와 로더가 각 섹션의 정확한 위치/크기를 파악하는 데 도움을 주고, 섹션 헤더 테이블을 이용해 디버깅 정보나 코드, 데이터 등이 파일 내 어디에 배치되었는지 확인해 디버깅/최적화에 도움을 준다.
2. 실행가능한 목적 파일 ( a.out file)
: 메모리에 직접 복사되어 실행될 수 있는 코드와 데이터를 포함하는 파일이다. 이 파일은 프로그램이 실행될 준비가 되어 있으며, 운영 체제에 의해 메모리에 로드되어 실행됩니다. 즉, 실행 가능한 객체 파일은 코드와 데이터를 즉시 실행할 수 있는 형태로 포함하고 있다.
3. 공유된 목적 파일 (.so file)
: 공유 라이브러리(shared library)를 담고 있는 파일이다. 주로 동적 링크(dynamic linking)에서 사용되며, 여러 프로그램이 동일한 라이브러리를 공유하여 메모리 효율성을 높이고, 코드 중복을 줄이는 데 중요한 역할을 한다.
'Major S-T-U-D-Y > System Programming' 카테고리의 다른 글
[시스템 프로그래밍] 7 (4) How to use Static Library 정적 라이브러리 (0) | 2024.11.26 |
---|---|
[시스템 프로그래밍] 7. (3) 링킹과정 1단계 : 심볼 해석과 재배치 (0) | 2024.11.26 |
[시스템 프로그래밍] 7. Linking (1) 분할컴파일과 extern/static modifier (0) | 2024.11.21 |
9. Derived type - array, pointer, and structure (1) (1) | 2024.11.12 |
8. Procedure Call and Stack (0) | 2024.11.12 |