eBPF内存泄露检测-arm64
视频讲解:
eBPF内存泄露检测-arm64
目标
把之前在x86-64平台上实现的内存泄露检测工具移植到 arm64
平台;
为什么不移植到 arm32
平台?
因为内存泄露检测工具中使用了blazesym
开源代码来解析堆栈指令地址的符号名,文件名,行号;但是blazesym
开源代码目前不支持 32bit 系统,非常遗憾;
不过也有解决办法,可以参考 bpf-developer-tutorial
: https://github.com/eunomia-bpf/bpf-developer-tutorial 的早期版本中 memleak
的代码实现;我就不详细讲了;
arm64 平台
在 ubuntu18.04 上用 qemu 模拟 arm64 平台
Linux (none) 4.19.304 #2 SMP PREEMPT Sun Jan 14 11:23:36 CST 2024 aarch64 GNU/Linux
网上有很多 qemu 模拟 arm64 平台的教程,我就不详细讲了;
需要交叉编译的库
- libz - - libbpf-bootstrap 需要使用
- libelf - - libbpf-bootstrap 需要使用
- blazesym - - 内存泄露检测工具解析堆栈指令地址时使用
安装交叉编译工具链
arm64 交叉编译工具链下载链接:
https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-a/downloads
# 选择:
x86_64 Linux hosted cross compilers
AArch64 GNU/Linux target (aarch64-none-linux-gnu)
下载后,解压,再 export 出来即可:
# 导出交叉编译工具链
export ARCH=arm64
export CROSS_COMPILE=aarch64-none-linux-gnu-
export PATH=$PATH:/home/zhanglong/Desktop/toolchain/arm64
交叉编译 libz
zlib源码下载链接:http://www.zlib.net/
tar -axf zlib-1.3.tar.gz
cd zlib-1.3
export PATH=$PATH:/home/zhanglong/Desktop/toolchain/arm64
export CC=aarch64-none-linux-gnu-gcc
./configure --prefix=$PWD/_install
make
make install
# 当前目录下的_install,就是编译出来的头文件和lib库
# /home/zhanglong/Desktop/ebpf/note/src/arm64/extra_libs/zlib-1.3/_install
交叉编译 libelf
elfutils源码下载链接:https://sourceware.org/elfutils/
tar -axf elfutils-latest.tar.bz2
cd elfutils-0.189
# 参考当前目录下的 INSTALL 文档 和 网上资料
# libelf 使用了之前交叉编译好的 zlib 库,
# 需要 CFLAGS 指定 zlib 库的头文件 和 LDFLAGS 指定 zlib 库路径
./configure --prefix=$PWD/_install --build=x86_64-linux-gnu \
--host=aarch64-none-linux-gnu \
CC=aarch64-none-linux-gnu-gcc CXX=aarch64-none-linux-gnu-g++ \
CFLAGS=-I/home/zhanglong/Desktop/ebpf/note/src/arm64/extra_libs/zlib-1.3/_install/include \
LDFLAGS=-L/home/zhanglong/Desktop/ebpf/note/src/arm64/extra_libs/zlib-1.3/_install/lib \
LIBS=-lz \
--disable-nls --disable-rpath --disable-libdebuginfod --disable-debuginfod \
--with-zlib
make
make install
# 当前目录下的_install,就是编译出来的头文件和lib库
# /home/zhanglong/Desktop/ebpf/note/src/arm64/extra_libs/elfutils-0.189/_install
交叉编译 blazesym
在之前的视频中讲过 blazesym 依赖的 rust 语言编译环境的安装,就不再重复讲了;
source ~/.cargo/env
# 安装 rust 的交叉编译工具链
rustup target add aarch64-unknown-linux-gnu
# 查看安装好的交叉编译工具链
rustup show
cd libbpf-bootstrap/blazesym/
cd .cargo
mv config config.bak # 不要使用原来的配置文件
touch config.toml
##############################################################
# config.toml 的文件内容
[build]
target = "aarch64-unknown-linux-gnu"
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-none-linux-gnu-gcc"
##############################################################
cd ../
cargo build --release
交叉编译 memleak
如果系统自带的 clang 编译器版本过低,
编译之前需要先修改: libbpf-bootstrap/examples/c/Makefile
文件,指定 clang 编译器:
CLANG ?= clang
# 改为:
CLANG ?= /your-clang-version/clang # your-clang-version 指的是自己下载的clang版本
# /home/zhanglong/Desktop/clang-16/clang
交叉编译命令:
# export 交叉编译工具链的路径
export PATH=$PATH:/home/zhanglong/Desktop/toolchain/arm64
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- \
EXTRA_CFLAGS="-I/home/zhanglong/Desktop/ebpf/note/src/arm64/extra_libs/zlib-1.3/_install/include -I/home/zhanglong/Desktop/ebpf/note/src/arm64/extra_libs/elfutils-0.189/_install/include" \
EXTRA_LDFLAGS="-L/home/zhanglong/Desktop/ebpf/note/src/arm64/extra_libs/zlib-1.3/_install/lib -L/home/zhanglong/Desktop/ebpf/note/src/arm64/extra_libs/elfutils-0.189/_install/lib" \
memleak V=1
为了编译方便,可以写一个shell脚本配置编译环境:
build_env.sh
#!/bin/sh
export PATH=$PATH:/home/zhanglong/Desktop/toolchain/arm64
export ARCH=arm64
export CROSS_COMPILE=aarch64-none-linux-gnu-
# 用 EXTRA_CFLAGS 指定 zlib 和 libelf 库的头文件路径
export EXTRA_CFLAGS="-I/home/zhanglong/Desktop/ebpf/note/src/arm64/extra_libs/zlib-1.3/_install/include -I/home/zhanglong/Desktop/ebpf/note/src/arm64/extra_libs/elfutils-0.189/_install/include"
# 用 EXTRA_LDFLAGS 指定 zlib 和 libelf 的库路径
export EXTRA_LDFLAGS="-L/home/zhanglong/Desktop/ebpf/note/src/arm64/extra_libs/zlib-1.3/_install/lib -L/home/zhanglong/Desktop/ebpf/note/src/arm64/extra_libs/elfutils-0.189/_install/lib"
交叉编译时先执行一次: source build_env.sh
之后就可以简单执行:make clean; make memleak
拷贝x86-64平台上实现的内存泄露检测工具代码
因为这次 arm64 使用的内核版本是 4.19.304
,还不支持 BTF,
所以memleak.bpf.c
不能直接包含 #include "vmlinux.h"
,需要手动定义如下的数据类型才能编译通过,
下面的数据类型定义都是从 libbpf-bootstrap/vmlinux/arm64/vmlinux.h
文件中拷贝过来,并简单修改;
#define BPF_NO_GLOBAL_DATA
#include <linux/bpf.h>
typedef __u32 u32;
typedef __u64 u64;
typedef int pid_t;
typedef long unsigned int size_t;
struct user_pt_regs {
__u64 regs[31];
__u64 sp;
__u64 pc;
__u64 pstate;
};
struct pt_regs {
union {
struct user_pt_regs user_regs;
struct {
__u64 regs[31];
__u64 sp;
__u64 pc;
__u64 pstate;
};
};
__u64 orig_x0;
__s32 syscallno;
__u32 unused2;
__u64 sdei_ttbr1;
__u64 pmr_save;
__u64 stackframe[2];
__u64 lockdep_hardirqs;
__u64 exit_rcu;
};
修改:libbpf-bootstrap/examples/c/Makefile
# BZS_APPS 加上 memleak 编译目标
BZS_APPS := profile memleak
###################################################################################
$(LIBBLAZESYM_SRC)/target/release/libblazesym.a::
$(Q)cd $(LIBBLAZESYM_SRC) && $(CARGO) build --release
$(LIBBLAZESYM_OBJ): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT)
$(call msg,LIB, $@)
$(Q)cp $(LIBBLAZESYM_SRC)/target/release/libblazesym.a $@
###### 改为:
$(LIBBLAZESYM_SRC)/target/aarch64-unknown-linux-gnu/release/libblazesym.a::
$(Q)cd $(LIBBLAZESYM_SRC) && $(CARGO) build --release
$(LIBBLAZESYM_OBJ): $(LIBBLAZESYM_SRC)/target/aarch64-unknown-linux-gnu/release/libblazesym.a | $(OUTPUT)
$(call msg,LIB, $@)
$(Q)cp $(LIBBLAZESYM_SRC)/target/aarch64-unknown-linux-gnu/release/libblazesym.a $@
内核需要打开ebpf相关的配置选项
ebpf 内核配置选项 (linux-4.19.304):
General setup --->
[*] Enable bpf() system call
[*] Permanently enable BPF JIT and remove BPF interpreter
General architecture-dependent options --->
[*] Kprobes
Kernel hacking --->
[*] Tracers --->
[*] Enable kprobes-based dynamic events (NEW)
[*] Enable uprobes-based dynamic events (NEW)