eBPF 프로그램은 C 언어로 작성한 후 컴파일만 한다고 바로 실행되는 것이 아닙니다. 컴파일된 eBPF Object 파일을 Linux Kernel에 등록하고, Verifier 검사를 거친 뒤 적절한 Hook에 연결해야 비로소 실행됩니다.
이 모든 과정을 담당하는 것이 eBPF Loader입니다.
초기의 eBPF 개발에서는 직접 BPF System Call을 호출하여 프로그램을 로드했지만, 현재는 대부분 libbpf Loader를 사용합니다. libbpf Loader는 프로그램 로드뿐 아니라 Map 생성, CO-RE Relocation, Hook 연결까지 자동으로 처리해 주므로 최신 eBPF 개발의 표준이 되었습니다.
이번 글에서는 eBPF Loader의 개념과 동작 원리, 로드 과정, libbpf Loader 구조, 실무 활용 방법을 자세히 알아보겠습니다.
Linux eBPF Loader란?
eBPF Loader는 컴파일된 eBPF 프로그램을 Linux Kernel에 등록하고 실행 가능한 상태로 만드는 구성 요소입니다.
기본 구조는 다음과 같습니다.
eBPF Source
↓
Clang
↓
ELF Object
↓
eBPF Loader
↓
Verifier
↓
Kernel
↓
Hook Attach
↓
Running
Loader는 단순히 파일을 읽는 것이 아니라 프로그램 실행에 필요한 모든 준비 작업을 수행합니다.
eBPF Loader가 필요한 이유
컴파일된 .o 파일에는 다음과 같은 정보가 포함되어 있습니다.
- eBPF Program
- BPF Map 정의
- BTF 정보
- CO-RE Relocation 정보
- Section 정보
이 정보를 Kernel이 이해할 수 있도록 해석하고 등록하는 작업이 필요합니다.
이 역할을 Loader가 수행합니다.
Loader의 동작 과정
전체 흐름은 다음과 같습니다.
ELF Object
↓
Section 분석
↓
Map 생성
↓
CO-RE 적용
↓
Verifier 검사
↓
Kernel Load
↓
Hook 연결
모든 단계가 성공해야 프로그램이 실행됩니다.
ELF Object 읽기
Loader는 먼저 ELF(Object) 파일을 분석합니다.
대표 Section
.text
.maps
.BTF
.BTF.ext
license
각 Section의 역할을 파악하여 필요한 작업을 수행합니다.
Map 생성
ELF 파일 안에 정의된 Map을 먼저 생성합니다.
예를 들어
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1024);
} counter_map SEC(".maps");
Loader는 이를 읽어 Kernel에 Hash Map을 생성합니다.
CO-RE Relocation
CO-RE를 사용하는 경우
Current Kernel
↓
BTF 확인
↓
Field Offset 계산
↓
Relocation
현재 Kernel 구조체에 맞게 자동 수정합니다.
Verifier 실행
Map 생성 후 Program이 Kernel에 전달됩니다.
Verifier는
- Pointer 검사
- Stack 검사
- Loop 검사
- Helper 검사
- Register 검사
를 수행합니다.
통과하지 못하면 프로그램은 로드되지 않습니다.
Hook 연결
Verifier를 통과하면 Program을 원하는 Hook에 연결합니다.
예를 들어
XDP
↓
eth0
또는
Tracepoint
↓
sched_process_exec
처럼 연결됩니다.
이후 이벤트가 발생하면 자동으로 실행됩니다.
libbpf Loader
현재 대부분의 프로젝트는 libbpf Loader를 사용합니다.
기본 구조
Application
↓
libbpf
↓
Loader
↓
Kernel
복잡한 System Call을 직접 구현하지 않아도 됩니다.
Skeleton Loader
Skeleton을 사용하는 경우
Program
↓
program.skel.h
↓
Auto Loader
↓
Kernel
Skeleton 내부에서 대부분의 Loader 기능이 자동 수행됩니다.
대표 함수
example_bpf__open();
Object 열기
example_bpf__load();
Kernel Load
example_bpf__attach();
Hook 연결
example_bpf__destroy();
종료
Loader와 bpftool
bpftool도 Loader 역할을 수행할 수 있습니다.
Program Load
sudo bpftool prog load demo.o /sys/fs/bpf/demo
Pinned Program 생성
sudo bpftool prog pin id 5 /sys/fs/bpf/demo
Pinned Program 확인
sudo bpftool prog show
Loader에서 자주 발생하는 오류
대표적인 오류는 다음과 같습니다.
| 오류 | 원인 |
|---|---|
| Invalid BTF | BTF 오류 |
| Verifier Reject | 안전성 검사 실패 |
| Map Creation Failed | Map 생성 실패 |
| Invalid License | 라이선스 정보 오류 |
| Invalid Section | Section 이름 오류 |
| Attach Failed | Hook 연결 실패 |
대부분은 Verifier 로그를 통해 원인을 확인할 수 있습니다.
실무에서 자주 사용하는 명령어
Kernel 버전 확인
uname -r
Program 확인
sudo bpftool prog show
Map 확인
sudo bpftool map show
Program 로드
sudo bpftool prog load demo.o /sys/fs/bpf/demo
BTF 확인
sudo bpftool btf show
libbpf 버전 확인
pkg-config --modversion libbpf
실무 사례
예를 들어 XDP 기반 방화벽 프로그램을 개발했다고 가정해 보겠습니다.
프로그램이 실행되는 과정은 다음과 같습니다.
- C 코드 작성
- Clang으로 eBPF Object 생성
- libbpf Loader가 ELF 파일 분석
- Hash Map 생성
- BTF 기반 CO-RE Relocation 수행
- Verifier 안전성 검사
- XDP Hook을 eth0 인터페이스에 연결
- 네트워크 패킷이 들어오면 eBPF 프로그램 실행
개발자는 대부분의 복잡한 과정을 직접 구현할 필요 없이 libbpf Loader가 자동으로 처리하는 기능을 활용할 수 있습니다.
자주 묻는 질문
Loader와 Verifier는 같은 역할인가요?
아닙니다. Loader는 eBPF 프로그램을 커널에 등록하고 필요한 자원을 준비하는 역할을 합니다. Verifier는 Loader가 전달한 프로그램이 안전한지 검사하는 역할을 수행합니다.
libbpf Loader를 반드시 사용해야 하나요?
필수는 아닙니다. 직접 BPF System Call을 사용하여 로드할 수도 있지만 구현이 매우 복잡합니다. 현재는 대부분의 프로젝트가 libbpf Loader를 사용합니다.
bpftool도 Loader 역할을 하나요?
네. bpftool은 eBPF 프로그램을 커널에 로드하고 관리할 수 있는 공식 도구입니다. 테스트나 디버깅 환경에서 자주 활용됩니다.
마무리
Linux eBPF Loader는 컴파일된 eBPF 프로그램을 커널에서 실행 가능한 상태로 만드는 핵심 구성 요소입니다. ELF Object 분석부터 Map 생성, CO-RE Relocation, Verifier 검사, Hook 연결까지 전체 과정을 담당하며, 최신 환경에서는 libbpf Loader가 사실상의 표준으로 자리 잡았습니다. Loader의 동작 원리를 이해하면 eBPF 프로그램의 실행 과정과 문제 해결 방법을 더욱 효과적으로 파악할 수 있습니다.