BPFプログラムのコード
まずはBPFプログラムのコードexecsnoop.
)
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/* SPDX-FileCopyrightText: 2021 Mitsuya Shibata */
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
SEC("tracepoint/syscalls/sys_enter_execve")
int syscalls__execve(void *ctx)
{
bpf_printk("Hi, execve!\n");
return 0;
}
char LICENSE[] SEC("license") = "Dual BSD/GPL";
SEC()
はlibbpfのマクロです。これ自体は引数や関数をELFバイナリの中の指定したセクションに配置だけのマクロで,__
」
最初のtracepoint/
セクションには,
- tracepoint:
/sys/
以下にあるイベント名をもとにkernel/ debug/ tracing/ events/ 「 tracepoint/イベント名
」「 tp/イベント名
」 - kprobe,
kretprobe: 「 kprobe/関数名
」
こんな感じで設定していきます。今回はsyscalls/
イベントなのでtracepoint/
」
関数名のほうは任意の名前が使えます。今回は第690回の例に合わせてみました。関数の中でやっていることはbpf_
」bpf_
」
- ※2
- 残念ながらlibbpfにはまだ網羅的なAPIドキュメントがありません。このため何かやりたい場合は,
libbpfのコードやBCC/ libbpf-tools以下のコードを参照しながら記述することになります。
最後のlicense
セクションは,
これはカーネル内部にロードされるBPFオブジェクトであっても同じで,
cannot call GPL-restricted function from non-GPL compatible program
ここでは3条項BSDとGPL 2.
- ※3
- BPFプログラムやそれを利用するユーザーランドプログラムのライセンスに関してはカーネルドキュメントの
「BPF licensing」 も参照してください。
BPFプログラムからオブジェクトとスケルトンヘッダーの生成
次にBPFプログラムからClangでBPFオブジェクトを作成し,
$ bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h $ clang -g -O2 -Wall -target bpf -D__TARGET_ARCH_x86 -c execsnoop.bpf.c -o execsnoop.bpf.o $ bpftool gen skeleton execsnoop.bpf.o > execsnoop.skel.h libbpf: elf: skipping unrecognized data section(4) .rodata.str1.1
まずターゲットカーネルのvmlinux.
をbpftool
コマンドで生成します。このvmlinux.
はカーネル内部の構造体定義を列挙したファイルで,
vmlinux.
自体はカーネルのバージョンに紐付いています。このためバージョンによっては構造体の中身が変わることがあります。しかしながらlibbpfがBPFオブジェクトをロードする際に,
- ※4
- たとえばBPF CO-RE版のlibbpfは,
amd64向けにはKernel 5. 5のvmlinux. を使っています。たとえばKernel 5.h 13を採用したUbuntu 22. 10であっても, この vmlinux.
を使ってビルドしたBPFバイナリはきちんと動作します。ただしlibbpfが吸収できない差異があった場合は,h ロード時に失敗することになります。
話をもとに戻すと,vmlinux.
を生成したら次はclang
コマンドによるコンパイルでBPFオブジェクトを生成します。-target bpf
」-D__
」
生成されたBPFオブジェクトを見ると,SEC()
マクロで指定したセクションが作られていることがわかります。
$ readelf -t execsnoop.bpf.o | grep -E "tracepoint|license" [ 2] tracepoint/syscalls/sys_enter_execve [ 4] license
このBPFオブジェクトをユーザーランドプログラムで使いやすいようにC言語のヘッダーファイルへと変換してくれるのが,bpftool gen skeleton
」
ちなみにスケルトンヘッダーファイルの各シンボルの命名規則はBPFオブジェクトのファイル名__シンボル名
」execsnoop.
」execsnoop.
」open
」execsnoop_
」struct execsnoop_
」bpftool gen skeleton execsnoop.
」name 名前
」
スケルトンヘッダーファイルを生成時に次のような警告メッセージが表示されいてます。
libbpf: elf: skipping unrecognized data section(4) .rodata.str1.1
これはlibbpfが古いことによるもので,
ここまででBPFオブジェクト側の準備が整いました。