한 문장 요약

ELF는 리눅스에서 실행 파일, 공유 라이브러리(.so), 오브젝트 파일(.o)의 디스크 저장 구조를 정의하는 "표준 바이너리 포맷"

정리

ELF가 쓰이는 대상

- 실행 파일 : /usr/bin/git, /bin/ls
- 공유 라이브러리(.so) : /lib/x86_64-linux-gnu/libc.so.6
- 오브젝트 파일(.o) : 컴파일 중간 산출물
- (자주) core dump도 ELF 형태

ELF 내부 구조

EC2 Ubuntu Instance - ELF Header

EC2 Ubuntu Instance - ELF Header

1. ELF Hedaer(readelf -h)
- 파일의 신분증
	- Class       : ELF32/ELF64
	- Data        : little/big endian
	- Machine     : x64-64, AArch64(CPU 정보)
	- Type        : EXEC / DYN
	- Entry point : 시작 지점

2. Program Header Table(readelf -l) - 실행에 핵심
- 커널이 보는 적재 지도
	- PT_LOAD    : 코드/데이터 등 메모리에 매핑할 구간
	- PT_DYNAMIC : 동적 링크 정보
	- PT_INTERP  : 동적 링커 경로(ld.so) - 있으면 동적 링크

3. Section Header Table(readelf -S) - 링킹/디버깅가 참조
	- .text           : 코드
	- .data/.bss      : 데이터
	- .dynsym/.dynstr : 동적 심볼 테이블
	- .got/.plt       : 동적 링킹 테이블

execve, ld.so 동작

execve(커널 syscall)
- ELF Header를 파싱해서 실행 파일 타입 확인(ET_EXEC / ET_DYN)
- Program Header의 "PT_LOAD" 세그먼트를 "mmap()"으로 메모리에 매핑
- 정적 바이너리면 "entry point"로 바로 점프

ld.so(동적 링커, /lib64/ld-linux-x86-64.so.2)
 - 동적 링크 바이너리일 경우 "execve"가 "ld.so를 먼저 실행"
 - "DT_NEEDED" 태그를 보고 의존 공유 라이브러리(.so) 탐색
 - 각 ".so"를 "mmap()"으로 메모리에 로드
 - 심볼 릴로케이션(GOT/PLT 패치) 수행 후 실제 entry point로 점프
소스 코드(.c / .java 등)
				↓ compiler(gcc등)
오브젝트 파일(.o)  <- ELF 형태로 디스크에 저장
				↓ linker(ld)
실행 파일 / .so    <- ELF 형태로 디스크에 저장
				↓ execve / ld.so
메모리(프로세스)   <- 여기서 실행

=>
- 컴파일러/링커가 ".o(ELF) 파일"들을 읽어서 -> 최종 ELF 바이너리를 디스크에 씀
- execve/ld.so는 그 디스크의 ELF를 읽어서 -> 메모리에 올림
이후. 프로그램 entry로 점프 후
-----------------------------
커널이 컨텍스트를 바꿔서 이제부터 유저 코드 실행 시작

kernel이 ELF를 읽는 흐름

execve("./hello") 호출
					│
	        ▼
Kernel : ELF Header 읽기
					↓
					"Section Header Table이 어디있지?" -> 찾음
					↓
					Section Header Table 읽기
					↓
					".text = 파일 offset 0x0080, 크기 0x0F80"
					".data = 파일 offset 0x1500, 크기 0x0500"
					↓
					VMA 생성(가상 주소 공간 지도 그리기)
					VMA에 "이 가상 주소는 이 파일의 이 offset이랑 연결"
					↓
					실제로 RAM에는 아직 안올림

Page Fault 발생 시:
					│
	        ▼
Kernel : "이 가상 주소 -> VMA 확인"
						- 이 파일의 offset 0x0080이랑 연결되어 있네
					↓
					RAM 프레임에 복사
					↓
					페이지 테이블 업데이트

# 직접 확인
$ readelf -S heelo

Section Headers:
  [Nr] Name    Type    Address   Offset  Size
  [ 1] .text   PROGBITS 00400080  000080  000F80  ← 여기!
  [ 2] .data   PROGBITS 00601500  001500  000500