링킹의 정의 및 중요성
링킹이란?
- 링킹은 프로그램을 여러 객체 파일로부터 구성할 수 있게 해주는 기술입니다.
- 프로그램의 수명 동안 다양한 시점에서 발생할 수 있습니다:
- 컴파일 시간: 프로그램이 컴파일될 때
- 로드 시간: 프로그램이 메모리에 로드될 때
- 런타임: 프로그램이 실행 중일 때

이 문서는 'main.c'와 'sum.c'라는 두 개의 소스 파일로 분할된 기본 C 프로그램으로 시작하며, 여러 개체 파일에서 단일 실행 파일을 생성하려면 연결이 필요함을 보여줍니다.
링커의 필요성
왜 Linkers? 처음부터 object 파일을 쓰면 되는거 아니야??🤔
-
모듈성 (Modularity)
- 프로그램을 작은 소스 파일 집합으로 작성할 수 있습니다.
- 공통 함수의 라이브러리를 구축할 수 있습니다 (예: 수학 라이브러리, 표준 C 라이브러리).
-
효율성 (Efficiency)
- 시간 효율성: 개별 파일을 컴파일하고 다시 링크하여 변경된 파일만 재컴파일합니다.
- 공간 효율성: 공통 함수는 단일 파일로 집계될 수 있으며, 실행 파일과 실행 중 메모리 이미지에는 실제로 사용하는 함수의 코드만 포함됩니다.
링커의 역할, 작업, 하는일
-
심볼 해석 (Symbol Resolution)
- 프로그램이 정의하고 참조하는 심볼(전역 변수 및 함수)을 관리합니다.
- 어셈블러는 심볼 테이블에 심볼 정의를 저장합니다.
- 링커는 각 심볼 참조를 정확히 하나의 심볼 정의와 연결합니다.
-
재배치 (Relocation)
- 개별 코드 및 데이터 섹션을 단일 섹션으로 병합합니다.
- .o 파일의 상대적인 위치에서 실행 파일의 절대 메모리 위치로 심볼을 이동합니다.
- 이러한 심볼 참조를 새로운 위치로 업데이트합니다.
바이너리 파일의 종류
재배치 가능 객체 파일 (.o 파일)
- 다른 재배치 가능 객체 파일과 결합하여 실행 가능한 객체 파일을 형성할 수 있는 코드와 데이터를 포함합니다.
- 각 .o 파일은 정확히 하나의 소스 (.c) 파일에서 생성됩니다.
실행 가능 객체 파일 (a.out 파일)
- 메모리에 직접 복사되어 실행될 수 있는 코드와 데이터를 포함합니다.
공유 객체 파일 (.so 파일)
- 로드 시간 또는 런타임에 동적으로 메모리에 로드되고 링크될 수 있는 특별한 유형의 재배치 가능 객체 파일입니다.
- Windows에서는 동적 링크 라이브러리 (DLL)로 불립니다.
- Windows의 DLL과 유사하게 로드 시 또는 런타임 시 동적으로 링크될 수 있습니다.
- ex) std.c 파일 => printf 같은
- 버츄얼 메모리에 올려서 실행해줌
#실행 가능 및 연결 가능 형식(ELF)

ELF 파일 형식
- 객체 파일의 표준 이진 형식입니다.
- 하나의 통합 형식으로 재배치 가능 객체 파일 (.o), 실행 가능 객체 파일 (a.out), 공유 객체 파일 (.so)을 포함합니다.
- ELF 바이너리는 다음과 같은 섹션을 가집니다:
- ELF 헤더: 워드 크기, 바이트 순서, 파일 유형, 기계 유형 등.
- 세그먼트 헤더 테이블: 페이지 크기, 가상 주소 메모리 세그먼트(섹션), 세그먼트 크기 등.
- .text 섹션: 코드.
- .rodata 섹션: 읽기 전용 데이터 (예: 점프 테이블).
- .data 섹션: 초기화된 전역 변수.
- .bss 섹션: 초기화되지 않은 전역 변수.
- .symtab 섹션: 심볼 테이블 (프로시저 및 정적 변수 이름, 섹션 이름 및 위치).
- .rel.text 섹션: .text 섹션의 재배치 정보.
- .rel.data 섹션: .data 섹션의 재배치 정보.
- .debug 섹션: 심볼릭 디버깅 정보.
- 섹션 헤더 테이블: 각 섹션의 오프셋 및 크기.
링커 심볼
심볼의 종류
-
전역 심볼 (Global Symbols)
- 모듈 m에 의해 정의되고 다른 모듈에서 참조될 수 있는 심볼.
- 예: 비정적 C 함수 및 비정적 전역 변수.
-
외부 심볼 (External Symbols)
- 모듈 m에서 참조되지만 다른 모듈에서 정의된 전역 심볼.
- 외부 obj 파일에서 정의된걸 사용할때
-
로컬 심볼 (Local Symbols)
- 모듈 m에 의해 정의되고 참조되는 심볼.
- 예: static 속성으로 정의된 C 함수 및 전역 변수.
- 글로벌 심볼과 반대.
- static 속성. 내 Obj 에서만 사용하겠다.
심볼 해석
- 링커는 심볼 참조를 해당 심볼 정의와 연결합니다.
- 중복 심볼 정의 규칙:
- 다중 강한 심볼은 허용되지 않음.
- 강한 심볼과 다수의 약한 심볼이 있는 경우, 강한 심볼을 선택.
- 다수의 약한 심볼이 있는 경우, 임의의 약한 심볼을 선택.
링커의 주요 내용 설명

중복 기호 정의 및 링커 기호 규칙
전역 및 로컬 기호 참조 확인
- 전역 기호 (Global Symbols):
- 모듈에 의해 정의되고 다른 모듈에서 참조될 수 있는 심볼.
- 예: 비정적 C 함수 및 비정적 전역 변수.
- 외부 기호 (External Symbols):
- 모듈에서 참조되지만 다른 모듈에서 정의된 전역 심볼.
- 로컬 기호 (Local Symbols):
- 모듈에 의해 정의되고 참조되는 심볼.
- 예:
static 속성으로 정의된 C 함수 및 전역 변수.
링커의 기호 규칙
-
중복 강한 기호는 허용되지 않음
- 각 항목은 한 번만 정의할 수 있음.
- 중복된 강한 기호가 있으면 링커 오류 발생.
-
강한 기호와 약한 기호가 동시에 있는 경우, 강한 기호 선택
-
여러 개의 약한 기호가 있는 경우, 임의의 약한 기호 선택
- GCC 옵션
-fno-common을 사용하여 이 규칙을 재정의할 수 있음.
지역 변수와 static 지역 변수
지역 변수
- 프로그램 실행 중 스택에 저장됨.
- 링커와 직접적인 관련이 없음.
- 프로그램이 실행하는 동작과정을 보면 지역변수는 딱히 링커에게 관심x
static 지역 변수
강한 심볼과 약한 심볼
- 강한 심볼 (Strong): 함수와 초기화된 전역 변수.
- 약한 심볼 (Weak): 초기화되지 않은 전역 변수.
링커 퍼즐 예시

-
중복 기호 정의
-
데이터 타입 충돌
- 같은 이름의 다른 데이터 타입이 정의되면 오류 발생 가능.
- 예:
int x;
double x;
-
약한 심볼과 강한 심볼의 충돌
- 약한 심볼과 강한 심볼이 충돌하면 강한 심볼이 선택됨.
- 예:
int x = 7;
int x;
-
동일한 이름의 약한 심볼
- 여러 약한 심볼이 있는 경우, 임의의 약한 심볼이 선택됨.
- 예:
int x;
int x;
밍 권장 사항
- 전역 변수 사용 지양
- static 사용
- 전역 변수를 사용해야 한다면 가능한 한
static 속성을 사용하여 모듈 범위로 제한.
- strong 심볼 사용
- strong 심볼을 사용하여 잠재적인 런타임 오류를 방지.
- 외부 전역 변수 사용
- 다른 파일의 전역 변수를 사용할 때는
extern 키워드를 사용하여 명시적으로 선언.
단계 2: 재배치 (Relocation)

재배치 항목 (Relocation Entries)

-
주소가 아직 정해지지 않았기 때문에 코드의 특정 위치에 대한 재배치 항목이 필요.
-
0:, 4: 하는 이유 = 주소가 아직 정해지지 않아서
-
예:
0: 48 83 ec 08 sub $0x8,%rsp
4: be 02 00 00 00 mov $0x2,%esi
9: bf 00 00 00 00 mov $0x0,%edi # %edi = &array
주소 확인
- 링커는
main과 sum의 주소를 알아내어 참조를 해결.
- 컴퓨터는 프로그램의 각 함수의 실제 메모리 주소를 필요로 함.
라이브러리와 동적 링킹

정적 라이브러리와 공유 라이브러리의 개념
정적 라이브러리


- 정적 라이브러리는 일반적으로 사용되는 기능을 패키지하는 데 사용되는 객체 파일의 아카이브입니다.
- 아카이브 파일(.a 파일)은 관련된 재배치 가능 객체 파일들을 하나의 파일로 묶어 만듭니다.
- 링커는 아카이브 파일을 스캔하고 필요한 심볼을 해결하며, 실행 파일에 필요한 함수만 포함시킵니다.
정적 라이브러리의 단점
- 저장된 실행 파일에 중복됨 (모든 함수는 libc가 필요).
- 실행 중인 실행 파일에 중복됨.
- 시스템 라이브러리의 사소한 버그 수정을 위해 각 애플리케이션을 명시적으로 다시 링크해야 함.
공유 라이브러리
- 공유 라이브러리(.so 파일)에는 애플리케이션에 동적으로 연결될 수 있는 코드와 데이터가 포함되어 있습니다.
- 여러 프로세스에서 공유 라이브러리의 코드를 공유하여 메모리 사용량을 줄일 수 있습니다.
현대적인 해결책: 공유 라이브러리
공유 라이브러리의 장점
- 로드 시간 또는 런타임에 애플리케이션에 동적으로 로드되고 링크됩니다.
- 여러 프로세스가 공유 라이브러리 루틴을 공유할 수 있습니다.
- 예: 고성능 웹 서버, 런타임 라이브러리 인터포지션 등.
동적 링크
-
로드 시간 동적 링크: 실행 파일이 처음 로드되고 실행될 때 발생합니다.
- 리눅스에서 ld-linux.so에 의해 자동으로 처리됩니다.
-
런타임 동적 링크: 프로그램이 시작된 후에 발생합니다.
- 리눅스에서는 dlopen() 인터페이스를 통해 수행됩니다.
-
동적 링킹은 로더가 수행하며, 프로그램이 가상 메모리에 올라가 동적으로 라이브러리를 불러서 실행할 수 있습니다.
-
동적 링크의 주체는 로더입니다.
-
프로그램이 실행될 때 라이브러리를 동적으로 링크하여 메모리에서 해당 라이브러리를 불러옵니다.
동적 링킹의 과정
-
프로그램 실행 시
- 프로그램이 실행 파일을 메모리에 로드합니다.
- 동적 링커(ld-linux.so)가 실행 파일의 헤더를 읽어 필요한 공유 라이브러리를 로드합니다.
-
라이브러리 로딩
- 로드 시간 동적 링킹: 실행 파일이 처음 로드되고 실행될 때 발생합니다. (예: printf 함수는 프로그램이 실행될 때 메모리에 로드됨)
- 런타임 동적 링킹: 프로그램이 실행된 후 동적 라이브러리를 로드할 수 있습니다. (예:
dlopen() 함수 사용)
동적 링킹의 장점
- 메모리 사용 절약: 여러 프로세스가 동일한 라이브러리를 공유하여 메모리 사용량을 줄일 수 있습니다.
- 유지보수 용이성: 라이브러리를 업데이트하면 모든 관련 프로그램이 재컴파일 없이도 최신 버전을 사용할 수 있습니다.