All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jay Wang <wanjay@amazon.com>
To: <bpf@vger.kernel.org>, Alexei Starovoitov <ast@kernel.org>,
	"Daniel Borkmann" <daniel@iogearbox.net>,
	Andrii Nakryiko <andrii@kernel.org>,
	"Eduard Zingerman" <eddyz87@gmail.com>,
	Kumar Kartikeya Dwivedi <memxor@gmail.com>
Cc: Ihor Solodrai <ihor.solodrai@linux.dev>,
	Alan Maguire <almagui@kernel.org>,
	Martin KaFai Lau <martin.lau@linux.dev>,
	Yonghong Song <yonghong.song@linux.dev>,
	Jiri Olsa <jolsa@kernel.org>,
	Steven Rostedt <rostedt@goodmis.org>,
	Masami Hiramatsu <mhiramat@kernel.org>,
	"Mathieu Desnoyers" <mathieu.desnoyers@efficios.com>,
	<linux-trace-kernel@vger.kernel.org>,
	Nathan Chancellor <nathan@kernel.org>,
	Nicolas Schier <nsc@kernel.org>, <linux-kbuild@vger.kernel.org>,
	"Arnd Bergmann" <arnd@arndb.de>, <linux-arch@vger.kernel.org>,
	<x86@kernel.org>, <linux-doc@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>,
	"Hazem Mohamed Abuelfotoh" <abuehaze@amazon.com>,
	Bjoern Doebel <doebel@amazon.de>,
	"Martin Pohlack" <mpohlack@amazon.de>,
	<jay.wang.upstream@gmail.com>
Subject: [PATCH bpf-next 09/13] bpf: keep the vmlinux BTF zstd-compressed in the kernel image
Date: Sat, 31 Oct 2026 13:09:55 +0000	[thread overview]
Message-ID: <20261031130959.23472-10-wanjay@amazon.com> (raw)
In-Reply-To: <20261031130959.23472-1-wanjay@amazon.com>

The vmlinux BTF is 5-7 MB of read-only data in every kernel built with
CONFIG_DEBUG_INFO_BTF=y, resident whether or not anything on the system
ever uses BTF.  Add CONFIG_DEBUG_INFO_BTF_LAZY to keep it compressed
with zstd in the image instead, at about a quarter of the size, and
decompress it in the kernel when it is first needed.  This is the
transport half of the option; the next two patches defer the
parsing.

kbuild: gen_btf_o() in scripts/gen-btf.sh compresses the final
${ELF}.BTF with zstd -19 and adds it as a second section, .init.BTF.zst,
of the same .btf.o, so neither an extra link pass nor a size
reservation is needed.  .BTF stays in the vmlinux ELF file as a
non-alloc section: pahole --btf_base, resolve_btfids for modules,
bpftool and libbpf's on-disk fallback find it by name and keep working
unchanged, and so do out-of-tree module builds.  The linker script
puts .init.BTF.zst in INIT_DATA, bounded by __start_BTF_zst and
__stop_BTF_zst, and .BTF in ELF_DETAILS at address 0.  The BTF macro
is renamed RO_BTF, because the token BTF in ".BTF 0 : { ... }" would
otherwise be macro-expanded.  x86 strips .BTF from the ELF it puts in
the compressed boot image, and arm64 makes its Image with objcopy
-O binary, which drops non-alloc sections.  The option is limited to
these two architectures for now: several others build their boot
image from a stripped ELF copy of vmlinux, which keeps non-alloc
sections and would carry the uncompressed BTF.  The inline BTF
(CONFIG_DEBUG_INFO_BTF_INLINE) is left as it is: built in, .BTF.inline
stays in the image; as a module it is not in memory anyway.

kernel: btf_vmlinux_data() returns the raw BTF.  With the option it
decompresses the blob once, under a leaf mutex, into vmalloc_user()
memory that is never freed; the size comes from the zstd frame header,
so /sys/kernel/btf/vmlinux gets its size without decompressing.  A
late_initcall_sync copies the blob out of init memory before that is
freed, unless it is decompressed by then, and the copy is freed after
decompression, so a system that uses BTF ends up with the same
footprint as today.  The copy uses vmalloc(): kvmemdup() of the
~1.4 MB blob would get a 2 MB kmalloc_large() allocation.  If the copy
fails, the BTF is unavailable for the rest of the boot: that is logged
once, and later fetches fail with -ENOMEM right away instead of
allocating the raw buffer for nothing each time.  A blob that does not
decompress is logged once and dropped, and fetches fail with -EINVAL
from then on.  btf_parse_vmlinux() and the read and mmap handlers of
/sys/kernel/btf/vmlinux use btf_vmlinux_data(); mmap of the
decompressed copy uses remap_vmalloc_range().  Without the option
btf_vmlinux_data() returns __start_BTF, and the sysfs file keeps its
current handlers.

btf_vmlinux_data() lives in sysfs_btf.c, which is now built whenever
CONFIG_DEBUG_INFO_BTF is enabled, with the sysfs file itself still
under CONFIG_SYSFS: btf_parse_vmlinux() needs the data without sysfs
too, and with the option the blob has to be copied out of init memory
either way.

Reading /sys/kernel/btf/vmlinux decompresses but never parses, and
only vmalloc and zstd run under btf_vmlinux_data_lock, so the sysfs
mmap path (mmap_lock -> kernfs -> btf_vmlinux_data_lock) cannot form
a cycle with module or cand_cache locks.

On its own this patch only changes where the bytes come from: initcall
kfunc registrations still parse the vmlinux BTF at boot.

Measured with a distribution config: the raw .BTF is 5.47 MB on x86_64
and 5.57 MB on arm64, and 1.44 and 1.46 MB with zstd -19.  xz -9e would
save another 0.17 MB but decodes about 6 times slower (72 vs 12 ms in
user space).  Decompression in the kernel takes ~7 ms on x86_64.

Trade-offs: the compressed blob stays resident on systems that never
use BTF (~1.4 MB), so with the next two patches they save about 80% of
what the BTF costs rather than all of it (~6 of ~7.5 MB, the raw BTF
plus what parsing it allocates); the x86 bzImage on disk is ~0.3 MB
smaller; building needs the zstd tool; __start_BTF/__stop_BTF do not
exist with the option (the ksyms and linked_vars selftests use them; a
later patch adapts them).

Co-developed-by: Ihor Solodrai <ihor.solodrai@linux.dev>
TODO-NEEDS-IHOR: Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
Signed-off-by: Jay Wang <wanjay@amazon.com>
---
 arch/x86/boot/compressed/Makefile |   2 +-
 include/asm-generic/vmlinux.lds.h |  24 ++++-
 include/linux/btf.h               |   5 +
 kernel/bpf/Makefile               |   2 +-
 kernel/bpf/btf.c                  |  13 ++-
 kernel/bpf/sysfs_btf.c            | 167 +++++++++++++++++++++++++++++-
 lib/Kconfig.debug                 |  16 +++
 scripts/gen-btf.sh                |  25 +++--
 8 files changed, 232 insertions(+), 22 deletions(-)

diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 680ad803cba7..58d05262dec8 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -116,7 +116,7 @@ endif
 $(obj)/vmlinux: $(vmlinux-objs-y) $(vmlinux-libs-y) FORCE
 	$(call if_changed,ld)
 
-OBJCOPYFLAGS_vmlinux.bin :=  -R .comment -S
+OBJCOPYFLAGS_vmlinux.bin :=  -R .comment $(if $(CONFIG_DEBUG_INFO_BTF_LAZY),-R .BTF) -S
 $(obj)/vmlinux.bin: vmlinux FORCE
 	$(call if_changed,objcopy)
 
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 3eb732942d69..7358aedab95c 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -553,7 +553,7 @@
 									\
 	RO_EXCEPTION_TABLE						\
 	NOTES								\
-	BTF								\
+	RO_BTF								\
 									\
 	. = ALIGN((align));						\
 	__end_rodata = .;
@@ -704,12 +704,24 @@
 #define BTF_INLINE_LINK
 #endif
 
-#ifdef CONFIG_DEBUG_INFO_BTF
-#define BTF								\
+#ifdef CONFIG_DEBUG_INFO_BTF_LAZY
+/* compressed BTF in init data (see sysfs_btf.c), raw BTF not loaded */
+#define BTF_LOADED
+#define BTF_ZST		. = ALIGN(8); BOUNDED_SECTION_BY(.init.BTF.zst, _BTF_zst)
+#define BTF_RAW		.BTF 0 : { *(.BTF) }
+#else
+#define BTF_LOADED							\
 	. = ALIGN(PAGE_SIZE);						\
 	.BTF : AT(ADDR(.BTF) - LOAD_OFFSET) {				\
 		BOUNDED_SECTION_BY(.BTF, _BTF)				\
-	}								\
+	}
+#define BTF_ZST
+#define BTF_RAW
+#endif
+
+#ifdef CONFIG_DEBUG_INFO_BTF
+#define RO_BTF								\
+	BTF_LOADED							\
 	BTF_INLINE							\
 	BTF_INLINE_LINK						\
 	. = ALIGN(PAGE_SIZE);						\
@@ -717,7 +729,7 @@
 		*(.BTF_ids)						\
 	}
 #else
-#define BTF
+#define RO_BTF
 #endif
 
 /*
@@ -748,6 +760,7 @@
 	KERNEL_CTORS()							\
 	MCOUNT_REC()							\
 	*(.init.rodata .init.rodata.*)					\
+	BTF_ZST								\
 	FTRACE_EVENTS()							\
 	TRACE_SYSCALLS()						\
 	KPROBE_BLACKLIST()						\
@@ -880,6 +893,7 @@
 /* Required sections not related to debugging. */
 #define ELF_DETAILS							\
 		.comment 0 : { *(.comment) }				\
+		BTF_RAW							\
 		.symtab 0 : { *(.symtab) }				\
 		.strtab 0 : { *(.strtab) }				\
 		.shstrtab 0 : { *(.shstrtab) }				\
diff --git a/include/linux/btf.h b/include/linux/btf.h
index b66e20a18c54..b6d5a7f59852 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -617,6 +617,11 @@ __u32 *btf_field_iter_next(struct btf_field_iter *it);
 const char *btf_name_by_offset(const struct btf *btf, u32 offset);
 const char *btf_str_by_offset(const struct btf *btf, u32 offset);
 struct btf *btf_parse_vmlinux(void);
+#ifdef CONFIG_DEBUG_INFO_BTF
+void *btf_vmlinux_data(u32 *size);
+#else
+static inline void *btf_vmlinux_data(u32 *size) { return ERR_PTR(-ENOENT); }
+#endif
 struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog);
 u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog);
 int btf_kfunc_check_flag(const struct btf *btf, u32 kfunc_btf_id, u32 flag);
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
index 269810b128c4..4f500927cec8 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -40,8 +40,8 @@ obj-$(CONFIG_CGROUP_BPF) += cgroup.o
 ifeq ($(CONFIG_INET),y)
 obj-$(CONFIG_BPF_SYSCALL) += reuseport_array.o
 endif
-ifeq ($(CONFIG_SYSFS),y)
 obj-$(CONFIG_DEBUG_INFO_BTF) += sysfs_btf.o
+ifeq ($(CONFIG_SYSFS),y)
 obj-$(CONFIG_DEBUG_INFO_BTF_INLINE) += btf_vmlinux_inline.o
 endif
 ifeq ($(CONFIG_BPF_JIT),y)
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 70adc52f5d7c..a7bb780743fb 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -1901,7 +1901,8 @@ static void btf_free(struct btf *btf)
 	kvfree(btf->resolved_sizes);
 	kvfree(btf->resolved_ids);
 	/* vmlinux does not allocate btf->data, it simply points it at
-	 * __start_BTF.
+	 * __start_BTF, or at the decompressed copy with
+	 * CONFIG_DEBUG_INFO_BTF_LAZY, which is never freed either.
 	 */
 	if (!btf_is_vmlinux(btf))
 		kvfree(btf->data);
@@ -6523,8 +6524,6 @@ static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr,
 	return ERR_PTR(err);
 }
 
-extern char __start_BTF[];
-extern char __stop_BTF[];
 extern struct btf *btf_vmlinux;
 
 #define BPF_MAP_TYPE(_id, _ops)
@@ -6900,15 +6899,21 @@ struct btf *btf_parse_vmlinux(void)
 	struct btf_verifier_env *env = NULL;
 	struct bpf_verifier_log *log;
 	struct btf *btf;
+	void *data;
+	u32 size;
 	int err;
 
+	data = btf_vmlinux_data(&size);
+	if (IS_ERR(data))
+		return ERR_CAST(data);
+
 	env = kzalloc_obj(*env, GFP_KERNEL | __GFP_NOWARN);
 	if (!env)
 		return ERR_PTR(-ENOMEM);
 
 	log = &env->log;
 	log->level = BPF_LOG_KERNEL;
-	btf = btf_parse_base(env, "vmlinux", __start_BTF, __stop_BTF - __start_BTF);
+	btf = btf_parse_base(env, "vmlinux", data, size);
 	if (IS_ERR(btf))
 		goto err_out;
 
diff --git a/kernel/bpf/sysfs_btf.c b/kernel/bpf/sysfs_btf.c
index 556fe92c3649..015cd8221154 100644
--- a/kernel/bpf/sysfs_btf.c
+++ b/kernel/bpf/sysfs_btf.c
@@ -12,11 +12,126 @@
 #include <linux/vmalloc.h>
 #include <linux/io.h>
 #include <linux/btf.h>
+#include <linux/zstd.h>
 
+#ifdef CONFIG_DEBUG_INFO_BTF_LAZY
+/* See scripts/gen-btf.sh, gen_btf_o() for details */
+extern char __start_BTF_zst[];
+extern char __stop_BTF_zst[];
+
+static DEFINE_MUTEX(btf_vmlinux_data_lock);
+static void *btf_vmlinux_raw;
+static u32 btf_vmlinux_raw_size;
+static bool btf_vmlinux_broken;
+/* Compressed BTF: init data until btf_vmlinux_keep_zst(), freed once decompressed */
+static void *btf_zst = __start_BTF_zst;
+
+static u32 btf_vmlinux_size(void)
+{
+	zstd_frame_header hdr;
+
+	guard(mutex)(&btf_vmlinux_data_lock);
+	if (!btf_vmlinux_raw_size && btf_zst &&
+	    !zstd_get_frame_header(&hdr, btf_zst, __stop_BTF_zst - __start_BTF_zst) &&
+	    hdr.frameContentSize < U32_MAX)
+		btf_vmlinux_raw_size = hdr.frameContentSize;
+	return btf_vmlinux_raw_size;
+}
+
+static void btf_vmlinux_drop_zst(void)
+{
+	if (btf_zst != __start_BTF_zst)
+		vfree(btf_zst);
+	btf_zst = NULL;
+}
+
+/*
+ * The raw vmlinux BTF, decompressed on first use and never freed, or
+ * ERR_PTR(-ENOMEM) if that cannot be done (now: memory; for good: no copy of
+ * the compressed BTF), or ERR_PTR(-EINVAL) if it is broken.
+ */
+void *btf_vmlinux_data(u32 *size)
+{
+	size_t ret, ws_size = zstd_dctx_workspace_bound();
+	void *raw, *ws;
+
+	/* Pairs with smp_store_release() below */
+	raw = smp_load_acquire(&btf_vmlinux_raw);
+	if (raw) {
+		*size = btf_vmlinux_raw_size;
+		return raw;
+	}
+
+	*size = btf_vmlinux_size();
+	guard(mutex)(&btf_vmlinux_data_lock);
+	if (btf_vmlinux_raw)
+		return btf_vmlinux_raw;
+	if (!btf_zst)
+		return ERR_PTR(btf_vmlinux_broken ? -EINVAL : -ENOMEM);
+	if (!*size) {
+		pr_err("BTF: no size in the compressed vmlinux BTF\n");
+		btf_vmlinux_broken = true;
+		btf_vmlinux_drop_zst();
+		return ERR_PTR(-EINVAL);
+	}
+	/* vmalloc_user(): btf_sysfs_vmlinux_mmap() maps it to user space */
+	raw = vmalloc_user(*size);
+	ws = kvmalloc(ws_size, GFP_KERNEL);
+	if (raw && ws) {
+		ret = zstd_decompress_dctx(zstd_init_dctx(ws, ws_size), raw, *size,
+					   btf_zst, __stop_BTF_zst - __start_BTF_zst);
+		if (!zstd_is_error(ret) && ret == *size) {
+			/* Pairs with smp_load_acquire() above */
+			smp_store_release(&btf_vmlinux_raw, raw);
+		} else {
+			pr_err("BTF: cannot decompress the vmlinux BTF: %s\n",
+			       zstd_is_error(ret) ? zstd_get_error_name(ret) : "size mismatch");
+			btf_vmlinux_broken = true;
+		}
+		btf_vmlinux_drop_zst();
+	}
+	kvfree(ws);
+	if (btf_vmlinux_raw)
+		return btf_vmlinux_raw;
+	vfree(raw);
+	return ERR_PTR(btf_vmlinux_broken ? -EINVAL : -ENOMEM);
+}
+
+/* Keep a copy of the compressed BTF before init memory is freed */
+static int __init btf_vmlinux_keep_zst(void)
+{
+	size_t size = __stop_BTF_zst - __start_BTF_zst;
+
+	guard(mutex)(&btf_vmlinux_data_lock);
+	if (btf_zst) {
+		/* vmalloc(): kmalloc() would round up to a power of 2 */
+		btf_zst = vmalloc(size);
+		if (btf_zst)
+			memcpy(btf_zst, __start_BTF_zst, size);
+		else
+			pr_err("BTF: out of memory, vmlinux BTF unavailable\n");
+	}
+	return 0;
+}
+late_initcall_sync(btf_vmlinux_keep_zst);
+#else
 /* See scripts/link-vmlinux.sh, gen_btf() func for details */
 extern char __start_BTF[];
 extern char __stop_BTF[];
 
+static u32 btf_vmlinux_size(void)
+{
+	return __stop_BTF - __start_BTF;
+}
+
+void *btf_vmlinux_data(u32 *size)
+{
+	*size = btf_vmlinux_size();
+	return __start_BTF;
+}
+#endif
+
+#ifdef CONFIG_SYSFS
 static int btf_sysfs_mmap_check(void *data, size_t size, struct vm_area_struct *vma)
 {
 	unsigned long pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
@@ -61,10 +176,49 @@ static int btf_sysfs_mmap_image(struct file *filp, struct kobject *kobj,
 	return remap_pfn_range(vma, vma->vm_start, pfn, vm_size, vma->vm_page_prot);
 }
 
+#ifdef CONFIG_DEBUG_INFO_BTF_LAZY
+/* Reading or mapping the file decompresses the BTF but does not parse it */
+static int btf_sysfs_vmlinux_mmap(struct file *filp, struct kobject *kobj,
+				  const struct bin_attribute *attr,
+				  struct vm_area_struct *vma)
+{
+	void *data;
+	u32 size;
+	int err;
+
+	data = btf_vmlinux_data(&size);
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	err = btf_sysfs_mmap_check(data, attr->size, vma);
+	if (err)
+		return err;
+
+	return remap_vmalloc_range(vma, data, 0);
+}
+
+static ssize_t btf_sysfs_vmlinux_read(struct file *filp, struct kobject *kobj,
+				      const struct bin_attribute *attr, char *buf,
+				      loff_t off, size_t count)
+{
+	void *data;
+	u32 size;
+
+	data = btf_vmlinux_data(&size);
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	return memory_read_from_buffer(buf, count, &off, data, size);
+}
+#else
+#define btf_sysfs_vmlinux_read	sysfs_bin_attr_simple_read
+#define btf_sysfs_vmlinux_mmap	btf_sysfs_mmap_image
+#endif
+
 static struct bin_attribute bin_attr_btf_vmlinux __ro_after_init = {
 	.attr = { .name = "vmlinux", .mode = 0444, },
-	.read = sysfs_bin_attr_simple_read,
-	.mmap = btf_sysfs_mmap_image,
+	.read = btf_sysfs_vmlinux_read,
+	.mmap = btf_sysfs_vmlinux_mmap,
 };
 
 struct kobject *btf_kobj;
@@ -180,8 +334,12 @@ void sysfs_btf_remove(struct bin_attribute *attr)
 
 static int __init btf_vmlinux_init(void)
 {
-	bin_attr_btf_vmlinux.private = __start_BTF;
-	bin_attr_btf_vmlinux.size = __stop_BTF - __start_BTF;
+	u32 size;
+
+	/* with CONFIG_DEBUG_INFO_BTF_LAZY the read and mmap handlers get it */
+	if (!IS_ENABLED(CONFIG_DEBUG_INFO_BTF_LAZY))
+		bin_attr_btf_vmlinux.private = btf_vmlinux_data(&size);
+	bin_attr_btf_vmlinux.size = btf_vmlinux_size();
 
 	if (bin_attr_btf_vmlinux.size == 0)
 		return 0;
@@ -194,3 +352,4 @@ static int __init btf_vmlinux_init(void)
 }
 
 subsys_initcall(btf_vmlinux_init);
+#endif /* CONFIG_SYSFS */
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 2d22638ec13a..700c4f67bdd8 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -408,6 +408,22 @@ config DEBUG_INFO_BTF
 	  Turning this on requires pahole v1.22 or later, which will convert
 	  DWARF type info into equivalent deduplicated BTF type info.
 
+config DEBUG_INFO_BTF_LAZY
+	bool "Compress vmlinux BTF and parse it on first use"
+	depends on DEBUG_INFO_BTF
+	depends on X86 || ARM64
+	select ZSTD_DECOMPRESS
+	help
+	  Keep the vmlinux BTF zstd-compressed in the kernel image, at about
+	  a quarter of its size, and decompress and parse it the first time
+	  something needs it instead of at boot.  Systems that never use BTF
+	  then keep only the compressed copy in memory, which saves about 80%
+	  of what the BTF costs; the first user pays a few tens of
+	  milliseconds once.  The vmlinux file keeps the uncompressed BTF as
+	  a non-loaded section for tools.  Building needs the zstd tool.
+
+	  If unsure, say N.
+
 config PAHOLE_HAS_BTF_TAG
 	def_bool PAHOLE_VERSION >= 123
 	depends on CC_IS_CLANG
diff --git a/scripts/gen-btf.sh b/scripts/gen-btf.sh
index b17f1189298a..c490b0630612 100755
--- a/scripts/gen-btf.sh
+++ b/scripts/gen-btf.sh
@@ -86,13 +86,24 @@ gen_btf_o()
 	btf_data=${ELF_FILE}.btf.o
 
 	# Create ${btf_data} which contains just BTF sections but no symbols. Add
-	# SHF_ALLOC because .BTF will be part of the vmlinux image. --strip-all
-	# deletes all symbols including __start_BTF and __stop_BTF, which will
-	# be redefined in the linker script.
+	# SHF_ALLOC to the sections that will be part of the vmlinux image.
+	# --strip-all deletes all symbols including __start_BTF and __stop_BTF,
+	# which will be redefined in the linker script.
 	echo "" | ${CC} ${CLANG_FLAGS} ${KBUILD_CPPFLAGS} ${KBUILD_CFLAGS} -fno-lto -c -x c -o ${btf_data} -
-	${OBJCOPY} --add-section .BTF=${ELF_FILE}.BTF \
-		--set-section-flags .BTF=alloc,readonly ${btf_data}
-	ONLY_SEC="--only-section=.BTF"
+	if is_enabled CONFIG_DEBUG_INFO_BTF_LAZY; then
+		# The image gets compressed BTF as init data, .BTF stays in
+		# the ELF file for tools.
+		${ZSTD} -q -f -19 ${ELF_FILE}.BTF -o ${ELF_FILE}.BTF.zst
+		${OBJCOPY} --add-section .BTF=${ELF_FILE}.BTF \
+			--set-section-flags .BTF=readonly \
+			--add-section .init.BTF.zst=${ELF_FILE}.BTF.zst \
+			--set-section-flags .init.BTF.zst=alloc,readonly ${btf_data}
+		ONLY_SEC="--only-section=.BTF --only-section=.init.BTF.zst"
+	else
+		${OBJCOPY} --add-section .BTF=${ELF_FILE}.BTF \
+			--set-section-flags .BTF=alloc,readonly ${btf_data}
+		ONLY_SEC="--only-section=.BTF"
+	fi
 	btf_inline=${ELF_FILE}.BTF.inline
 	if [ "${BTF_INLINE}" = "m" ]; then
 		if [ -f "${btf_inline}" ]; then
@@ -145,7 +156,7 @@ embed_btf_data()
 cleanup()
 {
 	rm -f "${ELF_FILE}.BTF.1"
-	rm -f "${ELF_FILE}.BTF"
+	rm -f "${ELF_FILE}.BTF" "${ELF_FILE}.BTF.zst"
 	rm -f "${ELF_FILE}.BTF.inline"
 	rm -f "${ELF_FILE}.BTF.inline.link"
 	if [ "${BTFGEN_MODE}" = "module" ]; then
-- 
2.47.3


  parent reply	other threads:[~2026-10-31 13:12 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-31 13:09 [PATCH bpf-next 00/13] bpf: keep the vmlinux BTF compressed until first use (CONFIG_DEBUG_INFO_BTF_LAZY) to save ~6 MB memory Jay Wang
2026-10-31 13:09 ` [PATCH bpf-next 01/13] bpf: match program context types by name without vmlinux BTF Jay Wang
2026-10-31 13:43   ` bot+bpf-ci
2026-10-31 13:09 ` [PATCH bpf-next 02/13] bpf: fetch the vmlinux BTF where kernel types enter a program Jay Wang
2026-10-31 13:42   ` sashiko-bot
2026-10-31 13:55   ` bot+bpf-ci
2026-10-31 13:09 ` [PATCH bpf-next 03/13] bpf: check CAP_SYS_ADMIN before looking up bpffs delegation names Jay Wang
2026-10-31 13:43   ` bot+bpf-ci
2026-10-31 17:03   ` Alexei Starovoitov
2026-10-31 13:09 ` [PATCH bpf-next 04/13] bpf: move bpf_get_btf_vmlinux() to btf.c Jay Wang
2026-10-31 13:43   ` bot+bpf-ci
2026-10-31 13:09 ` [PATCH bpf-next 05/13] bpf: let btf_parse_module() adopt the caller's copy of the module BTF Jay Wang
2026-10-31 13:09 ` [PATCH bpf-next 06/13] bpf: look up the struct_ops common value type in the given BTF Jay Wang
2026-10-31 13:09 ` [PATCH bpf-next 07/13] bpf: parse the vmlinux BTF before taking cand_cache_mutex Jay Wang
2026-10-31 13:43   ` bot+bpf-ci
2026-10-31 13:09 ` [PATCH bpf-next 08/13] tracing: parse the vmlinux BTF when BTF-typed output is enabled Jay Wang
2026-10-31 13:37   ` sashiko-bot
2026-10-31 13:43   ` bot+bpf-ci
2026-10-31 13:09 ` Jay Wang [this message]
2026-10-31 13:33   ` [PATCH bpf-next 09/13] bpf: keep the vmlinux BTF zstd-compressed in the kernel image sashiko-bot
2026-10-31 13:55   ` bot+bpf-ci
2026-10-31 13:09 ` [PATCH bpf-next 10/13] bpf: defer kfunc and struct_ops registrations until first BTF use Jay Wang
2026-10-31 13:38   ` sashiko-bot
2026-10-31 13:55   ` bot+bpf-ci
2026-10-31 17:07   ` Alexei Starovoitov
2026-10-31 13:09 ` [PATCH bpf-next 11/13] bpf: keep module BTF until the vmlinux BTF is parsed Jay Wang
2026-10-31 13:37   ` sashiko-bot
2026-10-31 13:55   ` bot+bpf-ci
2026-10-31 17:10     ` Alexei Starovoitov
2026-10-31 13:09 ` [PATCH bpf-next 12/13] selftests/bpf: keep ksyms and linked_vars working without __start_BTF Jay Wang
2026-10-31 13:09 ` [PATCH bpf-next 13/13] Documentation/bpf: describe CONFIG_DEBUG_INFO_BTF_LAZY Jay Wang

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=20261031130959.23472-10-wanjay@amazon.com \
    --to=wanjay@amazon.com \
    --cc=abuehaze@amazon.com \
    --cc=almagui@kernel.org \
    --cc=andrii@kernel.org \
    --cc=arnd@arndb.de \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=doebel@amazon.de \
    --cc=eddyz87@gmail.com \
    --cc=ihor.solodrai@linux.dev \
    --cc=jay.wang.upstream@gmail.com \
    --cc=jolsa@kernel.org \
    --cc=linux-arch@vger.kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kbuild@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-trace-kernel@vger.kernel.org \
    --cc=martin.lau@linux.dev \
    --cc=mathieu.desnoyers@efficios.com \
    --cc=memxor@gmail.com \
    --cc=mhiramat@kernel.org \
    --cc=mpohlack@amazon.de \
    --cc=nathan@kernel.org \
    --cc=nsc@kernel.org \
    --cc=rostedt@goodmis.org \
    --cc=x86@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.