Ubuntu Weekly Recipe

第694回 libbpfとclangでポータブルなBPF CO-REバイナリ作成

この記事を読むのに必要な時間:およそ 10.5 分

setrlimit()の必要性

eBPFのカーネル上のリソースは,Locked Memoryを利用していました。そのため,大きめのBPFプログラムをロードしようとすると,リソースが足りなくなります。そこでlibbpfを使うプログラムは,最初にsetrlimit()Locked Memoryのサイズを増やすことが定番でした。

しかしながらKernel 5.11以降はMemory Cgroup(memcg)を使うように変更されています。よってより新しいカーネルならsetrlimit()の呼び出しは不要(なはず)です。今回のサンプルコードでは,Ubuntu 22.04 LTSで参照されることを想定してsetrlimit()の呼び出しは行っていません。実行環境ごとの必要に応じて追加してください。もちろん,新しいカーネルであっても,あらかじめsetrlimit()を呼び出しておくことは可能です。

VM版のLXDでBPF CO-REバイナリを動かすには

VM版のLXDで使われているlinux-kvmは,他のフレーバーに比べるとかなりカーネルコンフィグを絞った状態で提供されています。結果的にBPF CO-REバイナリやBCC/bpftraceのいくつかの機能が使えません。たとえば今回のサンプルも,SYSFSのトレーシング機能が無効化されているため,次のようなエラーメッセージが表示されます。

root@impishvm:~# ./execsnoop
libbpf: elf: skipping unrecognized data section(3) .rodata.str1.1
libbpf: failed to determine tracepoint 'syscalls/sys_enter_execve' perf event ID: No such file or directory
libbpf: prog 'syscalls__execve': failed to create tracepoint 'syscalls/sys_enter_execve' perf event: No such file or directory
libbpf: failed to auto-attach program 'syscalls__execve': -2
failed to attach BPF object

linux-kvmフレーバー向けに,BPFプログラム側で使用する機能を絞るか,次のようにgenericフレーバーに切り替えてしまいましょう。

$ sudo apt install -y linux-generic
$ sudo apt remove -y --purge '~nlinux-.*kvm ~i'
$ sudo reboot

2行目でKVMフレーバーを削除しているのは,genericフレーバーのみで起動するようにしたいためです。generic/KVMを併用する場合はそのまま残しても問題ありません。その場合,grub-rebootコマンドで設定するかGRUBメニューから手動でUbuntu, with Linux 5.13.0-21-genericと末尾に「generic」と書かれたメニューを選択してください。

なお,KVMカーネルで起動している状態でKVMカーネルを削除しようとすると,⁠起動中のカーネルを削除しようとしているが,きちんと別のカーネルで起動してから削除することを強く推奨する。よって削除を中止して良いか?と聞かれます。削除したい場合は,きちんとNoを選びましょう。

ちなみに今のところ問題になりそうなのは,KVMフレーバーのみです。AWS・GCP・GKE・Azureフレーバーについてはざっと見た限り問題なさそうです。

BCCのlibbpf-toolsをビルドするには

BCCにはlibbpf版のツールが用意されています。実際にBPFプログラムを作る際にとても参考になるため,これをUbuntu 21.10上でコンパイルする方法も紹介しておきましょう。

まずは必要なパッケージをインストールし,ソースコードを取得しておきます。

$ sudo apt install build-essential libbpf-dev clang llvm linux-tools-common git make
$ git clone https://github.com/iovisor/bcc.git
$ cd bcc/libbpf-tools

ここでmakeしたいところではあるのですが,libbpf-toolsではlibbpfも自分でビルドする想定となっています。つまりgit clone時に--recursiveオプションを付けるか,git cloneしたあとにgit submodule initした上でgit submodule updateする必要があります。もちろん自分でビルドしても良いのですが,ここはパッケージ版のlibbpfを流用するよう設定しておきます。

$ git diff
diff --git a/libbpf-tools/Makefile b/libbpf-tools/Makefile
index 5f7a9295..621a514b 100644
--- a/libbpf-tools/Makefile
+++ b/libbpf-tools/Makefile
@@ -4,7 +4,8 @@ CLANG ?= clang
 LLVM_STRIP ?= llvm-strip
 BPFTOOL ?= bin/bpftool
 LIBBPF_SRC := $(abspath ../src/cc/libbpf/src)
-LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a)
+#LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a)
+LIBBPF_OBJ := /usr/lib/$(shell clang -print-multiarch)/libbpf.a
 INCLUDES := -I$(OUTPUT) -I../src/cc/libbpf/include/uapi
 CFLAGS := -g -O2 -Wall
 INSTALL ?= install

あとは作りたいツールを単にmakeするだけです。

$ make execsnoop
  MKDIR    .output
  BPF      execsnoop.bpf.o
  GEN-SKEL execsnoop.skel.h
  CC       execsnoop.o
  CC       trace_helpers.o
  CC       syscall_helpers.o
  CC       errno_helpers.o
  CC       map_helpers.o
  CC       uprobe_helpers.o
  BINARY   execsnoop

$ file execsnoop
execsnoop: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), (略)

$ ls -sh execsnoop
452K execsnoop

$ ldd execsnoop
        linux-vdso.so.1 (0x00007fffab3dd000)
        libelf.so.1 => /lib/x86_64-linux-gnu/libelf.so.1 (0x00007f4d5e379000)
        libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f4d5e35d000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4d5e135000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f4d5e3fc000)

サイズがとても小さいにも関わらず,実際に実行してみるときちんと動作することがわかります。

$ sudo ./execsnoop
PCOMM            PID    PPID   RET ARGS
sh               1683163 3517     0 /bin/sh -c byobu-status tmux_left
byobu-status     1683165 1683163   0 /usr/bin/byobu-status tmux_left
sh               1683164 3517     0 /bin/sh -c byobu-status tmux_right
byobu-status     1683166 1683164   0 /usr/bin/byobu-status tmux_right

ちなみにUbuntuのカーネルや各種パッケージのバージョンと,BCC側の状態に応じて,いくつかのコマンドはビルドできないかもしれません。またより新しいlibbpfを期待しているツールもあるかもしれませんので,どうしてもビルドできない場合はlibbpfのビルドから始めると良いでしょう。

著者プロフィール

柴田充也(しばたみつや)

Ubuntu Japanese Team Member株式会社 創夢所属。数年前にLaunchpad上でStellariumの翻訳をしたことがきっかけで,Ubuntuの翻訳にも関わるようになりました。