I'd like to make my simple BPF program CO-RE-able to some older Debian OS

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello!
I'm new to BPF CO-RE and I tried to write a simple BPF program. Here
are some informations about my Debian 11 amd64 main compilation
environment:


# uname -r
5.10.0-9-amd64

# llc --version
LLVM (http://llvm.org/):
  LLVM version 14.0.0git
  Optimized build.
  Default target: x86_64-unknown-linux-gnu
  Host CPU: skylake

  Registered Targets:
    aarch64    - AArch64 (little endian)
    aarch64_32 - AArch64 (little endian ILP32)
    aarch64_be - AArch64 (big endian)
    amdgcn     - AMD GCN GPUs
    arm        - ARM
    arm64      - ARM64 (little endian)
    arm64_32   - ARM64 (little endian ILP32)
    armeb      - ARM (big endian)
    avr        - Atmel AVR Microcontroller
    bpf        - BPF (host endian)
    bpfeb      - BPF (big endian)
    bpfel      - BPF (little endian)
    hexagon    - Hexagon
    lanai      - Lanai
    mips       - MIPS (32-bit big endian)
    mips64     - MIPS (64-bit big endian)
    mips64el   - MIPS (64-bit little endian)
    mipsel     - MIPS (32-bit little endian)
    msp430     - MSP430 [experimental]
    nvptx      - NVIDIA PTX 32-bit
    nvptx64    - NVIDIA PTX 64-bit
    ppc32      - PowerPC 32
    ppc32le    - PowerPC 32 LE
    ppc64      - PowerPC 64
    ppc64le    - PowerPC 64 LE
    r600       - AMD GPUs HD2XXX-HD6XXX
    riscv32    - 32-bit RISC-V
    riscv64    - 64-bit RISC-V
    sparc      - Sparc
    sparcel    - Sparc LE
    sparcv9    - Sparc V9
    systemz    - SystemZ
    thumb      - Thumb
    thumbeb    - Thumb (big endian)
    wasm32     - WebAssembly 32-bit
    wasm64     - WebAssembly 64-bit
    x86        - 32-bit X86: Pentium-Pro and above
    x86-64     - 64-bit X86: EM64T and AMD64
    xcore      - XCore

# clang --version
clang version 14.0.0 (https://github.com/llvm/llvm-project.git
5f4c91583ee772a6ce2c4f192e25b07e6075eb00)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

# gcc --version
gcc (Debian 10.2.1-6) 10.2.1 20210110
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

# make --version
GNU Make 4.3
Built for x86_64-pc-linux-gnu
Copyright (C) 1988-2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

# bpftool --version
bpftool v5.10.70
features:


Here are two programs -- "hello" and "maps" -- that I wrote, five
files in total.
Including "hello.c", "hello.bpf.c", "maps.h", "maps.c", and "maps.bpf.c":


hello.c:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/resource.h>

#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include "hello.skel.h"

void read_trace_pipe(void)
{
int trace_fd;

trace_fd = open("/sys/kernel/debug/tracing/trace_pipe", O_RDONLY, 0);
if (trace_fd < 0)
return;

while (1) {
static char buf[4096];
ssize_t sz;

sz = read(trace_fd, buf, sizeof(buf) - 1);
if (sz > 0) {
buf[sz] = 0;
puts(buf);
}
}
}

int main(void)
{
struct hello_bpf *obj;
int err = 0;

struct rlimit rlim = {
.rlim_cur = 512UL << 20,
.rlim_max = 512UL << 20,
};

err = setrlimit(RLIMIT_MEMLOCK, &rlim);
if (err) {
fprintf(stderr, "failed to change rlimit\n");
return 1;
}


obj = hello_bpf__open();
if (!obj) {
fprintf(stderr, "failed to open and/or load BPF object\n");
return 1;
}

err = hello_bpf__load(obj);
if (err) {
fprintf(stderr, "failed to load BPF object %d\n", err);
goto cleanup;
}

err = hello_bpf__attach(obj);
if (err) {
fprintf(stderr, "failed to attach BPF programs\n");
goto cleanup;
}

read_trace_pipe();

cleanup:
hello_bpf__destroy(obj);
return err != 0;
}


hello.bpf.c:

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>

SEC("tracepoint/syscalls/sys_enter_execve")
int tracepoint__syscalls__sys_enter_execve(struct
trace_event_raw_sys_enter *ctx)
{
bpf_printk("Hello world!\n");
return 0;
}

char LICENSE[] SEC("license") = "GPL";


maps.h:

#ifndef _MAPS_H
#define _MAPS_H

#define TASK_COMM_LEN 16

struct event {
char comm[TASK_COMM_LEN];
pid_t pid;
uid_t uid;
};

#endif


maps.c:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/resource.h>
#include <argp.h>

#include <bpf/libbpf.h>
#include <bpf/bpf.h>

#include "maps.skel.h"
#include "maps.h"

static struct env {
bool verbose;
} env = {
.verbose = false,
};

const char *argp_program_version = "bpf loggin example";
const char argp_program_doc[] =
"An example BPF CO-RE application that demonstrates a libbpf maps set up\n";
static const struct argp_option opts[] = {
{NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help"},
{"verbose", 'v', NULL, 0, "verbose debug output"},
{},
};

int libbpf_print_fn(enum libbpf_print_level level,
const char *format, va_list args)
{
if (level == LIBBPF_DEBUG && !env.verbose) {
return 0;
}

return vfprintf(stderr, format, args);
}

static error_t parse_arg(int key, char *arg, struct argp_state *state)
{
switch (key) {
case 'v':
env.verbose = true;
break;
case 'h':
argp_usage(state);
break;
default:
return ARGP_ERR_UNKNOWN;

}

return 0;
}

static int print_execs(int fd)
{
int err;
struct event ev;
pid_t lookup_key = 0, next_key;

while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) {
err = bpf_map_lookup_elem(fd, &next_key, &ev);
if (err < 0) {
fprintf(stderr, "failed to lookup exec: %d\n", err);
return -1;
}
printf("\nProcess Name = %s, uid = %u, pid = %u\n", ev.comm, ev.uid, ev.pid);
err = bpf_map_delete_elem(fd, &next_key);
if (err < 0) {
fprintf(stderr, "failed to cleanup execs : %d\n", err);
return -1;
}
lookup_key = next_key;
}

return 0;
}

int main(int argc, char **argv) {
struct maps_bpf *obj;
int err = 0;
struct rlimit rlim = {
.rlim_cur = 512UL << 20,
.rlim_max = 512UL << 20,
};
const struct argp argp = {
.options = opts,
.parser = parse_arg,
.doc = argp_program_doc,
};
int fd;

err = setrlimit(RLIMIT_MEMLOCK, &rlim);
if (err) {
fprintf(stderr, "failed to change rlimit\n");
return 1;
}

err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
if (err) {
fprintf(stderr, "failed to parse command line arguments\n");
return 1;
}

libbpf_set_print(libbpf_print_fn);
obj = maps_bpf__open();
if (!obj) {
fprintf(stderr, "failed to open and/or load BPF object\n");
return 1;
}

err = maps_bpf__load(obj);
if (err) {
fprintf(stderr, "failed to load BPF object %d\n", err);
goto cleanup;
}

err = maps_bpf__attach(obj);
if (err) {
fprintf(stderr, "failed to attach BPF programs\n");
goto cleanup;
}

fd = bpf_map__fd(obj->maps.execs);

printf("printing executed commands\n");

while (1) {
print_execs(fd);
fd = bpf_map__fd(obj->maps.execs);
}

cleanup:
maps_bpf__destroy(obj);
return err != 0;
}


maps.bpf.c:

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include "maps.h"

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 128);
__type(key, pid_t);
__type(value, struct event);
} execs SEC(".maps");

SEC("tracepoint/syscalls/sys_enter_execve")
int tracepoint__syscalls__sys_enter_execve(struct
trace_event_raw_sys_enter *ctx)
{
struct event *event;
pid_t pid;
u64 id;
uid_t uid = (u32) bpf_get_current_uid_gid();

id = bpf_get_current_pid_tgid();
pid = (pid_t)id;

if (bpf_map_update_elem(&execs, &pid, &((struct event){}), 1)) {
return 0;
}

event = bpf_map_lookup_elem(&execs, &pid);
if (!event) {
return 0;
}

event->pid = pid;
event->uid = uid;
bpf_get_current_comm(&event->comm, sizeof(event->comm));

/*
if (bpf_map_update_elem(&execs, &pid, event, 2)) {
return 0;
}
*/

return 0;
}

char LICENSE[] SEC("license") = "GPL";


And here is how I compile them:


bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
clang -g -O2 -target bpf -D__TARGET_ARCH_x86_64 -I . -c hello.bpf.c -o
hello.bpf.o
bpftool gen skeleton hello.bpf.o > hello.skel.h
clang -g -O2 -Wall -I . -c hello.c -o hello.o
git clone https://github.com/libbpf/libbpf
cd libbpf/src
make BUILD_STATIC_ONLY=1 OBJDIR=../build/libbpf DESTDIR=../build
INCLUDEDIR= LIBDIR= UAPIDIR= install
cd ../../
clang -Wall -O2 -g hello.o libbpf/build/libbpf.a -lelf -lz -o hello
clang -g -O2 -target bpf -D__TARGET_ARCH_x86_64 -I . -c maps.bpf.c -o maps.bpf.o
bpftool gen skeleton maps.bpf.o > maps.skel.h
clang -g -O2 -Wall -I . -c maps.c -o maps.o
clang -Wall -O2 -g maps.o libbpf/build/libbpf.a -lelf -lz -o maps


Both binaries are executable on Debian 11. Then I tried to move them
into Debain 9 and Debian 10.
Here are some informations about Debian9:


# uname -r
4.9.0-16-amd64

# grep BPF /boot/config-4.9.0-16-amd64
CONFIG_BPF=y
CONFIG_BPF_SYSCALL=y
# CONFIG_BPF_JIT_ALWAYS_ON is not set
CONFIG_NETFILTER_XT_MATCH_BPF=m
CONFIG_NET_CLS_BPF=m
CONFIG_NET_ACT_BPF=m
CONFIG_BPF_JIT=y
CONFIG_HAVE_EBPF_JIT=y
CONFIG_BPF_EVENTS=y
CONFIG_TEST_BPF=m

# ./hello
libbpf: kernel doesn't support global data
libbpf: failed to load object 'hello_bpf'
libbpf: failed to load BPF skeleton 'hello_bpf': -95
failed to load BPF object -95

# ./maps
libbpf: load bpf program failed: Permission denied
libbpf: -- BEGIN DUMP LOG ---
libbpf:
0: (85) call 15
1: (bf) r6 = r0
2: (85) call 14
3: (63) *(u32 *)(r10 -4) = r0
4: (b7) r1 = 0
5: (7b) *(u64 *)(r10 -32) = r1
6: (7b) *(u64 *)(r10 -24) = r1
7: (7b) *(u64 *)(r10 -16) = r1
8: (bf) r2 = r10
9: (07) r2 += -4
10: (bf) r3 = r10
11: (07) r3 += -32
12: (18) r1 = 0xffff88253a768480
14: (b7) r4 = 1
15: (85) call 2
16: (55) if r0 != 0x0 goto pc+12
 R0=inv,min_value=0,max_value=0 R6=inv R10=fp
17: (bf) r2 = r10
18: (07) r2 += -4
19: (18) r1 = 0xffff88253a768480
21: (85) call 1
22: (15) if r0 == 0x0 goto pc+6
 R0=map_value(ks=4,vs=24,id=0),min_value=0,max_value=0 R6=inv R10=fp
23: (61) r1 = *(u32 *)(r10 -4)
24: (63) *(u32 *)(r0 +20) = r6
25: (63) *(u32 *)(r0 +16) = r1
26: (bf) r1 = r0
27: (b7) r2 = 16
28: (85) call 16
R1 type=map_value expected=fp

libbpf: -- END LOG --
libbpf: failed to load program 'tracepoint__syscalls__sys_enter_execve'
libbpf: failed to load object 'maps_bpf'
libbpf: failed to load BPF skeleton 'maps_bpf': -4007
failed to load BPF object -4007

Both binaries failed on Debian 9. Now to Debian 10:

# uname -r
4.19.0-17-amd64

# grep BPF /boot/config-4.19.0-17-amd64
CONFIG_CGROUP_BPF=y
CONFIG_BPF=y
CONFIG_BPF_SYSCALL=y
# CONFIG_BPF_JIT_ALWAYS_ON is not set
CONFIG_IPV6_SEG6_BPF=y
CONFIG_NETFILTER_XT_MATCH_BPF=m
# CONFIG_BPFILTER is not set
CONFIG_NET_CLS_BPF=m
CONFIG_NET_ACT_BPF=m
CONFIG_BPF_JIT=y
CONFIG_BPF_STREAM_PARSER=y
CONFIG_LWTUNNEL_BPF=y
CONFIG_HAVE_EBPF_JIT=y
CONFIG_BPF_EVENTS=y
# CONFIG_BPF_KPROBE_OVERRIDE is not set
CONFIG_TEST_BPF=m

# ./hello
libbpf: Error loading BTF: Invalid argument(22)
libbpf: magic: 0xeb9f
version: 1
flags: 0x0
hdr_len: 24
type_off: 0
type_len: 464
str_off: 464
str_len: 415
btf_total_size: 903
[1] PTR (anon) type_id=2
[2] STRUCT trace_event_raw_sys_enter size=64 vlen=4
ent type_id=3 bits_offset=0
id type_id=7 bits_offset=64
args type_id=9 bits_offset=128
__data type_id=12 bits_offset=512
[3] STRUCT trace_entry size=8 vlen=4
type type_id=4 bits_offset=0
flags type_id=5 bits_offset=16
preempt_count type_id=5 bits_offset=24
pid type_id=6 bits_offset=32
[4] INT unsigned short size=2 bits_offset=0 nr_bits=16 encoding=(none)
[5] INT unsigned char size=1 bits_offset=0 nr_bits=8 encoding=(none)
[6] INT int size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
[7] INT long size=8 bits_offset=0 nr_bits=64 encoding=SIGNED
[8] INT unsigned long size=8 bits_offset=0 nr_bits=64 encoding=(none)
[9] ARRAY (anon) type_id=8 index_type_id=10 nr_elems=6
[10] INT __ARRAY_SIZE_TYPE__ size=4 bits_offset=0 nr_bits=32 encoding=(none)
[11] INT char size=1 bits_offset=0 nr_bits=8 encoding=SIGNED
[12] ARRAY (anon) type_id=11 index_type_id=10 nr_elems=0
[13] ENUM (anon) size=4 vlen=1
ctx val=1
[14] TYPEDEF tracepoint__syscalls__sys_enter_execve type_id=13
[15] CONST (anon) type_id=11
[16] ARRAY (anon) type_id=15 index_type_id=10 nr_elems=14
[17] INT tracepoint__syscalls__sys_enter_execve.____fmt size=1
bits_offset=0 nr_bits=8 encoding=(none)
[18] ARRAY (anon) type_id=11 index_type_id=10 nr_elems=4
[19] INT LICENSE size=1 bits_offset=0 nr_bits=8 encoding=(none)
[20] STRUCT _rodata size=14 vlen=1
tracepoint__syscalls__sys_enter_execve.____fmt type_id=17
bits_offset=0 Invalid name

libbpf: Error loading .BTF into kernel: -22. BTF is optional, ignoring.
libbpf: kernel doesn't support global data
libbpf: failed to load object 'hello_bpf'
libbpf: failed to load BPF skeleton 'hello_bpf': -95
failed to load BPF object -95

# ./maps
printing executed commands


"hello" failed on Debian 10, but "maps" succeeded. How can I improve
my result? Maybe make them CO-RE-able on Debain 9?



[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux