📘
eBPF第5章演習
5.11 演習
1.bpftool btf dump map / bpftool btf dump progを実行して、それぞれMapとプログラムに関連するBTF情報を確認する問題
MapのBTF情報はそのままProgに含まれていることを確認。またID、名前などから指定できる。
Map のBTF情報
$ sudo bpftool map list
98: hash name my_config flags 0x0
key 4B value 12B max_entries 10240 memlock 1002112B
btf_id 85
# or specify by ID: sudo bpftool btf dump map id 98
$ sudo bpftool btf dump map name my_config
[12] TYPEDEF 'u32' type_id=13
[16] STRUCT 'user_msg_t' size=12 vlen=1
'message' type_id=18 bits_offset=0
Ditto for prog
$ sudo bpftool prog list
...
221: kprobe name hello tag aade5e7208515a74 gpl
loaded_at 2024-07-11T12:46:24+0000 uid 0
xlated 512B jited 274B memlock 4096B map_ids 101,98,100,97
btf_id 85
# or specify by ID: sudo bpftool btf dump prog id 221
$ sudo bpftool btf dump prog name hello
[1] PTR '(anon)' type_id=3
...
[12] TYPEDEF 'u32' type_id=13
[13] TYPEDEF '__u32' type_id=14
[14] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)
[15] PTR '(anon)' type_id=16
[16] STRUCT 'user_msg_t' size=12 vlen=1
'message' type_id=18 bits_offset=0
...
2. bpftool btf dump file & dump progが同じ出力であることを確認する問題
$ sudo bpftool btf dump file hello-buffer-config.bpf.o
[1] PTR '(anon)' type_id=3
[2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
...
$ sudo bpftool btf dump prog id 221
[1] PTR '(anon)' type_id=3
[2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
3 bpftool -d prog load からのデバッグ出力を眺める問題
$ sudo bpftool -d prog load hello-buffer-config.bpf.o /sys/fs/bpf/hello
Output
libbpf: loading hello-buffer-config.bpf.o
libbpf: elf: section(3) ksyscall/execve, size 792, link 0, flags 6, type=1 # here, セクションロード
libbpf: sec 'ksyscall/execve': found program 'hello' at insn offset 0 (0 bytes), code size 99 insns (792 bytes)
libbpf: elf: section(4) .relksyscall/execve, size 96, link 13, flags 40, type=9
libbpf: elf: section(5) .data, size 12, link 0, flags 3, type=1
libbpf: elf: section(6) license, size 13, link 0, flags 3, type=1 # here, ライセンス
libbpf: license of hello-buffer-config.bpf.o is Dual BSD/GPL
libbpf: elf: section(7) .maps, size 56, link 0, flags 3, type=1
libbpf: elf: section(8) .BTF, size 1805, link 0, flags 0, type=1
libbpf: elf: section(10) .BTF.ext, size 668, link 0, flags 0, type=1
libbpf: elf: section(13) .symtab, size 264, link 1, flags 0, type=2
libbpf: looking for externs among 11 symbols...
libbpf: collected 1 externs total
libbpf: extern (kcfg) #0: symbol 6, off 0, name LINUX_HAS_SYSCALL_WRAPPER
libbpf: map 'output': at sec_idx 7, offset 0.
libbpf: map 'output': found type = 4.
libbpf: map 'output': found key_size = 4.
libbpf: map 'output': found value_size = 4.
libbpf: map 'my_config': at sec_idx 7, offset 24.
libbpf: map 'my_config': found type = 1.
libbpf: map 'my_config': found key [12], sz = 4.
libbpf: map 'my_config': found value [16], sz = 12.
libbpf: map 'my_config': found max_entries = 10240.
libbpf: map 'hello_bu.data' (global data): at sec_idx 5, offset 0, flags 400.
libbpf: map 2 is "hello_bu.data"
libbpf: map 'hello_b.kconfig' (global data): at sec_idx 13, offset 0, flags 480.
libbpf: map 3 is "hello_b.kconfig"
libbpf: sec '.relksyscall/execve': collecting relocation for section(3) 'ksyscall/execve'
libbpf: sec '.relksyscall/execve': relo #0: insn #1 against 'LINUX_HAS_SYSCALL_WRAPPER'
libbpf: prog 'hello': found extern #0 'LINUX_HAS_SYSCALL_WRAPPER' (sym 6) for insn #1
libbpf: sec '.relksyscall/execve': relo #1: insn #41 against 'my_config'
libbpf: prog 'hello': found map 1 (my_config, sec 7, off 24) for insn #41
libbpf: sec '.relksyscall/execve': relo #2: insn #44 against 'message'
libbpf: prog 'hello': found data map 2 (hello_bu.data, sec 5, off 0) for insn 44
libbpf: sec '.relksyscall/execve': relo #3: insn #77 against 'my_config'
libbpf: prog 'hello': found map 1 (my_config, sec 7, off 24) for insn #77
libbpf: sec '.relksyscall/execve': relo #4: insn #80 against 'message'
libbpf: prog 'hello': found data map 2 (hello_bu.data, sec 5, off 0) for insn 80
libbpf: sec '.relksyscall/execve': relo #5: insn #91 against 'output'
libbpf: prog 'hello': found map 0 (output, sec 7, off 0) for insn #91
libbpf: loading kernel BTF '/sys/kernel/btf/vmlinux': 0
libbpf: extern (kcfg) 'LINUX_HAS_SYSCALL_WRAPPER': set to 0x1
libbpf: sec 'ksyscall/execve': found 3 CO-RE relocations
libbpf: CO-RE relocating [22] struct pt_regs: found target candidate [83] struct pt_regs in [vmlinux] # 再配置
libbpf: prog 'hello': relo #0: <byte_off> [22] struct pt_regs.di (0:14 @ offset 112)
libbpf: prog 'hello': relo #0: matching candidate #0 <byte_off> [83] struct pt_regs.di (0:14 @ offset 112)
libbpf: prog 'hello': relo #0: patched insn #5 (ALU/ALU64) imm 112 -> 112
libbpf: prog 'hello': relo #1: <byte_off> [22] struct pt_regs.di (0:14 @ offset 112)
libbpf: prog 'hello': relo #1: matching candidate #0 <byte_off> [83] struct pt_regs.di (0:14 @ offset 112)
libbpf: prog 'hello': relo #1: patched insn #6 (LDX/ST/STX) off 112 -> 112
libbpf: prog 'hello': relo #2: <byte_off> [22] struct pt_regs.di (0:14 @ offset 112)
libbpf: prog 'hello': relo #2: matching candidate #0 <byte_off> [83] struct pt_regs.di (0:14 @ offset 112)
libbpf: prog 'hello': relo #2: patched insn #48 (LDX/ST/STX) off 112 -> 112
libbpf: map 'output': setting size to 16
libbpf: map 'output': created successfully, fd=4
libbpf: map 'my_config': created successfully, fd=5
libbpf: map 'hello_bu.data': created successfully, fd=6
libbpf: map 'hello_b.kconfig': created successfully, fd=7
libbpf: prog 'hello': -- BEGIN PROG LOAD LOG --
func#0 @0
0: R1=ctx() R10=fp0
; int BPF_KPROBE_SYSCALL(hello, const char *pathname)
0: (bf) r6 = r1 ; R1=ctx() R6_w=ctx()
; int BPF_KPROBE_SYSCALL(hello, const char *pathname)
1: (18) r1 = 0xffffa776033fe000 ; R1_w=map_value(map=hello_b.kconfig,ks=4,vs=1)
3: (71) r1 = *(u8 *)(r1 +0) ; R1_w=1
4: (15) if r1 == 0x0 goto pc+43
mark_precise: frame0: last_idx 4 first_idx 0 subseq_idx -1
mark_precise: frame0: regs=r1 stack= before 3: (71) r1 = *(u8 *)(r1 +0)
4: R1_w=1
5: (b7) r1 = 112 ; R1_w=112
6: (79) r3 = *(u64 *)(r6 +112) ; R3_w=scalar() R6_w=ctx()
7: (0f) r3 += r1 ; R1_w=112 R3_w=scalar()
8: (bf) r1 = r10 ; R1_w=fp0 R10=fp0
9: (07) r1 += -56 ; R1_w=fp-56
; int BPF_KPROBE_SYSCALL(hello, const char *pathname)
10: (b7) r2 = 8 ; R2_w=8
11: (85) call bpf_probe_read_kernel#113
mark_precise: frame0: last_idx 11 first_idx 0 subseq_idx -1
mark_precise: frame0: regs=r2 stack= before 10: (b7) r2 = 8
12: R0=scalar() fp-56=mmmmmmmm
; int BPF_KPROBE_SYSCALL(hello, const char *pathname)
12: (79) r7 = *(u64 *)(r10 -56) ; R7_w=scalar() R10=fp0 fp-56=mmmmmmmm
13: (b7) r1 = 0 ; R1_w=0
; struct data_t data = {};
14: (63) *(u32 *)(r10 -12) = r1
mark_precise: frame0: last_idx 14 first_idx 12 subseq_idx -1
mark_precise: frame0: regs=r1 stack= before 13: (b7) r1 = 0
15: R1_w=0 R10=fp0 fp-16=0000????
15: (63) *(u32 *)(r10 -16) = r1 ; R1_w=0 R10=fp0 fp-16=00000
16: (63) *(u32 *)(r10 -20) = r1
mark_precise: frame0: last_idx 16 first_idx 12 subseq_idx -1
mark_precise: frame0: regs=r1 stack= before 15: (63) *(u32 *)(r10 -16) = r1
mark_precise: frame0: regs=r1 stack= before 14: (63) *(u32 *)(r10 -12) = r1
mark_precise: frame0: regs=r1 stack= before 13: (b7) r1 = 0
17: R1_w=0 R10=fp0 fp-24=0000????
17: (63) *(u32 *)(r10 -24) = r1 ; R1_w=0 R10=fp0 fp-24=00000
18: (63) *(u32 *)(r10 -28) = r1
mark_precise: frame0: last_idx 18 first_idx 12 subseq_idx -1
mark_precise: frame0: regs=r1 stack= before 17: (63) *(u32 *)(r10 -24) = r1
mark_precise: frame0: regs=r1 stack= before 16: (63) *(u32 *)(r10 -20) = r1
mark_precise: frame0: regs=r1 stack= before 15: (63) *(u32 *)(r10 -16) = r1
mark_precise: frame0: regs=r1 stack= before 14: (63) *(u32 *)(r10 -12) = r1
mark_precise: frame0: regs=r1 stack= before 13: (b7) r1 = 0
19: R1_w=0 R10=fp0 fp-32=0000????
19: (63) *(u32 *)(r10 -32) = r1 ; R1_w=0 R10=fp0 fp-32=00000
20: (63) *(u32 *)(r10 -36) = r1
mark_precise: frame0: last_idx 20 first_idx 12 subseq_idx -1
mark_precise: frame0: regs=r1 stack= before 19: (63) *(u32 *)(r10 -32) = r1
mark_precise: frame0: regs=r1 stack= before 18: (63) *(u32 *)(r10 -28) = r1
mark_precise: frame0: regs=r1 stack= before 17: (63) *(u32 *)(r10 -24) = r1
mark_precise: frame0: regs=r1 stack= before 16: (63) *(u32 *)(r10 -20) = r1
mark_precise: frame0: regs=r1 stack= before 15: (63) *(u32 *)(r10 -16) = r1
mark_precise: frame0: regs=r1 stack= before 14: (63) *(u32 *)(r10 -12) = r1
mark_precise: frame0: regs=r1 stack= before 13: (b7) r1 = 0
21: R1_w=0 R10=fp0 fp-40=0000????
21: (63) *(u32 *)(r10 -40) = r1 ; R1_w=0 R10=fp0 fp-40=00000
22: (63) *(u32 *)(r10 -44) = r1
mark_precise: frame0: last_idx 22 first_idx 12 subseq_idx -1
mark_precise: frame0: regs=r1 stack= before 21: (63) *(u32 *)(r10 -40) = r1
mark_precise: frame0: regs=r1 stack= before 20: (63) *(u32 *)(r10 -36) = r1
mark_precise: frame0: regs=r1 stack= before 19: (63) *(u32 *)(r10 -32) = r1
mark_precise: frame0: regs=r1 stack= before 18: (63) *(u32 *)(r10 -28) = r1
mark_precise: frame0: regs=r1 stack= before 17: (63) *(u32 *)(r10 -24) = r1
mark_precise: frame0: regs=r1 stack= before 16: (63) *(u32 *)(r10 -20) = r1
mark_precise: frame0: regs=r1 stack= before 15: (63) *(u32 *)(r10 -16) = r1
mark_precise: frame0: regs=r1 stack= before 14: (63) *(u32 *)(r10 -12) = r1
mark_precise: frame0: regs=r1 stack= before 13: (b7) r1 = 0
23: R1_w=0 R10=fp0 fp-48=0000????
23: (63) *(u32 *)(r10 -48) = r1 ; R1_w=0 R10=fp0 fp-48=00000
24: (63) *(u32 *)(r10 -8) = r1 ; R1_w=0 R10=fp0 fp-8=????0
; data.pid = bpf_get_current_pid_tgid() >> 32;
25: (85) call bpf_get_current_pid_tgid#14 ; R0_w=scalar()
; data.pid = bpf_get_current_pid_tgid() >> 32;
26: (77) r0 >>= 32 ; R0_w=scalar(smin=0,smax=umax=0xffffffff,var_off=(0x0; 0xffffffff))
; data.pid = bpf_get_current_pid_tgid() >> 32;
27: (63) *(u32 *)(r10 -56) = r0 ; R0_w=scalar(smin=0,smax=umax=0xffffffff,var_off=(0x0; 0xffffffff)) R10=fp0 fp-56=mmmmscalar(smin=0,smax=umax=0xffffffff,var_off=(0x0; 0xffffffff))
; data.uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;
28: (85) call bpf_get_current_uid_gid#15 ; R0=scalar()
; data.uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;
29: (63) *(u32 *)(r10 -52) = r0 ; R0=scalar() R10=fp0 fp-56=mmmmmmmm
; struct data_t data = {};
30: (bf) r1 = r10 ; R1_w=fp0 R10=fp0
31: (07) r1 += -48 ; R1_w=fp-48
; bpf_get_current_comm(&data.command, sizeof(data.command));
32: (b7) r2 = 16 ; R2_w=16
33: (85) call bpf_get_current_comm#16
mark_precise: frame0: last_idx 33 first_idx 29 subseq_idx -1
mark_precise: frame0: regs=r2 stack= before 32: (b7) r2 = 16
34: R0_w=scalar() fp-40=mmmmmmmm fp-48=mmmmmmmm
; struct data_t data = {};
34: (bf) r1 = r10 ; R1_w=fp0 R10=fp0
35: (07) r1 += -20 ; R1_w=fp-20
; bpf_probe_read_user_str(&data.path, sizeof(data.path), pathname);
36: (b7) r2 = 16 ; R2_w=16
37: (bf) r3 = r7 ; R3_w=scalar(id=1) R7=scalar(id=1)
38: (85) call bpf_probe_read_user_str#114
mark_precise: frame0: last_idx 38 first_idx 29 subseq_idx -1
mark_precise: frame0: regs=r2 stack= before 37: (bf) r3 = r7
mark_precise: frame0: regs=r2 stack= before 36: (b7) r2 = 16
39: R0=scalar(smin=smin32=-4095,smax=smax32=16) fp-8=????mmmm fp-16=mmmmmmmm fp-24=mmmmmmmm
; data.uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;
39: (bf) r2 = r10 ; R2_w=fp0 R10=fp0
40: (07) r2 += -52 ; R2_w=fp-52
; p = bpf_map_lookup_elem(&my_config, &data.uid);
41: (18) r1 = 0xffff96e656576c00 ; R1_w=map_ptr(map=my_config,ks=4,vs=12)
43: (85) call bpf_map_lookup_elem#1 ; R0_w=map_value_or_null(id=2,map=my_config,ks=4,vs=12)
44: (18) r3 = 0xffffa776033f6000 ; R3_w=map_value(map=hello_bu.data,ks=4,vs=12)
46: (15) if r0 == 0x0 goto pc+37 ; R0_w=map_value(map=my_config,ks=4,vs=12)
47: (05) goto pc+35
; p = bpf_map_lookup_elem(&my_config, &data.uid);
83: (bf) r3 = r0 ; R0=map_value(map=my_config,ks=4,vs=12) R3=map_value(map=my_config,ks=4,vs=12)
; struct data_t data = {};
84: (bf) r1 = r10 ; R1_w=fp0 R10=fp0
85: (07) r1 += -32 ; R1_w=fp-32
;
86: (b7) r2 = 12 ; R2_w=12
87: (85) call bpf_probe_read_kernel_str#115
mark_precise: frame0: last_idx 87 first_idx 84 subseq_idx -1
mark_precise: frame0: regs=r2 stack= before 86: (b7) r2 = 12
88: R0_w=scalar(smin=smin32=-4095,smax=smax32=12) fp-24=mmmmmmmm fp-32=mmmmmmmm
88: (bf) r4 = r10 ; R4_w=fp0 R10=fp0
; struct data_t data = {};
89: (07) r4 += -56 ; R4_w=fp-56
; bpf_perf_event_output(ctx, &output, BPF_F_CURRENT_CPU, &data, sizeof(data));
90: (bf) r1 = r6 ; R1_w=ctx() R6=ctx()
91: (18) r2 = 0xffff96e60c2c2600 ; R2_w=map_ptr(map=output,ks=4,vs=4)
93: (18) r3 = 0xffffffff ; R3_w=0xffffffff
95: (b7) r5 = 52 ; R5_w=52
96: (85) call bpf_perf_event_output#25
mark_precise: frame0: last_idx 96 first_idx 84 subseq_idx -1
mark_precise: frame0: regs=r5 stack= before 95: (b7) r5 = 52
97: R0=scalar()
; int BPF_KPROBE_SYSCALL(hello, const char *pathname)
97: (b7) r0 = 0 ; R0_w=0
98: (95) exit
from 46 to 84: R0_w=0 R3_w=map_value(map=hello_bu.data,ks=4,vs=12) R6=ctx() R7=scalar(id=1) R10=fp0 fp-8=????mmmm fp-16=mmmmmmmm fp-24=mmmmmmmm fp-32=00000 fp-40=mmmmmmmm fp-48=mmmmmmmm fp-56=mmmmmmmm
84: R0_w=0 R3_w=map_value(map=hello_bu.data,ks=4,vs=12) R6=ctx() R7=scalar(id=1) R10=fp0 fp-8=????mmmm fp-16=mmmmmmmm fp-24=mmmmmmmm fp-32=00000 fp-40=mmmmmmmm fp-48=mmmmmmmm fp-56=mmmmmmmm
; struct data_t data = {};
84: (bf) r1 = r10 ; R1_w=fp0 R10=fp0
85: (07) r1 += -32 ; R1_w=fp-32
;
86: (b7) r2 = 12 ; R2_w=12
87: (85) call bpf_probe_read_kernel_str#115
mark_precise: frame0: last_idx 87 first_idx 39 subseq_idx -1
mark_precise: frame0: regs=r2 stack= before 86: (b7) r2 = 12
88: R0_w=scalar(smin=smin32=-4095,smax=smax32=12) fp-24=mmmmmmmm fp-32=mmmmmmmm
88: (bf) r4 = r10 ; R4_w=fp0 R10=fp0
; struct data_t data = {};
89: (07) r4 += -56 ; R4_w=fp-56
; bpf_perf_event_output(ctx, &output, BPF_F_CURRENT_CPU, &data, sizeof(data));
90: (bf) r1 = r6 ; R1_w=ctx() R6=ctx()
91: (18) r2 = 0xffff96e60c2c2600 ; R2_w=map_ptr(map=output,ks=4,vs=4)
93: (18) r3 = 0xffffffff ; R3_w=0xffffffff
95: (b7) r5 = 52 ; R5_w=52
96: (85) call bpf_perf_event_output#25
mark_precise: frame0: last_idx 96 first_idx 39 subseq_idx -1
mark_precise: frame0: regs=r5 stack= before 95: (b7) r5 = 52
97: safe
verification time 205 usec
stack depth 56
processed 71 insns (limit 1000000) max_states_per_insn 1 total_states 5 peak_states 5 mark_read 4
-- END PROG LOAD LOG --
Error: failed to pin program ksyscall/execve
4 BTFHubからさまざまなヘッダファイルをダウンロードして、それぞれを使ってBPFプログラムをビルドする問題
自分のVMのアーキテクチャ
$ uname -a
Linux rmitani-cloudvm 6.8.0-1010-aws #10-Ubuntu SMP Thu Jun 13 17:36:15 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
こちらのREADMEがいい感じに説明してくれている。
別で traceeのレポジトリもクローンしておく必要がある。
$ git clone https://github.com/aquasecurity/tracee.git
$ make all
btfhub repository: (READMEに書かれているのと少し違う)
$ sudo ./tools/btfgen.sh -a x86_64 -o $HOME/indeed/tracee/dist/tracee.bpf.o
Processing 4.14.336-256.557.amzn2.x86_64.btf...
Processing 4.14.318-241.531.amzn2.x86_64.btf...
Processing 4.14.256-197.484.amzn2.x86_64.btf...
...
custom-archive/fedora/30/x86_64/5.0.9-301.fc30.x86_64.btf で試してみる。
$ find custom-archive
custom-archive
custom-archive/.gitignore
custom-archive/fedora
custom-archive/fedora/30
custom-archive/fedora/30/x86_64
custom-archive/fedora/30/x86_64/5.0.9-301.fc30.x86_64.btf
custom-archive/fedora/30/x86_64/5.6.13-100.fc30.x86_64.btf
custom-archive/fedora/26
$ sudo bpftool btf dump file custom-archive/fedora/30/x86_64/5.0.9-301.fc30.x86_64.btf format c
# Cのコードが出力される
技評ページにもありましたが、こっちはうまくいきませんでした。。。異なるカーネルのvmlinux.hを生成するには
5 hello-buffer-config.cプログラムを修正して異なるユーザに対して異なるメッセージを設定できるようにする。
mapを更新するコードを追加しました。
hello-buffer-config.cの変更点
int main()
{
int err;
struct perf_buffer *pb = NULL;
libbpf_set_print(libbpf_print_fn);
skel = hello_buffer_config_bpf__open_and_load();
if (!skel) {
printf("Failed to open BPF object\n");
return 1;
}
err = hello_buffer_config_bpf__attach(skel);
if (err) {
fprintf(stderr, "Failed to attach BPF skeleton: %d\n", err);
hello_buffer_config_bpf__destroy(skel);
return 1;
}
+ // Populate the my_config map with user-specific messages
+ __u32 uid = 1002; // User ID 1002
+ struct user_msg_t user_message_1002 = {"User 1002!"};
+
+ int fd = bpf_map__fd(skel->maps.my_config);
+ if (bpf_map_update_elem(fd, &uid, &user_message_1002, BPF_ANY) != 0) {
+ fprintf(stderr, "Failed to update BPF map for user 1002: %s\n",
strerror(errno));
+ hello_buffer_config_bpf__destroy(skel);
+ return 1;
+ }
+
+ __u32 root_uid = 0; // Root user ID
+ struct user_msg_t user_message_root = {"Root user!"};
+
+ if (bpf_map_update_elem(fd, &root_uid, &user_message_root, BPF_ANY) != 0
) {
+ fprintf(stderr, "Failed to update BPF map for root user: %s\n",
strerror(errno));
+ hello_buffer_config_bpf__destroy(skel);
+ return 1;
+ }
+
// 以下同じ
...
結果
$ sudo ./hello-buffer-config
9195 0 falcon-task /opt/CrowdStrik Root user!
9196 0 falcon-task /opt/CrowdStrik Root user!
9199 1002 bash /usr/bin/id User 1002!
6 SEC自作?
ELFファイルになんとかSection Nameを書き込む必要がある。
かつてはみんな自分のカスタムのセクションネームを追加していたらしい。今はカスタムの名前は禁止されている。
people started adding random suffixes
(e.g., SEC("xdp_my_prog1")) to create a unique identifier
both as a convenience and as a work around for the single
BPF program per SEC() limitation.
オブジェクトファイルのセクション名をXDPに直接書き換えるCのスクリプトを書いてみました(これが問題の意図かはわかりません)。
$ cat your_bpf_program.c
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
SEC("custom_section")
int custom_prog(struct xdp_md *ctx) {
// BPF program logic here
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
Compile and check ELF file:
$ sudo clang -O2 -target bpf -c your_bpf_program.c -o your_bpf_program.o
$ readelf -S your_bpf_program.o
There are 7 section headers, starting at offset 0x118:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .strtab STRTAB 0000000000000000 000000ba
000000000000005c 0000000000000000 0 0 1
[ 2] .text PROGBITS 0000000000000000 00000040
0000000000000000 0000000000000000 AX 0 0 4
[ 3] custom_section PROGBITS 0000000000000000 00000040
0000000000000010 0000000000000000 AX 0 0 8
ELFファイルのセクションを書き換える rename.c
$ cat rename_section.c
#include <libelf.h>
#include <fcntl.h>
#include <gelf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void rename_section(const char *filename, const char *old_name, const char *new_name) {
if (elf_version(EV_CURRENT) == EV_NONE) {
fprintf(stderr, "ELF library initialization failed: %s\n", elf_errmsg(-1));
exit(EXIT_FAILURE);
}
int fd = open(filename, O_RDWR);
if (fd < 0) {
perror("open");
exit(EXIT_FAILURE);
}
Elf *e = elf_begin(fd, ELF_C_RDWR, NULL);
if (!e) {
perror("elf_begin");
close(fd);
exit(EXIT_FAILURE);
}
size_t shstrndx;
if (elf_getshstrndx(e, &shstrndx) != 0) {
fprintf(stderr, "elf_getshstrndx() failed: %s\n", elf_errmsg(-1));
elf_end(e);
close(fd);
exit(EXIT_FAILURE);
}
Elf_Scn *shstrscn = elf_getscn(e, shstrndx);
if (!shstrscn) {
fprintf(stderr, "elf_getscn() failed: %s\n", elf_errmsg(-1));
elf_end(e);
close(fd);
exit(EXIT_FAILURE);
}
GElf_Shdr shstrshdr;
if (!gelf_getshdr(shstrscn, &shstrshdr)) {
fprintf(stderr, "gelf_getshdr() failed: %s\n", elf_errmsg(-1));
elf_end(e);
close(fd);
exit(EXIT_FAILURE);
}
Elf_Data *shstrdata = elf_getdata(shstrscn, NULL);
if (!shstrdata) {
fprintf(stderr, "elf_getdata() failed: %s\n", elf_errmsg(-1));
elf_end(e);
close(fd);
exit(EXIT_FAILURE);
}
// Find the section with old_name and update it to new_name
Elf_Scn *scn = NULL;
while ((scn = elf_nextscn(e, scn)) != NULL) {
GElf_Shdr shdr;
gelf_getshdr(scn, &shdr);
const char *name = elf_strptr(e, shstrndx, shdr.sh_name);
if (name && strcmp(name, old_name) == 0) {
size_t old_name_len = strlen(old_name);
size_t new_name_len = strlen(new_name);
if (new_name_len > old_name_len) {
fprintf(stderr, "New name is longer than old name, resizing not supported.\n");
elf_end(e);
close(fd);
exit(EXIT_FAILURE);
}
// Update the string table in place
char *shstrtab = (char *)shstrdata->d_buf;
strncpy(&shstrtab[shdr.sh_name], new_name, new_name_len);
// Null-terminate if new_name is shorter than old_name
if (new_name_len < old_name_len) {
shstrtab[shdr.sh_name + new_name_len] = '\0';
}
if (elf_update(e, ELF_C_WRITE) < 0) {
fprintf(stderr, "elf_update() failed: %s\n", elf_errmsg(-1));
elf_end(e);
close(fd);
exit(EXIT_FAILURE);
}
break;
}
}
elf_end(e);
close(fd);
}
int main() {
const char *filename = "your_bpf_program.o";
const char *old_name = "custom_section";
const char *new_name = "xdp";
rename_section(filename, old_name, new_name);
return 0;
}
$ sudo gcc -o rename_section rename_section.c -lelf
$ sudo ./rename_section
XDPに変わったことがわかる:
$ readelf -S your_bpf_program.o
There are 7 section headers, starting at offset 0x118:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .strtab STRTAB 0000000000000000 00000040
000000000000005c 0000000000000000 0 0 1
[ 2] .text PROGBITS 0000000000000000 0000009c
0000000000000000 0000000000000000 AX 0 0 4
[ 3] xdp PROGBITS 0000000000000000 000000a0
「アタッチ用のコードを書く」とはちょっと違う気がするが。。。
Discussion