eBPF C source를 작성했다고 해서 그 C code가 그대로 kernel에서 실행되는 것은 아니다. compiler가 BPF instruction으로 변환하고, loader가 필요한 relocation을 처리해 kernel에 전달한다. kernel verifier의 검사를 통과한 program만 hook에 attach할 수 있으며, 실행은 interpreter 또는 architecture별 JIT compiler가 만든 native code로 이뤄질 수 있다.
전체 흐름을 먼저 연결하면 다음과 같다.
restricted C source
↓ clang/LLVM
BPF instructions in ELF (+ BTF)
↓ libbpf: open · relocate · load
kernel verifier
↓ accept
attach to hook
↓ event / packet / syscall path
interpreter or optional JIT execution
↕
BPF maps · helpers · kfuncs
BPF instruction set과 register
eBPF는 register 기반 instruction set을 사용한다. ABI에는 R0부터 R10까지 11개의 64-bit register가 정의되어 있다.
R0: function return valueR1~`R5`: function call argumentsR6~`R9`: callee가 보존하는 registerR10: read-only frame pointer
program type에 따라 entry context의 형태가 다르고, helper를 호출하면 argument register 규칙을 따라야 한다. “R1은 언제나 PID”처럼 고정된 의미를 붙이면 안 된다.
stack과 instruction은 제한된 실행 환경 안에서 사용된다. arbitrary kernel function을 일반 C program처럼 자유롭게 호출하는 모델이 아니다.
libbpf가 load 전후를 연결한다
clang은 BPF target의 object file을 만들 수 있고, ELF section에는 program과 map definition, BTF type 정보가 담길 수 있다. libbpf는 object를 열고, CO-RE relocation 같은 조정을 수행한 뒤 BPF system call interface를 통해 map과 program을 load한다.
load와 attach는 같은 단계가 아니다. verifier를 통과해 kernel에 load된 program도 적절한 tracepoint, kprobe, network hook 등에 attach해야 실제 경로에서 호출된다. hook 선택은 kprobe·tracepoint·uprobe의 차이와 연결된다.
verifier는 안전성 gate이지 정답 증명기가 아니다
verifier는 instruction과 control flow를 분석하며 register·stack state, pointer range, memory access, helper call 규칙 등을 추적한다. 안전하다고 증명할 수 없는 경로가 있으면 load를 거부한다.
하지만 verifier 통과가 business logic의 정확성이나 낮은 overhead를 보장하지는 않는다. 잘못된 key로 통계를 집계하거나 지나치게 많은 event를 내보내는 program도 memory safety 조건을 만족할 수 있다. verifier는 kernel에서 허용 가능한 실행인지를 판단하는 gate로 이해해야 한다.
검증 규칙은 kernel version과 program type에 따라 발전한다. 특정 error log를 만났다면 일반적인 우회 code를 복사하기보다 실제 kernel의 verifier log와 context type을 확인한다.
map은 kernel object다
BPF map은 key-value data를 저장하는 kernel object이며 file descriptor로 참조할 수 있다. user space에서 syscall을 통해 읽고 쓸 수 있고, BPF program도 helper 등을 이용해 접근한다. program 사이에서 state를 공유하거나 event를 user space로 전달하는 데도 쓰인다.
BPF program ── lookup/update ── BPF map
↑
user process ── fd / libbpf ──────┘
map을 단순히 “kernel과 user space 사이의 pipe”라고만 설명하면 부족하다. counter·LRU cache·per-CPU state·program array처럼 kernel 안의 program끼리 사용하는 상태도 map으로 구성할 수 있다. map type마다 concurrency, memory accounting, eviction 의미가 다르다.
helper와 kfunc는 허용된 kernel 기능의 경계다
BPF program은 등록된 helper function을 호출할 수 있다. 어떤 helper가 허용되는지는 program type에 따라 다르다. 최근 kernel에서는 BTF 정보를 사용하는 kfunc도 제공되지만, 이 역시 임의의 kernel function 호출이 아니며 허용 집합과 lifetime 규칙을 따라야 한다.
따라서 sample code를 다른 hook에 옮겼을 때 verifier가 거부할 수 있다. target kernel version, program type, attach point가 모두 호환되는지 확인해야 한다.
JIT는 선택 사항이다
verifier를 통과한 BPF instruction은 interpreter가 실행할 수 있고, kernel configuration과 architecture가 지원하면 JIT compiler가 native instruction으로 변환할 수 있다. JIT 사용 여부와 hardening 설정은 kernel build와 sysctl policy에 달려 있다.
# 읽기 전용 확인. 파일이 없으면 해당 kernel/config에서 제공되지 않을 수 있다.
sysctl net.core.bpf_jit_enable 2>/dev/null
값 하나만으로 program별 실제 실행 상태나 성능을 단정하지 않는다. production에서 JIT 설정을 바꾸는 것은 system-wide security·performance policy 변경이므로, 읽기 확인과 변경 작업을 분리하고 kernel 문서와 조직 기준을 먼저 검토한다.
eBPF가 허용하는 경계를 더 자세히 보려면 eBPF verifier와 안전성, memory allocation 추적 사례는 eBPF로 메모리를 추적하는 방법으로 이어진다.
참고 자료
'배움과 성장 > 시스템·성능' 카테고리의 다른 글
| 리눅스 메모리 분석 순서: RSS 증가부터 smaps·할당 경로까지 (0) | 2026.04.10 |
|---|---|
| eBPF 관측 Hook 선택: tracepoint·kprobe·fentry·uprobe 차이 (0) | 2026.04.09 |
| eBPF verifier가 보장하는 것과 보장하지 않는 것 (0) | 2026.04.07 |
| eBPF로 사용자 공간 메모리 할당 추적하기: malloc 호출과 누수 구분 (0) | 2026.04.06 |
| /proc/PID/smaps 메모리 분석: RSS·PSS·Anonymous를 구분하는 법 (0) | 2026.04.05 |
댓글