No member named 'si' in 'struct pt_regs'

674 Views Asked by At

I am trying to compile the following eBPF code,

#include "vmlinux.h"

#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

SEC("kprobe/do_sys_openat2")
int kprobe__do_sys_openat2(struct pt_regs *ctx)
{
    char file_name[256];
    bpf_probe_read(file_name, sizeof(file_name), PT_REGS_PARM2(ctx));

    char fmt[] = "open file %s\n.";
    bpf_trace_printk(fmt, sizeof(fmt), &file_name);

    return 0;
}

using the following Makefile,

OUTPUT = ./output
LIBBPF = ../libbpf

LIBBPF_SRC = $(abspath $(LIBBPF)/src)
LIBBPF_OBJ = $(abspath $(OUTPUT)/libbpf.a)

CC = gcc
CLANG = clang

ARCH := $(shell uname -m)
# ARCH := $(subst x86_64,amd64,$(ARCH))
GOARCH := $(ARCH)

BPFTOOL = $(shell which bpftool || /bin/false)
BTFFILE = /sys/kernel/btf/vmlinux
DBGVMLINUX = /usr/lib/debug/boot/vmlinux-$(shell uname -r)
GIT = $(shell which git || /bin/false)
VMLINUXH = vmlinux.h

# libbpf

LIBBPF_OBJDIR = $(abspath ./$(OUTPUT)/libbpf)
LIBBPF_DESTDIR = $(abspath ./$(OUTPUT))

CFLAGS = -ggdb -gdwarf -O2 -Wall -fpie -Wno-unused-variable -Wno-unused-function
LDFLAGS =

BPF_CFLAGS_STATIC = "-I$(abspath $(OUTPUT))"
BPF_LDFLAGS_STATIC = "-lelf -lz $(LIBBPF_OBJ)"

CGO_CFLAGS_STATIC = "-I$(abspath $(OUTPUT))"
CGO_LDFLAGS_STATIC = "-lelf -lz $(LIBBPF_OBJ)"
CGO_EXTLDFLAGS_STATIC = '-w -extldflags "-static"'

CGO_CFGLAGS_DYN = "-I. -I/usr/include/"
CGO_LDFLAGS_DYN = "-lelf -lz -lbpf"
CGO_EXTLDFLAGS_DYN = '-w'

## program

.PHONY: $(PROGRAM)
.PHONY: $(PROGRAM).bpf.c

PROGRAM = main

all:
    $(MAKE) -C . $(PROGRAM)

# vmlinux header file

.PHONY: vmlinuxh
vmlinuxh: $(VMLINUXH)

$(VMLINUXH): $(OUTPUT)
ifeq ($(wildcard $(BPFTOOL)),)
    @echo "ERROR: could not find bpftool"
    @exit 1
endif
    @if [ -f $(DBGVMLINUX) ]; then \
        echo "INFO: found dbg kernel, generating $(VMLINUXH) from $(DBGVMLINUX)"; \
        $(BPFTOOL) btf dump file $(DBGVMLINUX) format c > $(VMLINUXH); \
    fi
    @if [ ! -f $(BTFFILE) ] && [ ! -f $(DBGVMLINUX) ]; then \
        echo "ERROR: kernel does not seem to support BTF"; \
        exit 1; \
    fi
    @if [ ! -f $(VMLINUXH) ]; then \
        echo "INFO: generating $(VMLINUXH) from $(BTFFILE)"; \
        $(BPFTOOL) btf dump file $(BTFFILE) format c > $(VMLINUXH); \
    fi

# static libbpf generation for the git submodule

.PHONY: libbpf
libbpf: $(LIBBPF_OBJ)

$(LIBBPF_OBJ): $(LIBBPF_SRC) $(wildcard $(LIBBPF_SRC)/*.[ch]) | $(OUTPUT)/libbpf
    CC="$(CC)" CFLAGS="$(CFLAGS)" LD_FLAGS="$(LDFLAGS)" \
       $(MAKE) -C $(LIBBPF_SRC) \
        BUILD_STATIC_ONLY=1 \
        OBJDIR=$(LIBBPF_OBJDIR) \
        DESTDIR=$(LIBBPF_DESTDIR) \
        INCLUDEDIR= LIBDIR= UAPIDIR= prefix= libdir= install

$(LIBBPF_SRC):
ifeq ($(wildcard $@), )
    echo "INFO: updating submodule 'libbpf'"
    $(GIT) submodule update --init --recursive
endif

# output dir

$(OUTPUT):
    mkdir -p $(OUTPUT)

$(OUTPUT)/libbpf:
    mkdir -p $(OUTPUT)/libbpf

## program bpf dependency

$(PROGRAM).bpf.o: $(PROGRAM).bpf.c | vmlinuxh
    $(CLANG) $(CFLAGS) -target bpf -D__TARGET_ARCH_x86 -I. -I$(OUTPUT) -c $< -o $@

## GO example

.PHONY: $(PROGRAM)

$(PROGRAM): libbpf | $(PROGRAM).bpf.o
    CC=$(CLANG) \
        CGO_CFLAGS=$(CGO_CFLAGS_STATIC) \
        CGO_LDFLAGS=$(CGO_LDFLAGS_STATIC) \
                GOARCH=$(GOARCH) \
                go build \
                -tags netgo -ldflags $(CGO_EXTLDFLAGS_STATIC) \
                -o $(PROGRAM) ./$(PROGRAM).go

## clean

clean:
    $(MAKE) -C $(LIBBPF_SRC) clean
    rm -rf $(OUTPUT)
    rm -rf $(VMLINUXH)
    rm -rf $(PROGRAM) $(PROGRAM)-*static $(PROGRAM)-*dynamic
    rm -rf $(PROGRAM).bpf.o $(PROGRAM).o

I am getting the error,

error: no member named 'si' in 'struct pt_regs'
    bpf_probe_read(file_name, sizeof(file_name), PT_REGS_PARM2(ctx));
                                                 ^~~~~~~~~~~~~~~~~~
./output/bpf/bpf_tracing.h:273:46: note: expanded from macro 'PT_REGS_PARM2'
#define PT_REGS_PARM2(x) (__PT_REGS_CAST(x)->__PT_PARM2_REG)
                          ~~~~~~~~~~~~~~~~~  ^
./output/bpf/bpf_tracing.h:78:24: note: expanded from macro '__PT_PARM2_REG'
#define __PT_PARM2_REG si
                       ^

I realized this is due to some problem with the definition of the MACRO PT_REGS_PARM2, which is defined as #define PT_REGS_PARM2(x) (__PT_REGS_CAST(x)->__PT_PARM2_REG) in bpf_tracing.h header file, which does some sort of pointer casting to obtain the pt_regs pointer for x, and then access the member given by __PT_PARM2_REG which is a MACRO defined as si. So I guess the error is that there is no such register in my x86 architecture specific struct pt_regs, I am not sure why it uses that register for this macro. Is there any error in the specification of the architecture in Makefile, or is there is some error in the code in bpf_tracing.h? The architecture I am compiling for is x86_64.

2

There are 2 best solutions below

0
On

I had the same problem but with the di register.

My problem was: the vmlinux.h file was not correct for my Linux. In order to solve that I create a new vmlinux.h file with this command:

  • bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h

You can find very good explantions on this book:

  • Liz Rice: Learning eBPF Programming the Linux Kernel for Enhanced Observability, Networking, and Security
0
On

So the solution was to replace PT_REGS_PARM2(ctx) by ctx->si directly, since the struct pt_regs is defined inside vmlinux.h itself, and using the macro PT_REGS_PARM2 defaults to using the struct definition inside bpf_tracing.h where the si member is not included for some reason. The struct pt_regs in vmlinux.h is defined as,

struct pt_regs {
    long unsigned int r15;
    long unsigned int r14;
    long unsigned int r13;
    long unsigned int r12;
    long unsigned int bp;
    long unsigned int bx;
    long unsigned int r11;
    long unsigned int r10;
    long unsigned int r9;
    long unsigned int r8;
    long unsigned int ax;
    long unsigned int cx;
    long unsigned int dx;
    long unsigned int si;
    long unsigned int di;
    long unsigned int orig_ax;
    long unsigned int ip;
    long unsigned int cs;
    long unsigned int flags;
    long unsigned int sp;
    long unsigned int ss;
};

So I used pointer casting to get the character pointer to ctx->si, and then read the contents onto file_name as before. The updated ebpf file,


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

SEC("kprobe/do_sys_openat2")
int kprobe__do_sys_openat2(struct pt_regs *ctx)
{
    char file_name[256];

    char *file_name_ptr = (char *)ctx->si;
    if (!file_name_ptr)
    {
        // handle error: invalid memory address
        return 0;
    }

    bpf_probe_read(file_name, sizeof(file_name), file_name_ptr);
    char fmt[] = "open file %s\n.";
    bpf_trace_printk(fmt, sizeof(fmt), &file_name);

    return 0;
}

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

This works!