Building the Linux kernel with Clang and LLVM
 help / color / mirror / Atom feed
From: "Alexei Starovoitov" <alexei.starovoitov@gmail.com>
To: <socketpair@gmail.com>, "Quentin Monnet" <qmo@kernel.org>,
	"Daniel Borkmann" <daniel@iogearbox.net>,
	"Andrii Nakryiko" <andrii@kernel.org>,
	"Martin KaFai Lau" <martin.lau@linux.dev>,
	"Eduard Zingerman" <eddyz87@gmail.com>,
	"Kumar Kartikeya Dwivedi" <memxor@gmail.com>,
	"Song Liu" <song@kernel.org>,
	"Yonghong Song" <yonghong.song@linux.dev>,
	"Jiri Olsa" <jolsa@kernel.org>,
	"Nathan Chancellor" <nathan@kernel.org>,
	"Bill Wendling" <morbo@google.com>,
	"Justin Stitt" <justinstitt@google.com>,
	"Emil Tsalapatis" <emil@etsalapatis.com>,
	"Ihor Solodrai" <ihor.solodrai@linux.dev>,
	"Nick Desaulniers" <ndesaulniers@google.com>
Cc: <linux-kernel@vger.kernel.org>, <bpf@vger.kernel.org>,
	<llvm@lists.linux.dev>
Subject: Re: [PATCH bpf-next v3 2/2] bpftool: Make the LLVM disassembler an optional runtime dependency
Date: Thu, 29 Oct 2026 15:24:22 +0000	[thread overview]
Message-ID: <DMHFCN3J36VN.2JJEE30A07C0Z@gmail.com> (raw)
In-Reply-To: <20261029-bpftool-plugin-v3-2-87af799da40a@gmail.com>

On Thu, Oct 29, 2026 at 06:05 AM Mark Korenberg via B4 Relay <devnull+socketpair.gmail.com@kernel.org> wrote:
> Move the LLVM disassembler to a plugin, $(libdir)/bpftool/bpftool-llvm.so,
> that bpftool loads with dlopen() only when disassembling JITed programs.
> Distributions can then ship it as an optional package.
>
> The plugin exports a single structure of callbacks, with an ABI version
> checked by bpftool. bpftool looks for the plugin next to its own binary,
> then in its install directory, but not in the current directory since it
> usually runs as root. "bpftool version" reports "llvm" only if the plugin
> can be loaded.

This is overkill.
The plugin wraps 7 llvm-c functions and the target init calls.
bpftool can dlopen() libLLVM.so itself and dlsym() them.
perf does that for libcapstone. See
commit b5c9bcde61b8 ("perf capstone: Support for dlopen-ing libcapstone.so").

                     this patch                  dlopen(libLLVM.so)
  -----------------  --------------------------  ------------------
  installed files    bpftool, bpftool-llvm.so    bpftool
  who finds the lib  llvm_plugin_find()          ld.so
  ABI to maintain    struct bpftool_llvm_ops     none
  Makefile           libdir, llvm-plugin-dir,    one -D
                     install, uninstall rules

v1, v2 and v3 each look for the plugin in a different way.
The bpftool that the selftests build still doesn't find it.
selftests/bpf, selftests/hid, selftests/sched_ext and tools/sched_ext do:

  make -C tools/bpf/bpftool prefix= DESTDIR=<dir>/ install-bin

  bpftool           <dir>/sbin/bpftool
  plugin            <dir>/lib/bpftool/bpftool-llvm.so
  bpftool looks in  <dir>/sbin, then /lib/bpftool

"prog dump jited" fails there with this patch.
"make install" in selftests/bpf copies only bpftool. The plugin stays behind.

In the diff below llvm-config gives the name of the library at build time.
ld.so finds it the same way as today.
The distro package recommends the LLVM libs and doesn't require them.
"bpftool version" keeps "llvm" as a compile time feature.
That's what common_options.rst says.
LLVM built with BUILD_SHARED_LIBS=ON is not handled.

Something like this (not even compile tested):

diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile
index b0f7168..7a49182 100644
--- a/tools/bpf/bpftool/Makefile
+++ b/tools/bpf/bpftool/Makefile
@@ -186,10 +186,15 @@ ifeq ($(HAS_LLVM),1)
   # Makefile includes lib.mk which sets -D_GNU_SOURCE) which would cause
   # compilation error due to redefinition. Let's filter it out here.
   CFLAGS  += $(filter-out -D_GNU_SOURCE,$(shell $(LLVM_CONFIG) --cflags))
-  LIBS    += $(shell $(LLVM_CONFIG) --libs $(LLVM_CONFIG_LIB_COMPONENTS))
   ifeq ($(shell $(LLVM_CONFIG) --shared-mode),static)
+    LIBS += $(shell $(LLVM_CONFIG) --libs $(LLVM_CONFIG_LIB_COMPONENTS))
     LIBS += $(shell $(LLVM_CONFIG) --system-libs $(LLVM_CONFIG_LIB_COMPONENTS))
     LIBS += -lstdc++
+  else
+    # Don't link with libLLVM.so. "prog dump jited" loads it with dlopen().
+    LIBLLVM_SO := $(shell $(LLVM_CONFIG) --libfiles $(LLVM_CONFIG_LIB_COMPONENTS))
+    CFLAGS += -DLIBLLVM_SO='"$(notdir $(realpath $(LIBLLVM_SO)))"'
+    LIBS += -ldl
   endif
   LDFLAGS += $(shell $(LLVM_CONFIG) --ldflags)
 else
diff --git a/tools/bpf/bpftool/jit_disasm.c b/tools/bpf/bpftool/jit_disasm.c
index 0454115..cca02cd 100644
--- a/tools/bpf/bpftool/jit_disasm.c
+++ b/tools/bpf/bpftool/jit_disasm.c
@@ -25,6 +25,7 @@
 #include <bpf/libbpf.h>

 #ifdef HAVE_LLVM_SUPPORT
+#include <dlfcn.h>
 #include <llvm-c/Core.h>
 #include <llvm-c/Disassembler.h>
 #include <llvm-c/Target.h>
@@ -47,6 +48,88 @@ static int oper_count;

 typedef LLVMDisasmContextRef disasm_ctx_t;

+#ifdef LIBLLVM_SO
+/*
+ * libLLVM.so is big and only "prog dump jited" needs it. bpftool is not linked
+ * with it. disasm_init() loads it.
+ */
+#define LLVM_FUNCS(F)			\
+	F(LLVMCreateDisasm)		\
+	F(LLVMCreateDisasmCPUFeatures)	\
+	F(LLVMDisasmDispose)		\
+	F(LLVMDisasmInstruction)	\
+	F(LLVMDisposeMessage)		\
+	F(LLVMGetDefaultTargetTriple)	\
+	F(LLVMNormalizeTargetTriple)
+
+#define F(fn) static __typeof__(&fn) p_##fn;
+LLVM_FUNCS(F)
+#undef F
+
+static const struct {
+	const char *name;
+	void *ptr;
+} llvm_funcs[] = {
+#define F(fn) { #fn, &p_##fn },
+	LLVM_FUNCS(F)
+#undef F
+};
+
+/* The functions that LLVMInitializeAll*() in llvm-c/Target.h call */
+static const char * const llvm_init_funcs[] = {
+#define LLVM_TARGET(t) "LLVMInitialize" #t "TargetInfo", "LLVMInitialize" #t "TargetMC",
+#include <llvm/Config/Targets.def>
+#define LLVM_DISASSEMBLER(t) "LLVMInitialize" #t "Disassembler",
+#include <llvm/Config/Disassemblers.def>
+};
+
+#define LLVMCreateDisasm		p_LLVMCreateDisasm
+#define LLVMCreateDisasmCPUFeatures	p_LLVMCreateDisasmCPUFeatures
+#define LLVMDisasmDispose		p_LLVMDisasmDispose
+#define LLVMDisasmInstruction		p_LLVMDisasmInstruction
+#define LLVMDisposeMessage		p_LLVMDisposeMessage
+#define LLVMGetDefaultTargetTriple	p_LLVMGetDefaultTargetTriple
+#define LLVMNormalizeTargetTriple	p_LLVMNormalizeTargetTriple
+
+static void *llvm_sym(void *lib, const char *name)
+{
+	void *sym = dlsym(lib, name);
+
+	if (!sym)
+		p_err("%s is not in %s", name, LIBLLVM_SO);
+	return sym;
+}
+
+int disasm_init(void)
+{
+	void (*init)(void);
+	void *lib, *sym;
+	size_t i;
+
+	lib = dlopen(LIBLLVM_SO, RTLD_NOW);
+	if (!lib) {
+		p_err("failed to load %s: %s", LIBLLVM_SO,
+		      dlerror() ?: "unknown error");
+		return -1;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(llvm_funcs); i++) {
+		sym = llvm_sym(lib, llvm_funcs[i].name);
+		if (!sym)
+			return -1;
+		*(void **)llvm_funcs[i].ptr = sym;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(llvm_init_funcs); i++) {
+		init = llvm_sym(lib, llvm_init_funcs[i]);
+		if (!init)
+			return -1;
+		init();
+	}
+	return 0;
+}
+#endif /* LIBLLVM_SO */
+
 static int printf_json(char *s)
 {
 	s = strtok(s, " \t");
@@ -135,6 +218,7 @@ disassemble_insn(disasm_ctx_t *ctx, unsigned char *image, ssize_t len, int pc,
 	return count;
 }

+#ifndef LIBLLVM_SO
 int disasm_init(void)
 {
 	LLVMInitializeAllTargetInfos();
@@ -142,6 +226,7 @@ int disasm_init(void)
 	LLVMInitializeAllDisassemblers();
 	return 0;
 }
+#endif
 #endif /* HAVE_LLVM_SUPPORT */

 #ifdef HAVE_LIBBFD_SUPPORT

Keep patch 1.

Also pls don't respin in 22 minutes only because a bot said something.

pw-bot: cr

      parent reply	other threads:[~2026-10-29 15:24 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-29 13:05 [PATCH bpf-next v3 0/2] bpftool: Make the LLVM disassembler an optional runtime dependency Mark Korenberg via B4 Relay
2026-10-29 13:05 ` [PATCH bpf-next v3 1/2] bpftool: Fix disposal of the LLVM disassembler context Mark Korenberg via B4 Relay
2026-10-29 13:05 ` [PATCH bpf-next v3 2/2] bpftool: Make the LLVM disassembler an optional runtime dependency Mark Korenberg via B4 Relay
2026-10-29 13:46   ` bot+bpf-ci
2026-10-29 15:24   ` Alexei Starovoitov [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=DMHFCN3J36VN.2JJEE30A07C0Z@gmail.com \
    --to=alexei.starovoitov@gmail.com \
    --cc=andrii@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=eddyz87@gmail.com \
    --cc=emil@etsalapatis.com \
    --cc=ihor.solodrai@linux.dev \
    --cc=jolsa@kernel.org \
    --cc=justinstitt@google.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=llvm@lists.linux.dev \
    --cc=martin.lau@linux.dev \
    --cc=memxor@gmail.com \
    --cc=morbo@google.com \
    --cc=nathan@kernel.org \
    --cc=ndesaulniers@google.com \
    --cc=qmo@kernel.org \
    --cc=socketpair@gmail.com \
    --cc=song@kernel.org \
    --cc=yonghong.song@linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox