commit:     72f43edb42071a1e39ccfe03e310ad57f675af65
Author:     Mike Pagano <mpagano <AT> gentoo <DOT> org>
AuthorDate: Thu Sep  5 19:09:56 2024 +0000
Commit:     Mike Pagano <mpagano <AT> gentoo <DOT> org>
CommitDate: Thu Sep  5 19:09:56 2024 +0000
URL:        https://gitweb.gentoo.org/proj/linux-patches.git/commit/?id=72f43edb

dtrace patch for 6.10.X (CTF, modules.builtin.objs) p2

Signed-off-by: Mike Pagano <mpagano <AT> gentoo.org>

 0000_README               |    4 +-
 2995_dtrace-6.10_p2.patch | 2368 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 2370 insertions(+), 2 deletions(-)

diff --git a/0000_README b/0000_README
index d179ade8..ad96829d 100644
--- a/0000_README
+++ b/0000_README
@@ -119,9 +119,9 @@ Patch:  
2990_libbpf-v2-workaround-Wmaybe-uninitialized-false-pos.patch
 From:   https://lore.kernel.org/bpf/
 Desc:   libbpf: workaround -Wmaybe-uninitialized false positive
 
-Patch:  2995_dtrace-6.10_p1.patch
+Patch:  2995_dtrace-6.10_p2.patch
 From:   https://github.com/thesamesam/linux/tree/dtrace-sam/v2/6.10
-Desc:   dtrace patch 6.10_p1
+Desc:   dtrace patch for 6.10.X (CTF, modules.builtin.objs)
 
 Patch:  3000_Support-printing-firmware-info.patch
 From:   https://bugs.gentoo.org/732852

diff --git a/2995_dtrace-6.10_p2.patch b/2995_dtrace-6.10_p2.patch
new file mode 100644
index 00000000..3686ca5b
--- /dev/null
+++ b/2995_dtrace-6.10_p2.patch
@@ -0,0 +1,2368 @@
+diff --git a/Documentation/dontdiff b/Documentation/dontdiff
+index 3c399f132e2db..75b9655e57914 100644
+--- a/Documentation/dontdiff
++++ b/Documentation/dontdiff
+@@ -179,7 +179,7 @@ mkutf8data
+ modpost
+ modules-only.symvers
+ modules.builtin
+-modules.builtin.modinfo
++modules.builtin.*
+ modules.nsdeps
+ modules.order
+ modversions.h*
+diff --git a/Documentation/kbuild/kbuild.rst b/Documentation/kbuild/kbuild.rst
+index 9c8d1d046ea56..79e104ffee715 100644
+--- a/Documentation/kbuild/kbuild.rst
++++ b/Documentation/kbuild/kbuild.rst
+@@ -17,6 +17,11 @@ modules.builtin
+ This file lists all modules that are built into the kernel. This is used
+ by modprobe to not fail when trying to load something builtin.
+ 
++modules.builtin.objs
++-----------------------
++This file contains object mapping of modules that are built into the kernel
++to their corresponding object files used to build the module.
++
+ modules.builtin.modinfo
+ -----------------------
+ This file contains modinfo from all modules that are built into the kernel.
+diff --git a/Documentation/process/changes.rst 
b/Documentation/process/changes.rst
+index 5685d7bfe4d0f..8db62fe4dadff 100644
+--- a/Documentation/process/changes.rst
++++ b/Documentation/process/changes.rst
+@@ -63,9 +63,13 @@ cpio                   any              cpio --version
+ GNU tar                1.28             tar --version
+ gtags (optional)       6.6.5            gtags --version
+ mkimage (optional)     2017.01          mkimage --version
++GNU AWK (optional)     5.1.0            gawk --version
++GNU C\ [#f2]_          12.0             gcc --version
++binutils\ [#f2]_       2.36             ld -v
+ ====================== ===============  
========================================
+ 
+ .. [#f1] Sphinx is needed only to build the Kernel documentation
++.. [#f2] These are needed at build-time when CONFIG_CTF is enabled
+ 
+ Kernel compilation
+ ******************
+@@ -198,6 +202,12 @@ platforms. The tool is available via the ``u-boot-tools`` 
package or can be
+ built from the U-Boot source code. See the instructions at
+ https://docs.u-boot.org/en/latest/build/tools.html#building-tools-for-linux
+ 
++GNU AWK
++-------
++
++GNU AWK is needed if you want kernel builds to generate address range data for
++builtin modules (CONFIG_BUILTIN_MODULE_RANGES).
++
+ System utilities
+ ****************
+ 
+diff --git a/Makefile b/Makefile
+index 2e5ac6ab3d476..635896f269f1f 100644
+--- a/Makefile
++++ b/Makefile
+@@ -1024,6 +1024,7 @@ include-$(CONFIG_UBSAN)          += 
scripts/Makefile.ubsan
+ include-$(CONFIG_KCOV)                += scripts/Makefile.kcov
+ include-$(CONFIG_RANDSTRUCT)  += scripts/Makefile.randstruct
+ include-$(CONFIG_GCC_PLUGINS) += scripts/Makefile.gcc-plugins
++include-$(CONFIG_CTF)         += scripts/Makefile.ctfa-toplevel
+ 
+ include $(addprefix $(srctree)/, $(include-y))
+ 
+@@ -1151,7 +1152,11 @@ PHONY += vmlinux_o
+ vmlinux_o: vmlinux.a $(KBUILD_VMLINUX_LIBS)
+       $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.vmlinux_o
+ 
+-vmlinux.o modules.builtin.modinfo modules.builtin: vmlinux_o
++MODULES_BUILTIN := modules.builtin.modinfo
++MODULES_BUILTIN += modules.builtin
++MODULES_BUILTIN += modules.builtin.objs
++
++vmlinux.o $(MODULES_BUILTIN): vmlinux_o
+       @:
+ 
+ PHONY += vmlinux
+@@ -1490,9 +1495,10 @@ endif # CONFIG_MODULES
+ 
+ # Directories & files removed with 'make clean'
+ CLEAN_FILES += vmlinux.symvers modules-only.symvers \
+-             modules.builtin modules.builtin.modinfo modules.nsdeps \
++             modules.builtin modules.builtin.* modules.nsdeps \
+              compile_commands.json rust/test \
+-             rust-project.json .vmlinux.objs .vmlinux.export.c
++             rust-project.json .vmlinux.objs .vmlinux.export.c \
++             vmlinux.ctfa
+ 
+ # Directories & files removed with 'make mrproper'
+ MRPROPER_FILES += include/config include/generated          \
+@@ -1586,6 +1592,8 @@ help:
+       @echo  '                    (requires a recent binutils and recent 
build (System.map))'
+       @echo  '  dir/file.ko     - Build module including final link'
+       @echo  '  modules_prepare - Set up for building external modules'
++      @echo  '  ctf             - Generate CTF type information, installed by 
make ctf_install'
++      @echo  '  ctf_install     - Install CTF to INSTALL_MOD_PATH (default: 
/)'
+       @echo  '  tags/TAGS       - Generate tags file for editors'
+       @echo  '  cscope          - Generate cscope index'
+       @echo  '  gtags           - Generate GNU GLOBAL index'
+@@ -1942,7 +1950,7 @@ clean: $(clean-dirs)
+       $(call cmd,rmfiles)
+       @find $(or $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \
+               \( -name '*.[aios]' -o -name '*.rsi' -o -name '*.ko' -o -name 
'.*.cmd' \
+-              -o -name '*.ko.*' \
++              -o -name '*.ko.*' -o -name '*.ctf' \
+               -o -name '*.dtb' -o -name '*.dtbo' \
+               -o -name '*.dtb.S' -o -name '*.dtbo.S' \
+               -o -name '*.dt.yaml' -o -name 'dtbs-list' \
+diff --git a/arch/arm/vdso/Makefile b/arch/arm/vdso/Makefile
+index 01067a2bc43b7..6464089596088 100644
+--- a/arch/arm/vdso/Makefile
++++ b/arch/arm/vdso/Makefile
+@@ -14,6 +14,12 @@ obj-vdso := $(addprefix $(obj)/, $(obj-vdso))
+ ccflags-y := -fPIC -fno-common -fno-builtin -fno-stack-protector
+ ccflags-y += -DDISABLE_BRANCH_PROFILING -DBUILD_VDSO32
+ 
++ifdef CONFIG_CTF
++  # CTF in the vDSO would introduce a new section, which would
++  # expand the vDSO to more than a page.
++  ccflags-y += $(call cc-option,-gctf0)
++endif
++
+ ldflags-$(CONFIG_CPU_ENDIAN_BE8) := --be8
+ ldflags-y := -Bsymbolic --no-undefined -soname=linux-vdso.so.1 \
+           -z max-page-size=4096 -shared $(ldflags-y) \
+diff --git a/arch/arm64/kernel/vdso/Makefile b/arch/arm64/kernel/vdso/Makefile
+index d63930c828397..9ef2050690653 100644
+--- a/arch/arm64/kernel/vdso/Makefile
++++ b/arch/arm64/kernel/vdso/Makefile
+@@ -33,6 +33,12 @@ ldflags-y += -T
+ ccflags-y := -fno-common -fno-builtin -fno-stack-protector -ffixed-x18
+ ccflags-y += -DDISABLE_BRANCH_PROFILING -DBUILD_VDSO
+ 
++ifdef CONFIG_CTF
++  # CTF in the vDSO would introduce a new section, which would
++  # expand the vDSO to more than a page.
++  ccflags-y += $(call cc-option,-gctf0)
++endif
++
+ # -Wmissing-prototypes and -Wmissing-declarations are removed from
+ # the CFLAGS of vgettimeofday.c to make possible to build the
+ # kernel with CONFIG_WERROR enabled.
+diff --git a/arch/loongarch/vdso/Makefile b/arch/loongarch/vdso/Makefile
+index d724d46b07c84..95ef763218f7b 100644
+--- a/arch/loongarch/vdso/Makefile
++++ b/arch/loongarch/vdso/Makefile
+@@ -22,6 +22,11 @@ cflags-vdso := $(ccflags-vdso) \
+       -fno-stack-protector -fno-jump-tables -DDISABLE_BRANCH_PROFILING \
+       $(call cc-option, -fno-asynchronous-unwind-tables) \
+       $(call cc-option, -fno-stack-protector)
++
++ifdef CONFIG_CTF
++  cflags-vdso += $(call cc-option,-gctf0)
++endif
++
+ aflags-vdso := $(ccflags-vdso) \
+       -D__ASSEMBLY__ -Wa,-gdwarf-2
+ 
+diff --git a/arch/mips/vdso/Makefile b/arch/mips/vdso/Makefile
+index b289b2c1b2946..6b5603f3b6a2d 100644
+--- a/arch/mips/vdso/Makefile
++++ b/arch/mips/vdso/Makefile
+@@ -34,6 +34,10 @@ cflags-vdso := $(ccflags-vdso) \
+ aflags-vdso := $(ccflags-vdso) \
+       -D__ASSEMBLY__ -Wa,-gdwarf-2
+ 
++ifdef CONFIG_CTF
++cflags-vdso += $(call cc-option,-gctf0)
++endif
++
+ ifneq ($(c-gettimeofday-y),)
+ CFLAGS_vgettimeofday.o = -include $(c-gettimeofday-y)
+ 
+diff --git a/arch/sparc/vdso/Makefile b/arch/sparc/vdso/Makefile
+index 243dbfc4609d8..432422fc3febb 100644
+--- a/arch/sparc/vdso/Makefile
++++ b/arch/sparc/vdso/Makefile
+@@ -46,6 +46,10 @@ CFL := $(PROFILING) -mcmodel=medlow -fPIC -O2 
-fasynchronous-unwind-tables -m64
+        -fno-omit-frame-pointer -foptimize-sibling-calls \
+        -DDISABLE_BRANCH_PROFILING -DBUILD_VDSO
+ 
++ifdef CONFIG_CTF
++CFL += $(call cc-option,-gctf0)
++endif
++
+ SPARC_REG_CFLAGS = -ffixed-g4 -ffixed-g5 -fcall-used-g5 -fcall-used-g7
+ 
+ $(vobjs): KBUILD_CFLAGS := $(filter-out $(RANDSTRUCT_CFLAGS) 
$(GCC_PLUGINS_CFLAGS) $(SPARC_REG_CFLAGS),$(KBUILD_CFLAGS)) $(CFL)
+diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile
+index 215a1b202a918..ffb33a1a7315b 100644
+--- a/arch/x86/entry/vdso/Makefile
++++ b/arch/x86/entry/vdso/Makefile
+@@ -54,6 +54,7 @@ $(obj)/vdso-image-%.c: $(obj)/vdso%.so.dbg $(obj)/vdso%.so 
$(obj)/vdso2c FORCE
+ CFL := $(PROFILING) -mcmodel=small -fPIC -O2 -fasynchronous-unwind-tables 
-m64 \
+        $(filter -g%,$(KBUILD_CFLAGS)) -fno-stack-protector \
+        -fno-omit-frame-pointer -foptimize-sibling-calls \
++       $(call cc-option,-gctf0) \
+        -DDISABLE_BRANCH_PROFILING -DBUILD_VDSO
+ 
+ ifdef CONFIG_MITIGATION_RETPOLINE
+@@ -131,6 +132,9 @@ KBUILD_CFLAGS_32 += -m32 -msoft-float -mregparm=0 -fpic
+ KBUILD_CFLAGS_32 += -fno-stack-protector
+ KBUILD_CFLAGS_32 += $(call cc-option, -foptimize-sibling-calls)
+ KBUILD_CFLAGS_32 += -fno-omit-frame-pointer
++ifdef CONFIG_CTF
++  KBUILD_CFLAGS_32 += $(call cc-option,-gctf0)
++endif
+ KBUILD_CFLAGS_32 += -DDISABLE_BRANCH_PROFILING
+ 
+ ifdef CONFIG_MITIGATION_RETPOLINE
+diff --git a/arch/x86/um/vdso/Makefile b/arch/x86/um/vdso/Makefile
+index 6a77ea6434ffd..d99a60494286a 100644
+--- a/arch/x86/um/vdso/Makefile
++++ b/arch/x86/um/vdso/Makefile
+@@ -42,6 +42,10 @@ CFL := $(PROFILING) -mcmodel=small -fPIC -O2 
-fasynchronous-unwind-tables -m64 \
+        $(filter -g%,$(KBUILD_CFLAGS)) -fno-stack-protector \
+        -fno-omit-frame-pointer -foptimize-sibling-calls
+ 
++if CONFIG_CTF
++CFL += $(call cc-option,-gctf0)
++endif
++
+ $(vobjs): KBUILD_CFLAGS += $(CFL)
+ 
+ #
+diff --git a/include/asm-generic/vmlinux.lds.h 
b/include/asm-generic/vmlinux.lds.h
+index f00a8e18f389f..2e307c0824574 100644
+--- a/include/asm-generic/vmlinux.lds.h
++++ b/include/asm-generic/vmlinux.lds.h
+@@ -1014,6 +1014,7 @@
+       *(.discard.*)                                                   \
+       *(.export_symbol)                                               \
+       *(.modinfo)                                                     \
++      *(.ctf)                                                         \
+       /* ld.bfd warns about .gnu.version* even when not emitted */    \
+       *(.gnu.version*)                                                \
+ 
+diff --git a/include/linux/module.h b/include/linux/module.h
+index 330ffb59efe51..ec828908470c9 100644
+--- a/include/linux/module.h
++++ b/include/linux/module.h
+@@ -180,7 +180,9 @@ extern void cleanup_module(void);
+ #ifdef MODULE
+ #define MODULE_FILE
+ #else
+-#define MODULE_FILE   MODULE_INFO(file, KBUILD_MODFILE);
++#define MODULE_FILE                                                         \
++                      MODULE_INFO(file, KBUILD_MODFILE);                    \
++                      MODULE_INFO(objs, KBUILD_MODOBJS);
+ #endif
+ 
+ /*
+diff --git a/init/Kconfig b/init/Kconfig
+index 9684e5d2b81c6..c1b00b2e4a43d 100644
+--- a/init/Kconfig
++++ b/init/Kconfig
+@@ -111,6 +111,12 @@ config PAHOLE_VERSION
+       int
+       default $(shell,$(srctree)/scripts/pahole-version.sh $(PAHOLE))
+ 
++config HAVE_CTF_TOOLCHAIN
++      def_bool $(cc-option,-gctf) && $(ld-option,-lbfd -liberty -lctf -lbfd 
-liberty -lz -ldl -lc -o /dev/null)
++      depends on CC_IS_GCC
++      help
++        GCC and binutils support CTF generation.
++
+ config CONSTRUCTORS
+       bool
+ 
+diff --git a/lib/Kconfig b/lib/Kconfig
+index b0a76dff5c182..61d0be30b3562 100644
+--- a/lib/Kconfig
++++ b/lib/Kconfig
+@@ -633,6 +633,16 @@ config DIMLIB
+ #
+ config LIBFDT
+       bool
++#
++# CTF support is select'ed if needed
++#
++config CTF
++        bool "Compact Type Format generation"
++        depends on HAVE_CTF_TOOLCHAIN
++        help
++          Emit a compact, compressed description of the kernel's datatypes and
++          global variables into the vmlinux.ctfa archive (for in-tree modules)
++          or into .ctf sections in kernel modules (for out-of-tree modules).
+ 
+ config OID_REGISTRY
+       tristate
+diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
+index 59b6765d86b8f..dab7e6983eace 100644
+--- a/lib/Kconfig.debug
++++ b/lib/Kconfig.debug
+@@ -571,6 +571,21 @@ config VMLINUX_MAP
+         pieces of code get eliminated with
+         CONFIG_LD_DEAD_CODE_DATA_ELIMINATION.
+ 
++config BUILTIN_MODULE_RANGES
++      bool "Generate address range information for builtin modules"
++      depends on !LTO
++      depends on VMLINUX_MAP
++      help
++       When modules are built into the kernel, there will be no module name
++       associated with its symbols in /proc/kallsyms.  Tracers may want to
++       identify symbols by module name and symbol name regardless of whether
++       the module is configured as loadable or not.
++
++       This option generates modules.builtin.ranges in the build tree with
++       offset ranges (per ELF section) for the module(s) they belong to.
++       It also records an anchor symbol to determine the load address of the
++       section.
++
+ config DEBUG_FORCE_WEAK_PER_CPU
+       bool "Force weak per-cpu definitions"
+       depends on DEBUG_KERNEL
+diff --git a/scripts/.gitignore b/scripts/.gitignore
+index 3dbb8bb2457bc..11339fa922abd 100644
+--- a/scripts/.gitignore
++++ b/scripts/.gitignore
+@@ -11,3 +11,4 @@
+ /sorttable
+ /target.json
+ /unifdef
++y!/Makefile.ctf
+diff --git a/scripts/Makefile b/scripts/Makefile
+index fe56eeef09dd4..8e7eb174d3154 100644
+--- a/scripts/Makefile
++++ b/scripts/Makefile
+@@ -54,6 +54,7 @@ targets += module.lds
+ 
+ subdir-$(CONFIG_GCC_PLUGINS) += gcc-plugins
+ subdir-$(CONFIG_MODVERSIONS) += genksyms
++subdir-$(CONFIG_CTF)         += ctf
+ subdir-$(CONFIG_SECURITY_SELINUX) += selinux
+ 
+ # Let clean descend into subdirs
+diff --git a/scripts/Makefile.ctfa b/scripts/Makefile.ctfa
+new file mode 100644
+index 0000000000000..b65d9d391c29c
+--- /dev/null
++++ b/scripts/Makefile.ctfa
+@@ -0,0 +1,92 @@
++# SPDX-License-Identifier: GPL-2.0-only
++# ===========================================================================
++# Module CTF/CTFA generation
++# ===========================================================================
++
++include include/config/auto.conf
++include $(srctree)/scripts/Kbuild.include
++
++# CTF is already present in every object file if CONFIG_CTF is enabled.
++# vmlinux.lds.h strips it out of the finished kernel, but if nothing is done
++# it will be deduplicated into module .ko's.  For out-of-tree module builds,
++# this is what we want, but for in-tree modules we can save substantial
++# space by deduplicating it against all the core kernel types as well.  So
++# split the CTF out of in-tree module .ko's into separate .ctf files so that
++# it doesn't take up space in the modules on disk, and let the specialized
++# ctfarchive tool consume it and all the CTF in the vmlinux.o files when
++# 'make ctf' is invoked, and use the same machinery that the linker uses to
++# do CTF deduplication to emit vmlinux.ctfa containing the deduplicated CTF.
++
++# Nothing special needs to be done if CTF is turned off or if a standalone
++# module is being built.
++module-ctf-postlink = mv $(1).tmp $(1)
++
++ifdef CONFIG_CTF
++
++# This is quite tricky.  The CTF machinery needs to be told about all the
++# built-in objects as well as all the external modules -- but 
Makefile.modfinal
++# only knows about the latter.  So the toplevel makefile emits the names of 
the
++# built-in objects into a temporary file, which is then catted and its 
contents
++# used as prerequisites by this rule.
++#
++# We write the names of the object files to be scanned for CTF content into a
++# file, then use that, to avoid hitting command-line length limits.
++
++ifeq ($(KBUILD_EXTMOD),)
++ctf-modules := $(shell find . -name '*.ko.ctf' -print)
++quiet_cmd_ctfa_raw = CTFARAW
++      cmd_ctfa_raw = scripts/ctf/ctfarchive $@ .tmp_objects.builtin 
modules.builtin.objs $(ctf-filelist)
++ctf-builtins := .tmp_objects.builtin
++ctf-filelist := .tmp_ctf.filelist
++ctf-filelist-raw := .tmp_ctf.filelist.raw
++
++define module-ctf-postlink =
++      $(OBJCOPY) --only-section=.ctf $(1).tmp $(1).ctf && \
++      $(OBJCOPY) --remove-section=.ctf $(1).tmp $(1) && rm -f $(1).tmp
++endef
++
++# Split a list up like shell xargs does.
++define xargs =
++$(1) $(wordlist 1,1024,$(2))
++$(if $(word 1025,$(2)),$(call xargs,$(1),$(wordlist 1025,$(words $(2)),$(2))))
++endef
++
++$(ctf-filelist-raw): $(ctf-builtins) $(ctf-modules)
++      @rm -f $(ctf-filelist-raw);
++      $(call xargs,@printf "%s\n" >> $(ctf-filelist-raw),$^)
++      @touch $(ctf-filelist-raw)
++
++$(ctf-filelist): $(ctf-filelist-raw)
++      @rm -f $(ctf-filelist);
++      @cat $(ctf-filelist-raw) | while read -r obj; do \
++              case $$obj in \
++              $(ctf-builtins)) cat $$obj >> $(ctf-filelist);; \
++              *.a) $(AR) t $$obj > $(ctf-filelist);; \
++              *.builtin) cat $$obj >> $(ctf-filelist);; \
++              *) echo "$$obj" >> $(ctf-filelist);; \
++              esac; \
++      done
++      @touch $(ctf-filelist)
++
++# The raw CTF depends on the output CTF file list, and that depends
++# on the .ko files for the modules.
++.tmp_vmlinux.ctfa.raw: $(ctf-filelist) FORCE
++      $(call if_changed,ctfa_raw)
++
++quiet_cmd_ctfa = CTFA
++      cmd_ctfa = { echo 'int main () { return 0; } ' | \
++              $(CC) -x c -c -o $<.stub -; \
++      $(OBJCOPY) '--remove-section=.*' --add-section=.ctf=$< \
++               $<.stub $@; }
++
++# The CTF itself is an ELF executable with one section: the CTF.  This lets
++# objdump work on it, at minimal size cost.
++vmlinux.ctfa: .tmp_vmlinux.ctfa.raw FORCE
++      $(call if_changed,ctfa)
++
++targets += vmlinux.ctfa
++
++endif         # KBUILD_EXTMOD
++
++endif         # !CONFIG_CTF
++
+diff --git a/scripts/Makefile.ctfa-toplevel b/scripts/Makefile.ctfa-toplevel
+new file mode 100644
+index 0000000000000..210bef3854e9b
+--- /dev/null
++++ b/scripts/Makefile.ctfa-toplevel
+@@ -0,0 +1,54 @@
++# SPDX-License-Identifier: GPL-2.0-only
++# ===========================================================================
++# CTF rules for the top-level makefile only
++# ===========================================================================
++
++KBUILD_CFLAGS += $(call cc-option,-gctf)
++KBUILD_LDFLAGS        += $(call ld-option, --ctf-variables)
++
++ifeq ($(KBUILD_EXTMOD),)
++
++# CTF generation for in-tree code (modules, built-in and not, and core kernel)
++
++# This contains all the object files that are built directly into the
++# kernel (including built-in modules), for consumption by ctfarchive in
++# Makefile.modfinal.
++# This is made doubly annoying by the presence of '.o' files which are 
actually
++# thin ar archives, and the need to support file(1) versions too old to
++# recognize them as archives at all.  (So we assume that everything that is 
notr
++# an ELF object is an archive.)
++ifeq ($(SRCARCH),x86)
++.tmp_objects.builtin: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),bzImage) FORCE
++else
++ifeq ($(SRCARCH),arm64)
++.tmp_objects.builtin: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),Image) FORCE
++else
++.tmp_objects.builtin: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux) FORCE
++endif
++endif
++      @echo $(KBUILD_VMLINUX_OBJS) | \
++              tr " " "\n" | grep "\.o$$" | xargs -r file | \
++              grep ELF | cut -d: -f1 > .tmp_objects.builtin
++      @for archive in $$(echo $(KBUILD_VMLINUX_OBJS) |\
++              tr " " "\n" | xargs -r file | grep -v ELF | cut -d: -f1); do \
++              $(AR) t "$$archive" >> .tmp_objects.builtin; \
++      done
++
++ctf: vmlinux.ctfa
++PHONY += ctf ctf_install
++
++# Making CTF needs the builtin files.  We need to force everything to be
++# built if not already done, since we need the .o files for the machinery
++# above to work.
++vmlinux.ctfa: KBUILD_BUILTIN := 1
++vmlinux.ctfa: modules.builtin.objs .tmp_objects.builtin
++      $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modfinal vmlinux.ctfa
++
++ctf_install:
++      $(Q)mkdir -p $(MODLIB)/kernel
++      @ln -sf $(abspath $(srctree)) $(MODLIB)/source
++      $(Q)cp -f $(objtree)/vmlinux.ctfa $(MODLIB)/kernel
++
++CLEAN_FILES += vmlinux.ctfa
++
++endif
+diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
+index 7f8ec77bf35c9..97b0ea2eee9d4 100644
+--- a/scripts/Makefile.lib
++++ b/scripts/Makefile.lib
+@@ -118,6 +118,8 @@ modname-multi = $(sort $(foreach m,$(multi-obj-ym),\
+ __modname = $(or $(modname-multi),$(basetarget))
+ 
+ modname = $(subst $(space),:,$(__modname))
++modname-objs = $($(modname)-objs) $($(modname)-y) $($(modname)-Y)
++modname-objs-prefixed = $(sort $(strip $(addprefix $(obj)/, $(modname-objs))))
+ modfile = $(addprefix $(obj)/,$(__modname))
+ 
+ # target with $(obj)/ and its suffix stripped
+@@ -131,7 +133,8 @@ name-fix = $(call stringify,$(call name-fix-token,$1))
+ basename_flags = -DKBUILD_BASENAME=$(call name-fix,$(basetarget))
+ modname_flags  = -DKBUILD_MODNAME=$(call name-fix,$(modname)) \
+                -D__KBUILD_MODNAME=kmod_$(call name-fix-token,$(modname))
+-modfile_flags  = -DKBUILD_MODFILE=$(call stringify,$(modfile))
++modfile_flags  = -DKBUILD_MODFILE=$(call stringify,$(modfile)) \
++                 -DKBUILD_MODOBJS=$(call stringify,$(modfile).o:$(subst 
$(space),|,$(modname-objs-prefixed)))
+ 
+ _c_flags       = $(filter-out $(CFLAGS_REMOVE_$(target-stem).o), \
+                      $(filter-out $(ccflags-remove-y), \
+@@ -238,7 +241,7 @@ modkern_rustflags =                                        
      \
+ 
+ modkern_aflags = $(if $(part-of-module),                              \
+                       $(KBUILD_AFLAGS_MODULE) $(AFLAGS_MODULE),       \
+-                      $(KBUILD_AFLAGS_KERNEL) $(AFLAGS_KERNEL))
++                      $(KBUILD_AFLAGS_KERNEL) $(AFLAGS_KERNEL) 
$(modfile_flags))
+ 
+ c_flags        = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE)     \
+                -include $(srctree)/include/linux/compiler_types.h       \
+@@ -248,7 +251,7 @@ c_flags        = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) 
$(LINUXINCLUDE)     \
+ rust_flags     = $(_rust_flags) $(modkern_rustflags) 
@$(objtree)/include/generated/rustc_cfg
+ 
+ a_flags        = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE)     \
+-               $(_a_flags) $(modkern_aflags)
++               $(_a_flags) $(modkern_aflags) $(modname_flags)
+ 
+ cpp_flags      = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE)     \
+                $(_cpp_flags)
+diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
+index 3bec9043e4f38..06807e403d162 100644
+--- a/scripts/Makefile.modfinal
++++ b/scripts/Makefile.modfinal
+@@ -30,11 +30,16 @@ quiet_cmd_cc_o_c = CC [M]  $@
+ %.mod.o: %.mod.c FORCE
+       $(call if_changed_dep,cc_o_c)
+ 
++# for module-ctf-postlink
++include $(srctree)/scripts/Makefile.ctfa
++
+ quiet_cmd_ld_ko_o = LD [M]  $@
+       cmd_ld_ko_o +=                                                  \
+       $(LD) -r $(KBUILD_LDFLAGS)                                      \
+               $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE)              \
+-              -T scripts/module.lds -o $@ $(filter %.o, $^)
++              -T scripts/module.lds $(LDFLAGS_$(modname)) -o [email protected]   \
++              $(filter %.o, $^) &&                                    \
++      $(call module-ctf-postlink,$@)                                  \
+ 
+ quiet_cmd_btf_ko = BTF [M] $@
+       cmd_btf_ko =                                                    \
+diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst
+index 0afd75472679f..e668469ce098c 100644
+--- a/scripts/Makefile.modinst
++++ b/scripts/Makefile.modinst
+@@ -30,10 +30,12 @@ $(MODLIB)/modules.order: modules.order FORCE
+ quiet_cmd_install_modorder = INSTALL $@
+       cmd_install_modorder = sed 's:^\(.*\)\.o$$:kernel/\1.ko:' $< > $@
+ 
+-# Install modules.builtin(.modinfo) even when CONFIG_MODULES is disabled.
+-install-y += $(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo)
++# Install modules.builtin(.modinfo,.ranges,.objs) even when CONFIG_MODULES is 
disabled.
++install-y += $(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo 
modules.builtin.objs)
+ 
+-$(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo): 
$(MODLIB)/%: % FORCE
++install-$(CONFIG_BUILTIN_MODULE_RANGES) += $(MODLIB)/modules.builtin.ranges
++
++$(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo 
modules.builtin.ranges modules.builtin.objs): $(MODLIB)/%: % FORCE
+       $(call cmd,install)
+ 
+ endif
+diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
+index 49946cb968440..7e8b703799c85 100644
+--- a/scripts/Makefile.vmlinux
++++ b/scripts/Makefile.vmlinux
+@@ -33,6 +33,24 @@ targets += vmlinux
+ vmlinux: scripts/link-vmlinux.sh vmlinux.o $(KBUILD_LDS) FORCE
+       +$(call if_changed_dep,link_vmlinux)
+ 
++# module.builtin.ranges
++# ---------------------------------------------------------------------------
++ifdef CONFIG_BUILTIN_MODULE_RANGES
++__default: modules.builtin.ranges
++
++quiet_cmd_modules_builtin_ranges = GEN     $@
++      cmd_modules_builtin_ranges = $(real-prereqs) > $@
++
++targets += modules.builtin.ranges
++modules.builtin.ranges: $(srctree)/scripts/generate_builtin_ranges.awk \
++                      modules.builtin vmlinux.map vmlinux.o.map FORCE
++      $(call if_changed,modules_builtin_ranges)
++
++vmlinux.map: vmlinux
++      @:
++
++endif
++
+ # Add FORCE to the prequisites of a target to force it to be always rebuilt.
+ # ---------------------------------------------------------------------------
+ 
+diff --git a/scripts/Makefile.vmlinux_o b/scripts/Makefile.vmlinux_o
+index 6de297916ce68..86d6f8887313f 100644
+--- a/scripts/Makefile.vmlinux_o
++++ b/scripts/Makefile.vmlinux_o
+@@ -1,7 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ 
+ PHONY := __default
+-__default: vmlinux.o modules.builtin.modinfo modules.builtin
++__default: vmlinux.o modules.builtin.modinfo modules.builtin 
modules.builtin.objs
+ 
+ include include/config/auto.conf
+ include $(srctree)/scripts/Kbuild.include
+@@ -27,6 +27,18 @@ ifdef CONFIG_LTO_CLANG
+ initcalls-lds := .tmp_initcalls.lds
+ endif
+ 
++# Generate a linker script to delete CTF sections
++# -----------------------------------------------
++
++quiet_cmd_gen_remove_ctf.lds = GEN     $@
++      cmd_gen_remove_ctf.lds = \
++      $(LD) $(KBUILD_LDFLAGS) -r --verbose | awk -f $(real-prereqs) > $@
++
++.tmp_remove-ctf.lds: $(srctree)/scripts/remove-ctf-lds.awk FORCE
++      $(call if_changed,gen_remove_ctf.lds)
++
++targets := .tmp_remove-ctf.lds
++
+ # objtool for vmlinux.o
+ # ---------------------------------------------------------------------------
+ #
+@@ -42,13 +54,18 @@ vmlinux-objtool-args-$(CONFIG_NOINSTR_VALIDATION)  += 
--noinstr \
+ 
+ objtool-args = $(vmlinux-objtool-args-y) --link
+ 
+-# Link of vmlinux.o used for section mismatch analysis
++# Link of vmlinux.o used for section mismatch analysis: we also strip the CTF
++# section out at this stage, since ctfarchive gets it from the underlying 
object
++# files and linking it further is a waste of time.
+ # ---------------------------------------------------------------------------
+ 
++vmlinux-o-ld-args-$(CONFIG_BUILTIN_MODULE_RANGES)     += [email protected]
++
+ quiet_cmd_ld_vmlinux.o = LD      $@
+       cmd_ld_vmlinux.o = \
+       $(LD) ${KBUILD_LDFLAGS} -r -o $@ \
+-      $(addprefix -T , $(initcalls-lds)) \
++      $(vmlinux-o-ld-args-y) \
++      $(addprefix -T , $(initcalls-lds)) -T .tmp_remove-ctf.lds \
+       --whole-archive vmlinux.a --no-whole-archive \
+       --start-group $(KBUILD_VMLINUX_LIBS) --end-group \
+       $(cmd_objtool)
+@@ -58,7 +75,7 @@ define rule_ld_vmlinux.o
+       $(call cmd,gen_objtooldep)
+ endef
+ 
+-vmlinux.o: $(initcalls-lds) vmlinux.a $(KBUILD_VMLINUX_LIBS) FORCE
++vmlinux.o: $(initcalls-lds) vmlinux.a $(KBUILD_VMLINUX_LIBS) 
.tmp_remove-ctf.lds FORCE
+       $(call if_changed_rule,ld_vmlinux.o)
+ 
+ targets += vmlinux.o
+@@ -87,6 +104,19 @@ targets += modules.builtin
+ modules.builtin: modules.builtin.modinfo FORCE
+       $(call if_changed,modules_builtin)
+ 
++# module.builtin.objs
++# ---------------------------------------------------------------------------
++quiet_cmd_modules_builtin_objs = GEN     $@
++      cmd_modules_builtin_objs = \
++      tr '\0' '\n' < $< | \
++      sed -n 's/^[[:alnum:]:_]*\.objs=//p' | \
++      tr ' ' '\n' | uniq | sed -e 's|:|: |' -e 's:|: :g' | \
++      tr -s ' ' > $@
++
++targets += modules.builtin.objs
++modules.builtin.objs: modules.builtin.modinfo FORCE
++      $(call if_changed,modules_builtin_objs)
++
+ # Add FORCE to the prequisites of a target to force it to be always rebuilt.
+ # ---------------------------------------------------------------------------
+ 
+diff --git a/scripts/ctf/.gitignore b/scripts/ctf/.gitignore
+new file mode 100644
+index 0000000000000..6a0eb1c3ceeab
+--- /dev/null
++++ b/scripts/ctf/.gitignore
+@@ -0,0 +1 @@
++ctfarchive
+diff --git a/scripts/ctf/Makefile b/scripts/ctf/Makefile
+new file mode 100644
+index 0000000000000..3b83f93bb9f9a
+--- /dev/null
++++ b/scripts/ctf/Makefile
+@@ -0,0 +1,5 @@
++ifdef CONFIG_CTF
++hostprogs-always-y    := ctfarchive
++ctfarchive-objs               := ctfarchive.o modules_builtin.o
++HOSTLDLIBS_ctfarchive := -lctf
++endif
+diff --git a/scripts/ctf/ctfarchive.c b/scripts/ctf/ctfarchive.c
+new file mode 100644
+index 0000000000000..92cc4912ed0ee
+--- /dev/null
++++ b/scripts/ctf/ctfarchive.c
+@@ -0,0 +1,413 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * ctfmerge.c: Read in CTF extracted from generated object files from a
++ * specified directory and generate a CTF archive whose members are the
++ * deduplicated CTF derived from those object files, split up by kernel
++ * module.
++ *
++ * Copyright (c) 2019, 2023, Oracle and/or its affiliates.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ */
++
++#define _GNU_SOURCE 1
++#include <errno.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <ctf-api.h>
++#include "modules_builtin.h"
++
++static ctf_file_t *output;
++
++static int private_ctf_link_add_ctf(ctf_file_t *fp,
++                                  const char *name)
++{
++#if !defined (CTF_LINK_FINAL)
++      return ctf_link_add_ctf(fp, NULL, name);
++#else
++      /* Non-upstreamed, erroneously-broken API.  */
++      return ctf_link_add_ctf(fp, NULL, name, NULL, 0);
++#endif
++}
++
++/*
++ * Add a file to the link.
++ */
++static void add_to_link(const char *fn)
++{
++      if (private_ctf_link_add_ctf(output, fn) < 0)
++      {
++              fprintf(stderr, "Cannot add CTF file %s: %s\n", fn,
++                      ctf_errmsg(ctf_errno(output)));
++              exit(1);
++      }
++}
++
++struct from_to
++{
++      char *from;
++      char *to;
++};
++
++/*
++ * The world's stupidest hash table of FROM -> TO.
++ */
++static struct from_to **from_tos[256];
++static size_t alloc_from_tos[256];
++static size_t num_from_tos[256];
++
++static unsigned char from_to_hash(const char *from)
++{
++      unsigned char hval = 0;
++
++      const char *p;
++      for (p = from; *p; p++)
++              hval += *p;
++
++      return hval;
++}
++
++/*
++ * Note that we will need to add a CU mapping later on.
++ *
++ * Present purely to work around a binutils bug that stops
++ * ctf_link_add_cu_mapping() working right when called repeatedly
++ * with the same FROM.
++ */
++static int add_cu_mapping(const char *from, const char *to)
++{
++      ssize_t i, j;
++
++      i = from_to_hash(from);
++
++      for (j = 0; j < num_from_tos[i]; j++)
++              if (strcmp(from, from_tos[i][j]->from) == 0) {
++                      char *tmp;
++
++                      free(from_tos[i][j]->to);
++                      tmp = strdup(to);
++                      if (!tmp)
++                              goto oom;
++                      from_tos[i][j]->to = tmp;
++                      return 0;
++                  }
++
++      if (num_from_tos[i] >= alloc_from_tos[i]) {
++              struct from_to **tmp;
++              if (alloc_from_tos[i] < 16)
++                      alloc_from_tos[i] = 16;
++              else
++                      alloc_from_tos[i] *= 2;
++
++              tmp = realloc(from_tos[i], alloc_from_tos[i] * sizeof(struct 
from_to *));
++              if (!tmp)
++                      goto oom;
++
++              from_tos[i] = tmp;
++      }
++
++      j = num_from_tos[i];
++      from_tos[i][j] = malloc(sizeof(struct from_to));
++      if (from_tos[i][j] == NULL)
++              goto oom;
++      from_tos[i][j]->from = strdup(from);
++      from_tos[i][j]->to = strdup(to);
++      if (!from_tos[i][j]->from || !from_tos[i][j]->to)
++              goto oom;
++      num_from_tos[i]++;
++
++      return 0;
++  oom:
++      fprintf(stderr,
++              "out of memory in add_cu_mapping\n");
++      exit(1);
++}
++
++/*
++ * Finally tell binutils to add all the CU mappings, with duplicate FROMs
++ * replaced with the most recent one.
++ */
++static void commit_cu_mappings(void)
++{
++      ssize_t i, j;
++
++      for (i = 0; i < 256; i++)
++              for (j = 0; j < num_from_tos[i]; j++)
++                      ctf_link_add_cu_mapping(output, from_tos[i][j]->from,
++                                              from_tos[i][j]->to);
++}
++
++/*
++ * Add a CU mapping to the link.
++ *
++ * CU mappings for built-in modules are added by suck_in_modules, below: here,
++ * we only want to add mappings for names ending in '.ko.ctf', i.e. external
++ * modules, which appear only in the filelist (since they are not built-in).
++ * The pathnames are stripped off because modules don't have any, and hyphens
++ * are translated into underscores.
++ */
++static void add_cu_mappings(const char *fn)
++{
++      const char *last_slash;
++      const char *modname = fn;
++      char *dynmodname = NULL;
++      char *dash;
++      size_t n;
++
++      last_slash = strrchr(modname, '/');
++      if (last_slash)
++              last_slash++;
++      else
++              last_slash = modname;
++      modname = last_slash;
++      if (strchr(modname, '-') != NULL)
++      {
++              dynmodname = strdup(last_slash);
++              dash = dynmodname;
++              while (dash != NULL) {
++                      dash = strchr(dash, '-');
++                      if (dash != NULL)
++                              *dash = '_';
++              }
++              modname = dynmodname;
++      }
++
++      n = strlen(modname);
++      if (strcmp(modname + n - strlen(".ko.ctf"), ".ko.ctf") == 0) {
++              char *mod;
++
++              n -= strlen(".ko.ctf");
++              mod = strndup(modname, n);
++              add_cu_mapping(fn, mod);
++              free(mod);
++      }
++      free(dynmodname);
++}
++
++/*
++ * Add the passed names as mappings to "vmlinux".
++ */
++static void add_builtins(const char *fn)
++{
++      if (add_cu_mapping(fn, "vmlinux") < 0)
++      {
++              fprintf(stderr, "Cannot add CTF CU mapping from %s to 
\"vmlinux\"\n",
++                      ctf_errmsg(ctf_errno(output)));
++              exit(1);
++      }
++}
++
++/*
++ * Do something with a file, line by line.
++ */
++static void suck_in_lines(const char *filename, void (*func)(const char 
*line))
++{
++      FILE *f;
++      char *line = NULL;
++      size_t line_size = 0;
++
++      f = fopen(filename, "r");
++      if (f == NULL) {
++              fprintf(stderr, "Cannot open %s: %s\n", filename,
++                      strerror(errno));
++              exit(1);
++      }
++
++      while (getline(&line, &line_size, f) >= 0) {
++              size_t len = strlen(line);
++
++              if (len == 0)
++                      continue;
++
++              if (line[len-1] == '\n')
++                      line[len-1] = '\0';
++
++              func(line);
++      }
++      free(line);
++
++      if (ferror(f)) {
++              fprintf(stderr, "Error reading from %s: %s\n", filename,
++                      strerror(errno));
++              exit(1);
++      }
++
++      fclose(f);
++}
++
++/*
++ * Pull in modules.builtin.objs and turn it into CU mappings.
++ */
++static void suck_in_modules(const char *modules_builtin_name)
++{
++      struct modules_builtin_iter *i;
++      char *module_name = NULL;
++      char **paths;
++
++      i = modules_builtin_iter_new(modules_builtin_name);
++      if (i == NULL) {
++              fprintf(stderr, "Cannot iterate over builtin module file.\n");
++              exit(1);
++      }
++
++      while ((paths = modules_builtin_iter_next(i, &module_name)) != NULL) {
++              size_t j;
++
++              for (j = 0; paths[j] != NULL; j++) {
++                      char *alloc = NULL;
++                      char *path = paths[j];
++                      /*
++                       * If the name doesn't start in ./, add it, to match 
the names
++                       * passed to add_builtins.
++                       */
++                      if (strncmp(paths[j], "./", 2) != 0) {
++                              char *p;
++                              if ((alloc = malloc(strlen(paths[j]) + 3)) == 
NULL) {
++                                      fprintf(stderr, "Cannot allocate memory 
for "
++                                              "builtin module object name 
%s.\n",
++                                              paths[j]);
++                                      exit(1);
++                              }
++                              p = alloc;
++                              p = stpcpy(p, "./");
++                              p = stpcpy(p, paths[j]);
++                              path = alloc;
++                      }
++                      if (add_cu_mapping(path, module_name) < 0) {
++                              fprintf(stderr, "Cannot add path -> module 
mapping for "
++                                      "%s -> %s: %s\n", path, module_name,
++                                      ctf_errmsg(ctf_errno(output)));
++                              exit(1);
++                      }
++                      free (alloc);
++              }
++              free(paths);
++      }
++      free(module_name);
++      modules_builtin_iter_free(i);
++}
++
++/*
++ * Strip the leading .ctf. off all the module names: transform the default 
name
++ * from _CTF_SECTION into shared_ctf, and chop any trailing .ctf off (since 
that
++ * derives from the intermediate file used to keep the CTF out of the final
++ * module).
++ */
++static char *transform_module_names(ctf_file_t *fp 
__attribute__((__unused__)),
++                                  const char *name,
++                                  void *arg __attribute__((__unused__)))
++{
++      if (strcmp(name, ".ctf") == 0)
++              return strdup("shared_ctf");
++
++      if (strncmp(name, ".ctf", 4) == 0) {
++              size_t n = strlen (name);
++              if (strcmp(name + n - 4, ".ctf") == 0)
++                      n -= 4;
++              return strndup(name + 4, n - 4);
++      }
++      return NULL;
++}
++
++int main(int argc, char *argv[])
++{
++      int err;
++      const char *output_file;
++      unsigned char *file_data = NULL;
++      size_t file_size;
++      FILE *fp;
++
++      if (argc != 5) {
++              fprintf(stderr, "Syntax: ctfarchive output-file objects.builtin 
modules.builtin\n");
++              fprintf(stderr, "                   filelist\n");
++              exit(1);
++      }
++
++      output_file = argv[1];
++
++      /*
++       * First pull in the input files and add them to the link.
++       */
++
++      output = ctf_create(&err);
++      if (!output) {
++              fprintf(stderr, "Cannot create output CTF archive: %s\n",
++                      ctf_errmsg(err));
++              return 1;
++      }
++
++      suck_in_lines(argv[4], add_to_link);
++
++      /*
++       * Make sure that, even if all their types are shared, all modules have
++       * a ctf member that can be used as a child of the shared CTF.
++       */
++      suck_in_lines(argv[4], add_cu_mappings);
++
++      /*
++       * Then pull in the builtin objects list and add them as
++       * mappings to "vmlinux".
++       */
++
++      suck_in_lines(argv[2], add_builtins);
++
++      /*
++       * Finally, pull in the object -> module mapping and add it
++       * as appropriate mappings.
++       */
++      suck_in_modules(argv[3]);
++
++      /*
++       * Commit the added CU mappings.
++       */
++      commit_cu_mappings();
++
++      /*
++       * Arrange to fix up the module names.
++       */
++      ctf_link_set_memb_name_changer(output, transform_module_names, NULL);
++
++      /*
++       * Do the link.
++       */
++      if (ctf_link(output, CTF_LINK_SHARE_DUPLICATED |
++                     CTF_LINK_EMPTY_CU_MAPPINGS) < 0)
++              goto ctf_err;
++
++      /*
++       * Write the output.
++       */
++
++      file_data = ctf_link_write(output, &file_size, 4096);
++      if (!file_data)
++              goto ctf_err;
++
++      fp = fopen(output_file, "w");
++      if (!fp)
++              goto err;
++
++      while ((err = fwrite(file_data, file_size, 1, fp)) == 0);
++      if (ferror(fp)) {
++              errno = ferror(fp);
++              goto err;
++      }
++      if (fclose(fp) < 0)
++              goto err;
++      free(file_data);
++      ctf_file_close(output);
++
++      return 0;
++err:
++      free(file_data);
++      fprintf(stderr, "Cannot create output CTF archive: %s\n",
++              strerror(errno));
++      return 1;
++ctf_err:
++      fprintf(stderr, "Cannot create output CTF archive: %s\n",
++              ctf_errmsg(ctf_errno(output)));
++      return 1;
++}
+diff --git a/scripts/ctf/modules_builtin.c b/scripts/ctf/modules_builtin.c
+new file mode 100644
+index 0000000000000..10af2bbc80e0c
+--- /dev/null
++++ b/scripts/ctf/modules_builtin.c
+@@ -0,0 +1,2 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++#include "../modules_builtin.c"
+diff --git a/scripts/ctf/modules_builtin.h b/scripts/ctf/modules_builtin.h
+new file mode 100644
+index 0000000000000..5e0299e5600c2
+--- /dev/null
++++ b/scripts/ctf/modules_builtin.h
+@@ -0,0 +1,2 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++#include "../modules_builtin.h"
+diff --git a/scripts/generate_builtin_ranges.awk 
b/scripts/generate_builtin_ranges.awk
+new file mode 100755
+index 0000000000000..51ae0458ffbdd
+--- /dev/null
++++ b/scripts/generate_builtin_ranges.awk
+@@ -0,0 +1,516 @@
++#!/usr/bin/gawk -f
++# SPDX-License-Identifier: GPL-2.0
++# generate_builtin_ranges.awk: Generate address range data for builtin modules
++# Written by Kris Van Hees <[email protected]>
++#
++# Usage: generate_builtin_ranges.awk modules.builtin vmlinux.map \
++#             vmlinux.o.map [ <build-dir> ] > modules.builtin.ranges
++#
++
++# Return the module name(s) (if any) associated with the given object.
++#
++# If we have seen this object before, return information from the cache.
++# Otherwise, retrieve it from the corresponding .cmd file.
++#
++function get_module_info(fn, mod, obj, s) {
++      if (fn in omod)
++              return omod[fn];
++
++      if (match(fn, /\/[^/]+$/) == 0)
++              return "";
++
++      obj = fn;
++      mod = "";
++      fn = kdir "/" substr(fn, 1, RSTART) "." substr(fn, RSTART + 1) ".cmd";
++      if (getline s <fn == 1) {
++              if (match(s, /DKBUILD_MODFILE=['"]+[^'"]+/) > 0) {
++                      mod = substr(s, RSTART + 16, RLENGTH - 16);
++                      gsub(/['"]/, "", mod);
++              } else if (match(s, /RUST_MODFILE=[^ ]+/) > 0)
++                      mod = substr(s, RSTART + 13, RLENGTH - 13);
++      }
++      close(fn);
++
++      # A single module (common case) also reflects objects that are not part
++      # of a module.  Some of those objects have names that are also a module
++      # name (e.g. core).  We check the associated module file name, and if
++      # they do not match, the object is not part of a module.
++      if (mod !~ / /) {
++              if (!(mod in mods))
++                      mod = "";
++      }
++
++      gsub(/([^/ ]*\/)+/, "", mod);
++      gsub(/-/, "_", mod);
++
++      # At this point, mod is a single (valid) module name, or a list of
++      # module names (that do not need validation).
++      omod[obj] = mod;
++
++      return mod;
++}
++
++# Update the ranges entry for the given module 'mod' in section 'osect'.
++#
++# We use a modified absolute start address (soff + base) as index because we
++# may need to insert an anchor record later that must be at the start of the
++# section data, and the first module may very well start at the same address.
++# So, we use (addr << 1) + 1 to allow a possible anchor record to be placed at
++# (addr << 1).  This is safe because the index is only used to sort the 
entries
++# before writing them out.
++#
++function update_entry(osect, mod, soff, eoff, sect, idx) {
++      sect = sect_in[osect];
++      idx = (soff + sect_base[osect]) * 2 + 1;
++      entries[idx] = sprintf("%s %08x-%08x %s", sect, soff, eoff, mod);
++      count[sect]++;
++}
++
++# Determine the kernel build directory to use (default is .).
++#
++BEGIN {
++      if (ARGC > 4) {
++              kdir = ARGV[ARGC - 1];
++              ARGV[ARGC - 1] = "";
++      } else
++              kdir = ".";
++}
++
++# (1) Build a lookup map of built-in module names.
++#
++# The first file argument is used as input (modules.builtin).
++#
++# Lines will be like:
++#     kernel/crypto/lzo-rle.ko
++# and we record the object name "crypto/lzo-rle".
++#
++ARGIND == 1 {
++      sub(/kernel\//, "");                    # strip off "kernel/" prefix
++      sub(/\.ko$/, "");                       # strip off .ko suffix
++
++      mods[$1] = 1;
++      next;
++}
++
++# (2) Collect address information for each section.
++#
++# The second file argument is used as input (vmlinux.map).
++#
++# We collect the base address of the section in order to convert all addresses
++# in the section into offset values.
++#
++# We collect the address of the anchor (or first symbol in the section if 
there
++# is no explicit anchor) to allow users of the range data to calculate address
++# ranges based on the actual load address of the section in the running 
kernel.
++#
++# We collect the start address of any sub-section (section included in the top
++# level section being processed).  This is needed when the final linking was
++# done using vmlinux.a because then the list of objects contained in each
++# section is to be obtained from vmlinux.o.map.  The offset of the sub-section
++# is recorded here, to be used as an addend when processing vmlinux.o.map
++# later.
++#
++
++# Both GNU ld and LLVM lld linker map format are supported by converting LLVM
++# lld linker map records into equivalent GNU ld linker map records.
++#
++# The first record of the vmlinux.map file provides enough information to know
++# which format we are dealing with.
++#
++ARGIND == 2 && FNR == 1 && NF == 7 && $1 == "VMA" && $7 == "Symbol" {
++      map_is_lld = 1;
++      if (dbg)
++              printf "NOTE: %s uses LLVM lld linker map format\n", FILENAME 
>"/dev/stderr";
++      next;
++}
++
++# (LLD) Convert a section record fronm lld format to ld format.
++#
++# lld: ffffffff82c00000          2c00000   2493c0  8192 .data
++#  ->
++# ld:  .data           0xffffffff82c00000   0x2493c0 load address 
0x0000000002c00000
++#
++ARGIND == 2 && map_is_lld && NF == 5 && /[0-9] [^ ]+$/ {
++      $0 = $5 " 0x"$1 " 0x"$3 " load address 0x"$2;
++}
++
++# (LLD) Convert an anchor record from lld format to ld format.
++#
++# lld: ffffffff81000000          1000000        0     1         _text = .
++#  ->
++# ld:                  0xffffffff81000000                _text = .
++#
++ARGIND == 2 && map_is_lld && !anchor && NF == 7 && raw_addr == "0x"$1 && $6 
== "=" && $7 == "." {
++      $0 = "  0x"$1 " " $5 " = .";
++}
++
++# (LLD) Convert an object record from lld format to ld format.
++#
++# lld:            11480            11480     1f07    16         
vmlinux.a(arch/x86/events/amd/uncore.o):(.text)
++#  ->
++# ld:   .text          0x0000000000011480     0x1f07 
arch/x86/events/amd/uncore.o
++#
++ARGIND == 2 && map_is_lld && NF == 5 && $5 ~ /:\(/ {
++      gsub(/\)/, "");
++      sub(/ vmlinux\.a\(/, " ");
++      sub(/:\(/, " ");
++      $0 = " "$6 " 0x"$1 " 0x"$3 " " $5;
++}
++
++# (LLD) Convert a symbol record from lld format to ld format.
++#
++# We only care about these while processing a section for which no anchor has
++# been determined yet.
++#
++# lld: ffffffff82a859a4          2a859a4        0     1                 
btf_ksym_iter_id
++#  ->
++# ld:                  0xffffffff82a859a4                btf_ksym_iter_id
++#
++ARGIND == 2 && map_is_lld && sect && !anchor && NF == 5 && $5 ~ 
/^[_A-Za-z][_A-Za-z0-9]*$/ {
++      $0 = "  0x"$1 " " $5;
++}
++
++# (LLD) We do not need any other ldd linker map records.
++#
++ARGIND == 2 && map_is_lld && /^[0-9a-f]{16} / {
++      next;
++}
++
++# (LD) Section records with just the section name at the start of the line
++#      need to have the next line pulled in to determine whether it is a
++#      loadable section.  If it is, the next line will contains a hex value
++#      as first and second items.
++#
++ARGIND == 2 && !map_is_lld && NF == 1 && /^[^ ]/ {
++      s = $0;
++      getline;
++      if ($1 !~ /^0x/ || $2 !~ /^0x/)
++              next;
++
++      $0 = s " " $0;
++}
++
++# (LD) Object records with just the section name denote records with a long
++#      section name for which the remainder of the record can be found on the
++#      next line.
++#
++# (This is also needed for vmlinux.o.map, when used.)
++#
++ARGIND >= 2 && !map_is_lld && NF == 1 && /^ [^ \*]/ {
++      s = $0;
++      getline;
++      $0 = s " " $0;
++}
++
++# Beginning a new section - done with the previous one (if any).
++#
++ARGIND == 2 && /^[^ ]/ {
++      sect = 0;
++}
++
++# Process a loadable section (we only care about .-sections).
++#
++# Record the section name and its base address.
++# We also record the raw (non-stripped) address of the section because it can
++# be used to identify an anchor record.
++#
++# Note:
++# Since some AWK implementations cannot handle large integers, we strip off 
the
++# first 4 hex digits from the address.  This is safe because the kernel space
++# is not large enough for addresses to extend into those digits.  The portion
++# to strip off is stored in addr_prefix as a regexp, so further clauses can
++# perform a simple substitution to do the address stripping.
++#
++ARGIND == 2 && /^\./ {
++      # Explicitly ignore a few sections that are not relevant here.
++      if ($1 ~ /^\.orc_/ || $1 ~ /_sites$/ || $1 ~ /\.percpu/)
++              next;
++
++      # Sections with a 0-address can be ignored as well.
++      if ($2 ~ /^0x0+$/)
++              next;
++
++      raw_addr = $2;
++      addr_prefix = "^" substr($2, 1, 6);
++      base = $2;
++      sub(addr_prefix, "0x", base);
++      base = strtonum(base);
++      sect = $1;
++      anchor = 0;
++      sect_base[sect] = base;
++      sect_size[sect] = strtonum($3);
++
++      if (dbg)
++              printf "[%s] BASE   %016x\n", sect, base >"/dev/stderr";
++
++      next;
++}
++
++# If we are not in a section we care about, we ignore the record.
++#
++ARGIND == 2 && !sect {
++      next;
++}
++
++# Record the first anchor symbol for the current section.
++#
++# An anchor record for the section bears the same raw address as the section
++# record.
++#
++ARGIND == 2 && !anchor && NF == 4 && raw_addr == $1 && $3 == "=" && $4 == "." 
{
++      anchor = sprintf("%s %08x-%08x = %s", sect, 0, 0, $2);
++      sect_anchor[sect] = anchor;
++
++      if (dbg)
++              printf "[%s] ANCHOR %016x = %s (.)\n", sect, 0, $2 
>"/dev/stderr";
++
++      next;
++}
++
++# If no anchor record was found for the current section, use the first symbol
++# in the section as anchor.
++#
++ARGIND == 2 && !anchor && NF == 2 && $1 ~ /^0x/ && $2 !~ /^0x/ {
++      addr = $1;
++      sub(addr_prefix, "0x", addr);
++      addr = strtonum(addr) - base;
++      anchor = sprintf("%s %08x-%08x = %s", sect, addr, addr, $2);
++      sect_anchor[sect] = anchor;
++
++      if (dbg)
++              printf "[%s] ANCHOR %016x = %s\n", sect, addr, $2 
>"/dev/stderr";
++
++      next;
++}
++
++# The first occurrence of a section name in an object record establishes the
++# addend (often 0) for that section.  This information is needed to handle
++# sections that get combined in the final linking of vmlinux (e.g. .head.text
++# getting included at the start of .text).
++#
++# If the section does not have a base yet, use the base of the encapsulating
++# section.
++#
++ARGIND == 2 && sect && NF == 4 && /^ [^ \*]/ && !($1 in sect_addend) {
++      if (!($1 in sect_base)) {
++              sect_base[$1] = base;
++
++              if (dbg)
++                      printf "[%s] BASE   %016x\n", $1, base >"/dev/stderr";
++      }
++
++      addr = $2;
++      sub(addr_prefix, "0x", addr);
++      addr = strtonum(addr);
++      sect_addend[$1] = addr - sect_base[$1];
++      sect_in[$1] = sect;
++
++      if (dbg)
++              printf "[%s] ADDEND %016x - %016x = %016x\n",  $1, addr, base, 
sect_addend[$1] >"/dev/stderr";
++
++      # If the object is vmlinux.o then we will need vmlinux.o.map to get the
++      # actual offsets of objects.
++      if ($4 == "vmlinux.o")
++              need_o_map = 1;
++}
++
++# (3) Collect offset ranges (relative to the section base address) for 
built-in
++# modules.
++#
++# If the final link was done using the actual objects, vmlinux.map contains 
all
++# the information we need (see section (3a)).
++# If linking was done using vmlinux.a as intermediary, we will need to process
++# vmlinux.o.map (see section (3b)).
++
++# (3a) Determine offset range info using vmlinux.map.
++#
++# Since we are already processing vmlinux.map, the top level section that is
++# being processed is already known.  If we do not have a base address for it,
++# we do not need to process records for it.
++#
++# Given the object name, we determine the module(s) (if any) that the current
++# object is associated with.
++#
++# If we were already processing objects for a (list of) module(s):
++#  - If the current object belongs to the same module(s), update the range 
data
++#    to include the current object.
++#  - Otherwise, ensure that the end offset of the range is valid.
++#
++# If the current object does not belong to a built-in module, ignore it.
++#
++# If it does, we add a new built-in module offset range record.
++#
++ARGIND == 2 && !need_o_map && /^ [^ ]/ && NF == 4 && $3 != "0x0" {
++      if (!(sect in sect_base))
++              next;
++
++      # Turn the address into an offset from the section base.
++      soff = $2;
++      sub(addr_prefix, "0x", soff);
++      soff = strtonum(soff) - sect_base[sect];
++      eoff = soff + strtonum($3);
++
++      # Determine which (if any) built-in modules the object belongs to.
++      mod = get_module_info($4);
++
++      # If we are processing a built-in module:
++      #   - If the current object is within the same module, we update its
++      #     entry by extending the range and move on
++      #   - Otherwise:
++      #       + If we are still processing within the same main section, we
++      #         validate the end offset against the start offset of the
++      #         current object (e.g. .rodata.str1.[18] objects are often
++      #         listed with an incorrect size in the linker map)
++      #       + Otherwise, we validate the end offset against the section
++      #         size
++      if (mod_name) {
++              if (mod == mod_name) {
++                      mod_eoff = eoff;
++                      update_entry(mod_sect, mod_name, mod_soff, eoff);
++
++                      next;
++              } else if (sect == sect_in[mod_sect]) {
++                      if (mod_eoff > soff)
++                              update_entry(mod_sect, mod_name, mod_soff, 
soff);
++              } else {
++                      v = sect_size[sect_in[mod_sect]];
++                      if (mod_eoff > v)
++                              update_entry(mod_sect, mod_name, mod_soff, v);
++              }
++      }
++
++      mod_name = mod;
++
++      # If we encountered an object that is not part of a built-in module, we
++      # do not need to record any data.
++      if (!mod)
++              next;
++
++      # At this point, we encountered the start of a new built-in module.
++      mod_name = mod;
++      mod_soff = soff;
++      mod_eoff = eoff;
++      mod_sect = $1;
++      update_entry($1, mod, soff, mod_eoff);
++
++      next;
++}
++
++# If we do not need to parse the vmlinux.o.map file, we are done.
++#
++ARGIND == 3 && !need_o_map {
++      if (dbg)
++              printf "Note: %s is not needed.\n", FILENAME >"/dev/stderr";
++      exit;
++}
++
++# (3) Collect offset ranges (relative to the section base address) for 
built-in
++# modules.
++#
++
++# (LLD) Convert an object record from lld format to ld format.
++#
++ARGIND == 3 && map_is_lld && NF == 5 && $5 ~ /:\(/ {
++      gsub(/\)/, "");
++      sub(/:\(/, " ");
++
++      sect = $6;
++      if (!(sect in sect_addend))
++              next;
++
++      sub(/ vmlinux\.a\(/, " ");
++      $0 = " "sect " 0x"$1 " 0x"$3 " " $5;
++}
++
++# (3b) Determine offset range info using vmlinux.o.map.
++#
++# If we do not know an addend for the object's section, we are interested in
++# anything within that section.
++#
++# Determine the top-level section that the object's section was included in
++# during the final link.  This is the section name offset range data will be
++# associated with for this object.
++#
++# The remainder of the processing of the current object record follows the
++# procedure outlined in (3a).
++#
++ARGIND == 3 && /^ [^ ]/ && NF == 4 && $3 != "0x0" {
++      osect = $1;
++      if (!(osect in sect_addend))
++              next;
++
++      # We need to work with the main section.
++      sect = sect_in[osect];
++
++      # Turn the address into an offset from the section base.
++      soff = $2;
++      sub(addr_prefix, "0x", soff);
++      soff = strtonum(soff) + sect_addend[osect];
++      eoff = soff + strtonum($3);
++
++      # Determine which (if any) built-in modules the object belongs to.
++      mod = get_module_info($4);
++
++      # If we are processing a built-in module:
++      #   - If the current object is within the same module, we update its
++      #     entry by extending the range and move on
++      #   - Otherwise:
++      #       + If we are still processing within the same main section, we
++      #         validate the end offset against the start offset of the
++      #         current object (e.g. .rodata.str1.[18] objects are often
++      #         listed with an incorrect size in the linker map)
++      #       + Otherwise, we validate the end offset against the section
++      #         size
++      if (mod_name) {
++              if (mod == mod_name) {
++                      mod_eoff = eoff;
++                      update_entry(mod_sect, mod_name, mod_soff, eoff);
++
++                      next;
++              } else if (sect == sect_in[mod_sect]) {
++                      if (mod_eoff > soff)
++                              update_entry(mod_sect, mod_name, mod_soff, 
soff);
++              } else {
++                      v = sect_size[sect_in[mod_sect]];
++                      if (mod_eoff > v)
++                              update_entry(mod_sect, mod_name, mod_soff, v);
++              }
++      }
++
++      mod_name = mod;
++
++      # If we encountered an object that is not part of a built-in module, we
++      # do not need to record any data.
++      if (!mod)
++              next;
++
++      # At this point, we encountered the start of a new built-in module.
++      mod_name = mod;
++      mod_soff = soff;
++      mod_eoff = eoff;
++      mod_sect = osect;
++      update_entry(osect, mod, soff, mod_eoff);
++
++      next;
++}
++
++# (4) Generate the output.
++#
++# Anchor records are added for each section that contains offset range data
++# records.  They are added at an adjusted section base address (base << 1) to
++# ensure they come first in the second records (see update_entry() above for
++# more information).
++#
++# All entries are sorted by (adjusted) address to ensure that the output can 
be
++# parsed in strict ascending address order.
++#
++END {
++      for (sect in count) {
++              if (sect in sect_anchor)
++                      entries[sect_base[sect] * 2] = sect_anchor[sect];
++      }
++
++      n = asorti(entries, indices);
++      for (i = 1; i <= n; i++)
++              print entries[indices[i]];
++}
+diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
+index f48d72d22dc2a..d7e6cd7781256 100644
+--- a/scripts/mod/modpost.c
++++ b/scripts/mod/modpost.c
+@@ -733,6 +733,7 @@ static const char *const section_white_list[] =
+       ".comment*",
+       ".debug*",
+       ".zdebug*",             /* Compressed debug sections. */
++        ".ctf",                       /* Type info */
+       ".GCC.command.line",    /* record-gcc-switches */
+       ".mdebug*",        /* alpha, score, mips etc. */
+       ".pdr",            /* alpha, score, mips etc. */
+diff --git a/scripts/modules_builtin.c b/scripts/modules_builtin.c
+new file mode 100644
+index 0000000000000..df52932a4417b
+--- /dev/null
++++ b/scripts/modules_builtin.c
+@@ -0,0 +1,200 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * A simple modules_builtin reader.
++ *
++ * (C) 2014, 2022 Oracle, Inc.  All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ */
++
++#include <errno.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++
++#include "modules_builtin.h"
++
++/*
++ * Read a modules.builtin.objs file and translate it into a stream of
++ * name / module-name pairs.
++ */
++
++/*
++ * Construct a modules.builtin.objs iterator.
++ */
++struct modules_builtin_iter *
++modules_builtin_iter_new(const char *modules_builtin_file)
++{
++      struct modules_builtin_iter *i;
++
++      i = calloc(1, sizeof(struct modules_builtin_iter));
++      if (i == NULL)
++              return NULL;
++
++      i->f = fopen(modules_builtin_file, "r");
++
++      if (i->f == NULL) {
++              fprintf(stderr, "Cannot open builtin module file %s: %s\n",
++                      modules_builtin_file, strerror(errno));
++              return NULL;
++      }
++
++      return i;
++}
++
++/*
++ * Iterate, returning a new null-terminated array of object file names, and a
++ * new dynamically-allocated module name.  (The module name passed in is 
freed.)
++ *
++ * The array of object file names should be freed by the caller: the strings 
it
++ * points to are owned by the iterator, and should not be freed.
++ */
++
++char ** __attribute__((__nonnull__))
++modules_builtin_iter_next(struct modules_builtin_iter *i, char **module_name)
++{
++      size_t npaths = 1;
++      char **module_paths;
++      char *last_slash;
++      char *last_dot;
++      char *trailing_linefeed;
++      char *object_name = i->line;
++      char *dash;
++      int composite = 0;
++
++      /*
++       * Read in all module entries, computing the suffixless, pathless name
++       * of the module and building the next arrayful of object file names for
++       * return.
++       *
++       * Modules can consist of multiple files: in this case, the portion
++       * before the colon is the path to the module (as before): the portion
++       * after the colon is a space-separated list of files that should be
++       * considered part of this module.  In this case, the portion before the
++       * name is an "object file" that does not actually exist: it is merged
++       * into built-in.a without ever being written out.
++       *
++       * All module names have - translated to _, to match what is done to the
++       * names of the same things when built as modules.
++       */
++
++      /*
++       * Reinvocation of exhausted iterator. Return NULL, once.
++       */
++retry:
++      if (getline(&i->line, &i->line_size, i->f) < 0) {
++              if (ferror(i->f)) {
++                      fprintf(stderr, "Error reading from modules_builtin 
file:"
++                              " %s\n", strerror(errno));
++                      exit(1);
++              }
++              rewind(i->f);
++              return NULL;
++      }
++
++      if (i->line[0] == '\0')
++              goto retry;
++
++      trailing_linefeed = strchr(i->line, '\n');
++      if (trailing_linefeed != NULL)
++              *trailing_linefeed = '\0';
++
++      /*
++       * Slice the line in two at the colon, if any.  If there is anything
++       * past the ': ', this is a composite module.  (We allow for no colon
++       * for robustness, even though one should always be present.)
++       */
++      if (strchr(i->line, ':') != NULL) {
++              char *name_start;
++
++              object_name = strchr(i->line, ':');
++              *object_name = '\0';
++              object_name++;
++              name_start = object_name + strspn(object_name, " \n");
++              if (*name_start != '\0') {
++                      composite = 1;
++                      object_name = name_start;
++              }
++      }
++
++      /*
++       * Figure out the module name.
++       */
++      last_slash = strrchr(i->line, '/');
++      last_slash = (!last_slash) ? i->line :
++              last_slash + 1;
++      free(*module_name);
++      *module_name = strdup(last_slash);
++      dash = *module_name;
++
++      while (dash != NULL) {
++              dash = strchr(dash, '-');
++              if (dash != NULL)
++                      *dash = '_';
++      }
++
++      last_dot = strrchr(*module_name, '.');
++      if (last_dot != NULL)
++              *last_dot = '\0';
++
++      /*
++       * Multifile separator? Object file names explicitly stated:
++       * slice them up and shuffle them in.
++       *
++       * The array size may be an overestimate if any object file
++       * names start or end with spaces (very unlikely) but cannot be
++       * an underestimate.  (Check for it anyway.)
++       */
++      if (composite) {
++              char *one_object;
++
++              for (npaths = 0, one_object = object_name;
++                   one_object != NULL;
++                   npaths++, one_object = strchr(one_object + 1, ' '));
++      }
++
++      module_paths = malloc((npaths + 1) * sizeof(char *));
++      if (!module_paths) {
++              fprintf(stderr, "%s: out of memory on module %s\n", __func__,
++                      *module_name);
++              exit(1);
++      }
++
++      if (composite) {
++              char *one_object;
++              size_t i = 0;
++
++              while ((one_object = strsep(&object_name, " ")) != NULL) {
++                      if (i >= npaths) {
++                              fprintf(stderr, "%s: num_objs overflow on 
module "
++                                      "%s: this is a bug.\n", __func__,
++                                      *module_name);
++                              exit(1);
++                      }
++
++                      module_paths[i++] = one_object;
++              }
++      } else
++              module_paths[0] = i->line;      /* untransformed module name */
++
++      module_paths[npaths] = NULL;
++
++      return module_paths;
++}
++
++/*
++ * Free an iterator. Can be called while iteration is underway, so even
++ * state that is freed at the end of iteration must be freed here too.
++ */
++void
++modules_builtin_iter_free(struct modules_builtin_iter *i)
++{
++      if (i == NULL)
++              return;
++      fclose(i->f);
++      free(i->line);
++      free(i);
++}
+diff --git a/scripts/modules_builtin.h b/scripts/modules_builtin.h
+new file mode 100644
+index 0000000000000..5138792b42ef0
+--- /dev/null
++++ b/scripts/modules_builtin.h
+@@ -0,0 +1,48 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * A simple modules.builtin.objs reader.
++ *
++ * (C) 2014, 2022 Oracle, Inc.  All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ */
++
++#ifndef _LINUX_MODULES_BUILTIN_H
++#define _LINUX_MODULES_BUILTIN_H
++
++#include <stdio.h>
++#include <stddef.h>
++
++/*
++ * modules.builtin.objs iteration state.
++ */
++struct modules_builtin_iter {
++      FILE *f;
++      char *line;
++      size_t line_size;
++};
++
++/*
++ * Construct a modules_builtin.objs iterator.
++ */
++struct modules_builtin_iter *
++modules_builtin_iter_new(const char *modules_builtin_file);
++
++/*
++ * Iterate, returning a new null-terminated array of object file names, and a
++ * new dynamically-allocated module name.  (The module name passed in is 
freed.)
++ *
++ * The array of object file names should be freed by the caller: the strings 
it
++ * points to are owned by the iterator, and should not be freed.
++ */
++
++char ** __attribute__((__nonnull__))
++modules_builtin_iter_next(struct modules_builtin_iter *i, char **module_name);
++
++void
++modules_builtin_iter_free(struct modules_builtin_iter *i);
++
++#endif
+diff --git a/scripts/package/kernel.spec b/scripts/package/kernel.spec
+index c52d517b93647..8f75906a96314 100644
+--- a/scripts/package/kernel.spec
++++ b/scripts/package/kernel.spec
+@@ -53,12 +53,18 @@ patch -p1 < %{SOURCE2}
+ 
+ %build
+ %{make} %{makeflags} KERNELRELEASE=%{KERNELRELEASE} 
KBUILD_BUILD_VERSION=%{release}
++%if %{with_ctf}
++%{make} %{makeflags} KERNELRELEASE=%{KERNELRELEASE} 
KBUILD_BUILD_VERSION=%{release} ctf
++%endif
+ 
+ %install
+ mkdir -p %{buildroot}/lib/modules/%{KERNELRELEASE}
+ cp $(%{make} %{makeflags} -s image_name) 
%{buildroot}/lib/modules/%{KERNELRELEASE}/vmlinuz
+ # DEPMOD=true makes depmod no-op. We do not package depmod-generated files.
+ %{make} %{makeflags} INSTALL_MOD_PATH=%{buildroot} DEPMOD=true modules_install
++%if %{with_ctf}
++%{make} %{makeflags} INSTALL_MOD_PATH=%{buildroot} ctf_install
++%endif
+ %{make} %{makeflags} INSTALL_HDR_PATH=%{buildroot}/usr headers_install
+ cp System.map %{buildroot}/lib/modules/%{KERNELRELEASE}
+ cp .config %{buildroot}/lib/modules/%{KERNELRELEASE}/config
+diff --git a/scripts/package/mkspec b/scripts/package/mkspec
+index ce201bfa8377c..aeb43c7ab1229 100755
+--- a/scripts/package/mkspec
++++ b/scripts/package/mkspec
+@@ -21,10 +21,16 @@ else
+ echo '%define with_devel 0'
+ fi
+ 
++if grep -q CONFIG_CTF=y include/config/auto.conf; then
++echo '%define with_ctf %{?_without_ctf: 0} %{?!_without_ctf: 1}'
++else
++echo '%define with_ctf 0'
++fi
+ cat<<EOF
+ %define ARCH ${ARCH}
+ %define KERNELRELEASE ${KERNELRELEASE}
+ %define pkg_release $("${srctree}/init/build-version")
++
+ EOF
+ 
+ cat "${srctree}/scripts/package/kernel.spec"
+diff --git a/scripts/remove-ctf-lds.awk b/scripts/remove-ctf-lds.awk
+new file mode 100644
+index 0000000000000..5d94d6ee99227
+--- /dev/null
++++ b/scripts/remove-ctf-lds.awk
+@@ -0,0 +1,12 @@
++# SPDX-License-Identifier: GPL-2.0
++# See Makefile.vmlinux_o
++
++BEGIN {
++    discards = 0; p = 0
++}
++
++/^====/ { p = 1; next; }
++p && /\.ctf/ { next; }
++p && !discards && /DISCARD/ { sub(/\} *$/, " *(.ctf) }"); discards = 1 }
++p && /^\}/ && !discards { print "  /DISCARD/ : { *(.ctf) }"; }
++p { print $0; }
+diff --git a/scripts/verify_builtin_ranges.awk 
b/scripts/verify_builtin_ranges.awk
+new file mode 100755
+index 0000000000000..f513841da83e1
+--- /dev/null
++++ b/scripts/verify_builtin_ranges.awk
+@@ -0,0 +1,356 @@
++#!/usr/bin/gawk -f
++# SPDX-License-Identifier: GPL-2.0
++# verify_builtin_ranges.awk: Verify address range data for builtin modules
++# Written by Kris Van Hees <[email protected]>
++#
++# Usage: verify_builtin_ranges.awk modules.builtin.ranges System.map \
++#                                modules.builtin vmlinux.map vmlinux.o.map \
++#                                [ <build-dir> ]
++#
++
++# Return the module name(s) (if any) associated with the given object.
++#
++# If we have seen this object before, return information from the cache.
++# Otherwise, retrieve it from the corresponding .cmd file.
++#
++function get_module_info(fn, mod, obj, s) {
++      if (fn in omod)
++              return omod[fn];
++
++      if (match(fn, /\/[^/]+$/) == 0)
++              return "";
++
++      obj = fn;
++      mod = "";
++      fn = kdir "/" substr(fn, 1, RSTART) "." substr(fn, RSTART + 1) ".cmd";
++      if (getline s <fn == 1) {
++              if (match(s, /DKBUILD_MODFILE=['"]+[^'"]+/) > 0) {
++                      mod = substr(s, RSTART + 16, RLENGTH - 16);
++                      gsub(/['"]/, "", mod);
++              } else if (match(s, /RUST_MODFILE=[^ ]+/) > 0)
++                      mod = substr(s, RSTART + 13, RLENGTH - 13);
++      } else {
++              print "ERROR: Failed to read: " fn "\n\n" \
++                    "  Invalid kernel build directory (" kdir ")\n" \
++                    "  or its content does not match " ARGV[1] >"/dev/stderr";
++              close(fn);
++              total = 0;
++              exit(1);
++      }
++      close(fn);
++
++      # A single module (common case) also reflects objects that are not part
++      # of a module.  Some of those objects have names that are also a module
++      # name (e.g. core).  We check the associated module file name, and if
++      # they do not match, the object is not part of a module.
++      if (mod !~ / /) {
++              if (!(mod in mods))
++                      mod = "";
++      }
++
++      gsub(/([^/ ]*\/)+/, "", mod);
++      gsub(/-/, "_", mod);
++
++      # At this point, mod is a single (valid) module name, or a list of
++      # module names (that do not need validation).
++      omod[obj] = mod;
++
++      return mod;
++}
++
++# Return a representative integer value for a given hexadecimal address.
++#
++# Since all kernel addresses fall within the same memory region, we can safely
++# strip off the first 6 hex digits before performing the hex-to-dec 
conversion,
++# thereby avoiding integer overflows.
++#
++function addr2val(val) {
++      sub(/^0x/, "", val);
++      if (length(val) == 16)
++              val = substr(val, 5);
++      return strtonum("0x" val);
++}
++
++# Determine the kernel build directory to use (default is .).
++#
++BEGIN {
++      if (ARGC > 6) {
++              kdir = ARGV[ARGC - 1];
++              ARGV[ARGC - 1] = "";
++      } else
++              kdir = ".";
++}
++
++# (1) Load the built-in module address range data.
++#
++ARGIND == 1 {
++      ranges[FNR] = $0;
++      rcnt++;
++      next;
++}
++
++# (2) Annotate System.map symbols with module names.
++#
++ARGIND == 2 {
++      addr = addr2val($1);
++      name = $3;
++
++      while (addr >= mod_eaddr) {
++              if (sect_symb) {
++                      if (sect_symb != name)
++                              next;
++
++                      sect_base = addr - sect_off;
++                      if (dbg)
++                              printf "[%s] BASE (%s) %016x - %016x = 
%016x\n", sect_name, sect_symb, addr, sect_off, sect_base >"/dev/stderr";
++                      sect_symb = 0;
++              }
++
++              if (++ridx > rcnt)
++                      break;
++
++              $0 = ranges[ridx];
++              sub(/-/, " ");
++              if ($4 != "=") {
++                      sub(/-/, " ");
++                      mod_saddr = strtonum("0x" $2) + sect_base;
++                      mod_eaddr = strtonum("0x" $3) + sect_base;
++                      $1 = $2 = $3 = "";
++                      sub(/^ +/, "");
++                      mod_name = $0;
++
++                      if (dbg)
++                              printf "[%s] %s from %016x to %016x\n", 
sect_name, mod_name, mod_saddr, mod_eaddr >"/dev/stderr";
++              } else {
++                      sect_name = $1;
++                      sect_off = strtonum("0x" $2);
++                      sect_symb = $5;
++              }
++      }
++
++      idx = addr"-"name;
++      if (addr >= mod_saddr && addr < mod_eaddr)
++              sym2mod[idx] = mod_name;
++
++      next;
++}
++
++# Once we are done annotating the System.map, we no longer need the ranges 
data.
++#
++FNR == 1 && ARGIND == 3 {
++      delete ranges;
++}
++
++# (3) Build a lookup map of built-in module names.
++#
++# Lines from modules.builtin will be like:
++#     kernel/crypto/lzo-rle.ko
++# and we record the object name "crypto/lzo-rle".
++#
++ARGIND == 3 {
++      sub(/kernel\//, "");                    # strip off "kernel/" prefix
++      sub(/\.ko$/, "");                       # strip off .ko suffix
++
++      mods[$1] = 1;
++      next;
++}
++
++# (4) Get a list of symbols (per object).
++#
++# Symbols by object are read from vmlinux.map, with fallback to vmlinux.o.map
++# if vmlinux is found to have inked in vmlinux.o.
++#
++
++# If we were able to get the data we need from vmlinux.map, there is no need 
to
++# process vmlinux.o.map.
++#
++FNR == 1 && ARGIND == 5 && total > 0 {
++      if (dbg)
++              printf "Note: %s is not needed.\n", FILENAME >"/dev/stderr";
++      exit;
++}
++
++# First determine whether we are dealing with a GNU ld or LLVM lld linker map.
++#
++ARGIND >= 4 && FNR == 1 && NF == 7 && $1 == "VMA" && $7 == "Symbol" {
++      map_is_lld = 1;
++      next;
++}
++
++# (LLD) Convert a section record fronm lld format to ld format.
++#
++ARGIND >= 4 && map_is_lld && NF == 5 && /[0-9] [^ ]/ {
++      $0 = $5 " 0x"$1 " 0x"$3 " load address 0x"$2;
++}
++
++# (LLD) Convert an object record from lld format to ld format.
++#
++ARGIND >= 4 && map_is_lld && NF == 5 && $5 ~ /:\(\./ {
++      gsub(/\)/, "");
++      sub(/:\(/, " ");
++      sub(/ vmlinux\.a\(/, " ");
++      $0 = " "$6 " 0x"$1 " 0x"$3 " " $5;
++}
++
++# (LLD) Convert a symbol record from lld format to ld format.
++#
++ARGIND >= 4 && map_is_lld && NF == 5 && $5 ~ /^[A-Za-z_][A-Za-z0-9_]*$/ {
++      $0 = "  0x" $1 " " $5;
++}
++
++# (LLD) We do not need any other ldd linker map records.
++#
++ARGIND >= 4 && map_is_lld && /^[0-9a-f]{16} / {
++      next;
++}
++
++# Handle section records with long section names (spilling onto a 2nd line).
++#
++ARGIND >= 4 && !map_is_lld && NF == 1 && /^[^ ]/ {
++      s = $0;
++      getline;
++      $0 = s " " $0;
++}
++
++# Next section - previous one is done.
++#
++ARGIND >= 4 && /^[^ ]/ {
++      sect = 0;
++}
++
++# Get the (top level) section name.
++#
++ARGIND >= 4 && /^[^ ]/ && $2 ~ /^0x/ && $3 ~ /^0x/ {
++      # Empty section or per-CPU section - ignore.
++      if (NF < 3 || $1 ~ /\.percpu/) {
++              sect = 0;
++              next;
++      }
++
++      sect = $1;
++
++      next;
++}
++
++# If we are not currently in a section we care about, ignore records.
++#
++!sect {
++      next;
++}
++
++# Handle object records with long section names (spilling onto a 2nd line).
++#
++ARGIND >= 4 && /^ [^ \*]/ && NF == 1 {
++      # If the section name is long, the remainder of the entry is found on
++      # the next line.
++      s = $0;
++      getline;
++      $0 = s " " $0;
++}
++
++# If the object is vmlinux.o, we need to consult vmlinux.o.map for per-object
++# symbol information
++#
++ARGIND == 4 && /^ [^ ]/ && NF == 4 {
++      idx = sect":"$1;
++      if (!(idx in sect_addend)) {
++              sect_addend[idx] = addr2val($2);
++              if (dbg)
++                      printf "ADDEND %s = %016x\n", idx, sect_addend[idx] 
>"/dev/stderr";
++      }
++      if ($4 == "vmlinux.o") {
++              need_o_map = 1;
++              next;
++      }
++}
++
++# If data from vmlinux.o.map is needed, we only process section and object
++# records from vmlinux.map to determine which section we need to pay attention
++# to in vmlinux.o.map.  So skip everything else from vmlinux.map.
++#
++ARGIND == 4 && need_o_map {
++      next;
++}
++
++# Get module information for the current object.
++#
++ARGIND >= 4 && /^ [^ ]/ && NF == 4 {
++      msect = $1;
++      mod_name = get_module_info($4);
++      mod_eaddr = addr2val($2) + addr2val($3);
++
++      next;
++}
++
++# Process a symbol record.
++#
++# Evaluate the module information obtained from vmlinux.map (or vmlinux.o.map)
++# as follows:
++#  - For all symbols in a given object:
++#     - If the symbol is annotated with the same module name(s) that the 
object
++#       belongs to, count it as a match.
++#     - Otherwise:
++#        - If the symbol is known to have duplicates of which at least one is
++#          in a built-in module, disregard it.
++#        - If the symbol us not annotated with any module name(s) AND the
++#          object belongs to built-in modules, count it as missing.
++#        - Otherwise, count it as a mismatch.
++#
++ARGIND >= 4 && /^ / && NF == 2 && $1 ~ /^0x/ {
++      idx = sect":"msect;
++      if (!(idx in sect_addend))
++              next;
++
++      addr = addr2val($1);
++
++      # Handle the rare but annoying case where a 0-size symbol is placed at
++      # the byte *after* the module range.  Based on vmlinux.map it will be
++      # considered part of the current object, but it falls just beyond the
++      # module address range.  Unfortunately, its address could be at the
++      # start of another built-in module, so the only safe thing to do is to
++      # ignore it.
++      if (mod_name && addr == mod_eaddr)
++              next;
++
++      # If we are processing vmlinux.o.map, we need to apply the base address
++      # of the section to the relative address on the record.
++      #
++      if (ARGIND == 5)
++              addr += sect_addend[idx];
++
++      idx = addr"-"$2;
++      mod = "";
++      if (idx in sym2mod) {
++              mod = sym2mod[idx];
++              if (sym2mod[idx] == mod_name) {
++                      mod_matches++;
++                      matches++;
++              } else if (mod_name == "") {
++                      print $2 " in " sym2mod[idx] " (should NOT be)";
++                      mismatches++;
++              } else {
++                      print $2 " in " sym2mod[idx] " (should be " mod_name 
")";
++                      mismatches++;
++              }
++      } else if (mod_name != "") {
++              print $2 " should be in " mod_name;
++              missing++;
++      } else
++              matches++;
++
++      total++;
++
++      next;
++}
++
++# Issue the comparison report.
++#
++END {
++      if (total) {
++              printf "Verification of %s:\n", ARGV[1];
++              printf "  Correct matches:  %6d (%d%% of total)\n", matches, 
100 * matches / total;
++              printf "    Module matches: %6d (%d%% of matches)\n", 
mod_matches, 100 * mod_matches / matches;
++              printf "  Mismatches:       %6d (%d%% of total)\n", mismatches, 
100 * mismatches / total;
++              printf "  Missing:          %6d (%d%% of total)\n", missing, 
100 * missing / total;
++      }
++}

Reply via email to