From patchwork Thu Aug 15 20:54:46 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13765149 X-Patchwork-Delegate: bpf@iogearbox.net Received: from mail-pj1-f44.google.com (mail-pj1-f44.google.com [209.85.216.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E0FA97E110 for ; Thu, 15 Aug 2024 20:56:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1723755394; cv=none; b=uj4WZk9UrUIXWvEc4DjF4HK0+aL6c3dbp5daDXGUepwTZx3aw1dAUWJHh+nTpq45f5M4H5dH7+QgYNUYE3bzXrMceg3vP6nRWFai6kwXV38+Ha6q7+kpTT86hDeeGVisqP/ypXhhzL3XIPftcRh4/Y+RYrCIb1XNjCjS6qSAuE0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1723755394; c=relaxed/simple; bh=kkYykJQL9R/aZ19ho2YF1MNC9bP/lNQhK5xnq9vtxUY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ISMCIBv3Ii4sCqJX/FpfDjgaVsnrVYZZRATlvlQMqRp7rOzBDwn1KtYQSalhpXZTlfBlZ/pEmqeH652LAsJcgnINODrRBae/oxJKEFFjrsnZMOxOY0k8gIOxsZaDc62E0yRVnwBacKr6q/39B/vmm5nNry43pzBfBe1YrzJhA10= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=VMm5LVhj; arc=none smtp.client-ip=209.85.216.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="VMm5LVhj" Received: by mail-pj1-f44.google.com with SMTP id 98e67ed59e1d1-2d3b595c18dso1336383a91.0 for ; Thu, 15 Aug 2024 13:56:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1723755392; x=1724360192; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=LT89j+dp+gUlp+EdN6Q/RK32F1udlyN11SN0eEj1uLc=; b=VMm5LVhj6rvaQf8og+VgotiMy6afBCo++FVMxrBgLMDcIu5ZFiUiy4zorDtATp5uV/ MhNW/4lhAJRSPJZae+K7tDZFK4F8Knn0dTeBOVh93KhdwWweH8aWM04EQQCA2EEFx85V M/FgfsobNZIKSGGpJZK9aqy9r/uqfQtXOcen0eE48vUWTXvJGqtZhQEAeV6V96idiRYM 0RJOY1N/QiutpSb/KU6mzZiU50KOwFmZ6twLJXN3g9h6sRVwos6qS1bJbNB5XVE6aIgb aDMfdb0w6b+LSnJrsG63Ys/m23M2cjnQwxKuU6Y5jCuH7eyWx7oxXvA1jm6ZmqKcA0+D JYMQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1723755392; x=1724360192; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=LT89j+dp+gUlp+EdN6Q/RK32F1udlyN11SN0eEj1uLc=; b=a+CGu8zHxfIGEbk8iemWR9Bqa+69A7oquavgol2fNG6/9vGA8gVQX3iMZHoL10k895 oQzrayi8Yra0lMy26uGRTv+elVoJldLExLUZUfy81VM0zgNqlXlrry7P8XztWhOks3Vz L8KfuMCZOtdrsFtJqJFq4rXqQJPZJV8XBs3995LHRAX/3LMjYoiwOFyBZDdkGmPJUB76 QlpOMDh35TzapuWOtCDpjFUFkfZATS4/DnNuSAOvCfAxD3iBpDJubH8G9L+jiEq+B/2e xLyZVAFaNVwfYH8R49dtTxOHyP5c4tEhE47UlKB1c0ufLJ5lpw7z8f9Y4/muAmasYItd gF4A== X-Gm-Message-State: AOJu0YwtIjBShR44HATKUOsir5YOEr+v5XO9h/XN6K/LHll8Q7uRp9KX S6fO3w7fXmmkVVugNJWU/SkT+wRha1ZNYVQs0ZtGhB2D7jhhE6oY0LFna8o2blY= X-Google-Smtp-Source: AGHT+IH3QDl9vHehy0g7oE7cDZu8Br3m1IACUhodV/bgawPj5dU1i744pSh6Z/lt/+8nYjxzaMPI4A== X-Received: by 2002:a17:90a:9ce:b0:2c9:36bf:ba6f with SMTP id 98e67ed59e1d1-2d3e4539feemr746473a91.3.1723755391676; Thu, 15 Aug 2024 13:56:31 -0700 (PDT) Received: from honey-badger.. ([38.34.87.7]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-2d3ac7f2eb2sm4024364a91.26.2024.08.15.13.56.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 15 Aug 2024 13:56:31 -0700 (PDT) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kernel-team@fb.com, yonghong.song@linux.dev, hffilwlqm@gmail.com, Eduard Zingerman Subject: [PATCH bpf-next v2 1/4] selftests/bpf: less spam in the log for message matching Date: Thu, 15 Aug 2024 13:54:46 -0700 Message-ID: <20240815205449.242556-2-eddyz87@gmail.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240815205449.242556-1-eddyz87@gmail.com> References: <20240815205449.242556-1-eddyz87@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net When running test_loader based tests in the verbose mode each matched message leaves a trace in the stderr, e.g.: ./test_progs -vvv -t ... validate_msgs:PASS:expect_msg 0 nsec validate_msgs:PASS:expect_msg 0 nsec validate_msgs:PASS:expect_msg 0 nsec validate_msgs:PASS:expect_msg 0 nsec validate_msgs:PASS:expect_msg 0 nsec This is not very helpful when debugging such tests and clobbers the log a lot. Signed-off-by: Eduard Zingerman --- tools/testing/selftests/bpf/test_loader.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c index 12b0c41e8d64..1b1290e090e7 100644 --- a/tools/testing/selftests/bpf/test_loader.c +++ b/tools/testing/selftests/bpf/test_loader.c @@ -531,7 +531,8 @@ static void validate_msgs(char *log_buf, struct expected_msgs *msgs, } } - if (!ASSERT_OK_PTR(match, "expect_msg")) { + if (!match) { + PRINT_FAIL("expect_msg\n"); if (env.verbosity == VERBOSE_NONE) emit_fn(log_buf, true /*force*/); for (j = 0; j <= i; j++) { From patchwork Thu Aug 15 20:54:47 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13765150 X-Patchwork-Delegate: bpf@iogearbox.net Received: from mail-pl1-f180.google.com (mail-pl1-f180.google.com [209.85.214.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5142838DC7 for ; Thu, 15 Aug 2024 20:56:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1723755396; cv=none; b=HaEksFsWKw96maaQsNga8loJyX9RC4Kj7WRQSkPEe2OiK0I2xLaYVFLxCCbrvRAQDgvt3QXhsbDL/pwH+PprlB6RsTzavGcZNpqKekrBDmKYfyJLHSy5oPrzRIa40QTUdEv2ICWj0iA1T/GA/zInJbxQe7Q1g6MmLEMGaou3p84= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1723755396; c=relaxed/simple; bh=8svz8hLcGQBLLl6v7s906A4XSqVqZtzWjxrNN322UbQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=OoTkxpBo23Sxh2NN7vU961OCTME6xlLpjUVXMAGDYxlV3ml1LU82UjDF+vsLynjCe3uM+iOZy3QGIn6ibWwmO8ZYwXdM3MUVQMPRabyhMDjS9AdPSAPYddcV7ZlWSbjGpoZO3GszGBuOj2CUCvwSO4vRwGaLq73M7877x+kdbpQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=eKX0SXBL; arc=none smtp.client-ip=209.85.214.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="eKX0SXBL" Received: by mail-pl1-f180.google.com with SMTP id d9443c01a7336-1fec34f94abso13681635ad.2 for ; Thu, 15 Aug 2024 13:56:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1723755393; x=1724360193; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=S5ThoDqZ4+AH2DIlG8lMUbHRTcUADtlux5x4v4uoxfM=; b=eKX0SXBLjD/2Tn+423DQH/2Yg7J9Hp5p1CHyhdLxn4/N6hgEwTbwREm1rHxDzHtOTQ kh3C8oltbYbvC6CAAVUmyPTS9dAFs5CK8wxG5Er7P/x0i0cJ54QQlhi9q3tRQLKGPQt7 l1ZIKEVtp9hiMkANmL6AcIcKw22mldzsZx/s7e2xSuH9CudDpQxIBml1x9j4JmoFThS1 GjdEPuBFm621bPMzOlD8eYb1AGEOTxZWI0M16dqbbj2wbf/KoHxUS2C1EY2sOrUCwDOG DTQpuqVGiu7FgBFGZY2IxP0uCegzftmNHc2HcfmJeze+9S4V3jB1qY1gfOwV2H5PWtcd DUTQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1723755393; x=1724360193; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=S5ThoDqZ4+AH2DIlG8lMUbHRTcUADtlux5x4v4uoxfM=; b=goSqyy/Hhayp8RO3xjvCxUgxYBwhVIRRIOABei9sV+DhngoejpOFOW1WvFdfumiA5K n0tQWAvUBxRf0wXFPLu74ANsqDRkOQLFvjBb5QHJNschNDyx32O84V7uRvUJ7e48avfc b1owykWZd1pu6/6NkkQ5HRPk/rwMcBRgNfqmryvzplivCe7NsXNPJt8X1ZOyLYmAopTu PPSQSAqtnhh67zQ7FbPTb9rWyhFTnjf91DnypABf+LU5uKdLc3oU10iFHNm8J4VKI4HC rOMSvrFEbLSzizufovjwyAeKTqSjj30MQMscH+qoiW7cEE5qhNkx17if4bjmei3NqoQw 1sLg== X-Gm-Message-State: AOJu0Yxw7S8xxq5HB6+VA0cIWJaHGeSV5gwdKmMRFBHrQ70+N3OVNIOR p3zrEa9+C3YxrqgQybCVfJ+kV74SX9K0fED8KbDfGDtdRyVI/WKmr004zMIRo/Q= X-Google-Smtp-Source: AGHT+IH6VYs6Q7iVkIdA/B/r5wzdjIYFM5Cakk9VGPU9yoVHAHwtpBrKea/MxflJpUB1pSch7JGU1A== X-Received: by 2002:a17:90b:1e4a:b0:2c8:81b:e798 with SMTP id 98e67ed59e1d1-2d3e00f2f83mr971650a91.30.1723755392850; Thu, 15 Aug 2024 13:56:32 -0700 (PDT) Received: from honey-badger.. ([38.34.87.7]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-2d3ac7f2eb2sm4024364a91.26.2024.08.15.13.56.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 15 Aug 2024 13:56:32 -0700 (PDT) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kernel-team@fb.com, yonghong.song@linux.dev, hffilwlqm@gmail.com, Eduard Zingerman Subject: [PATCH bpf-next v2 2/4] selftests/bpf: utility function to get program disassembly after jit Date: Thu, 15 Aug 2024 13:54:47 -0700 Message-ID: <20240815205449.242556-3-eddyz87@gmail.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240815205449.242556-1-eddyz87@gmail.com> References: <20240815205449.242556-1-eddyz87@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net This commit adds a utility function to get disassembled text for jited representation of a BPF program designated by file descriptor. Function prototype looks as follows: int get_jited_program_text(int fd, char *text, size_t text_sz) Where 'fd' is a file descriptor for the program, 'text' and 'text_sz' refer to a destination buffer for disassembled text. 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") Acked-by: Yonghong Song Signed-off-by: Eduard Zingerman --- tools/testing/selftests/bpf/.gitignore | 1 + tools/testing/selftests/bpf/Makefile | 51 +++- .../selftests/bpf/jit_disasm_helpers.c | 234 ++++++++++++++++++ .../selftests/bpf/jit_disasm_helpers.h | 10 + 4 files changed, 294 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) + # 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 diff --git a/tools/testing/selftests/bpf/jit_disasm_helpers.c b/tools/testing/selftests/bpf/jit_disasm_helpers.c new file mode 100644 index 000000000000..d1ded19be759 --- /dev/null +++ b/tools/testing/selftests/bpf/jit_disasm_helpers.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +#include +#include +#include + +#ifdef HAVE_LLVM_SUPPORT + +#include +#include +#include +#include + +/* The intent is to use get_jited_program_text() for small test + * programs written in BPF assembly, thus assume that 32 local labels + * would be sufficient. + */ +#define MAX_LOCAL_LABELS 32 + +static bool llvm_initialized; + +struct local_labels { + bool print_phase; + __u32 prog_len; + __u32 cnt; + __u32 pcs[MAX_LOCAL_LABELS]; + char names[MAX_LOCAL_LABELS][4]; +}; + +static const char *lookup_symbol(void *data, uint64_t ref_value, uint64_t *ref_type, + uint64_t ref_pc, const char **ref_name) +{ + struct local_labels *labels = data; + uint64_t type = *ref_type; + int i; + + *ref_type = LLVMDisassembler_ReferenceType_InOut_None; + *ref_name = NULL; + if (type != LLVMDisassembler_ReferenceType_In_Branch) + return NULL; + /* Depending on labels->print_phase either discover local labels or + * return a name assigned with local jump target: + * - if print_phase is true and ref_value is in labels->pcs, + * return corresponding labels->name. + * - if print_phase is false, save program-local jump targets + * in labels->pcs; + */ + if (labels->print_phase) { + for (i = 0; i < labels->cnt; ++i) + if (labels->pcs[i] == ref_value) + return labels->names[i]; + } else { + if (labels->cnt < MAX_LOCAL_LABELS && ref_value < labels->prog_len) + labels->pcs[labels->cnt++] = ref_value; + } + return NULL; +} + +static int disasm_insn(LLVMDisasmContextRef ctx, uint8_t *image, __u32 len, __u32 pc, + char *buf, __u32 buf_sz) +{ + int i, cnt; + + cnt = LLVMDisasmInstruction(ctx, image + pc, len - pc, pc, + buf, buf_sz); + if (cnt > 0) + return cnt; + PRINT_FAIL("Can't disasm instruction at offset %d:", pc); + for (i = 0; i < 16 && pc + i < len; ++i) + printf(" %02x", image[pc + i]); + printf("\n"); + return -EINVAL; +} + +static int cmp_u32(const void *_a, const void *_b) +{ + __u32 a = *(__u32 *)_a; + __u32 b = *(__u32 *)_b; + + if (a < b) + return -1; + if (a > b) + return 1; + return 0; +} + +static int disasm_one_func(FILE *text_out, uint8_t *image, __u32 len) +{ + char *label, *colon, *triple = NULL; + LLVMDisasmContextRef ctx = NULL; + struct local_labels labels = {}; + __u32 *label_pc, pc; + int i, cnt, err = 0; + char buf[64]; + + triple = LLVMGetDefaultTargetTriple(); + ctx = LLVMCreateDisasm(triple, &labels, 0, NULL, lookup_symbol); + if (!ASSERT_OK_PTR(ctx, "LLVMCreateDisasm")) { + err = -EINVAL; + goto out; + } + + cnt = LLVMSetDisasmOptions(ctx, LLVMDisassembler_Option_PrintImmHex); + if (!ASSERT_EQ(cnt, 1, "LLVMSetDisasmOptions")) { + err = -EINVAL; + goto out; + } + + /* discover labels */ + labels.prog_len = len; + pc = 0; + while (pc < len) { + cnt = disasm_insn(ctx, image, len, pc, buf, 1); + if (cnt < 0) { + err = cnt; + goto out; + } + pc += cnt; + } + qsort(labels.pcs, labels.cnt, sizeof(*labels.pcs), cmp_u32); + for (i = 0; i < labels.cnt; ++i) + /* use (i % 100) to avoid format truncation warning */ + snprintf(labels.names[i], sizeof(labels.names[i]), "L%d", i % 100); + + /* now print with labels */ + labels.print_phase = true; + pc = 0; + while (pc < len) { + cnt = disasm_insn(ctx, image, len, pc, buf, sizeof(buf)); + if (cnt < 0) { + err = cnt; + goto out; + } + label_pc = bsearch(&pc, labels.pcs, labels.cnt, sizeof(*labels.pcs), cmp_u32); + label = ""; + colon = ""; + if (label_pc) { + label = labels.names[label_pc - labels.pcs]; + colon = ":"; + } + fprintf(text_out, "%x:\t", pc); + for (i = 0; i < cnt; ++i) + fprintf(text_out, "%02x ", image[pc + i]); + for (i = cnt * 3; i < 12 * 3; ++i) + fputc(' ', text_out); + fprintf(text_out, "%s%s%s\n", label, colon, buf); + pc += cnt; + } + +out: + if (triple) + LLVMDisposeMessage(triple); + if (ctx) + LLVMDisasmDispose(ctx); + return err; +} + +int get_jited_program_text(int fd, char *text, size_t text_sz) +{ + struct bpf_prog_info info = {}; + __u32 info_len = sizeof(info); + __u32 jited_funcs, len, pc; + __u32 *func_lens = NULL; + FILE *text_out = NULL; + uint8_t *image = NULL; + int i, err = 0; + + if (!llvm_initialized) { + LLVMInitializeAllTargetInfos(); + LLVMInitializeAllTargetMCs(); + LLVMInitializeAllDisassemblers(); + llvm_initialized = 1; + } + + text_out = fmemopen(text, text_sz, "w"); + if (!ASSERT_OK_PTR(text_out, "open_memstream")) { + err = -errno; + goto out; + } + + /* first call is to find out jited program len */ + err = bpf_prog_get_info_by_fd(fd, &info, &info_len); + if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd #1")) + goto out; + + len = info.jited_prog_len; + image = malloc(len); + if (!ASSERT_OK_PTR(image, "malloc(info.jited_prog_len)")) { + err = -ENOMEM; + goto out; + } + + jited_funcs = info.nr_jited_func_lens; + func_lens = malloc(jited_funcs * sizeof(__u32)); + if (!ASSERT_OK_PTR(func_lens, "malloc(info.nr_jited_func_lens)")) { + err = -ENOMEM; + goto out; + } + + memset(&info, 0, sizeof(info)); + info.jited_prog_insns = (__u64)image; + info.jited_prog_len = len; + info.jited_func_lens = (__u64)func_lens; + info.nr_jited_func_lens = jited_funcs; + err = bpf_prog_get_info_by_fd(fd, &info, &info_len); + if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd #2")) + goto out; + + for (pc = 0, i = 0; i < jited_funcs; ++i) { + fprintf(text_out, "func #%d:\n", i); + disasm_one_func(text_out, image + pc, func_lens[i]); + fprintf(text_out, "\n"); + pc += func_lens[i]; + } + +out: + if (text_out) + fclose(text_out); + if (image) + free(image); + if (func_lens) + free(func_lens); + return err; +} + +#else /* HAVE_LLVM_SUPPORT */ + +int get_jited_program_text(int fd, char *text, size_t text_sz) +{ + if (env.verbosity >= VERBOSE_VERY) + printf("compiled w/o llvm development libraries, can't dis-assembly binary code"); + return -ENOTSUP; +} + +#endif /* HAVE_LLVM_SUPPORT */ diff --git a/tools/testing/selftests/bpf/jit_disasm_helpers.h b/tools/testing/selftests/bpf/jit_disasm_helpers.h new file mode 100644 index 000000000000..e6924fd65ecf --- /dev/null +++ b/tools/testing/selftests/bpf/jit_disasm_helpers.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ + +#ifndef __JIT_DISASM_HELPERS_H +#define __JIT_DISASM_HELPERS_H + +#include + +int get_jited_program_text(int fd, char *text, size_t text_sz); + +#endif /* __JIT_DISASM_HELPERS_H */ From patchwork Thu Aug 15 20:54:48 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13765151 X-Patchwork-Delegate: bpf@iogearbox.net Received: from mail-pg1-f173.google.com (mail-pg1-f173.google.com [209.85.215.173]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D11BF14AD20 for ; Thu, 15 Aug 2024 20:56:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1723755396; cv=none; b=BOENVqv0KnQhUkuFc1g8PTWXYyroOLeY2NJvAFxnQGLFBf4mBAIM2IyiSdLL0I6Yr0JFT7TekOJxqeElbI3LLq824Tp+3v1a56xMUGnLGwrNsCqoBEWzsV5e2Duyh+o704Nj554J7htPcNc9ncEY5SEwGGWbM2djJ5sSLXfkvH4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1723755396; c=relaxed/simple; bh=+bEcgy+Knm2fOzHL2BXU+qhDmeMeYb994e1Jn1xripI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=GVzQEE4bM5J4ueNB0p2EGl2940Op36b37V4+qAT6d/MXBX4GquqAUCDRlIM7L4WCxbe30tkzob83shHoObICWwyzDUlY5tbu3zbeuzmySNGhmmROIiDSn30mNp1RlGJEl2UjsG8UIuWVUjAkXC7zZ54uYEew9hpL6CBcOGN7moo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=AQ2Gvkv5; arc=none smtp.client-ip=209.85.215.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="AQ2Gvkv5" Received: by mail-pg1-f173.google.com with SMTP id 41be03b00d2f7-7a103ac7be3so1022746a12.3 for ; Thu, 15 Aug 2024 13:56:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1723755394; x=1724360194; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=B2Da15pCeD9ruYHPrpQ4Yz/TpNZJwB5SHIMUzfSkr18=; b=AQ2Gvkv5/Oy3btx0JOeZYkAvdRmhoYhM5SfOWGD5jZZ4pJpJMlR4y4l01YX/93F4Ud sHvOw5CCCJk4GGhXydmvJXgeQkCEekXVNyjEbOWFTVD5tzdFZ8PdnyHQ5zGkAS25NQN/ b1ZSpc3eiI0UY6fw82hUIgRSaNeXU/nZDRIwbGctIi5bkuSIA/G2hATvzUkuHtupc0hX 8qVb/dZCKWzHrD8SGtrcGucOduPaBBfCDZvyCeSXDfpmlbVBg/zLe4Krfi6+NTtiIThM cBmbXZCf11VY5EkesFzcuOJATzNaWWE1tIqphMbDIo3enyJkHW8RU+xS75/R06ssuF3D UfHw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1723755394; x=1724360194; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=B2Da15pCeD9ruYHPrpQ4Yz/TpNZJwB5SHIMUzfSkr18=; b=dbsq6XSZeK9H3+4jwxVQZo9Ex4h7/ufJd/jHOA/Ed4kGmTSVJp2uBseLi3zjW0TJTg fawEyByqtee4uUYGNmyx5rMpXc8VoZxNoBYipjh3RBT2HGO6zAxTaa/XC7aNkYDyUY6H v5/AjRMgOr9ypYcGme6hly9C0AAabxpO2rTdTZfgzwRbaxi5I9BfdFLfn5NIXc5jtbMO noFcFqlvqiIr+bcYnqHlcEtw5+8W8ew3wbpED57rKFFeljTQwkC2aqF8o86Ndkabd+i5 MChc/0UltwMU5dhsCKLFsIo0qu2ZAk7DZYJ1ckE1uwX8reWQUfisZ0PW1DQO9Z2H7aGj +JoQ== X-Gm-Message-State: AOJu0YyDbydKdTAZvfSCqqj/KhAVyd/AsNh9JcwaAzhTHTrsVjh3cZ2A gBmt8qfiqkILZOfBATOrmvjxlJrs4dRbkDEhysdXp3Gl7Vjn+sVLPetC6Hc+QXg= X-Google-Smtp-Source: AGHT+IH7l+8OQsRDUeOfGKmgaWr4dwY06sfIWBMyKyuGz0OEvH2z/c8PEUHXYuTQ38PNVMif/Q6btA== X-Received: by 2002:a17:90a:34cc:b0:2c9:6278:27c9 with SMTP id 98e67ed59e1d1-2d3e0409761mr985422a91.38.1723755393779; Thu, 15 Aug 2024 13:56:33 -0700 (PDT) Received: from honey-badger.. ([38.34.87.7]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-2d3ac7f2eb2sm4024364a91.26.2024.08.15.13.56.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 15 Aug 2024 13:56:33 -0700 (PDT) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kernel-team@fb.com, yonghong.song@linux.dev, hffilwlqm@gmail.com, Eduard Zingerman Subject: [PATCH bpf-next v2 3/4] selftests/bpf: __jited_x86 test tag to check x86 assembly after jit Date: Thu, 15 Aug 2024 13:54:48 -0700 Message-ID: <20240815205449.242556-4-eddyz87@gmail.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240815205449.242556-1-eddyz87@gmail.com> References: <20240815205449.242556-1-eddyz87@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net Allow to verify jit behaviour by writing tests as below: SEC("tp") __jit_x86("endbr64") __jit_x86("movabs $0x.*,%r9") __jit_x86("add %gs:0x.*,%r9") __jit_x86("mov $0x1,%edi") __jit_x86("mov %rdi,-0x8(%r9)") __jit_x86("mov -0x8(%r9),%rdi") __jit_x86("xor %eax,%eax") __jit_x86("lock xchg %rax,-0x8(%r9)") __jit_x86("lock xadd %rax,-0x8(%r9)") __naked void stack_access_insns(void) { asm volatile (... ::: __clobber_all); } Use regular expressions by default, use basic regular expressions class in order to avoid escaping symbols like $(), often used in AT&T disassembly syntax. Signed-off-by: Eduard Zingerman --- tools/testing/selftests/bpf/progs/bpf_misc.h | 2 + tools/testing/selftests/bpf/test_loader.c | 156 +++++++++++++------ 2 files changed, 112 insertions(+), 46 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h index a225cd87897c..06e353a0a5b1 100644 --- a/tools/testing/selftests/bpf/progs/bpf_misc.h +++ b/tools/testing/selftests/bpf/progs/bpf_misc.h @@ -90,6 +90,8 @@ #define __arch_x86_64 __arch("X86_64") #define __arch_arm64 __arch("ARM64") #define __arch_riscv64 __arch("RISCV64") +#define __jit_x86(basic_regex) __attribute__((btf_decl_tag("comment:test_jit_x86=" basic_regex))) +#define __jit_x86_unpriv(basic_regex) __attribute__((btf_decl_tag("comment:test_jit_x86_unpriv=" basic_regex))) /* Convenience macro for use with 'asm volatile' blocks */ #define __naked __attribute__((naked)) diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c index 1b1290e090e7..7d8a0cf9904a 100644 --- a/tools/testing/selftests/bpf/test_loader.c +++ b/tools/testing/selftests/bpf/test_loader.c @@ -10,6 +10,7 @@ #include "disasm_helpers.h" #include "unpriv_helpers.h" #include "cap_helpers.h" +#include "jit_disasm_helpers.h" #define str_has_pfx(str, pfx) \ (strncmp(str, pfx, __builtin_constant_p(pfx) ? sizeof(pfx) - 1 : strlen(pfx)) == 0) @@ -35,6 +36,8 @@ #define TEST_TAG_AUXILIARY_UNPRIV "comment:test_auxiliary_unpriv" #define TEST_BTF_PATH "comment:test_btf_path=" #define TEST_TAG_ARCH "comment:test_arch=" +#define TEST_TAG_JIT_X86_PFX "comment:test_jit_x86=" +#define TEST_TAG_JIT_X86_PFX_UNPRIV "comment:test_jit_x86_unpriv=" /* Warning: duplicated in bpf_misc.h */ #define POINTER_VALUE 0xcafe4all @@ -53,10 +56,18 @@ enum mode { UNPRIV = 2 }; +enum arch { + ARCH_X86_64 = 1, + ARCH_ARM64 = 2, + ARCH_RISCV64 = 3, + ARCH_MAX +}; + struct expect_msg { const char *substr; /* substring match */ const char *regex_str; /* regex-based match */ regex_t regex; + int regex_flags; }; struct expected_msgs { @@ -69,6 +80,7 @@ struct test_subspec { bool expect_failure; struct expected_msgs expect_msgs; struct expected_msgs expect_xlated; + struct expected_msgs jited[ARCH_MAX]; int retval; bool execute; }; @@ -120,11 +132,17 @@ static void free_msgs(struct expected_msgs *msgs) static void free_test_spec(struct test_spec *spec) { + int i; + /* Deallocate expect_msgs arrays. */ free_msgs(&spec->priv.expect_msgs); free_msgs(&spec->unpriv.expect_msgs); free_msgs(&spec->priv.expect_xlated); free_msgs(&spec->unpriv.expect_xlated); + for (i = 0; i < ARCH_MAX; ++i) { + free_msgs(&spec->priv.jited[i]); + free_msgs(&spec->unpriv.jited[i]); + } free(spec->priv.name); free(spec->unpriv.name); @@ -132,7 +150,8 @@ static void free_test_spec(struct test_spec *spec) spec->unpriv.name = NULL; } -static int push_msg(const char *substr, const char *regex_str, struct expected_msgs *msgs) +static int __push_msg(const char *substr, const char *regex_str, int regex_flags, + struct expected_msgs *msgs) { void *tmp; int regcomp_res; @@ -151,10 +170,12 @@ static int push_msg(const char *substr, const char *regex_str, struct expected_m if (substr) { msg->substr = substr; msg->regex_str = NULL; + msg->regex_flags = 0; } else { msg->regex_str = regex_str; msg->substr = NULL; - regcomp_res = regcomp(&msg->regex, regex_str, REG_EXTENDED|REG_NEWLINE); + msg->regex_flags = regex_flags; + regcomp_res = regcomp(&msg->regex, regex_str, regex_flags|REG_NEWLINE); if (regcomp_res != 0) { regerror(regcomp_res, &msg->regex, error_msg, sizeof(error_msg)); PRINT_FAIL("Regexp compilation error in '%s': '%s'\n", @@ -167,6 +188,35 @@ static int push_msg(const char *substr, const char *regex_str, struct expected_m return 0; } +static int clone_msgs(struct expected_msgs *from, struct expected_msgs *to) +{ + struct expect_msg *msg; + int i, err; + + for (i = 0; i < from->cnt; i++) { + msg = &from->patterns[i]; + err = __push_msg(msg->substr, msg->regex_str, msg->regex_flags, to); + if (err) + return err; + } + return 0; +} + +static int push_msg(const char *substr, struct expected_msgs *msgs) +{ + return __push_msg(substr, NULL, 0, msgs); +} + +static int push_extended_regex(const char *regex_str, struct expected_msgs *msgs) +{ + return __push_msg(NULL, regex_str, REG_EXTENDED, msgs); +} + +static int push_basic_regex(const char *regex_str, struct expected_msgs *msgs) +{ + return __push_msg(NULL, regex_str, 0, msgs); +} + static int parse_int(const char *str, int *val, const char *name) { char *end; @@ -215,12 +265,6 @@ static void update_flags(int *flags, int flag, bool clear) *flags |= flag; } -enum arch { - ARCH_X86_64 = 0x1, - ARCH_ARM64 = 0x2, - ARCH_RISCV64 = 0x4, -}; - /* Uses btf_decl_tag attributes to describe the expected test * behavior, see bpf_misc.h for detailed description of each attribute * and attribute combinations. @@ -292,37 +336,49 @@ static int parse_test_spec(struct test_loader *tester, spec->mode_mask |= UNPRIV; } else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX)) { msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX) - 1; - err = push_msg(msg, NULL, &spec->priv.expect_msgs); + err = push_msg(msg, &spec->priv.expect_msgs); if (err) goto cleanup; spec->mode_mask |= PRIV; } else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX_UNPRIV)) { msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX_UNPRIV) - 1; - err = push_msg(msg, NULL, &spec->unpriv.expect_msgs); + err = push_msg(msg, &spec->unpriv.expect_msgs); if (err) goto cleanup; spec->mode_mask |= UNPRIV; } else if (str_has_pfx(s, TEST_TAG_EXPECT_REGEX_PFX)) { msg = s + sizeof(TEST_TAG_EXPECT_REGEX_PFX) - 1; - err = push_msg(NULL, msg, &spec->priv.expect_msgs); + err = push_extended_regex(msg, &spec->priv.expect_msgs); if (err) goto cleanup; spec->mode_mask |= PRIV; } else if (str_has_pfx(s, TEST_TAG_EXPECT_REGEX_PFX_UNPRIV)) { msg = s + sizeof(TEST_TAG_EXPECT_REGEX_PFX_UNPRIV) - 1; - err = push_msg(NULL, msg, &spec->unpriv.expect_msgs); + err = push_extended_regex(msg, &spec->unpriv.expect_msgs); + if (err) + goto cleanup; + spec->mode_mask |= UNPRIV; + } else if (str_has_pfx(s, TEST_TAG_JIT_X86_PFX)) { + msg = s + sizeof(TEST_TAG_JIT_X86_PFX) - 1; + err = push_basic_regex(msg, &spec->priv.jited[ARCH_X86_64]); + if (err) + goto cleanup; + spec->mode_mask |= PRIV; + } else if (str_has_pfx(s, TEST_TAG_JIT_X86_PFX_UNPRIV)) { + msg = s + sizeof(TEST_TAG_JIT_X86_PFX_UNPRIV) - 1; + err = push_basic_regex(msg, &spec->unpriv.jited[ARCH_X86_64]); if (err) goto cleanup; spec->mode_mask |= UNPRIV; } else if (str_has_pfx(s, TEST_TAG_EXPECT_XLATED_PFX)) { msg = s + sizeof(TEST_TAG_EXPECT_XLATED_PFX) - 1; - err = push_msg(msg, NULL, &spec->priv.expect_xlated); + err = push_msg(msg, &spec->priv.expect_xlated); if (err) goto cleanup; spec->mode_mask |= PRIV; } else if (str_has_pfx(s, TEST_TAG_EXPECT_XLATED_PFX_UNPRIV)) { msg = s + sizeof(TEST_TAG_EXPECT_XLATED_PFX_UNPRIV) - 1; - err = push_msg(msg, NULL, &spec->unpriv.expect_xlated); + err = push_msg(msg, &spec->unpriv.expect_xlated); if (err) goto cleanup; spec->mode_mask |= UNPRIV; @@ -376,11 +432,11 @@ static int parse_test_spec(struct test_loader *tester, } else if (str_has_pfx(s, TEST_TAG_ARCH)) { val = s + sizeof(TEST_TAG_ARCH) - 1; if (strcmp(val, "X86_64") == 0) { - arch_mask |= ARCH_X86_64; + arch_mask |= 1u << ARCH_X86_64; } else if (strcmp(val, "ARM64") == 0) { - arch_mask |= ARCH_ARM64; + arch_mask |= 1u << ARCH_ARM64; } else if (strcmp(val, "RISCV64") == 0) { - arch_mask |= ARCH_RISCV64; + arch_mask |= 1u << ARCH_RISCV64; } else { PRINT_FAIL("bad arch spec: '%s'", val); err = -EINVAL; @@ -434,26 +490,13 @@ static int parse_test_spec(struct test_loader *tester, spec->unpriv.execute = spec->priv.execute; } - if (spec->unpriv.expect_msgs.cnt == 0) { - for (i = 0; i < spec->priv.expect_msgs.cnt; i++) { - struct expect_msg *msg = &spec->priv.expect_msgs.patterns[i]; - - err = push_msg(msg->substr, msg->regex_str, - &spec->unpriv.expect_msgs); - if (err) - goto cleanup; - } - } - if (spec->unpriv.expect_xlated.cnt == 0) { - for (i = 0; i < spec->priv.expect_xlated.cnt; i++) { - struct expect_msg *msg = &spec->priv.expect_xlated.patterns[i]; - - err = push_msg(msg->substr, msg->regex_str, - &spec->unpriv.expect_xlated); - if (err) - goto cleanup; - } - } + if (spec->unpriv.expect_msgs.cnt == 0) + clone_msgs(&spec->priv.expect_msgs, &spec->unpriv.expect_msgs); + if (spec->unpriv.expect_xlated.cnt == 0) + clone_msgs(&spec->priv.expect_xlated, &spec->unpriv.expect_xlated); + for (i = 0; i < ARCH_MAX; ++i) + if (spec->unpriv.jited[i].cnt == 0) + clone_msgs(&spec->priv.jited[i], &spec->unpriv.jited[i]); } spec->valid = true; @@ -508,6 +551,13 @@ static void emit_xlated(const char *xlated, bool force) fprintf(stdout, "XLATED:\n=============\n%s=============\n", xlated); } +static void emit_jited(const char *jited, bool force) +{ + if (!force && env.verbosity == VERBOSE_NONE) + return; + fprintf(stdout, "JITED:\n=============\n%s=============\n", jited); +} + static void validate_msgs(char *log_buf, struct expected_msgs *msgs, void (*emit_fn)(const char *buf, bool force)) { @@ -702,18 +752,16 @@ static int get_xlated_program_text(int prog_fd, char *text, size_t text_sz) return err; } -static bool run_on_current_arch(int arch_mask) +static int get_current_arch(void) { - if (arch_mask == 0) - return true; #if defined(__x86_64__) - return arch_mask & ARCH_X86_64; + return ARCH_X86_64; #elif defined(__aarch64__) - return arch_mask & ARCH_ARM64; + return ARCH_ARM64; #elif defined(__riscv) && __riscv_xlen == 64 - return arch_mask & ARCH_RISCV64; + return ARCH_RISCV64; #endif - return false; + return -1; } /* this function is forced noinline and has short generic name to look better @@ -732,15 +780,16 @@ void run_subtest(struct test_loader *tester, struct bpf_program *tprog = NULL, *tprog_iter; struct test_spec *spec_iter; struct cap_state caps = {}; + int retval, err, i, arch; struct bpf_object *tobj; struct bpf_map *map; - int retval, err, i; bool should_load; if (!test__start_subtest(subspec->name)) return; - if (!run_on_current_arch(spec->arch_mask)) { + arch = get_current_arch(); + if (spec->arch_mask && (arch < 0 || (spec->arch_mask & (1u << arch)) == 0)) { test__skip(); return; } @@ -817,6 +866,21 @@ void run_subtest(struct test_loader *tester, validate_msgs(tester->log_buf, &subspec->expect_xlated, emit_xlated); } + if (arch > 0 && subspec->jited[arch].cnt) { + err = get_jited_program_text(bpf_program__fd(tprog), + tester->log_buf, tester->log_buf_sz); + if (err == -ENOTSUP) { + printf("%s:SKIP: jited programs disassembly is not supported,\n", __func__); + printf("%s:SKIP: tests are built w/o LLVM development libs\n", __func__); + test__skip(); + goto tobj_cleanup; + } + if (!ASSERT_EQ(err, 0, "get_jited_program_text")) + goto tobj_cleanup; + emit_jited(tester->log_buf, false /*force*/); + validate_msgs(tester->log_buf, &subspec->jited[arch], emit_jited); + } + if (should_do_test_run(spec, subspec)) { /* For some reason test_verifier executes programs * with all capabilities restored. Do the same here. From patchwork Thu Aug 15 20:54:49 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13765152 X-Patchwork-Delegate: bpf@iogearbox.net Received: from mail-pj1-f49.google.com (mail-pj1-f49.google.com [209.85.216.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DFF3914B092 for ; Thu, 15 Aug 2024 20:56:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1723755397; cv=none; b=jnm4GStWZPnut4raEul8+u5w2o7qqOzfwKl86FY4fDQQlVItImn0KtaXI6vgj2ug/5yOpyuDXuXA0FNAkbwMs6KZqTwRaYswJYDhp40Og7QHl1udxqquykhhjsO+iCiaZ5WsVKEsqR2IBHelhsREoj2oZEq/yeAplKo5sWyINkU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1723755397; c=relaxed/simple; bh=EgeL4RrSGKogPAXhFdM47AF9nV5PFYXAdWvyJ/QMzqg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZRybc+soBs3eivimeq4nS1P6VjKmkRjP76apeingU1GCjOZkz+bb36/IdoW9uwy868FE0/3ObDek3l9/WGqW0pU0b/B4mJkOE5pK5MO0sPeMbQntdVQ85d4D6VmsPy6bLxaNWyui8LGWKcwMF4FF1YxfjoID0X6l2KTeM+7/6/0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=N8cAoh2a; arc=none smtp.client-ip=209.85.216.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="N8cAoh2a" Received: by mail-pj1-f49.google.com with SMTP id 98e67ed59e1d1-2d3e44b4613so144995a91.3 for ; Thu, 15 Aug 2024 13:56:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1723755395; x=1724360195; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=5lpr1bqtVW2E6vV1iJ3WXpF3fVUh4Rj80PjVcMFzSYQ=; b=N8cAoh2aEjLh2/sixYwJHLD5GlYqjHkr0lC+/yCcgLVmDvrqmFPeRdZqw2/v952Oo+ UBOn6k7TKv9MznB8lhSbMr79lD3nVxS51e9axa5LHu92LsHl7v4hYPuqBr7K+5b52M9d 5gySJloI/GDEUmbLP1O2KMvLF6m199XtcPRQ4T2JC4+hLX+3sg6yZp7XGJlEL8pp0Voi Mo1Z0lhvrkZX/Rqd76zRhsxfSPRogI/fMZjllqtBcLoY8l1LfYPbSnnpAP0Z1WQazS5c 9VXx4lwuaJtJilleCWwsH/RwO20VLJuyvwQXfOleiCcswnOg8GMj1gSSogXF4d9I3DTq 6DDA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1723755395; x=1724360195; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=5lpr1bqtVW2E6vV1iJ3WXpF3fVUh4Rj80PjVcMFzSYQ=; b=RSh68brU1eYoyGJhlsIknLDR6guOVLLCETuiCcX7CzsFe3hrdmQ8dU2qrCqTnh3Y1c gfYLS+BHb67GrEqhba7e6ukPKQ7CwE0WWWssGlhFZQ842Bm7dEKi52by/241BGNz+dI6 Ian+FEnUeDIZHvwS7hj7tQfM8FCxsaeml781v9aiIgtcWDwGt/cBP58b0+P7KMR0FqJA 8r77aHRy1MMojM1Z8/XpgrPA8htWEsjOQTPvWYnv3k0Qq8DBB9Ib0Y36J4xhIgqZlq4e h7dUeipRgA5F/hmhY58HYGmw3GNiFDyoRbq6f7czvDZ3dvyqYC8SiAGkZnNimH/GAj7T gNbg== X-Gm-Message-State: AOJu0YxggIUMkoMWgPlpKkWtm02ZKommnNxxjMfwp7I2AFGBCRrinwHN D6U7EUF/at8a15SibWxkBTNu+bUggmxHngGwQnOJBer5O0MMWQUqra2DZCL60No= X-Google-Smtp-Source: AGHT+IG4cz9ct5y4bBv0MIajKNmPWEKmuEWcGUPLXvrsBZxl4aOHWRwUW7uLVjs50Pgqip751qGJnA== X-Received: by 2002:a17:90a:eb08:b0:2c9:8189:7b4f with SMTP id 98e67ed59e1d1-2d3e00f4185mr976209a91.32.1723755394978; Thu, 15 Aug 2024 13:56:34 -0700 (PDT) Received: from honey-badger.. ([38.34.87.7]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-2d3ac7f2eb2sm4024364a91.26.2024.08.15.13.56.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 15 Aug 2024 13:56:34 -0700 (PDT) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kernel-team@fb.com, yonghong.song@linux.dev, hffilwlqm@gmail.com, Eduard Zingerman Subject: [PATCH bpf-next v2 4/4] selftests/bpf: validate jit behaviour for tail calls Date: Thu, 15 Aug 2024 13:54:49 -0700 Message-ID: <20240815205449.242556-5-eddyz87@gmail.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240815205449.242556-1-eddyz87@gmail.com> References: <20240815205449.242556-1-eddyz87@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net A program calling sub-program which does a tail call. The idea is to verify instructions generated by jit for tail calls: - in program and sub-program prologues; - for subprogram call instruction; - for tail call itself. Signed-off-by: Eduard Zingerman --- .../selftests/bpf/prog_tests/verifier.c | 2 + .../bpf/progs/verifier_tailcall_jit.c | 103 ++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/verifier_tailcall_jit.c diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c index f8f546eba488..cf3662dbd24f 100644 --- a/tools/testing/selftests/bpf/prog_tests/verifier.c +++ b/tools/testing/selftests/bpf/prog_tests/verifier.c @@ -75,6 +75,7 @@ #include "verifier_stack_ptr.skel.h" #include "verifier_subprog_precision.skel.h" #include "verifier_subreg.skel.h" +#include "verifier_tailcall_jit.skel.h" #include "verifier_typedef.skel.h" #include "verifier_uninit.skel.h" #include "verifier_unpriv.skel.h" @@ -198,6 +199,7 @@ void test_verifier_spin_lock(void) { RUN(verifier_spin_lock); } void test_verifier_stack_ptr(void) { RUN(verifier_stack_ptr); } void test_verifier_subprog_precision(void) { RUN(verifier_subprog_precision); } void test_verifier_subreg(void) { RUN(verifier_subreg); } +void test_verifier_tailcall_jit(void) { RUN(verifier_tailcall_jit); } void test_verifier_typedef(void) { RUN(verifier_typedef); } void test_verifier_uninit(void) { RUN(verifier_uninit); } void test_verifier_unpriv(void) { RUN(verifier_unpriv); } diff --git a/tools/testing/selftests/bpf/progs/verifier_tailcall_jit.c b/tools/testing/selftests/bpf/progs/verifier_tailcall_jit.c new file mode 100644 index 000000000000..1a09c76d7be0 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/verifier_tailcall_jit.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include "bpf_misc.h" + +int main(void); + +struct { + __uint(type, BPF_MAP_TYPE_PROG_ARRAY); + __uint(max_entries, 1); + __uint(key_size, sizeof(__u32)); + __array(values, void (void)); +} jmp_table SEC(".maps") = { + .values = { + [0] = (void *) &main, + }, +}; + +__noinline __auxiliary +static __naked int sub(void) +{ + asm volatile ( + "r2 = %[jmp_table] ll;" + "r3 = 0;" + "call 12;" + "exit;" + : + : __imm_addr(jmp_table) + : __clobber_all); +} + +__success +/* program entry for main(), regular function prologue */ +__jit_x86(" endbr64") +__jit_x86(" nopl (%rax,%rax)") +__jit_x86(" xorq %rax, %rax") +__jit_x86(" pushq %rbp") +__jit_x86(" movq %rsp, %rbp") +/* tail call prologue for program: + * - establish memory location for tail call counter at &rbp[-8]; + * - spill tail_call_cnt_ptr at &rbp[-16]; + * - expect tail call counter to be passed in rax; + * - for entry program rax is a raw counter, value < 33; + * - for tail called program rax is tail_call_cnt_ptr (value > 33). + */ +__jit_x86(" endbr64") +__jit_x86(" cmpq $0x21, %rax") +__jit_x86(" ja L0") +__jit_x86(" pushq %rax") +__jit_x86(" movq %rsp, %rax") +__jit_x86(" jmp L1") +__jit_x86("L0: pushq %rax") /* rbp[-8] = rax */ +__jit_x86("L1: pushq %rax") /* rbp[-16] = rax */ +/* on subprogram call restore rax to be tail_call_cnt_ptr from rbp[-16] + * (cause original rax might be clobbered by this point) + */ +__jit_x86(" movq -0x10(%rbp), %rax") +__jit_x86(" callq 0x[0-9a-f]\\+") /* call to sub() */ +__jit_x86(" xorl %eax, %eax") +__jit_x86(" leave") +__jit_x86(" retq") +/* subprogram entry for sub(), regular function prologue */ +__jit_x86(" endbr64") +__jit_x86(" nopl (%rax,%rax)") +__jit_x86(" nopl (%rax)") +__jit_x86(" pushq %rbp") +__jit_x86(" movq %rsp, %rbp") +/* tail call prologue for subprogram address of tail call counter + * stored at rbp[-16]. + */ +__jit_x86(" endbr64") +__jit_x86(" pushq %rax") /* rbp[-8] = rax */ +__jit_x86(" pushq %rax") /* rbp[-16] = rax */ +__jit_x86(" movabsq $-0x[0-9a-f]\\+, %rsi") /* r2 = &jmp_table */ +__jit_x86(" xorl %edx, %edx") /* r3 = 0 */ +/* bpf_tail_call implementation: + * - load tail_call_cnt_ptr from rbp[-16]; + * - if *tail_call_cnt_ptr < 33, increment it and jump to target; + * - otherwise do nothing. + */ +__jit_x86(" movq -0x10(%rbp), %rax") +__jit_x86(" cmpq $0x21, (%rax)") +__jit_x86(" jae L0") +__jit_x86(" nopl (%rax,%rax)") +__jit_x86(" addq $0x1, (%rax)") /* *tail_call_cnt_ptr += 1 */ +__jit_x86(" popq %rax") +__jit_x86(" popq %rax") +__jit_x86(" jmp 0x[0-9a-f]\\+") /* jump to tail call tgt */ +__jit_x86("L0: leave") +__jit_x86(" retq") +SEC("tc") +__naked int main(void) +{ + asm volatile ( + "call %[sub];" + "r0 = 0;" + "exit;" + : + : __imm(sub) + : __clobber_all); +} + +char __license[] SEC("license") = "GPL";