Re: [PATCH bpf-next 2/4] selftests/bpf: utility function to get program disassembly after jit

Andrii Nakryiko
2024-08-15T21:07:03Z

On Thu, Aug 8, 2024 at 6:05 PM Eduard Zingerman <eddyz87@gmail.com> wrote:
>
> int get_jited_program_text(int fd, char *text, size_t text_sz)
>
> Loads and disassembles jited instructions for program pointed to by fd.
> Much like 'bpftool prog dump jited ...'.
>
> The code and makefile changes are inspired by jit_disasm.c from bpftool.
> Use llvm libraries to disassemble BPF program instead of libbfd to avoid
> issues with disassembly output stability pointed out in [1].
>
> Selftests makefile uses Makefile.feature to detect if LLVM libraries
> are available. If that is not the case selftests build proceeds but
> the function returns -ENOTSUP at runtime.
>
> [1] commit eb9d1acf634b ("bpftool: Add LLVM as default library for disassembling JIT-ed programs")
>
> Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
> ---
> tools/testing/selftests/bpf/.gitignore | 1 +
> tools/testing/selftests/bpf/Makefile | 51 +++-
> .../selftests/bpf/jit_disasm_helpers.c | 228 ++++++++++++++++++
> .../selftests/bpf/jit_disasm_helpers.h | 10 +
> 4 files changed, 288 insertions(+), 2 deletions(-)
> create mode 100644 tools/testing/selftests/bpf/jit_disasm_helpers.c
> create mode 100644 tools/testing/selftests/bpf/jit_disasm_helpers.h
>
> diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore
> index 8f14d8faeb0b..7de4108771a0 100644
> --- a/tools/testing/selftests/bpf/.gitignore
> +++ b/tools/testing/selftests/bpf/.gitignore
> @@ -8,6 +8,7 @@ test_lru_map
> test_lpm_map
> test_tag
> FEATURE-DUMP.libbpf
> +FEATURE-DUMP.selftests
> fixdep
> /test_progs
> /test_progs-no_alu32
> diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
> index f54185e96a95..b1a52739d9e7 100644
> --- a/tools/testing/selftests/bpf/Makefile
> +++ b/tools/testing/selftests/bpf/Makefile
> @@ -33,6 +33,13 @@ OPT_FLAGS ?= $(if $(RELEASE),-O2,-O0)
> LIBELF_CFLAGS := $(shell $(PKG_CONFIG) libelf --cflags 2>/dev/null)
> LIBELF_LIBS := $(shell $(PKG_CONFIG) libelf --libs 2>/dev/null || echo -lelf)
>
> +ifeq ($(srctree),)
> +srctree := $(patsubst %/,%,$(dir $(CURDIR)))
> +srctree := $(patsubst %/,%,$(dir $(srctree)))
> +srctree := $(patsubst %/,%,$(dir $(srctree)))
> +srctree := $(patsubst %/,%,$(dir $(srctree)))
> +endif
> +
> CFLAGS += -g $(OPT_FLAGS) -rdynamic \
> -Wall -Werror -fno-omit-frame-pointer \
> $(GENFLAGS) $(SAN_CFLAGS) $(LIBELF_CFLAGS) \
> @@ -55,6 +62,11 @@ progs/test_sk_lookup.c-CFLAGS := -fno-strict-aliasing
> progs/timer_crash.c-CFLAGS := -fno-strict-aliasing
> progs/test_global_func9.c-CFLAGS := -fno-strict-aliasing
>
> +# Some utility functions use LLVM libraries
> +jit_disasm_helpers.c-CFLAGS = $(LLVM_CFLAGS)
> +test_progs-LDLIBS = $(LLVM_LDLIBS)
> +test_progs-LDFLAGS = $(LLVM_LDFLAGS)
> +
> ifneq ($(LLVM),)
> # Silence some warnings when compiled with clang
> CFLAGS += -Wno-unused-command-line-argument
> @@ -164,6 +176,31 @@ endef
>
> include ../lib.mk
>
> +NON_CHECK_FEAT_TARGETS := clean docs-clean
> +CHECK_FEAT := $(filter-out $(NON_CHECK_FEAT_TARGETS),$(or $(MAKECMDGOALS), "none"))
> +ifneq ($(CHECK_FEAT),)
> +FEATURE_USER := .selftests
> +FEATURE_TESTS := llvm
> +FEATURE_DISPLAY := $(FEATURE_TESTS)
> +
> +# Makefile.feature expects OUTPUT to end with a slash
> +$(let OUTPUT,$(OUTPUT)/,\
> + $(eval include ../../../build/Makefile.feature))
> +endif
> +
> +ifeq ($(feature-llvm),1)
> + LLVM_CFLAGS += -DHAVE_LLVM_SUPPORT
> + LLVM_CONFIG_LIB_COMPONENTS := mcdisassembler all-targets
> + # both llvm-config and lib.mk add -D_GNU_SOURCE, which ends up as conflict
> + LLVM_CFLAGS += $(filter-out -D_GNU_SOURCE,$(shell $(LLVM_CONFIG) --cflags))
> + LLVM_LDLIBS += $(shell $(LLVM_CONFIG) --libs $(LLVM_CONFIG_LIB_COMPONENTS))
> + ifeq ($(shell $(LLVM_CONFIG) --shared-mode),static)
> + LLVM_LDLIBS += $(shell $(LLVM_CONFIG) --system-libs $(LLVM_CONFIG_LIB_COMPONENTS))
> + LLVM_LDLIBS += -lstdc++
> + endif
> + LLVM_LDFLAGS += $(shell $(LLVM_CONFIG) --ldflags)
> +endif
> +
> SCRATCH_DIR := $(OUTPUT)/tools
> BUILD_DIR := $(SCRATCH_DIR)/build
> INCLUDE_DIR := $(SCRATCH_DIR)/include
> @@ -488,6 +525,7 @@ define DEFINE_TEST_RUNNER
>
> TRUNNER_OUTPUT := $(OUTPUT)$(if $2,/)$2
> TRUNNER_BINARY := $1$(if $2,-)$2
> +TRUNNER_BASE_NAME := $1
> TRUNNER_TEST_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.test.o, \
> $$(notdir $$(wildcard $(TRUNNER_TESTS_DIR)/*.c)))
> TRUNNER_EXTRA_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.o, \
> @@ -611,6 +649,10 @@ ifeq ($(filter clean docs-clean,$(MAKECMDGOALS)),)
> include $(wildcard $(TRUNNER_TEST_OBJS:.o=.d))
> endif
>
> +# add per extra obj CFGLAGS definitions
> +$(foreach N,$(patsubst $(TRUNNER_OUTPUT)/%.o,%,$(TRUNNER_EXTRA_OBJS)), \
> + $(eval $(TRUNNER_OUTPUT)/$(N).o: CFLAGS += $($(N).c-CFLAGS)))
> +
> $(TRUNNER_EXTRA_OBJS): $(TRUNNER_OUTPUT)/%.o: \
> %.c \
> $(TRUNNER_EXTRA_HDRS) \
> @@ -627,6 +669,9 @@ ifneq ($2:$(OUTPUT),:$(shell pwd))
> $(Q)rsync -aq $$^ $(TRUNNER_OUTPUT)/
> endif
>
> +$(OUTPUT)/$(TRUNNER_BINARY): LDLIBS += $$($(TRUNNER_BASE_NAME)-LDLIBS)
> +$(OUTPUT)/$(TRUNNER_BINARY): LDFLAGS += $$($(TRUNNER_BASE_NAME)-LDFLAGS)

is there any reason why you need to have this blah-LDFLAGS convention
and then applying that with extra pass, instead of just writing

$(OUTPUT)/$(TRUNNER_BINARY): LDFLAGS += $(LLVM_LDFLAGS)

I'm not sure I understand the need for extra logical hops to do this

> +
> # some X.test.o files have runtime dependencies on Y.bpf.o files
> $(OUTPUT)/$(TRUNNER_BINARY): | $(TRUNNER_BPF_OBJS)
>
> @@ -636,7 +681,7 @@ $(OUTPUT)/$(TRUNNER_BINARY): $(TRUNNER_TEST_OBJS) \
> $(TRUNNER_BPFTOOL) \
> | $(TRUNNER_BINARY)-extras
> $$(call msg,BINARY,,$$@)
> - $(Q)$$(CC) $$(CFLAGS) $$(filter %.a %.o,$$^) $$(LDLIBS) -o $$@
> + $(Q)$$(CC) $$(CFLAGS) $$(filter %.a %.o,$$^) $$(LDLIBS) $$(LDFLAGS) -o $$@
> $(Q)$(RESOLVE_BTFIDS) --btf $(TRUNNER_OUTPUT)/btf_data.bpf.o $$@
> $(Q)ln -sf $(if $2,..,.)/tools/build/bpftool/$(USE_BOOTSTRAP)bpftool \
> $(OUTPUT)/$(if $2,$2/)bpftool
> @@ -655,6 +700,7 @@ TRUNNER_EXTRA_SOURCES := test_progs.c \
> cap_helpers.c \
> unpriv_helpers.c \
> netlink_helpers.c \
> + jit_disasm_helpers.c \
> test_loader.c \
> xsk.c \
> disasm.c \
> @@ -797,7 +843,8 @@ EXTRA_CLEAN := $(SCRATCH_DIR) $(HOST_SCRATCH_DIR) \
> $(addprefix $(OUTPUT)/,*.o *.d *.skel.h *.lskel.h *.subskel.h \
> no_alu32 cpuv4 bpf_gcc bpf_testmod.ko \
> bpf_test_no_cfi.ko \
> - liburandom_read.so)
> + liburandom_read.so) \
> + $(OUTPUT)/FEATURE-DUMP.selftests
>
> .PHONY: docs docs-clean …