mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Alan Maguire <alan.maguire@oracle.com>
To: Jay Wang <wanjay@amazon.com>,
	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: Martin KaFai Lau <martin.lau@linux.dev>,
	Yonghong Song <yonghong.song@linux.dev>,
	Jiri Olsa <jolsa@kernel.org>,
	Nathan Chancellor <nathan@kernel.org>,
	Nicolas Schier <nsc@kernel.org>,
	linux-kbuild@vger.kernel.org,
	Luis Chamberlain <mcgrof@kernel.org>,
	Petr Pavlu <petr.pavlu@suse.com>,
	Sami Tolvanen <samitolvanen@google.com>,
	linux-modules@vger.kernel.org, Miguel Ojeda <ojeda@kernel.org>,
	rust-for-linux@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>,
	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: Re: [PATCH bpf-next v3 9/9] kbuild, bpf: allow building the vmlinux BTF as a module
Date: Mon, 28 Sep 2026 11:00:17 +0100	[thread overview]
Message-ID: <de28ce03-67c3-4a03-b452-80b92cdfd243@oracle.com> (raw)
In-Reply-To: <20260925224229.1850-10-wanjay@amazon.com>

On 25/09/2026 23:42, Jay Wang wrote:
> 
> Make CONFIG_DEBUG_INFO_BTF a tristate.  With =m the vmlinux BTF is not
> part of the kernel image: it is carried by a new module, btf_vmlinux, and
> loaded the first time something needs it.  Nothing that works with =y
> stops working; the 5.4 MiB of read-only data (distribution config) is
> simply not there on systems where nothing uses it.
> 
> The only way to save that memory today is CONFIG_DEBUG_INFO_BTF=n, which
> a distribution cannot ship: one binary goes to every user, and off takes
> BTF away from the users of CO-RE, fentry/fexit, kfuncs, struct_ops,
> sched_ext or bpf-lsm.  Whether BTF is used is a property of the
> workload, not of the build, so let the first user decide.
> 
> The BTF is generated as before, but with =m the .BTF section is linked
> as a non-loadable section (like .comment), so the kernel image does not
> load it, and the final step that makes vmlinux from vmlinux.unstripped
> strips it: every boot image made from vmlinux, whether a raw binary or
> an ELF copy, is without it.  Module BTF is generated against
> vmlinux.unstripped, which keeps it.  .BTF_ids stays loadable, the
> verifier needs it once the BTF is loaded.  The object that carries .BTF
> also carries .BTF.meta, the size and SHA-256 of the BTF (struct
> btf_vmlinux_meta, checked by the module notifier); the first link, which
> the BTF is generated from, gets a zeroed .BTF.meta of the same size.
> kernel/bpf/btf_vmlinux.c is an empty carrier module; scripts/gen-btf.sh
> gives it the vmlinux .BTF as its own .BTF section instead of generating
> split BTF for it, so that one module depends on vmlinux with =m; with
> CONFIG_DEBUG_INFO_BTF_MODULES all of them do, as before.
> 
> CONFIG_BPF_PRELOAD is not selectable with =m: its iterator programs
> attach through the vmlinux BTF, so every bpffs mount (systemd does one
> at boot) would load it and defeat the point.  Module BTF is still kept
> when a module loads, as with =y; the saving is the vmlinux BTF only.
> Programs that need kernel types before the root file system is mounted
> need btf_vmlinux.ko in the initramfs; the Kconfig help says so.
> 
> The module has no exit: once loaded the BTF stays, as with =y.  The
> runtime side -- loading the module on first use, checking it against
> .BTF.meta, deferring kfunc and struct_ops registrations and module BTF
> until it arrives -- and the IS_ENABLED()/$(subst m,y,...) preparation of
> the existing checks are in the preceding patches; this one makes it
> selectable.
> 


A few thoughts here; .BTF.meta seems too vague as a name. This is very
like .gnu_debuglink ; should we call it .BTF.link ? It also could be
nicely extended to .BTF.inline (.BTF.inlink.link) where inline info
is delivered as a module.

I also wonder if the right place to populate the section is in
the script? We might run into endian issues etc; why not do it
as part of resolve_btfids? Pass a --btf_link parameter or similar,
and resolve_btfids (which has access to BTF size after carrying
out various operations) can add it and the SHA. resolve_btfids is
already updating other BTF-related sections so seems like a natural
home for this.


> Tested with 1 GiB of memory, same tree, =y vs =m, both with
> CONFIG_DEBUG_INFO_BTF_MODULES=y:
> 
>  - MemTotal is ~5.4 MB higher with =m while the BTF is unused: the size
>    of the .BTF section.
>  - stat() of /sys/kernel/btf/vmlinux reports the BTF size before it is
>    loaded, as the btf_sysfs selftest expects.
>  - With BTF in use, MemFree is the same within run-to-run noise.
>  - Modules loaded before the trigger (ext4, nf_conntrack and its kfuncs,
>    xfrm_interface) appear in /sys/kernel/btf immediately and get BTF ids
>    once the BTF is loaded; a socket filter loads without loading the
>    module; a kprobe program calling bpf_get_current_task_btf(), opening
>    /sys/kernel/btf/vmlinux or BPF_BTF_GET_NEXT_ID each load it.
>  - A carrier module with one byte of its .BTF changed is refused with
>    "BTF does not match this kernel" and leaves no state behind.
>  - After the load: fstat/read/mmap of /sys/kernel/btf/vmlinux, a
>    struct_ops map for tcp_congestion_ops, a syscall program calling the
>    bpf_task_from_pid()/bpf_task_release() kfuncs, and modules loaded
>    afterwards (nf_nat) all work as with =y.
>  - =m without DEBUG_INFO_BTF_MODULES, and =y, build and pass the same
>    tests.
> 
> Signed-off-by: Jay Wang <wanjay@amazon.com>
> ---
>  Documentation/bpf/btf.rst         | 35 ++++++++++++
>  Makefile                          |  5 +-
>  include/asm-generic/vmlinux.lds.h | 31 +++++++++-
>  kernel/bpf/Makefile               |  4 ++
>  kernel/bpf/btf_vmlinux.c          | 23 ++++++++
>  kernel/bpf/preload/Kconfig        |  4 ++
>  lib/Kconfig.debug                 | 22 ++++++-
>  scripts/Makefile.modfinal         | 26 +++++++--
>  scripts/Makefile.vmlinux          |  5 ++
>  scripts/gen-btf.sh                | 95 +++++++++++++++++++++++++++++--
>  scripts/link-vmlinux.sh           | 25 ++++++--
>  11 files changed, 257 insertions(+), 18 deletions(-)
>  create mode 100644 kernel/bpf/btf_vmlinux.c
> 
> diff --git a/Documentation/bpf/btf.rst b/Documentation/bpf/btf.rst
> index 29de1222c3e7..1bd0d35cdb05 100644
> --- a/Documentation/bpf/btf.rst
> +++ b/Documentation/bpf/btf.rst
> @@ -1276,6 +1276,41 @@ format.::
>              .long   58
>              .long   8206                    # Line 8 Col 14
>  
> +6.1 Kernel BTF
> +--------------
> +
> +With CONFIG_DEBUG_INFO_BTF=y the BTF of the kernel is generated at link time
> +from its DWARF and placed in the .BTF section of vmlinux, which is read-only
> +data of the kernel image. It is available as /sys/kernel/btf/vmlinux and, if
> +CONFIG_DEBUG_INFO_BTF_MODULES is set, module BTF is generated as split BTF
> +against it and available as /sys/kernel/btf/<module>.
> +
> +With CONFIG_DEBUG_INFO_BTF=m the same BTF is generated, but it is not part of
> +the kernel image or of the vmlinux ELF file (vmlinux.unstripped in the build
> +tree keeps it, for module BTF generation). It is delivered by the
> +btf_vmlinux module, which the kernel loads on demand the first time the BTF is
> +needed: when /sys/kernel/btf/vmlinux is read or mmap()ed, when kernel BTF
> +objects are enumerated (BPF_BTF_GET_NEXT_ID), or when a BPF program needs
> +kernel type information (an attach_btf_id, a kfunc call, a ksym, a map pointer
> +or a helper that takes or returns a kernel BTF pointer). Until then no memory
> +is used for it, and afterwards nothing differs from =y. In particular:
> +
> +  * /sys/kernel/btf/vmlinux exists from boot with its final size.
> +  * Modules loaded before the vmlinux BTF are exposed in /sys/kernel/btf right
> +    away, their BTF is parsed and gets a BTF id once the vmlinux BTF is
> +    loaded, together with their kfunc and struct_ops registrations.
> +  * kfunc, dtor kfunc and struct_ops registrations of the kernel itself are
> +    applied before the BTF becomes visible.
> +  * The kernel only accepts the BTF it was built with: the size and SHA-256 of
> +    the BTF are linked into the kernel and checked against the module.
> +  * Once loaded the BTF stays; the module cannot be unloaded.
> +
> +If the module is not available (not installed, or the root file system is not
> +mounted yet), the kernel behaves as one built without BTF and retries next
> +time. CONFIG_BPF_PRELOAD is not available with =m: its iterators attach through
> +the vmlinux BTF, so mounting bpffs would load it. bpf_snprintf_btf() and bpf_seq_printf_btf() only use the BTF if it has
> +already been parsed, as they run in program context.
> +
>  7. Testing
>  ==========
>  
> diff --git a/Makefile b/Makefile
> index f561516e1735..7ce5d478abd3 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -1745,8 +1745,9 @@ endif
>  #
>  
>  # *.ko are usually independent of vmlinux, but CONFIG_DEBUG_INFO_BTF_MODULES
> -# is an exception.
> -ifdef CONFIG_DEBUG_INFO_BTF_MODULES
> +# is an exception, and so is the btf_vmlinux module with CONFIG_DEBUG_INFO_BTF=m,
> +# which carries the vmlinux BTF.
> +ifneq ($(CONFIG_DEBUG_INFO_BTF_MODULES)$(filter m,$(CONFIG_DEBUG_INFO_BTF)),)
>  KBUILD_BUILTIN := y
>  modules: vmlinux
>  endif
> diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
> index b2988aa12f66..204a9ee171c6 100644
> --- a/include/asm-generic/vmlinux.lds.h
> +++ b/include/asm-generic/vmlinux.lds.h
> @@ -674,8 +674,18 @@
>  
>  /*
>   * .BTF
> + *
> + * With CONFIG_DEBUG_INFO_BTF=y the vmlinux BTF is loaded as read-only data and
> + * bounded by __start_BTF/__stop_BTF.  With CONFIG_DEBUG_INFO_BTF=m it is
> + * linked as a non-loadable section (see BTF_NOLOAD in ELF_DETAILS), so that
> + * module BTF generation can read it from vmlinux.unstripped; it is stripped
> + * from vmlinux (scripts/Makefile.vmlinux), and the btf_vmlinux module carries
> + * a copy and provides it on demand at runtime.
> + * What is loaded instead is .BTF.meta, the size and hash of that BTF (see
> + * scripts/gen-btf.sh), empty in the first link that the BTF is generated
> + * from.  .BTF_ids is needed by the kernel in both cases.
>   */
> -#ifdef CONFIG_DEBUG_INFO_BTF
> +#if IS_BUILTIN(CONFIG_DEBUG_INFO_BTF)
>  #define BTF								\
>  	. = ALIGN(PAGE_SIZE);						\
>  	.BTF : AT(ADDR(.BTF) - LOAD_OFFSET) {				\
> @@ -685,10 +695,28 @@
>  	.BTF_ids : AT(ADDR(.BTF_ids) - LOAD_OFFSET) {			\
>  		*(.BTF_ids)						\
>  	}
> +#elif IS_MODULE(CONFIG_DEBUG_INFO_BTF)
> +#define BTF								\
> +	. = ALIGN(8);							\
> +	.BTF.meta : AT(ADDR(.BTF.meta) - LOAD_OFFSET) {			\
> +		BOUNDED_SECTION_BY(.BTF.meta, _BTF_meta)		\
> +	}								\
> +	. = ALIGN(PAGE_SIZE);						\
> +	.BTF_ids : AT(ADDR(.BTF_ids) - LOAD_OFFSET) {			\
> +		*(.BTF_ids)						\
> +	}
>  #else
>  #define BTF
>  #endif
>  
> +#if IS_MODULE(CONFIG_DEBUG_INFO_BTF)
> +/* quoted: BTF is a macro, an unquoted .BTF here would expand it */
> +#define BTF_NOLOAD							\
> +		".BTF" 0 : { *(".BTF") }
> +#else
> +#define BTF_NOLOAD
> +#endif
> +
>  /*
>   * Init task
>   */
> @@ -849,6 +877,7 @@
>  /* Required sections not related to debugging. */
>  #define ELF_DETAILS							\
>  		.comment 0 : { *(.comment) }				\
> +		BTF_NOLOAD						\
>  		.symtab 0 : { *(.symtab) }				\
>  		.strtab 0 : { *(.strtab) }				\
>  		.shstrtab 0 : { *(.shstrtab) }				\
> diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
> index 0b7db88f1bed..8a7ad4303c0d 100644
> --- a/kernel/bpf/Makefile
> +++ b/kernel/bpf/Makefile
> @@ -43,6 +43,10 @@ endif
>  ifeq ($(CONFIG_SYSFS),y)
>  obj-$(subst m,y,$(CONFIG_DEBUG_INFO_BTF)) += sysfs_btf.o
>  endif
> +# With CONFIG_DEBUG_INFO_BTF=m the vmlinux BTF is carried by this module
> +ifeq ($(CONFIG_DEBUG_INFO_BTF),m)
> +obj-m += btf_vmlinux.o
> +endif
>  ifeq ($(CONFIG_BPF_JIT),y)
>  obj-$(CONFIG_BPF_SYSCALL) += bpf_struct_ops.o
>  obj-$(CONFIG_BPF_SYSCALL) += cpumask.o
> diff --git a/kernel/bpf/btf_vmlinux.c b/kernel/bpf/btf_vmlinux.c
> new file mode 100644
> index 000000000000..8d89b4bb3c43
> --- /dev/null
> +++ b/kernel/bpf/btf_vmlinux.c
> @@ -0,0 +1,23 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Carrier module for the vmlinux BTF when CONFIG_DEBUG_INFO_BTF=m.
> + *
> + * This module has no code of its own.  Its .BTF section is a copy of the
> + * vmlinux BTF (see scripts/gen-btf.sh), which the BTF module notifier in
> + * kernel/bpf/btf.c recognizes by module name and installs as the vmlinux BTF.
> + * The kernel loads it on demand, the first time the vmlinux BTF is needed.
> + *
> + * There is deliberately no module_exit(): once the BTF is in use it cannot
> + * be taken away again, exactly as with CONFIG_DEBUG_INFO_BTF=y.
> + */
> +#include <linux/init.h>
> +#include <linux/module.h>
> +
> +static int __init btf_vmlinux_init(void)
> +{
> +	return 0;
> +}
> +module_init(btf_vmlinux_init);
> +
> +MODULE_DESCRIPTION("BTF type information for vmlinux");
> +MODULE_LICENSE("GPL");
> diff --git a/kernel/bpf/preload/Kconfig b/kernel/bpf/preload/Kconfig
> index aef7b0bc96d6..b1600bdce7a0 100644
> --- a/kernel/bpf/preload/Kconfig
> +++ b/kernel/bpf/preload/Kconfig
> @@ -6,6 +6,10 @@ menuconfig BPF_PRELOAD
>  	# The dependency on !COMPILE_TEST prevents it from being enabled
>  	# in allmodconfig or allyesconfig configurations
>  	depends on !COMPILE_TEST
> +	# The preloaded iterators attach through the vmlinux BTF, so with
> +	# CONFIG_DEBUG_INFO_BTF=m every bpffs mount would load the BTF, which
> +	# defeats the point of =m on any system that mounts bpffs at boot.
> +	depends on DEBUG_INFO_BTF!=m
>  	help
>  	  This builds kernel module with several embedded BPF programs that are
>  	  pinned into BPF FS mount point as human readable files that are
> diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
> index 134b15a44625..418025c5e657 100644
> --- a/lib/Kconfig.debug
> +++ b/lib/Kconfig.debug
> @@ -396,7 +396,7 @@ config DEBUG_INFO_SPLIT
>  	  Incompatible with older versions of ccache.
>  
>  config DEBUG_INFO_BTF
> -	bool "Generate BTF type information"
> +	tristate "Generate BTF type information"
>  	depends on !DEBUG_INFO_SPLIT && !DEBUG_INFO_REDUCED
>  	depends on !GCC_PLUGIN_RANDSTRUCT || COMPILE_TEST
>  	depends on BPF_SYSCALL
> @@ -408,6 +408,26 @@ 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.
>  
> +	  If built as a module (=m), the vmlinux BTF is not part of the
> +	  kernel image.  It is carried by the btf_vmlinux module, which is
> +	  loaded on demand the first time the BTF is needed: when a BPF
> +	  program requires kernel type information, or when
> +	  /sys/kernel/btf/vmlinux is opened.  Until then, no memory is
> +	  spent on it.  The vmlinux ELF file does not carry the BTF
> +	  either; module BTF is generated against vmlinux.unstripped, and
> +	  tools that read the BTF from a file can use that or
> +	  /sys/kernel/btf/vmlinux.
> +
> +	  Module BTF (DEBUG_INFO_BTF_MODULES) is kept when a module loads,
> +	  as with =y, and registered once the vmlinux BTF is available; the
> +	  saving is the vmlinux BTF only.
> +
> +	  If BPF programs that use kernel types run before the root file
> +	  system is mounted, put btf_vmlinux.ko into the initramfs: until
> +	  the module can be loaded, such programs fail as on a kernel
> +	  without BTF.  Not compatible with BPF_PRELOAD, whose iterators
> +	  would load the BTF at every bpffs mount.
> +
>  config PAHOLE_HAS_BTF_TAG
>  	def_bool PAHOLE_VERSION >= 123
>  	depends on CC_IS_CLANG
> diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
> index 01a37ec872b9..6a0958cf0e6f 100644
> --- a/scripts/Makefile.modfinal
> +++ b/scripts/Makefile.modfinal
> @@ -38,20 +38,34 @@ quiet_cmd_ld_ko_o = LD [M]  $@
>  		$(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE)		\
>  		-T $(objtree)/scripts/module.lds -o $@ $(filter %.o, $^)
>  
> +# The ELF file with the vmlinux BTF: with CONFIG_DEBUG_INFO_BTF=m the BTF is
> +# stripped from vmlinux (scripts/Makefile.vmlinux), vmlinux.unstripped keeps it.
> +btf-vmlinux := $(objtree)/vmlinux$(if $(filter m,$(CONFIG_DEBUG_INFO_BTF)),.unstripped)
> +
>  quiet_cmd_btf_ko = BTF [M] $@
>        cmd_btf_ko = 							\
> -	if [ ! -f $(objtree)/vmlinux ]; then				\
> +	if [ ! -f $(btf-vmlinux) ]; then				\
>  		printf "Skipping BTF generation for %s due to unavailability of vmlinux\n" $@ 1>&2; \
>  	else	\
> -		$(CONFIG_SHELL) $(srctree)/scripts/gen-btf.sh --btf_base $(objtree)/vmlinux $@; \
> +		$(CONFIG_SHELL) $(srctree)/scripts/gen-btf.sh --btf_base $(btf-vmlinux) $@; \
>  	fi;
>  
> -# Re-generate module BTFs if either module's .ko or vmlinux changed
> -%.ko: %.o %.mod.o .module-common.o $(objtree)/scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),$(objtree)/vmlinux) FORCE
> -	+$(call if_changed,ld_ko_o)
> +# Modules that get a .BTF section: all of them with CONFIG_DEBUG_INFO_BTF_MODULES,
> +# otherwise only the vmlinux BTF carrier module with CONFIG_DEBUG_INFO_BTF=m.
>  ifdef CONFIG_DEBUG_INFO_BTF_MODULES
> -	+$(if $(newer-prereqs),$(call cmd,btf_ko))
> +btf-modules := $(modules:%.o=%.ko)
> +else ifeq ($(CONFIG_DEBUG_INFO_BTF),m)
> +btf-modules := $(filter %/btf_vmlinux.ko,$(modules:%.o=%.ko))
> +# Only the carrier depends on vmlinux, not every module
> +ifdef KBUILD_BUILTIN
> +$(btf-modules): $(btf-vmlinux)
> +endif
>  endif
> +
> +# Re-generate module BTFs if either module's .ko or vmlinux changed
> +%.ko: %.o %.mod.o .module-common.o $(objtree)/scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),$(btf-vmlinux)) FORCE
> +	+$(call if_changed,ld_ko_o)
> +	+$(if $(and $(filter $@,$(btf-modules)),$(newer-prereqs)),$(call cmd,btf_ko))
>  	+$(call cmd,check_tracepoint)
>  
>  targets += $(modules:%.o=%.ko) $(modules:%.o=%.mod.o) .module-common.o
> diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
> index fcae1e432d9a..557db1ee1f3b 100644
> --- a/scripts/Makefile.vmlinux
> +++ b/scripts/Makefile.vmlinux
> @@ -86,6 +86,11 @@ remove-section-$(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS) += '.rel*' '!.rel*.dyn'
>  # for compatibility with binutils < 2.32
>  # https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=c12d9fa2afe7abcbe407a00e15719e1a1350c2a7
>  remove-section-$(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS) += '.rel.*'
> +# With CONFIG_DEBUG_INFO_BTF=m the btf_vmlinux module carries the vmlinux BTF;
> +# only vmlinux.unstripped keeps it, for module BTF generation.
> +ifeq ($(CONFIG_DEBUG_INFO_BTF),m)
> +remove-section-y += .BTF
> +endif
>  
>  remove-symbols := -w --strip-unneeded-symbol='__mod_device_table__*'
>  
> diff --git a/scripts/gen-btf.sh b/scripts/gen-btf.sh
> index 8ca96eb10a69..d808d0006d32 100755
> --- a/scripts/gen-btf.sh
> +++ b/scripts/gen-btf.sh
> @@ -22,16 +22,27 @@
>  #   - ${1}.btf.o ready for linking into vmlinux
>  #   - ${1}.BTF_ids with .BTF_ids data blob
>  # This output is consumed by scripts/link-vmlinux.sh
> +#
> +# With CONFIG_DEBUG_INFO_BTF=m the .BTF section in ${1}.btf.o is not
> +# allocatable, so the kernel image does not carry the BTF; vmlinux.unstripped
> +# does, for module BTF generation, and scripts/Makefile.vmlinux strips it from
> +# vmlinux.  ${1}.btf.o then also carries .BTF.meta, the size and SHA-256 of
> +# the BTF for the kernel (struct btf_vmlinux_meta); "--placeholder ${1}"
> +# produces a ${1}.btf.o with a zeroed .BTF.meta and no .BTF for the first
> +# vmlinux link, which the BTF is generated from.  The btf_vmlinux module gets
> +# no BTF of its own; its .BTF section is a copy of the vmlinux BTF, extracted
> +# from --btf_base.
>  
>  set -e
>  
>  usage()
>  {
> -	echo "Usage: $0 [--btf_base <file>] <target ELF file>"
> +	echo "Usage: $0 [--btf_base <file>] [--placeholder] <target ELF file>"
>  	exit 1
>  }
>  
>  BTF_BASE=""
> +PLACEHOLDER=""
>  
>  while [ $# -gt 0 ]; do
>  	case "$1" in
> @@ -39,6 +50,10 @@ while [ $# -gt 0 ]; do
>  		BTF_BASE="$2"
>  		shift 2
>  		;;
> +	--placeholder)
> +		PLACEHOLDER=1
> +		shift
> +		;;
>  	-*)
>  		echo "Unknown option: $1" >&2
>  		usage
> @@ -60,6 +75,10 @@ is_enabled() {
>  	grep -q "^$1=y" ${objtree}/include/config/auto.conf
>  }
>  
> +is_module() {
> +	grep -q "^$1=m" ${objtree}/include/config/auto.conf
> +}
> +
>  case "${KBUILD_VERBOSE}" in
>  *1*)
>  	set -x
> @@ -79,6 +98,30 @@ gen_btf_data()
>  		--btf ${btf1} "${ELF_FILE}"
>  }
>  
> +# Write one byte with value $1 (0..255)
> +put_byte()
> +{
> +	printf "\\$(printf '%03o' "$1")"
> +}
> +
> +# CONFIG_DEBUG_INFO_BTF=m: write struct btf_vmlinux_meta { u32 size; u8
> +# sha256[32]; } for the BTF in $1 to $2, in the target's byte order.
> +gen_btf_meta()
> +{
> +	size=$(${CONFIG_SHELL} "${srctree}/scripts/file-size.sh" "$1")
> +	sha256=$(sha256sum < "$1" | cut -d' ' -f1)
> +	{
> +		if is_enabled CONFIG_CPU_BIG_ENDIAN; then
> +			for shift in 24 16 8 0; do put_byte $(( (size >> shift) & 255 )); done
> +		else
> +			for shift in 0 8 16 24; do put_byte $(( (size >> shift) & 255 )); done
> +		fi
> +		for byte in $(echo "${sha256}" | sed 's/../& /g'); do
> +			put_byte $(( 0x${byte} ))
> +		done
> +	} > "$2"
> +}
> +
>  gen_btf_o()
>  {
>  	btf_data=${ELF_FILE}.btf.o
> @@ -88,9 +131,23 @@ gen_btf_o()
>  	# 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}
> -	${OBJCOPY} --only-section=.BTF --strip-all ${btf_data}
> +	if is_module CONFIG_DEBUG_INFO_BTF; then
> +		# CONFIG_DEBUG_INFO_BTF=m: .BTF stays non-allocatable, kept in
> +		# vmlinux.unstripped for module BTF but not loaded; the btf_vmlinux
> +		# module provides it at runtime.  What is loaded is .BTF.meta,
> +		# its size and hash, so that /sys/kernel/btf/vmlinux has the right
> +		# size from boot and only the matching BTF is accepted.
> +		gen_btf_meta ${ELF_FILE}.BTF ${ELF_FILE}.BTF.meta
> +		${OBJCOPY} --add-section .BTF=${ELF_FILE}.BTF \
> +			--set-section-flags .BTF=readonly \
> +			--add-section .BTF.meta=${ELF_FILE}.BTF.meta \
> +			--set-section-flags .BTF.meta=alloc,readonly ${btf_data}
> +		${OBJCOPY} --only-section=.BTF --only-section=.BTF.meta --strip-all ${btf_data}
> +	else
> +		${OBJCOPY} --add-section .BTF=${ELF_FILE}.BTF \
> +			--set-section-flags .BTF=alloc,readonly ${btf_data}
> +		${OBJCOPY} --only-section=.BTF --strip-all ${btf_data}
> +	fi
>  
>  	# Change e_type to ET_REL so that it can be used to link final vmlinux.
>  	# GNU ld 2.35+ and lld do not allow an ET_EXEC input.
> @@ -121,6 +178,7 @@ cleanup()
>  {
>  	rm -f "${ELF_FILE}.BTF.1"
>  	rm -f "${ELF_FILE}.BTF"
> +	rm -f "${ELF_FILE}.BTF.meta"
>  	if [ "${BTFGEN_MODE}" = "module" ]; then
>  		rm -f "${ELF_FILE}.BTF.base"
>  		rm -f "${ELF_FILE}.BTF_ids"
> @@ -133,6 +191,35 @@ if [ -n "${BTF_BASE}" ]; then
>  	BTFGEN_MODE="module"
>  fi
>  
> +if [ -n "${PLACEHOLDER}" ]; then
> +	btf_data=${ELF_FILE}.btf.o
> +	echo "" | ${CC} ${CLANG_FLAGS} ${KBUILD_CPPFLAGS} ${KBUILD_CFLAGS} -fno-lto -c -x c -o ${btf_data} -
> +	dd if=/dev/zero of=${ELF_FILE}.BTF.meta bs=36 count=1 2>/dev/null
> +	${OBJCOPY} --add-section .BTF.meta=${ELF_FILE}.BTF.meta \
> +		--set-section-flags .BTF.meta=alloc,readonly ${btf_data}
> +	${OBJCOPY} --only-section=.BTF.meta --strip-all ${btf_data}
> +	exit 0
> +fi
> +
> +# CONFIG_DEBUG_INFO_BTF=m: the btf_vmlinux module carries the vmlinux BTF
> +# itself.  Its own types are of no interest, so instead of generating split
> +# BTF for it, copy the (non-loadable) .BTF section of --btf_base
> +# (vmlinux.unstripped) into the module.
> +# The kernel recognizes the module by name and treats its .BTF as base BTF.
> +case "${BTFGEN_MODE}:${ELF_FILE}" in
> +module:*/btf_vmlinux.ko)
> +	if is_module CONFIG_DEBUG_INFO_BTF; then
> +		# -O binary only emits allocatable sections; make .BTF one for
> +		# the extraction.  ${BTF_BASE} itself is not modified.
> +		${OBJCOPY} -O binary --only-section=.BTF			\
> +			--set-section-flags .BTF=alloc,load,readonly	\
> +			"${BTF_BASE}" "${ELF_FILE}.BTF"
> +		${OBJCOPY} --add-section .BTF="${ELF_FILE}.BTF" "${ELF_FILE}"
> +		exit 0
> +	fi
> +	;;
> +esac
> +
>  gen_btf_data
>  
>  case "${BTFGEN_MODE}" in
> diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
> index ab0b8125c8cb..a9a066267ef6 100755
> --- a/scripts/link-vmlinux.sh
> +++ b/scripts/link-vmlinux.sh
> @@ -37,6 +37,15 @@ is_enabled() {
>  	grep -q "^$1=y" include/config/auto.conf
>  }
>  
> +is_module() {
> +	grep -q "^$1=m" include/config/auto.conf
> +}
> +
> +# =y or =m
> +is_set() {
> +	grep -q "^$1=[ym]" include/config/auto.conf
> +}
> +
>  # Nice output in kbuild format
>  # Will be suppressed by "make -s"
>  info()
> @@ -211,17 +220,25 @@ if is_enabled CONFIG_KALLSYMS; then
>  	kallsyms .tmp_vmlinux0.syms .tmp_vmlinux0.kallsyms
>  fi
>  
> -if is_enabled CONFIG_KALLSYMS || is_enabled CONFIG_DEBUG_INFO_BTF; then
> +if is_module CONFIG_DEBUG_INFO_BTF; then
> +	# The kernel refers to the size and hash of its BTF, which only the
> +	# BTF generated from the first link can provide; link a placeholder
> +	# of the same layout until then.
> +	${CONFIG_SHELL} ${srctree}/scripts/gen-btf.sh --placeholder .tmp_vmlinux0
> +	btf_vmlinux_bin_o=.tmp_vmlinux0.btf.o
> +fi
> +
> +if is_enabled CONFIG_KALLSYMS || is_set CONFIG_DEBUG_INFO_BTF; then
>  
>  	# The kallsyms linking does not need debug symbols, but the BTF does.
> -	if ! is_enabled CONFIG_DEBUG_INFO_BTF; then
> +	if ! is_set CONFIG_DEBUG_INFO_BTF; then
>  		strip_debug=1
>  	fi
>  
>  	vmlinux_link .tmp_vmlinux1
>  fi
>  
> -if is_enabled CONFIG_DEBUG_INFO_BTF; then
> +if is_set CONFIG_DEBUG_INFO_BTF; then
>  	info BTF .tmp_vmlinux1
>  	if ! ${CONFIG_SHELL} ${srctree}/scripts/gen-btf.sh .tmp_vmlinux1; then
>  		echo >&2 "Failed to generate BTF for vmlinux"
> @@ -287,7 +304,7 @@ fi
>  
>  vmlinux_link "${VMLINUX}"
>  
> -if is_enabled CONFIG_DEBUG_INFO_BTF; then
> +if is_set CONFIG_DEBUG_INFO_BTF; then
>  	info BTFIDS ${VMLINUX}
>  	${RESOLVE_BTFIDS} --patch_btfids ${btfids_vmlinux} ${VMLINUX}
>  fi
> -- 
> 2.47.3
> 


      parent reply	other threads:[~2026-09-28 10:01 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-25 22:42 [PATCH bpf-next v3 0/9] bpf: make the vmlinux BTF an on-demand loadable module (CONFIG_DEBUG_INFO_BTF=m) to save ~5.4 MB memory Jay Wang
2026-09-25 22:42 ` [PATCH bpf-next v3 1/9] bpf: pass the vmlinux BTF to btf_parse_module() and let it adopt the data Jay Wang
2026-09-25 22:42 ` [PATCH bpf-next v3 2/9] bpf: split the kfunc, dtor kfunc and struct_ops registration bodies Jay Wang
2026-09-25 22:42 ` [PATCH bpf-next v3 3/9] bpf: fetch the vmlinux BTF where kernel types enter a program Jay Wang
2026-09-25 22:42 ` [PATCH bpf-next v3 4/9] bpf: take the vmlinux BTF from the btf_vmlinux module Jay Wang
2026-09-25 23:23   ` bot+bpf-ci
2026-09-26  8:29   ` Alexei Starovoitov
2026-09-25 22:42 ` [PATCH bpf-next v3 5/9] bpf: defer vmlinux kfunc and struct_ops registrations Jay Wang
2026-09-25 23:34   ` bot+bpf-ci
2026-09-25 22:42 ` [PATCH bpf-next v3 6/9] bpf: keep module BTF until the vmlinux BTF is available Jay Wang
2026-09-25 22:42 ` [PATCH bpf-next v3 7/9] bpf: expose deferred .BTF.base module BTF in sysfs from module load Jay Wang
2026-09-25 23:23   ` bot+bpf-ci
2026-09-25 22:42 ` [PATCH bpf-next v3 8/9] bpf, trace, net: prepare CONFIG_DEBUG_INFO_BTF checks for a tristate Jay Wang
2026-09-25 23:23   ` bot+bpf-ci
2026-09-25 22:42 ` [PATCH bpf-next v3 9/9] kbuild, bpf: allow building the vmlinux BTF as a module Jay Wang
2026-09-25 23:34   ` bot+bpf-ci
2026-09-28 10:00   ` Alan Maguire [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=de28ce03-67c3-4a03-b452-80b92cdfd243@oracle.com \
    --to=alan.maguire@oracle.com \
    --cc=abuehaze@amazon.com \
    --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=jay.wang.upstream@gmail.com \
    --cc=jolsa@kernel.org \
    --cc=linux-kbuild@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-modules@vger.kernel.org \
    --cc=martin.lau@linux.dev \
    --cc=mcgrof@kernel.org \
    --cc=memxor@gmail.com \
    --cc=mpohlack@amazon.de \
    --cc=nathan@kernel.org \
    --cc=nsc@kernel.org \
    --cc=ojeda@kernel.org \
    --cc=petr.pavlu@suse.com \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=samitolvanen@google.com \
    --cc=wanjay@amazon.com \
    --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

all inboxes | Powered by JetHome®