출발점

근본 원인 :
 - "컴파일 시점에 최종 메모리 주소를 모름"

이 하나의 문제에서 모든 개념이 파생

모르는 이유

이유 1. 여러 ".o 파일"이 합쳐져야 최종 배치가 결정
		-> 내부 심볼 주소를 모름
		-> ld(링커)가 릴로케이션을 하여 해결

이유 2. "공유 라이브러리"는 실행 시점에 메모리에 올라감
		-> 외부 심볼 주소 모름
		-> ld.so(동적 링커)가 해결

동작 설명

각각 설명

PIC vs PIE

공통점 : 둘 다 "절대주소 안 쓰는 코드 생성"
차이점 : 내부 전역 심볼을 GOT로 가느냐 마느냐

PIE (실행 파일)
 -> 내 전역변수는 나만 쓰니까 RIP-relative로 직접
 -> 외부만 GOT/PLT

PIC (.so)
 -> 내 전역변수도 다른 데서 덮어쓸 수 있으니까 GOT 경유
 -> 외부도 당연히 GOT/PLT

왜?
 -> symbol iterposition : .so의 전역 심볼은 실행 파일이나 다른 .so 같은 이름으로 재정의 가능
 -> 그래서 "내 변수인데 최종 주소가 내 거 아닐 수 있음"
 -> 그래서 안전하게 GOT를 거침

소스코드의 목적에 따른 컴파일

소스코드 → 실행파일 (.out, ELF executable)
            "직접 실행되는 프로그램"
            예: ./a.out, /usr/bin/ls

소스코드 → 공유 라이브러리 (.so)
            "다른 프로그램이 가져다 쓰는 부품"
            예: /lib/x86_64-linux-gnu/libc.so.6
---------------------------------------------------
libc.so를 만드는 소스코드 -> -fPIC로 컴파일
a.out을 만드는 소스코드   -> -fPIE로 컴파일

같은 C 소스코드라도 "최종 산출물이 뭐냐"에 따라 컴파일 옵션이 달라짐

GOT vs PLT

GOT = 테이블 (데이터)
 -> 주소가 저장되는 "칸"
 -> 변수든 함수든 실제 주소가 여기 들어감
 -> 두 종류 : 
		 .got     = 변수용 (eager, 로드 시 바로 채움)
		 .got.plt = 함수용 (lazy, 첫 호출 시 채움)

PLT = 코드
 -> 외부 "함수" 호출 전용 트램펄린
 -> GOT를 읽어서 점프하는 짧은 코드
 -> 변수 접근에는 PLT가 안 쓰임

즉 :
 외부 변수 접근 : 코드 -> GOT -> 실제 변수
 외부 함수 호출 : 코드 -> PLT -> GOT -> 실제 함수

PLT가 함수에만 있는 이유 :
 -> 함수는 lazy binding이 가능 (첫 호출까지 resolve 지연)
 -> 변수는 접근하는 순간 바로 값이 필요하니까 lazy 불가

Lazy Binding

프로그램이 시작할 때 외부 함수 주소를 전부 찾아두는 게 아니라, 실제로 호출되는 순간에 비로소 주소를 찾는 방식