본격적으로 링킹과정에 대해 깊이 있게 다뤄보는 시간이다!
링커는 입력 재배치 가능 목적파일들의 심볼 테이블로부터 정확히 한 개의 심볼 정의에 각 참조를 연결시켜서 심볼 참조를 해석한다. 심볼의 해석은 동일한 모듈 내에 정의된 지역 심볼들로 참조를 한 경우에 대해서는 간단하다. 하지만, 컴파일러는 모듈 당 하나의 지역 심볼 정의만을 허용하기에 전역심볼은 심볼 해석이 까다로울 수 있다.
STEP 1. Symbol Resolution 심볼 해석
: 심볼 참조를 정확하게 하나의 심볼 정의에 연결되어 있는지 확인한다. one symbol definition
링커는 입력 재배치 가능 목적파일들의 심볼 테이블로부터 정확히 한 개의 심볼 정의에 각 참조를 연결시켜서 심볼 참조를 해석한다. 심볼의 해석은 동일한 모듈 내에 정의된 지역 심볼들로 참조를 한 경우에 대해서는 간단하다. 다만, 컴파일러는 모듈당 당 하나의 지역 심볼 정의만을 허용하기에 전역심볼은 심볼 해석이 까다로울 수 있다.
symbol 을 binding 속성으로 분류하면 local, global, external symbol 이 있다.
- local symbol : 모듈 내에서 정의하고, 참조된 심볼. 컴파일 단계에서 이미 고려된다. → 이미 목적파일이 만들어진 링커 단계에서는 고려하지 않아도 된다.
- external - 모듈 내에서 참조하고 있으나, 정의가 없는 심볼. 리스트로 관리되는 심볼들 중 같은 이름을 가진 심볼을 찾아내면 된다.
- global symbol - 모듈 내에서 정의할 수 있고, 다른 모듈에서 참조할 수도 있는 심볼
Problem ... Duplicate global symbol with same name
$gcc -fno -common
//전역변수들이 공통 심볼로 처리되도록 만드는 gcc 옵션
//초기화되지 않은 전역변수가 공통 심볼로 취급되지 않도록 강제한다. -> 충돌 방지
//엄격하게 접근
//gcc에서 전역변수가 초기화되지 않은 상태로 선언될 때, 기본적으로 -fcommon이라는 동작이 활성화 된다.
//fcommon이 기본값일 때는 여러 소스파일에서 같은 이름의 초기화되지 않은 전역변수를 선언하면
하나의 변수로 병합해 오류가 발생하지 않을 수 있다. 즉, 링커가 임의의 변수를 선택해 definition으로 사용한다.
Strong symbol
- 초기화된 심볼
- 함수
Weak symbol
- 초기화되지 않은 심볼
전역변수의 링킹 심볼 법칙 (default state -fcommon)
1. strong symbol 2개 이상 → 링킹 에러
2. strong / weak symbol → strong symbol 선택
3. weak symbol 2개 이상 → 임의의 심볼 선택 (개발자 의도를 알 수 없음...)
Q. multiple weak symbols , but type 이 다르다면...? 임의의 심볼을 선택하지만... surprise 발생!!!
//1
int x;
int y ;
p1() {};
//2
double x;
p2(){};
/* x in 2 might overwrite y!! */
//1
int x =7;
int y =5;
p1() {};
//2
double x;
p2() {};
/* x in 2 will overwrite y !! */
Weak symbol's section
//symbol table option 3가지
objdump -t foo.o
//or
nm foo.o //주소, 섹션이름, 심볼이름 정도만
//or
readelf -s foo.o
/* Object 단위에서의 symbol table 이기에 *COM* 존재 */
weak 심볼은 링킹 후 선택될 수도, 사라질 수도 있는 모호한 상태이다. 즉, 경합 전에 해당 심볼이 definition일지, external 이 붙을지 알 수 없다. → 당장 .bss 로 갈 수 없다.
아직 선택되지 않은 전역변수 *COM*
cf) 정의되지 않은 external function → *UND*
Pseudo section - 섹션 헤더 테이블에 엔트리가 없음
ABS : 재배치해서는 안 되는 심볼
UNDFF : 정의되지 않은 심볼, 참조만 되고 다른 곳에서 정의된 심볼
COMMON : 아직 할당되거나 초기화되지 않은 데이터 객체 (주로 초기화하지 않은 전역변수)
전역변수 사용을 줄여야 하는 이유
PS나 CS 전공 수업을 들으면서 전역변수제발쓰지마세요라는교수님말씀을 정말~ 많이 들었는데, 항상 왜 쓰면 안 되는지도 모르고, 음 그런갑다... 생각했었다.
링킹이 끝난 후에 심볼의 섹션이 명확히 배정되기 때문에 메모리 성능 측면에서도, Multi thread 할 때도 전역변수 할당은 리스크를 감수해야한다. 그냥 쓰지말라면 쓰지마
STEP 2. Relocation 재배치
들어가기 전...
1. 섹션/심볼 재배치 → 같은 타입의 모든 섹션들을 새로운 섹션에 할당 + 런타임 메모리 주소를 섹션/심볼에 할당
2. 섹션 내 심볼 주소 수정(최종적인 주소 업데이트)
1. Merging : Section/symbol definition 재배치
→ 각각의 section/symbol 은 고유한 런타임 메모리 주소(final address)를 갖게 된다!
2. Modifying : Symbol references
Relocation entry : 재배치 엔트리
: 어셈블러가 생성한 지시사항. 다른 오브젝트 파일과 경합될 때 수정이 필요한 .text .data 내 심볼 위치를 알려준다.
- 재배치 엔트리는 .rel.text (코드) .rel.data(초기화된 데이터) section에 있다.
오브젝트 단위에서 참조만 되는 변수 → 어셈블러는 재배치 엔트리를 만든다.
$objdump -d -r main.o
//-r 옵션을 주면 오브젝트에 재배치 엔트리 정보가 남아있게 된다.
ELF 재배치 항목
offset : 수정될 필요가 있는 참조의 섹션 오프셋
type : 링커가 어떻게 새로운 참조를 수정할지
symbol : 수정된 참조가 가리켜야 하는 심볼 이름
append : 부호를 갖는 상수, 변경된 참조 값을 조정하기 위한 예외적 상황 (ex, PC가 자동적으로 다음 인스트럭션 가리킬 때 )
'Major S-T-U-D-Y > System Programming' 카테고리의 다른 글
[시스템 프로그래밍] 12 (1) 예외 제어흐름 (커널 모드) (0) | 2024.12.02 |
---|---|
[시스템 프로그래밍] 7 (4) How to use Static Library 정적 라이브러리 (0) | 2024.11.26 |
[시스템 프로그래밍] 7. (2) Linking 정적/동적 링킹 과정과 (0) | 2024.11.22 |
[시스템 프로그래밍] 7. Linking (1) 분할컴파일과 extern/static modifier (0) | 2024.11.21 |
9. Derived type - array, pointer, and structure (1) (1) | 2024.11.12 |