summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/.gitignore1
-rw-r--r--scripts/Kbuild.include2
-rw-r--r--scripts/Kconfig.include11
-rw-r--r--scripts/Makefile6
-rw-r--r--scripts/Makefile.autofdo24
-rw-r--r--scripts/Makefile.btf8
-rw-r--r--scripts/Makefile.build180
-rw-r--r--scripts/Makefile.clang3
-rw-r--r--scripts/Makefile.clean2
-rw-r--r--scripts/Makefile.compiler29
-rw-r--r--scripts/Makefile.defconf13
-rw-r--r--scripts/Makefile.dtbs144
-rw-r--r--scripts/Makefile.extrawarn15
-rw-r--r--scripts/Makefile.host13
-rw-r--r--scripts/Makefile.kasan84
-rw-r--r--scripts/Makefile.lib219
-rw-r--r--scripts/Makefile.modfinal34
-rw-r--r--scripts/Makefile.modinst20
-rw-r--r--scripts/Makefile.modpost26
-rw-r--r--scripts/Makefile.package10
-rw-r--r--scripts/Makefile.propeller39
-rw-r--r--scripts/Makefile.ubsan10
-rw-r--r--scripts/Makefile.vmlinux102
-rw-r--r--scripts/Makefile.vmlinux_o20
-rw-r--r--scripts/bash-completion/make451
-rw-r--r--scripts/basic/fixdep.c15
-rwxr-xr-xscripts/bpf_doc.py53
-rwxr-xr-xscripts/checkpatch.pl104
-rwxr-xr-xscripts/checktransupdate.py214
-rwxr-xr-xscripts/clang-tools/gen_compile_commands.py8
-rwxr-xr-xscripts/coccicheck6
-rw-r--r--scripts/coccinelle/api/stream_open.cocci1
-rw-r--r--scripts/coccinelle/api/string_choices.cocci277
-rw-r--r--scripts/coccinelle/misc/newline_in_nl_msg.cocci13
-rw-r--r--scripts/coccinelle/misc/secs_to_jiffies.cocci32
-rwxr-xr-xscripts/config26
-rw-r--r--scripts/const_structs.checkpatch1
-rwxr-xr-xscripts/decode_stacktrace.sh70
-rwxr-xr-xscripts/depmod.sh4
-rwxr-xr-xscripts/documentation-file-ref-check2
-rw-r--r--scripts/dtc/checks.c16
-rwxr-xr-xscripts/dtc/dt-extract-compatibles13
-rw-r--r--scripts/dtc/fdtoverlay.c2
-rw-r--r--scripts/dtc/version_gen.h2
-rwxr-xr-xscripts/export_report.pl186
-rwxr-xr-xscripts/extract-fwblobs30
-rwxr-xr-xscripts/faddr2line2
-rw-r--r--scripts/gcc-plugins/randomize_layout_plugin.c4
-rwxr-xr-xscripts/gcc-x86_32-has-stack-protector.sh8
-rwxr-xr-xscripts/gcc-x86_64-has-stack-protector.sh4
-rw-r--r--scripts/gdb/linux/cpus.py26
-rw-r--r--scripts/gdb/linux/kasan.py44
-rw-r--r--scripts/gdb/linux/modules.py3
-rw-r--r--scripts/gdb/linux/proc.py4
-rw-r--r--scripts/gdb/linux/rbtree.py12
-rw-r--r--scripts/gdb/linux/stackdepot.py27
-rw-r--r--scripts/gdb/linux/symbols.py47
-rw-r--r--scripts/gdb/linux/timerlist.py31
-rw-r--r--scripts/gdb/linux/utils.py35
-rw-r--r--scripts/gdb/vmlinux-gdb.py1
-rwxr-xr-xscripts/gen-crc-consts.py291
-rw-r--r--scripts/gen_packed_field_checks.c37
-rw-r--r--scripts/gendwarfksyms/.gitignore2
-rw-r--r--scripts/gendwarfksyms/Makefile12
-rw-r--r--scripts/gendwarfksyms/cache.c51
-rw-r--r--scripts/gendwarfksyms/die.c166
-rw-r--r--scripts/gendwarfksyms/dwarf.c1171
-rw-r--r--scripts/gendwarfksyms/examples/kabi.h157
-rw-r--r--scripts/gendwarfksyms/examples/kabi_ex.c30
-rw-r--r--scripts/gendwarfksyms/examples/kabi_ex.h263
-rw-r--r--scripts/gendwarfksyms/examples/symbolptr.c33
-rw-r--r--scripts/gendwarfksyms/gendwarfksyms.c187
-rw-r--r--scripts/gendwarfksyms/gendwarfksyms.h298
-rw-r--r--scripts/gendwarfksyms/kabi.c336
-rw-r--r--scripts/gendwarfksyms/symbols.c341
-rw-r--r--scripts/gendwarfksyms/types.c481
-rwxr-xr-xscripts/generate_builtin_ranges.awk513
-rwxr-xr-xscripts/generate_rust_analyzer.py110
-rw-r--r--scripts/generate_rust_target.rs124
-rw-r--r--scripts/genksyms/Makefile18
-rw-r--r--scripts/genksyms/genksyms.c186
-rw-r--r--scripts/genksyms/genksyms.h9
-rw-r--r--scripts/genksyms/lex.l23
-rw-r--r--scripts/genksyms/parse.y166
-rwxr-xr-xscripts/get_abi.pl1103
-rwxr-xr-xscripts/get_abi.py214
-rwxr-xr-xscripts/get_feat.pl4
-rwxr-xr-xscripts/get_maintainer.pl66
-rwxr-xr-xscripts/gfp-translate66
-rw-r--r--scripts/head-object-list.txt2
-rw-r--r--scripts/include/hash.h28
-rw-r--r--scripts/include/hashtable.h50
-rw-r--r--scripts/include/list.h119
-rw-r--r--scripts/include/xalloc.h53
-rw-r--r--scripts/integer-wrap-ignore.scl3
-rw-r--r--scripts/ipe/Makefile2
-rw-r--r--scripts/ipe/polgen/.gitignore (renamed from scripts/selinux/genheaders/.gitignore)2
-rw-r--r--scripts/ipe/polgen/Makefile5
-rw-r--r--scripts/ipe/polgen/polgen.c137
-rw-r--r--scripts/kallsyms.c137
-rw-r--r--scripts/kconfig/Makefile4
-rw-r--r--scripts/kconfig/conf.c5
-rw-r--r--scripts/kconfig/confdata.c26
-rw-r--r--scripts/kconfig/expr.c483
-rw-r--r--scripts/kconfig/expr.h27
-rw-r--r--scripts/kconfig/internal.h6
-rw-r--r--scripts/kconfig/lexer.l1
-rw-r--r--scripts/kconfig/lkc.h6
-rw-r--r--scripts/kconfig/lkc_proto.h1
-rw-r--r--scripts/kconfig/mconf.c1
-rw-r--r--scripts/kconfig/menu.c51
-rwxr-xr-xscripts/kconfig/merge_config.sh6
-rw-r--r--scripts/kconfig/nconf.c3
-rw-r--r--scripts/kconfig/nconf.gui.c10
-rw-r--r--scripts/kconfig/parser.y51
-rw-r--r--scripts/kconfig/preprocess.c1
-rw-r--r--scripts/kconfig/qconf.cc218
-rw-r--r--scripts/kconfig/qconf.h19
-rwxr-xr-xscripts/kconfig/streamline_config.pl18
-rw-r--r--scripts/kconfig/symbol.c38
-rw-r--r--scripts/kconfig/util.c63
-rwxr-xr-xscripts/kernel-doc216
-rw-r--r--scripts/lib/abi/abi_parser.py628
-rw-r--r--scripts/lib/abi/abi_regex.py234
-rw-r--r--scripts/lib/abi/helpers.py38
-rw-r--r--scripts/lib/abi/system_symbols.py378
-rwxr-xr-xscripts/link-vmlinux.sh76
-rwxr-xr-xscripts/macro_checker.py131
-rwxr-xr-xscripts/make_fit.py6
-rwxr-xr-xscripts/min-tool-version.sh4
-rwxr-xr-xscripts/mksysmap4
-rw-r--r--scripts/mod/devicetable-offsets.c5
-rw-r--r--scripts/mod/file2alias.c831
-rw-r--r--scripts/mod/mk_elfconfig.c25
-rw-r--r--scripts/mod/modpost.c344
-rw-r--r--scripts/mod/modpost.h65
-rw-r--r--scripts/mod/sumversion.c28
-rw-r--r--scripts/mod/symsearch.c6
-rw-r--r--scripts/module-common.c25
-rw-r--r--scripts/module.lds.S15
-rw-r--r--scripts/nsdeps12
-rw-r--r--scripts/package/PKGBUILD59
-rwxr-xr-xscripts/package/builddeb55
-rwxr-xr-xscripts/package/debian/rules10
-rwxr-xr-xscripts/package/install-extmod-build45
-rw-r--r--scripts/package/kernel.spec46
-rwxr-xr-xscripts/package/mkdebian40
-rwxr-xr-xscripts/package/mkspec10
-rwxr-xr-xscripts/remove-stale-files5
-rwxr-xr-xscripts/rust_is_available.sh21
-rw-r--r--scripts/rust_is_available_bindgen_libclang_concat.h3
-rwxr-xr-xscripts/rust_is_available_test.py34
-rwxr-xr-xscripts/rustc-llvm-version.sh22
-rwxr-xr-xscripts/rustc-version.sh26
-rw-r--r--scripts/rustdoc_test_gen.rs12
-rw-r--r--scripts/selinux/Makefile2
-rw-r--r--scripts/selinux/genheaders/Makefile5
-rw-r--r--scripts/selinux/genheaders/genheaders.c157
-rwxr-xr-xscripts/selinux/install_policy.sh15
-rw-r--r--scripts/selinux/mdp/Makefile2
-rw-r--r--scripts/selinux/mdp/mdp.c7
-rwxr-xr-xscripts/setlocalversion58
-rwxr-xr-xscripts/show_delta2
-rw-r--r--scripts/sign-file.c132
-rw-r--r--scripts/sorttable.c1119
-rw-r--r--scripts/sorttable.h497
-rwxr-xr-xscripts/spdxcheck.py6
-rw-r--r--scripts/spelling.txt70
-rwxr-xr-xscripts/sphinx-pre-install2
-rw-r--r--scripts/ssl-common.h32
-rw-r--r--scripts/subarch.include2
-rw-r--r--scripts/syscall.tbl11
-rwxr-xr-xscripts/tags.sh52
-rw-r--r--scripts/test_fortify.sh66
-rwxr-xr-xscripts/tracing/draw_functrace.py129
-rwxr-xr-xscripts/verify_builtin_ranges.awk370
-rwxr-xr-xscripts/xz_wrap.sh158
177 files changed, 12246 insertions, 4930 deletions
diff --git a/scripts/.gitignore b/scripts/.gitignore
index 3dbb8bb2457b..c2ef68848da5 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
/asn1_compiler
+/gen_packed_field_checks
/generate_rust_target
/insert-sys-cert
/kallsyms
diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include
index ed8a7493524b..8c311b997e24 100644
--- a/scripts/Kbuild.include
+++ b/scripts/Kbuild.include
@@ -205,7 +205,7 @@ if_changed_dep = $(if $(if-changed-cond),$(cmd_and_fixdep),@:)
cmd_and_fixdep = \
$(cmd); \
- scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(dot-target).cmd;\
+ $(objtree)/scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(dot-target).cmd;\
rm -f $(depfile)
# Usage: $(call if_changed_rule,foo)
diff --git a/scripts/Kconfig.include b/scripts/Kconfig.include
index 3500a3d62f0d..33193ca6e803 100644
--- a/scripts/Kconfig.include
+++ b/scripts/Kconfig.include
@@ -64,3 +64,14 @@ ld-version := $(shell,set -- $(ld-info) && echo $2)
cc-option-bit = $(if-success,$(CC) -Werror $(1) -E -x c /dev/null -o /dev/null,$(1))
m32-flag := $(cc-option-bit,-m32)
m64-flag := $(cc-option-bit,-m64)
+
+rustc-version := $(shell,$(srctree)/scripts/rustc-version.sh $(RUSTC))
+rustc-llvm-version := $(shell,$(srctree)/scripts/rustc-llvm-version.sh $(RUSTC))
+
+# $(rustc-option,<flag>)
+# Return y if the Rust compiler supports <flag>, n otherwise
+# Calls to this should be guarded so that they are not evaluated if
+# CONFIG_RUST_IS_AVAILABLE is not set.
+# If you are testing for unstable features, consider testing RUSTC_VERSION
+# instead, as features may have different completeness while available.
+rustc-option = $(success,trap "rm -rf .tmp_$$" EXIT; mkdir .tmp_$$; $(RUSTC) $(1) --crate-type=rlib /dev/null --out-dir=.tmp_$$ -o .tmp_$$/tmp.rlib)
diff --git a/scripts/Makefile b/scripts/Makefile
index dccef663ca82..46f860529df5 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -47,14 +47,16 @@ HOSTCFLAGS_sorttable.o += -DMCOUNT_SORT_ENABLED
endif
# The following programs are only built on demand
-hostprogs += unifdef
+hostprogs += unifdef gen_packed_field_checks
# The module linker script is preprocessed on demand
targets += module.lds
subdir-$(CONFIG_GCC_PLUGINS) += gcc-plugins
-subdir-$(CONFIG_MODVERSIONS) += genksyms
+subdir-$(CONFIG_GENKSYMS) += genksyms
+subdir-$(CONFIG_GENDWARFKSYMS) += gendwarfksyms
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
+subdir-$(CONFIG_SECURITY_IPE) += ipe
# Let clean descend into subdirs
subdir- += basic dtc gdb kconfig mod
diff --git a/scripts/Makefile.autofdo b/scripts/Makefile.autofdo
new file mode 100644
index 000000000000..1caf2457e585
--- /dev/null
+++ b/scripts/Makefile.autofdo
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# Enable available and selected Clang AutoFDO features.
+
+CFLAGS_AUTOFDO_CLANG := -fdebug-info-for-profiling -mllvm -enable-fs-discriminator=true -mllvm -improved-fs-discriminator=true
+
+ifndef CONFIG_DEBUG_INFO
+ CFLAGS_AUTOFDO_CLANG += -gmlt
+endif
+
+ifdef CLANG_AUTOFDO_PROFILE
+ CFLAGS_AUTOFDO_CLANG += -fprofile-sample-use=$(CLANG_AUTOFDO_PROFILE) -ffunction-sections
+ CFLAGS_AUTOFDO_CLANG += -fsplit-machine-functions
+endif
+
+ifdef CONFIG_LTO_CLANG_THIN
+ ifdef CLANG_AUTOFDO_PROFILE
+ KBUILD_LDFLAGS += --lto-sample-profile=$(CLANG_AUTOFDO_PROFILE)
+ endif
+ KBUILD_LDFLAGS += --mllvm=-enable-fs-discriminator=true --mllvm=-improved-fs-discriminator=true -plugin-opt=thinlto
+ KBUILD_LDFLAGS += -plugin-opt=-split-machine-functions
+endif
+
+export CFLAGS_AUTOFDO_CLANG
diff --git a/scripts/Makefile.btf b/scripts/Makefile.btf
index b75f09f3f424..fbaaec2187e5 100644
--- a/scripts/Makefile.btf
+++ b/scripts/Makefile.btf
@@ -3,6 +3,8 @@
pahole-ver := $(CONFIG_PAHOLE_VERSION)
pahole-flags-y :=
+JOBS := $(patsubst -j%,%,$(filter -j%,$(MAKEFLAGS)))
+
ifeq ($(call test-le, $(pahole-ver), 125),y)
# pahole 1.18 through 1.21 can't handle zero-sized per-CPU vars
@@ -12,17 +14,17 @@ endif
pahole-flags-$(call test-ge, $(pahole-ver), 121) += --btf_gen_floats
-pahole-flags-$(call test-ge, $(pahole-ver), 122) += -j
+pahole-flags-$(call test-ge, $(pahole-ver), 122) += -j$(JOBS)
pahole-flags-$(call test-ge, $(pahole-ver), 125) += --skip_encoding_btf_inconsistent_proto --btf_gen_optimized
else
# Switch to using --btf_features for v1.26 and later.
-pahole-flags-$(call test-ge, $(pahole-ver), 126) = -j --btf_features=encode_force,var,float,enum64,decl_tag,type_tag,optimized_func,consistent_func,decl_tag_kfuncs
+pahole-flags-$(call test-ge, $(pahole-ver), 126) = -j$(JOBS) --btf_features=encode_force,var,float,enum64,decl_tag,type_tag,optimized_func,consistent_func,decl_tag_kfuncs
ifneq ($(KBUILD_EXTMOD),)
-module-pahole-flags-$(call test-ge, $(pahole-ver), 126) += --btf_features=distilled_base
+module-pahole-flags-$(call test-ge, $(pahole-ver), 128) += --btf_features=distilled_base
endif
endif
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index efacca63c897..13dcd86e74ca 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -3,7 +3,7 @@
# Building
# ==========================================================================
-src := $(if $(VPATH),$(VPATH)/)$(obj)
+src := $(srcroot)/$(obj)
PHONY := $(obj)/
$(obj)/:
@@ -20,10 +20,6 @@ always-m :=
targets :=
subdir-y :=
subdir-m :=
-EXTRA_AFLAGS :=
-EXTRA_CFLAGS :=
-EXTRA_CPPFLAGS :=
-EXTRA_LDFLAGS :=
asflags-y :=
ccflags-y :=
rustflags-y :=
@@ -34,27 +30,13 @@ subdir-asflags-y :=
subdir-ccflags-y :=
# Read auto.conf if it exists, otherwise ignore
--include include/config/auto.conf
+-include $(objtree)/include/config/auto.conf
include $(srctree)/scripts/Kbuild.include
include $(srctree)/scripts/Makefile.compiler
include $(kbuild-file)
include $(srctree)/scripts/Makefile.lib
-# Do not include hostprogs rules unless needed.
-# $(sort ...) is used here to remove duplicated words and excessive spaces.
-hostprogs := $(sort $(hostprogs))
-ifneq ($(hostprogs),)
-include $(srctree)/scripts/Makefile.host
-endif
-
-# Do not include userprogs rules unless needed.
-# $(sort ...) is used here to remove duplicated words and excessive spaces.
-userprogs := $(sort $(userprogs))
-ifneq ($(userprogs),)
-include $(srctree)/scripts/Makefile.userprogs
-endif
-
ifndef obj
$(warning kbuild: Makefile.build is included improperly)
endif
@@ -71,7 +53,6 @@ endif
# subdir-builtin and subdir-modorder may contain duplications. Use $(sort ...)
subdir-builtin := $(sort $(filter %/built-in.a, $(real-obj-y)))
subdir-modorder := $(sort $(filter %/modules.order, $(obj-m)))
-subdir-dtbslist := $(sort $(filter %/dtbs-list, $(dtb-y)))
targets-for-builtin := $(extra-y)
@@ -122,19 +103,24 @@ cmd_cpp_i_c = $(CPP) $(c_flags) -o $@ $<
$(obj)/%.i: $(obj)/%.c FORCE
$(call if_changed_dep,cpp_i_c)
-genksyms = scripts/genksyms/genksyms \
- $(if $(1), -T $(2)) \
- $(if $(KBUILD_PRESERVE), -p) \
- -r $(or $(wildcard $(2:.symtypes=.symref)), /dev/null)
+getexportsymbols = $(NM) $@ | sed -n 's/.* __export_symbol_\(.*\)/$(1)/p'
-# These mirror gensymtypes_S and co below, keep them in synch.
-cmd_gensymtypes_c = $(CPP) -D__GENKSYMS__ $(c_flags) $< | $(genksyms)
+gendwarfksyms = $(objtree)/scripts/gendwarfksyms/gendwarfksyms \
+ $(if $(KBUILD_SYMTYPES), --symtypes $(@:.o=.symtypes)) \
+ $(if $(KBUILD_GENDWARFKSYMS_STABLE), --stable)
-quiet_cmd_cc_symtypes_c = SYM $(quiet_modtag) $@
- cmd_cc_symtypes_c = $(call cmd_gensymtypes_c,true,$@) >/dev/null
+genksyms = $(objtree)/scripts/genksyms/genksyms \
+ $(if $(KBUILD_SYMTYPES), -T $(@:.o=.symtypes)) \
+ $(if $(KBUILD_PRESERVE), -p) \
+ $(addprefix -r , $(wildcard $(@:.o=.symref)))
-$(obj)/%.symtypes : $(obj)/%.c FORCE
- $(call cmd,cc_symtypes_c)
+# These mirror gensymtypes_S and co below, keep them in synch.
+ifdef CONFIG_GENDWARFKSYMS
+cmd_gensymtypes_c = $(if $(skip_gendwarfksyms),, \
+ $(call getexportsymbols,\1) | $(gendwarfksyms) $@)
+else
+cmd_gensymtypes_c = $(CPP) -D__GENKSYMS__ $(c_flags) $< | $(genksyms)
+endif # CONFIG_GENDWARFKSYMS
# LLVM assembly
# Generate .ll files from .c
@@ -150,17 +136,6 @@ $(obj)/%.ll: $(obj)/%.c FORCE
is-single-obj-m = $(and $(part-of-module),$(filter $@, $(obj-m)),y)
-# When a module consists of a single object, there is no reason to keep LLVM IR.
-# Make $(LD) covert LLVM IR to ELF here.
-ifdef CONFIG_LTO_CLANG
-cmd_ld_single_m = $(if $(is-single-obj-m), ; $(LD) $(ld_flags) -r -o $(tmp-target) $@; mv $(tmp-target) $@)
-endif
-
-quiet_cmd_cc_o_c = CC $(quiet_modtag) $@
- cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< \
- $(cmd_ld_single_m) \
- $(cmd_objtool)
-
ifdef CONFIG_MODVERSIONS
# When module versioning is enabled the following steps are executed:
# o compile a <file>.o from <file>.c
@@ -173,8 +148,7 @@ ifdef CONFIG_MODVERSIONS
gen_symversions = \
if $(NM) $@ 2>/dev/null | grep -q ' __export_symbol_'; then \
- $(call cmd_gensymtypes_$(1),$(KBUILD_SYMTYPES),$(@:.o=.symtypes)) \
- >> $(dot-target).cmd; \
+ $(cmd_gensymtypes_$1) >> $(dot-target).cmd; \
fi
cmd_gen_symversions_c = $(call gen_symversions,c)
@@ -216,29 +190,14 @@ endif # CONFIG_FTRACE_MCOUNT_USE_RECORDMCOUNT
is-standard-object = $(if $(filter-out y%, $(OBJECT_FILES_NON_STANDARD_$(target-stem).o)$(OBJECT_FILES_NON_STANDARD)n),$(is-kernel-object))
+ifdef CONFIG_OBJTOOL
$(obj)/%.o: private objtool-enabled = $(if $(is-standard-object),$(if $(delay-objtool),$(is-single-obj-m),y))
+endif
ifneq ($(findstring 1, $(KBUILD_EXTRA_WARN)),)
cmd_warn_shared_object = $(if $(word 2, $(modname-multi)),$(warning $(kbuild-file): $*.o is added to multiple modules: $(modname-multi)))
endif
-define rule_cc_o_c
- $(call cmd_and_fixdep,cc_o_c)
- $(call cmd,checksrc)
- $(call cmd,checkdoc)
- $(call cmd,gen_objtooldep)
- $(call cmd,gen_symversions_c)
- $(call cmd,record_mcount)
- $(call cmd,warn_shared_object)
-endef
-
-define rule_as_o_S
- $(call cmd_and_fixdep,as_o_S)
- $(call cmd,gen_objtooldep)
- $(call cmd,gen_symversions_S)
- $(call cmd,warn_shared_object)
-endef
-
# Built-in and composite module parts
$(obj)/%.o: $(obj)/%.c $(recordmcount_source) FORCE
$(call if_changed_rule,cc_o_c)
@@ -263,17 +222,18 @@ $(obj)/%.lst: $(obj)/%.c FORCE
# Compile Rust sources (.rs)
# ---------------------------------------------------------------------------
-rust_allowed_features := new_uninit
+rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,raw_ref_op
# `--out-dir` is required to avoid temporaries being created by `rustc` in the
# current working directory, which may be not accessible in the out-of-tree
# modules case.
rust_common_cmd = \
+ OBJTREE=$(abspath $(objtree)) \
RUST_MODFILE=$(modfile) $(RUSTC_OR_CLIPPY) $(rust_flags) \
-Zallow-features=$(rust_allowed_features) \
-Zcrate-attr=no_std \
-Zcrate-attr='feature($(rust_allowed_features))' \
- -Zunstable-options --extern force:alloc --extern kernel \
+ -Zunstable-options --extern pin_init --extern kernel \
--crate-type rlib -L $(objtree)/rust/ \
--crate-name $(basename $(notdir $@)) \
--sysroot=/dev/null \
@@ -288,10 +248,15 @@ rust_common_cmd = \
# would not match each other.
quiet_cmd_rustc_o_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@
- cmd_rustc_o_rs = $(rust_common_cmd) --emit=obj=$@ $<
+ cmd_rustc_o_rs = $(rust_common_cmd) --emit=obj=$@ $< $(cmd_objtool)
+
+define rule_rustc_o_rs
+ $(call cmd_and_fixdep,rustc_o_rs)
+ $(call cmd,gen_objtooldep)
+endef
$(obj)/%.o: $(obj)/%.rs FORCE
- +$(call if_changed_dep,rustc_o_rs)
+ +$(call if_changed_rule,rustc_o_rs)
quiet_cmd_rustc_rsi_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@
cmd_rustc_rsi_rs = \
@@ -313,6 +278,12 @@ quiet_cmd_rustc_ll_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@
$(obj)/%.ll: $(obj)/%.rs FORCE
+$(call if_changed_dep,rustc_ll_rs)
+quiet_cmd_rustc_rs_rs_S = RSCPP $(quiet_modtag) $@
+ cmd_rustc_rs_rs_S = $(CPP) $(c_flags) -xc -C -P $< | sed '1,/^\/\/ Cut here.$$/d' >$@
+
+$(obj)/%.rs: $(obj)/%.rs.S FORCE
+ +$(call if_changed_dep,rustc_rs_rs_S)
+
# Compile assembler sources (.S)
# ---------------------------------------------------------------------------
@@ -324,21 +295,26 @@ $(obj)/%.ll: $(obj)/%.rs FORCE
# This is convoluted. The .S file must first be preprocessed to run guards and
# expand names, then the resulting exports must be constructed into plain
# EXPORT_SYMBOL(symbol); to build our dummy C file, and that gets preprocessed
-# to make the genksyms input.
+# to make the genksyms input or compiled into an object for gendwarfksyms.
#
# These mirror gensymtypes_c and co above, keep them in synch.
-cmd_gensymtypes_S = \
- { echo "\#include <linux/kernel.h>" ; \
- echo "\#include <asm/asm-prototypes.h>" ; \
- $(NM) $@ | sed -n 's/.* __export_symbol_\(.*\)/EXPORT_SYMBOL(\1);/p' ; } | \
- $(CPP) -D__GENKSYMS__ $(c_flags) -xc - | $(genksyms)
-
-quiet_cmd_cc_symtypes_S = SYM $(quiet_modtag) $@
- cmd_cc_symtypes_S = $(call cmd_gensymtypes_S,true,$@) >/dev/null
-
-$(obj)/%.symtypes : $(obj)/%.S FORCE
- $(call cmd,cc_symtypes_S)
-
+getasmexports = \
+ { echo "\#include <linux/kernel.h>" ; \
+ echo "\#include <linux/string.h>" ; \
+ echo "\#include <asm/asm-prototypes.h>" ; \
+ $(call getexportsymbols,EXPORT_SYMBOL(\1);) ; }
+
+ifdef CONFIG_GENDWARFKSYMS
+cmd_gensymtypes_S = \
+ $(getasmexports) | \
+ $(CC) $(c_flags) -c -o $(@:.o=.gendwarfksyms.o) -xc -; \
+ $(call getexportsymbols,\1) | \
+ $(gendwarfksyms) $(@:.o=.gendwarfksyms.o)
+else
+cmd_gensymtypes_S = \
+ $(getasmexports) | \
+ $(CPP) -D__GENKSYMS__ $(c_flags) -xc - | $(genksyms)
+endif # CONFIG_GENDWARFKSYMS
quiet_cmd_cpp_s_S = CPP $(quiet_modtag) $@
cmd_cpp_s_S = $(CPP) $(a_flags) -o $@ $<
@@ -346,9 +322,6 @@ cmd_cpp_s_S = $(CPP) $(a_flags) -o $@ $<
$(obj)/%.s: $(obj)/%.S FORCE
$(call if_changed_dep,cpp_s_S)
-quiet_cmd_as_o_S = AS $(quiet_modtag) $@
- cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $< $(cmd_objtool)
-
ifdef CONFIG_ASM_MODVERSIONS
# versioning matches the C process described above, with difference that
@@ -363,7 +336,7 @@ $(obj)/%.o: $(obj)/%.S FORCE
targets += $(filter-out $(subdir-builtin), $(real-obj-y))
targets += $(filter-out $(subdir-modorder), $(real-obj-m))
-targets += $(real-dtb-y) $(lib-y) $(always-y)
+targets += $(lib-y) $(always-y)
# Linker scripts preprocessor (.lds.S -> .lds)
# ---------------------------------------------------------------------------
@@ -389,7 +362,6 @@ $(obj)/%.asn1.c $(obj)/%.asn1.h: $(src)/%.asn1 $(objtree)/scripts/asn1_compiler
# To build objects in subdirs, we need to descend into the directories
$(subdir-builtin): $(obj)/%/built-in.a: $(obj)/% ;
$(subdir-modorder): $(obj)/%/modules.order: $(obj)/% ;
-$(subdir-dtbslist): $(obj)/%/dtbs-list: $(obj)/% ;
#
# Rule to compile a set of .o files into one .a file (without symbol table)
@@ -405,12 +377,8 @@ quiet_cmd_ar_builtin = AR $@
$(obj)/built-in.a: $(real-obj-y) FORCE
$(call if_changed,ar_builtin)
-#
-# Rule to create modules.order and dtbs-list
-#
-# This is a list of build artifacts (module or dtb) from the current Makefile
-# and its sub-directories. The timestamp should be updated when any of the
-# member files.
+# This is a list of build artifacts from the current Makefile and its
+# sub-directories. The timestamp should be updated when any of the member files.
cmd_gen_order = { $(foreach m, $(real-prereqs), \
$(if $(filter %/$(notdir $@), $m), cat $m, echo $m);) :; } \
@@ -419,9 +387,6 @@ cmd_gen_order = { $(foreach m, $(real-prereqs), \
$(obj)/modules.order: $(obj-m) FORCE
$(call if_changed,gen_order)
-$(obj)/dtbs-list: $(dtb-y) FORCE
- $(call if_changed,gen_order)
-
#
# Rule to compile a set of .o files into one .a file (with symbol table)
#
@@ -450,15 +415,26 @@ intermediate_targets = $(foreach sfx, $(2), \
$(patsubst %$(strip $(1)),%$(sfx), \
$(filter %$(strip $(1)), $(targets))))
# %.asn1.o <- %.asn1.[ch] <- %.asn1
-# %.dtb.o <- %.dtb.S <- %.dtb <- %.dts
-# %.dtbo.o <- %.dtbo.S <- %.dtbo <- %.dtso
-# %.lex.o <- %.lex.c <- %.l
-# %.tab.o <- %.tab.[ch] <- %.y
-targets += $(call intermediate_targets, .asn1.o, .asn1.c .asn1.h) \
- $(call intermediate_targets, .dtb.o, .dtb.S .dtb) \
- $(call intermediate_targets, .dtbo.o, .dtbo.S .dtbo) \
- $(call intermediate_targets, .lex.o, .lex.c) \
- $(call intermediate_targets, .tab.o, .tab.c .tab.h)
+targets += $(call intermediate_targets, .asn1.o, .asn1.c .asn1.h)
+
+# Include additional build rules when necessary
+# ---------------------------------------------------------------------------
+
+# $(sort ...) is used here to remove duplicated words and excessive spaces.
+hostprogs := $(sort $(hostprogs))
+ifneq ($(hostprogs),)
+include $(srctree)/scripts/Makefile.host
+endif
+
+# $(sort ...) is used here to remove duplicated words and excessive spaces.
+userprogs := $(sort $(userprogs))
+ifneq ($(userprogs),)
+include $(srctree)/scripts/Makefile.userprogs
+endif
+
+ifneq ($(need-dtbslist)$(dtb-y)$(dtb-)$(filter %.dtb %.dtb.o %.dtbo.o,$(targets)),)
+include $(srctree)/scripts/Makefile.dtbs
+endif
# Build
# ---------------------------------------------------------------------------
@@ -487,7 +463,7 @@ $(subdir-ym):
need-modorder=$(if $(filter $@/modules.order, $(subdir-modorder)),1) \
$(filter $@/%, $(single-subdir-goals))
-# Add FORCE to the prequisites of a target to force it to be always rebuilt.
+# Add FORCE to the prerequisites of a target to force it to be always rebuilt.
# ---------------------------------------------------------------------------
PHONY += FORCE
diff --git a/scripts/Makefile.clang b/scripts/Makefile.clang
index 6c23c6af797f..b67636b28c35 100644
--- a/scripts/Makefile.clang
+++ b/scripts/Makefile.clang
@@ -10,7 +10,10 @@ CLANG_TARGET_FLAGS_mips := mipsel-linux-gnu
CLANG_TARGET_FLAGS_powerpc := powerpc64le-linux-gnu
CLANG_TARGET_FLAGS_riscv := riscv64-linux-gnu
CLANG_TARGET_FLAGS_s390 := s390x-linux-gnu
+CLANG_TARGET_FLAGS_sparc := sparc64-linux-gnu
CLANG_TARGET_FLAGS_x86 := x86_64-linux-gnu
+# This is only for i386 UM builds, which need the 32-bit target not -m32
+CLANG_TARGET_FLAGS_i386 := i386-linux-gnu
CLANG_TARGET_FLAGS_um := $(CLANG_TARGET_FLAGS_$(SUBARCH))
CLANG_TARGET_FLAGS := $(CLANG_TARGET_FLAGS_$(SRCARCH))
diff --git a/scripts/Makefile.clean b/scripts/Makefile.clean
index 4fcfab40ed61..6ead00ec7313 100644
--- a/scripts/Makefile.clean
+++ b/scripts/Makefile.clean
@@ -3,7 +3,7 @@
# Cleaning up
# ==========================================================================
-src := $(if $(VPATH),$(VPATH)/)$(obj)
+src := $(srcroot)/$(obj)
PHONY := __clean
__clean:
diff --git a/scripts/Makefile.compiler b/scripts/Makefile.compiler
index 92be0c9a13ee..8956587b8547 100644
--- a/scripts/Makefile.compiler
+++ b/scripts/Makefile.compiler
@@ -13,7 +13,7 @@ cc-cross-prefix = $(firstword $(foreach c, $(1), \
$(if $(shell command -v -- $(c)gcc 2>/dev/null), $(c))))
# output directory for tests below
-TMPOUT = $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/).tmp_$$$$
+TMPOUT = .tmp_$$$$
# try-run
# Usage: option = $(call try-run, $(CC)...-o "$$TMP",option-ok,otherwise)
@@ -53,13 +53,11 @@ cc-option = $(call __cc-option, $(CC),\
# cc-option-yn
# Usage: flag := $(call cc-option-yn,-march=winchip-c6)
-cc-option-yn = $(call try-run,\
- $(CC) -Werror $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(1) -c -x c /dev/null -o "$$TMP",y,n)
+cc-option-yn = $(if $(call cc-option,$1),y,n)
# cc-disable-warning
# Usage: cflags-y += $(call cc-disable-warning,unused-but-set-variable)
-cc-disable-warning = $(call try-run,\
- $(CC) -Werror $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -W$(strip $(1)) -c -x c /dev/null -o "$$TMP",-Wno-$(strip $(1)))
+cc-disable-warning = $(if $(call cc-option,-W$(strip $1)),-Wno-$(strip $1))
# gcc-min-version
# Usage: cflags-$(call gcc-min-version, 70100) += -foo
@@ -69,6 +67,27 @@ gcc-min-version = $(call test-ge, $(CONFIG_GCC_VERSION), $1)
# Usage: cflags-$(call clang-min-version, 110000) += -foo
clang-min-version = $(call test-ge, $(CONFIG_CLANG_VERSION), $1)
+# rustc-min-version
+# Usage: rustc-$(call rustc-min-version, 108500) += -Cfoo
+rustc-min-version = $(call test-ge, $(CONFIG_RUSTC_VERSION), $1)
+
# ld-option
# Usage: KBUILD_LDFLAGS += $(call ld-option, -X, -Y)
ld-option = $(call try-run, $(LD) $(KBUILD_LDFLAGS) $(1) -v,$(1),$(2),$(3))
+
+# __rustc-option
+# Usage: MY_RUSTFLAGS += $(call __rustc-option,$(RUSTC),$(MY_RUSTFLAGS),-Cinstrument-coverage,-Zinstrument-coverage)
+# TODO: remove RUSTC_BOOTSTRAP=1 when we raise the minimum GNU Make version to 4.4
+__rustc-option = $(call try-run,\
+ echo '#![allow(missing_docs)]#![feature(no_core)]#![no_core]' | RUSTC_BOOTSTRAP=1\
+ $(1) --sysroot=/dev/null $(filter-out --sysroot=/dev/null,$(2)) $(3)\
+ --crate-type=rlib --out-dir=$(TMPOUT) --emit=obj=- - >/dev/null,$(3),$(4))
+
+# rustc-option
+# Usage: rustflags-y += $(call rustc-option,-Cinstrument-coverage,-Zinstrument-coverage)
+rustc-option = $(call __rustc-option, $(RUSTC),\
+ $(KBUILD_RUSTFLAGS),$(1),$(2))
+
+# rustc-option-yn
+# Usage: flag := $(call rustc-option-yn,-Cinstrument-coverage)
+rustc-option-yn = $(if $(call rustc-option,$1),y,n)
diff --git a/scripts/Makefile.defconf b/scripts/Makefile.defconf
index 226ea3df3b4b..a44307f08e9d 100644
--- a/scripts/Makefile.defconf
+++ b/scripts/Makefile.defconf
@@ -1,6 +1,11 @@
# SPDX-License-Identifier: GPL-2.0
# Configuration heplers
+cmd_merge_fragments = \
+ $(srctree)/scripts/kconfig/merge_config.sh \
+ $4 -m -O $(objtree) $(srctree)/arch/$(SRCARCH)/configs/$2 \
+ $(foreach config,$3,$(srctree)/arch/$(SRCARCH)/configs/$(config).config)
+
# Creates 'merged defconfigs'
# ---------------------------------------------------------------------------
# Usage:
@@ -8,9 +13,7 @@
#
# Input config fragments without '.config' suffix
define merge_into_defconfig
- $(Q)$(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh \
- -m -O $(objtree) $(srctree)/arch/$(SRCARCH)/configs/$(1) \
- $(foreach config,$(2),$(srctree)/arch/$(SRCARCH)/configs/$(config).config)
+ $(call cmd,merge_fragments,$1,$2)
+$(Q)$(MAKE) -f $(srctree)/Makefile olddefconfig
endef
@@ -22,8 +25,6 @@ endef
#
# Input config fragments without '.config' suffix
define merge_into_defconfig_override
- $(Q)$(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh \
- -Q -m -O $(objtree) $(srctree)/arch/$(SRCARCH)/configs/$(1) \
- $(foreach config,$(2),$(srctree)/arch/$(SRCARCH)/configs/$(config).config)
+ $(call cmd,merge_fragments,$1,$2,-Q)
+$(Q)$(MAKE) -f $(srctree)/Makefile olddefconfig
endef
diff --git a/scripts/Makefile.dtbs b/scripts/Makefile.dtbs
new file mode 100644
index 000000000000..8d56c0815f33
--- /dev/null
+++ b/scripts/Makefile.dtbs
@@ -0,0 +1,144 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+# If CONFIG_OF_ALL_DTBS is enabled, all DT blobs are built
+dtb-$(CONFIG_OF_ALL_DTBS) += $(dtb-)
+
+# Composite DTB (i.e. DTB constructed by overlay)
+multi-dtb-y := $(call multi-search, $(dtb-y), .dtb, -dtbs)
+# Primitive DTB compiled from *.dts
+real-dtb-y := $(call real-search, $(dtb-y), .dtb, -dtbs)
+# Base DTB that overlay is applied onto
+base-dtb-y := $(filter %.dtb, $(call real-search, $(multi-dtb-y), .dtb, -dtbs))
+
+dtb-y := $(addprefix $(obj)/, $(dtb-y))
+multi-dtb-y := $(addprefix $(obj)/, $(multi-dtb-y))
+real-dtb-y := $(addprefix $(obj)/, $(real-dtb-y))
+
+always-y += $(dtb-y)
+targets += $(real-dtb-y)
+
+# dtbs-list
+# ---------------------------------------------------------------------------
+
+ifdef need-dtbslist
+subdir-dtbslist := $(addsuffix /dtbs-list, $(subdir-ym))
+dtb-y += $(subdir-dtbslist)
+always-y += $(obj)/dtbs-list
+endif
+
+$(subdir-dtbslist): $(obj)/%/dtbs-list: $(obj)/% ;
+
+$(obj)/dtbs-list: $(dtb-y) FORCE
+ $(call if_changed,gen_order)
+
+# Assembly file to wrap dtb(o)
+# ---------------------------------------------------------------------------
+
+builtin-dtb-section = $(if $(filter arch/$(SRCARCH)/boot/dts%, $(obj)),.dtb.init.rodata,.rodata)
+
+# Generate an assembly file to wrap the output of the device tree compiler
+quiet_cmd_wrap_S_dtb = WRAP $@
+ cmd_wrap_S_dtb = { \
+ symbase=__$(patsubst .%,%,$(suffix $<))_$(subst -,_,$(notdir $*)); \
+ echo '\#include <asm-generic/vmlinux.lds.h>'; \
+ echo '.section $(builtin-dtb-section),"a"'; \
+ echo '.balign STRUCT_ALIGNMENT'; \
+ echo ".global $${symbase}_begin"; \
+ echo "$${symbase}_begin:"; \
+ echo '.incbin "$<" '; \
+ echo ".global $${symbase}_end"; \
+ echo "$${symbase}_end:"; \
+ echo '.balign STRUCT_ALIGNMENT'; \
+ } > $@
+
+$(obj)/%.dtb.S: $(obj)/%.dtb FORCE
+ $(call if_changed,wrap_S_dtb)
+
+$(obj)/%.dtbo.S: $(obj)/%.dtbo FORCE
+ $(call if_changed,wrap_S_dtb)
+
+# Schema check
+# ---------------------------------------------------------------------------
+
+ifneq ($(CHECK_DTBS),)
+DT_CHECKER ?= dt-validate
+DT_CHECKER_FLAGS ?= $(if $(DT_SCHEMA_FILES),-l $(DT_SCHEMA_FILES),-m)
+DT_BINDING_DIR := Documentation/devicetree/bindings
+DT_TMP_SCHEMA := $(objtree)/$(DT_BINDING_DIR)/processed-schema.json
+dtb-check-enabled = $(if $(filter %.dtb, $@),y)
+endif
+
+quiet_dtb_check_tag = $(if $(dtb-check-enabled),[C], )
+cmd_dtb_check = $(if $(dtb-check-enabled),; $(DT_CHECKER) $(DT_CHECKER_FLAGS) -u $(srctree)/$(DT_BINDING_DIR) -p $(DT_TMP_SCHEMA) $@ || true)
+
+# Overlay
+# ---------------------------------------------------------------------------
+
+# NOTE:
+# Do not replace $(filter %.dtb %.dtbo, $^) with $(real-prereqs). When a single
+# DTB is turned into a multi-blob DTB, $^ will contain header file dependencies
+# recorded in the .*.cmd file.
+quiet_cmd_fdtoverlay = OVL $(quiet_dtb_check_tag) $@
+ cmd_fdtoverlay = $(objtree)/scripts/dtc/fdtoverlay -o $@ -i $(filter %.dtb %.dtbo, $^) $(cmd_dtb_check)
+
+$(multi-dtb-y): $(DT_TMP_SCHEMA) FORCE
+ $(call if_changed,fdtoverlay)
+$(call multi_depend, $(multi-dtb-y), .dtb, -dtbs)
+
+# DTC
+# ---------------------------------------------------------------------------
+
+DTC ?= $(objtree)/scripts/dtc/dtc
+DTC_FLAGS += -Wno-unique_unit_address
+
+# Disable noisy checks by default
+ifeq ($(findstring 1,$(KBUILD_EXTRA_WARN)),)
+DTC_FLAGS += -Wno-unit_address_vs_reg \
+ -Wno-avoid_unnecessary_addr_size \
+ -Wno-alias_paths \
+ -Wno-graph_child_address \
+ -Wno-simple_bus_reg
+else
+DTC_FLAGS += -Wunique_unit_address_if_enabled
+endif
+
+ifneq ($(findstring 2,$(KBUILD_EXTRA_WARN)),)
+DTC_FLAGS += -Wnode_name_chars_strict \
+ -Wproperty_name_chars_strict \
+ -Wunique_unit_address
+endif
+
+DTC_FLAGS += $(DTC_FLAGS_$(target-stem))
+
+# Set -@ if the target is a base DTB that overlay is applied onto
+DTC_FLAGS += $(if $(filter $(patsubst $(obj)/%,%,$@), $(base-dtb-y)), -@)
+
+DTC_INCLUDE := $(srctree)/scripts/dtc/include-prefixes
+
+dtc_cpp_flags = -Wp,-MMD,$(depfile).pre.tmp -nostdinc -I $(DTC_INCLUDE) -undef -D__DTS__
+
+dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp)
+
+quiet_cmd_dtc = DTC $(quiet_dtb_check_tag) $@
+ cmd_dtc = \
+ $(HOSTCC) -E $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $< ; \
+ $(DTC) -o $@ -b 0 $(addprefix -i,$(dir $<) $(DTC_INCLUDE)) \
+ $(DTC_FLAGS) -d $(depfile).dtc.tmp $(dtc-tmp) ; \
+ cat $(depfile).pre.tmp $(depfile).dtc.tmp > $(depfile) \
+ $(cmd_dtb_check)
+
+$(obj)/%.dtb: $(obj)/%.dts $(DTC) $(DT_TMP_SCHEMA) FORCE
+ $(call if_changed_dep,dtc)
+
+$(obj)/%.dtbo: $(src)/%.dtso $(DTC) FORCE
+ $(call if_changed_dep,dtc)
+
+# targets
+# ---------------------------------------------------------------------------
+
+targets += $(always-y)
+
+# %.dtb.o <- %.dtb.S <- %.dtb <- %.dts
+# %.dtbo.o <- %.dtbo.S <- %.dtbo <- %.dtso
+targets += $(call intermediate_targets, .dtb.o, .dtb.S .dtb) \
+ $(call intermediate_targets, .dtbo.o, .dtbo.S .dtbo)
diff --git a/scripts/Makefile.extrawarn b/scripts/Makefile.extrawarn
index 1d13cecc7cc7..d75897559d18 100644
--- a/scripts/Makefile.extrawarn
+++ b/scripts/Makefile.extrawarn
@@ -31,6 +31,11 @@ KBUILD_CFLAGS-$(CONFIG_CC_NO_ARRAY_BOUNDS) += -Wno-array-bounds
ifdef CONFIG_CC_IS_CLANG
# The kernel builds with '-std=gnu11' so use of GNU extensions is acceptable.
KBUILD_CFLAGS += -Wno-gnu
+
+# Clang checks for overflow/truncation with '%p', while GCC does not:
+# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111219
+KBUILD_CFLAGS += $(call cc-disable-warning, format-overflow-non-kprintf)
+KBUILD_CFLAGS += $(call cc-disable-warning, format-truncation-non-kprintf)
else
# gcc inanely warns about local variables called 'main'
@@ -102,11 +107,6 @@ KBUILD_CFLAGS += $(call cc-disable-warning, packed-not-aligned)
KBUILD_CFLAGS += $(call cc-disable-warning, format-overflow)
ifdef CONFIG_CC_IS_GCC
KBUILD_CFLAGS += $(call cc-disable-warning, format-truncation)
-else
-# Clang checks for overflow/truncation with '%p', while GCC does not:
-# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111219
-KBUILD_CFLAGS += $(call cc-disable-warning, format-overflow-non-kprintf)
-KBUILD_CFLAGS += $(call cc-disable-warning, format-truncation-non-kprintf)
endif
KBUILD_CFLAGS += $(call cc-disable-warning, stringop-truncation)
@@ -130,7 +130,6 @@ KBUILD_CFLAGS += $(call cc-disable-warning, pointer-to-enum-cast)
KBUILD_CFLAGS += -Wno-tautological-constant-out-of-range-compare
KBUILD_CFLAGS += $(call cc-disable-warning, unaligned-access)
KBUILD_CFLAGS += -Wno-enum-compare-conditional
-KBUILD_CFLAGS += -Wno-enum-enum-conversion
endif
endif
@@ -154,6 +153,10 @@ KBUILD_CFLAGS += -Wno-missing-field-initializers
KBUILD_CFLAGS += -Wno-type-limits
KBUILD_CFLAGS += -Wno-shift-negative-value
+ifdef CONFIG_CC_IS_CLANG
+KBUILD_CFLAGS += -Wno-enum-enum-conversion
+endif
+
ifdef CONFIG_CC_IS_GCC
KBUILD_CFLAGS += -Wno-maybe-uninitialized
endif
diff --git a/scripts/Makefile.host b/scripts/Makefile.host
index e85be7721a48..c1dedf646a39 100644
--- a/scripts/Makefile.host
+++ b/scripts/Makefile.host
@@ -96,12 +96,10 @@ hostrust_flags = --out-dir $(dir $@) --emit=dep-info=$(depfile) \
$(KBUILD_HOSTRUSTFLAGS) $(HOST_EXTRARUSTFLAGS) \
$(HOSTRUSTFLAGS_$(target-stem))
-# $(objtree)/$(obj) for including generated headers from checkin source files
-ifeq ($(KBUILD_EXTMOD),)
+# $(obj) for including generated headers from checkin source files
ifdef building_out_of_srctree
-hostc_flags += -I $(objtree)/$(obj)
-hostcxx_flags += -I $(objtree)/$(obj)
-endif
+hostc_flags += -I $(obj)
+hostcxx_flags += -I $(obj)
endif
#####
@@ -160,3 +158,8 @@ $(host-rust): $(obj)/%: $(src)/%.rs FORCE
targets += $(host-csingle) $(host-cmulti) $(host-cobjs) \
$(host-cxxmulti) $(host-cxxobjs) $(host-rust)
+
+# %.lex.o <- %.lex.c <- %.l
+# %.tab.o <- %.tab.[ch] <- %.y
+targets += $(call intermediate_targets, .lex.o, .lex.c) \
+ $(call intermediate_targets, .tab.o, .tab.c .tab.h)
diff --git a/scripts/Makefile.kasan b/scripts/Makefile.kasan
index 390658a2d5b7..693dbbebebba 100644
--- a/scripts/Makefile.kasan
+++ b/scripts/Makefile.kasan
@@ -12,6 +12,11 @@ endif
KASAN_SHADOW_OFFSET ?= $(CONFIG_KASAN_SHADOW_OFFSET)
cc-param = $(call cc-option, -mllvm -$(1), $(call cc-option, --param $(1)))
+rustc-param = $(call rustc-option, -Cllvm-args=-$(1),)
+
+check-args = $(foreach arg,$(2),$(call $(1),$(arg)))
+
+kasan_params :=
ifdef CONFIG_KASAN_STACK
stack_enable := 1
@@ -22,57 +27,78 @@ endif
ifdef CONFIG_KASAN_GENERIC
ifdef CONFIG_KASAN_INLINE
+ # When the number of memory accesses in a function is less than this
+ # call threshold number, the compiler will use inline instrumentation.
+ # 10000 is chosen offhand as a sufficiently large number to make all
+ # kernel functions to be instrumented inline.
call_threshold := 10000
else
call_threshold := 0
endif
-CFLAGS_KASAN_MINIMAL := -fsanitize=kernel-address
-
-# -fasan-shadow-offset fails without -fsanitize
-CFLAGS_KASAN_SHADOW := $(call cc-option, -fsanitize=kernel-address \
- -fasan-shadow-offset=$(KASAN_SHADOW_OFFSET), \
- $(call cc-option, -fsanitize=kernel-address \
- -mllvm -asan-mapping-offset=$(KASAN_SHADOW_OFFSET)))
-
-ifeq ($(strip $(CFLAGS_KASAN_SHADOW)),)
- CFLAGS_KASAN := $(CFLAGS_KASAN_MINIMAL)
-else
- # Now add all the compiler specific options that are valid standalone
- CFLAGS_KASAN := $(CFLAGS_KASAN_SHADOW) \
- $(call cc-param,asan-globals=1) \
- $(call cc-param,asan-instrumentation-with-call-threshold=$(call_threshold)) \
- $(call cc-param,asan-instrument-allocas=1)
-endif
-
-CFLAGS_KASAN += $(call cc-param,asan-stack=$(stack_enable))
+# First, enable -fsanitize=kernel-address together with providing the shadow
+# mapping offset, as for GCC, -fasan-shadow-offset fails without -fsanitize
+# (GCC accepts the shadow mapping offset via -fasan-shadow-offset instead of
+# a --param like the other KASAN parameters).
+# Instead of ifdef-checking the compiler, rely on cc-option.
+CFLAGS_KASAN := $(call cc-option, -fsanitize=kernel-address \
+ -fasan-shadow-offset=$(KASAN_SHADOW_OFFSET), \
+ $(call cc-option, -fsanitize=kernel-address \
+ -mllvm -asan-mapping-offset=$(KASAN_SHADOW_OFFSET)))
+
+# The minimum supported `rustc` version has a minimum supported LLVM
+# version late enough that we can assume support for -asan-mapping-offset.
+RUSTFLAGS_KASAN := -Zsanitizer=kernel-address \
+ -Zsanitizer-recover=kernel-address \
+ -Cllvm-args=-asan-mapping-offset=$(KASAN_SHADOW_OFFSET)
+
+# Now, add other parameters enabled similarly in GCC, Clang, and rustc.
+# As some of them are not supported by older compilers, these will be filtered
+# through `cc-param` or `rust-param` as applicable.
+kasan_params += asan-instrumentation-with-call-threshold=$(call_threshold) \
+ asan-stack=$(stack_enable) \
+ asan-instrument-allocas=1 \
+ asan-globals=1
# Instrument memcpy/memset/memmove calls by using instrumented __asan_mem*()
# instead. With compilers that don't support this option, compiler-inserted
# memintrinsics won't be checked by KASAN on GENERIC_ENTRY architectures.
-CFLAGS_KASAN += $(call cc-param,asan-kernel-mem-intrinsic-prefix=1)
+kasan_params += asan-kernel-mem-intrinsic-prefix=1
endif # CONFIG_KASAN_GENERIC
ifdef CONFIG_KASAN_SW_TAGS
+CFLAGS_KASAN := -fsanitize=kernel-hwaddress
+
+# This sets flags that will enable SW_TAGS KASAN once enabled in Rust. These
+# will not work today, and is guarded against in dependencies for CONFIG_RUST.
+RUSTFLAGS_KASAN := -Zsanitizer=kernel-hwaddress \
+ -Zsanitizer-recover=kernel-hwaddress
+
ifdef CONFIG_KASAN_INLINE
- instrumentation_flags := $(call cc-param,hwasan-mapping-offset=$(KASAN_SHADOW_OFFSET))
+ kasan_params += hwasan-mapping-offset=$(KASAN_SHADOW_OFFSET)
else
- instrumentation_flags := $(call cc-param,hwasan-instrument-with-calls=1)
+ kasan_params += hwasan-instrument-with-calls=1
endif
-CFLAGS_KASAN := -fsanitize=kernel-hwaddress \
- $(call cc-param,hwasan-instrument-stack=$(stack_enable)) \
- $(call cc-param,hwasan-use-short-granules=0) \
- $(call cc-param,hwasan-inline-all-checks=0) \
- $(instrumentation_flags)
+kasan_params += hwasan-instrument-stack=$(stack_enable) \
+ hwasan-use-short-granules=0 \
+ hwasan-inline-all-checks=0
# Instrument memcpy/memset/memmove calls by using instrumented __hwasan_mem*().
ifeq ($(call clang-min-version, 150000)$(call gcc-min-version, 130000),y)
-CFLAGS_KASAN += $(call cc-param,hwasan-kernel-mem-intrinsic-prefix=1)
+ kasan_params += hwasan-kernel-mem-intrinsic-prefix=1
endif
endif # CONFIG_KASAN_SW_TAGS
-export CFLAGS_KASAN CFLAGS_KASAN_NOSANITIZE
+# Add all as-supported KASAN LLVM parameters requested by the configuration.
+CFLAGS_KASAN += $(call check-args, cc-param, $(kasan_params))
+
+ifdef CONFIG_RUST
+ # Avoid calling `rustc-param` unless Rust is enabled.
+ RUSTFLAGS_KASAN += $(call check-args, rustc-param, $(kasan_params))
+endif # CONFIG_RUST
+
+export CFLAGS_KASAN CFLAGS_KASAN_NOSANITIZE RUSTFLAGS_KASAN
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index fe3668dc4954..2fe73cda0bdd 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -1,9 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
-# Backward compatibility
-asflags-y += $(EXTRA_AFLAGS)
-ccflags-y += $(EXTRA_CFLAGS)
-cppflags-y += $(EXTRA_CPPFLAGS)
-ldflags-y += $(EXTRA_LDFLAGS)
# flags that take effect in current and sub directories
KBUILD_AFLAGS += $(subdir-asflags-y)
@@ -45,11 +40,6 @@ else
obj-y := $(filter-out %/, $(obj-y))
endif
-ifdef need-dtbslist
-dtb-y += $(addsuffix /dtbs-list, $(subdir-ym))
-always-y += dtbs-list
-endif
-
# Expand $(foo-objs) $(foo-y) etc. by replacing their individuals
suffix-search = $(strip $(foreach s, $3, $($(1:%$(strip $2)=%$s))))
# List composite targets that are constructed by combining other targets
@@ -80,19 +70,6 @@ always-y += $(hostprogs-always-y) $(hostprogs-always-m)
userprogs += $(userprogs-always-y) $(userprogs-always-m)
always-y += $(userprogs-always-y) $(userprogs-always-m)
-# DTB
-# If CONFIG_OF_ALL_DTBS is enabled, all DT blobs are built
-dtb-$(CONFIG_OF_ALL_DTBS) += $(dtb-)
-
-# Composite DTB (i.e. DTB constructed by overlay)
-multi-dtb-y := $(call multi-search, $(dtb-y), .dtb, -dtbs)
-# Primitive DTB compiled from *.dts
-real-dtb-y := $(call real-search, $(dtb-y), .dtb, -dtbs)
-# Base DTB that overlay is applied onto
-base-dtb-y := $(filter %.dtb, $(call real-search, $(multi-dtb-y), .dtb, -dtbs))
-
-always-y += $(dtb-y)
-
# Add subdir path
ifneq ($(obj),.)
@@ -104,9 +81,6 @@ lib-y := $(addprefix $(obj)/,$(lib-y))
real-obj-y := $(addprefix $(obj)/,$(real-obj-y))
real-obj-m := $(addprefix $(obj)/,$(real-obj-m))
multi-obj-m := $(addprefix $(obj)/, $(multi-obj-m))
-dtb-y := $(addprefix $(obj)/, $(dtb-y))
-multi-dtb-y := $(addprefix $(obj)/, $(multi-dtb-y))
-real-dtb-y := $(addprefix $(obj)/, $(real-dtb-y))
subdir-ym := $(addprefix $(obj)/,$(subdir-ym))
endif
@@ -167,6 +141,9 @@ ifneq ($(CONFIG_KASAN_HW_TAGS),y)
_c_flags += $(if $(patsubst n%,, \
$(KASAN_SANITIZE_$(target-stem).o)$(KASAN_SANITIZE)$(is-kernel-object)), \
$(CFLAGS_KASAN), $(CFLAGS_KASAN_NOSANITIZE))
+_rust_flags += $(if $(patsubst n%,, \
+ $(KASAN_SANITIZE_$(target-stem).o)$(KASAN_SANITIZE)$(is-kernel-object)), \
+ $(RUSTFLAGS_KASAN))
endif
endif
@@ -184,8 +161,8 @@ _c_flags += $(if $(patsubst n%,, \
$(UBSAN_SANITIZE_$(target-stem).o)$(UBSAN_SANITIZE)$(is-kernel-object)), \
$(CFLAGS_UBSAN))
_c_flags += $(if $(patsubst n%,, \
- $(UBSAN_SIGNED_WRAP_$(target-stem).o)$(UBSAN_SANITIZE_$(target-stem).o)$(UBSAN_SIGNED_WRAP)$(UBSAN_SANITIZE)$(is-kernel-object)), \
- $(CFLAGS_UBSAN_SIGNED_WRAP))
+ $(UBSAN_INTEGER_WRAP_$(target-stem).o)$(UBSAN_SANITIZE_$(target-stem).o)$(UBSAN_INTEGER_WRAP)$(UBSAN_SANITIZE)$(is-kernel-object)), \
+ $(CFLAGS_UBSAN_INTEGER_WRAP))
endif
ifeq ($(CONFIG_KCOV),y)
@@ -209,15 +186,33 @@ _c_flags += $(if $(patsubst n%,, \
-D__KCSAN_INSTRUMENT_BARRIERS__)
endif
+#
+# Enable AutoFDO build flags except some files or directories we don't want to
+# enable (depends on variables AUTOFDO_PROFILE_obj.o and AUTOFDO_PROFILE).
+#
+ifeq ($(CONFIG_AUTOFDO_CLANG),y)
+_c_flags += $(if $(patsubst n%,, \
+ $(AUTOFDO_PROFILE_$(target-stem).o)$(AUTOFDO_PROFILE)$(is-kernel-object)), \
+ $(CFLAGS_AUTOFDO_CLANG))
+endif
+
+#
+# Enable Propeller build flags except some files or directories we don't want to
+# enable (depends on variables AUTOFDO_PROPELLER_obj.o and PROPELLER_PROFILE).
+#
+ifdef CONFIG_PROPELLER_CLANG
+_c_flags += $(if $(patsubst n%,, \
+ $(AUTOFDO_PROFILE_$(target-stem).o)$(AUTOFDO_PROFILE)$(PROPELLER_PROFILE))$(is-kernel-object), \
+ $(CFLAGS_PROPELLER_CLANG))
+endif
+
# $(src) for including checkin headers from generated source files
# $(obj) for including generated headers from checkin source files
-ifeq ($(KBUILD_EXTMOD),)
ifdef building_out_of_srctree
_c_flags += $(addprefix -I, $(src) $(obj))
_a_flags += $(addprefix -I, $(src) $(obj))
_cpp_flags += $(addprefix -I, $(src) $(obj))
endif
-endif
# If $(is-kernel-object) is 'y', this object will be linked to vmlinux or modules
is-kernel-object = $(or $(part-of-builtin),$(part-of-module))
@@ -238,7 +233,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,19 +243,13 @@ 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)
ld_flags = $(KBUILD_LDFLAGS) $(ldflags-y) $(LDFLAGS_$(@F))
-DTC_INCLUDE := $(srctree)/scripts/dtc/include-prefixes
-
-dtc_cpp_flags = -Wp,-MMD,$(depfile).pre.tmp -nostdinc \
- $(addprefix -I,$(DTC_INCLUDE)) \
- -undef -D__DTS__
-
ifdef CONFIG_OBJTOOL
objtool := $(objtree)/tools/objtool/objtool
@@ -281,8 +270,9 @@ objtool-args-$(CONFIG_MITIGATION_SLS) += --sls
objtool-args-$(CONFIG_STACK_VALIDATION) += --stackval
objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE) += --static-call
objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION) += --uaccess
-objtool-args-$(CONFIG_GCOV_KERNEL) += --no-unreachable
+objtool-args-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV)) += --no-unreachable
objtool-args-$(CONFIG_PREFIX_SYMBOLS) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES)
+objtool-args-$(CONFIG_OBJTOOL_WERROR) += --Werror
objtool-args = $(objtool-args-y) \
$(if $(delay-objtool), --link) \
@@ -293,6 +283,8 @@ delay-objtool := $(or $(CONFIG_LTO_CLANG),$(CONFIG_X86_KERNEL_IBT))
cmd_objtool = $(if $(objtool-enabled), ; $(objtool) $(objtool-args) $@)
cmd_gen_objtooldep = $(if $(objtool-enabled), { echo ; echo '$@: $$(wildcard $(objtool))' ; } >> $(dot-target).cmd)
+objtool-enabled := y
+
endif # CONFIG_OBJTOOL
# Useful for describing the dependency of composite objects
@@ -304,6 +296,42 @@ $(foreach m, $1, \
$(addprefix $(obj)/, $(call suffix-search, $(patsubst $(obj)/%,%,$m), $2, $3))))
endef
+# Build commands
+# ===========================================================================
+# These are shared by some Makefile.* files.
+
+ifdef CONFIG_LTO_CLANG
+# Run $(LD) here to convert LLVM IR to ELF in the following cases:
+# - when this object needs objtool processing, as objtool cannot process LLVM IR
+# - when this is a single-object module, as modpost cannot process LLVM IR
+cmd_ld_single = $(if $(objtool-enabled)$(is-single-obj-m), ; $(LD) $(ld_flags) -r -o $(tmp-target) $@; mv $(tmp-target) $@)
+endif
+
+quiet_cmd_cc_o_c = CC $(quiet_modtag) $@
+ cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< \
+ $(cmd_ld_single) \
+ $(cmd_objtool)
+
+define rule_cc_o_c
+ $(call cmd_and_fixdep,cc_o_c)
+ $(call cmd,checksrc)
+ $(call cmd,checkdoc)
+ $(call cmd,gen_objtooldep)
+ $(call cmd,gen_symversions_c)
+ $(call cmd,record_mcount)
+ $(call cmd,warn_shared_object)
+endef
+
+quiet_cmd_as_o_S = AS $(quiet_modtag) $@
+ cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $< $(cmd_objtool)
+
+define rule_as_o_S
+ $(call cmd_and_fixdep,as_o_S)
+ $(call cmd,gen_objtooldep)
+ $(call cmd,gen_symversions_S)
+ $(call cmd,warn_shared_object)
+endef
+
# Copy a file
# ===========================================================================
# 'cp' preserves permissions. If you use it to copy a file in read-only srctree,
@@ -350,102 +378,6 @@ cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@
quiet_cmd_gzip = GZIP $@
cmd_gzip = cat $(real-prereqs) | $(KGZIP) -n -f -9 > $@
-# DTC
-# ---------------------------------------------------------------------------
-DTC ?= $(objtree)/scripts/dtc/dtc
-DTC_FLAGS += \
- -Wno-unique_unit_address
-
-# Disable noisy checks by default
-ifeq ($(findstring 1,$(KBUILD_EXTRA_WARN)),)
-DTC_FLAGS += -Wno-unit_address_vs_reg \
- -Wno-avoid_unnecessary_addr_size \
- -Wno-alias_paths \
- -Wno-graph_child_address \
- -Wno-simple_bus_reg
-else
-DTC_FLAGS += \
- -Wunique_unit_address_if_enabled
-endif
-
-ifneq ($(findstring 2,$(KBUILD_EXTRA_WARN)),)
-DTC_FLAGS += -Wnode_name_chars_strict \
- -Wproperty_name_chars_strict \
- -Wunique_unit_address
-endif
-
-DTC_FLAGS += $(DTC_FLAGS_$(target-stem))
-
-# Set -@ if the target is a base DTB that overlay is applied onto
-DTC_FLAGS += $(if $(filter $(patsubst $(obj)/%,%,$@), $(base-dtb-y)), -@)
-
-# Generate an assembly file to wrap the output of the device tree compiler
-quiet_cmd_wrap_S_dtb = WRAP $@
- cmd_wrap_S_dtb = { \
- symbase=__$(patsubst .%,%,$(suffix $<))_$(subst -,_,$(notdir $*)); \
- echo '\#include <asm-generic/vmlinux.lds.h>'; \
- echo '.section .dtb.init.rodata,"a"'; \
- echo '.balign STRUCT_ALIGNMENT'; \
- echo ".global $${symbase}_begin"; \
- echo "$${symbase}_begin:"; \
- echo '.incbin "$<" '; \
- echo ".global $${symbase}_end"; \
- echo "$${symbase}_end:"; \
- echo '.balign STRUCT_ALIGNMENT'; \
- } > $@
-
-$(obj)/%.dtb.S: $(obj)/%.dtb FORCE
- $(call if_changed,wrap_S_dtb)
-
-$(obj)/%.dtbo.S: $(obj)/%.dtbo FORCE
- $(call if_changed,wrap_S_dtb)
-
-quiet_cmd_dtc = DTC $@
-cmd_dtc = $(HOSTCC) -E $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $< ; \
- $(DTC) -o $@ -b 0 \
- $(addprefix -i,$(dir $<) $(DTC_INCLUDE)) $(DTC_FLAGS) \
- -d $(depfile).dtc.tmp $(dtc-tmp) ; \
- cat $(depfile).pre.tmp $(depfile).dtc.tmp > $(depfile)
-
-DT_CHECK_CMD = $(DT_CHECKER) $(DT_CHECKER_FLAGS) -u $(srctree)/$(DT_BINDING_DIR) -p $(DT_TMP_SCHEMA)
-
-# NOTE:
-# Do not replace $(filter %.dtb %.dtbo, $^) with $(real-prereqs). When a single
-# DTB is turned into a multi-blob DTB, $^ will contain header file dependencies
-# recorded in the .*.cmd file.
-ifneq ($(CHECK_DTBS),)
-quiet_cmd_fdtoverlay = DTOVLCH $@
- cmd_fdtoverlay = $(objtree)/scripts/dtc/fdtoverlay -o $@ -i $(filter %.dtb %.dtbo, $^) ; $(DT_CHECK_CMD) $@ || true
-else
-quiet_cmd_fdtoverlay = DTOVL $@
- cmd_fdtoverlay = $(objtree)/scripts/dtc/fdtoverlay -o $@ -i $(filter %.dtb %.dtbo, $^)
-endif
-
-$(multi-dtb-y): FORCE
- $(call if_changed,fdtoverlay)
-$(call multi_depend, $(multi-dtb-y), .dtb, -dtbs)
-
-ifneq ($(CHECK_DTBS),)
-DT_CHECKER ?= dt-validate
-DT_CHECKER_FLAGS ?= $(if $(DT_SCHEMA_FILES),-l $(DT_SCHEMA_FILES),-m)
-DT_BINDING_DIR := Documentation/devicetree/bindings
-DT_TMP_SCHEMA := $(objtree)/$(DT_BINDING_DIR)/processed-schema.json
-
-quiet_cmd_dtb = DTC_CHK $@
- cmd_dtb = $(cmd_dtc) ; $(DT_CHECK_CMD) $@ || true
-else
-quiet_cmd_dtb = $(quiet_cmd_dtc)
- cmd_dtb = $(cmd_dtc)
-endif
-
-$(obj)/%.dtb: $(obj)/%.dts $(DTC) $(DT_TMP_SCHEMA) FORCE
- $(call if_changed_dep,dtb)
-
-$(obj)/%.dtbo: $(src)/%.dtso $(DTC) FORCE
- $(call if_changed_dep,dtc)
-
-dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp)
-
# Bzip2
# ---------------------------------------------------------------------------
@@ -491,10 +423,10 @@ quiet_cmd_lzo_with_size = LZO $@
cmd_lzo_with_size = { cat $(real-prereqs) | $(KLZOP) -9; $(size_append); } > $@
quiet_cmd_lz4 = LZ4 $@
- cmd_lz4 = cat $(real-prereqs) | $(LZ4) -l -c1 stdin stdout > $@
+ cmd_lz4 = cat $(real-prereqs) | $(LZ4) -l -9 - - > $@
quiet_cmd_lz4_with_size = LZ4 $@
- cmd_lz4_with_size = { cat $(real-prereqs) | $(LZ4) -l -c1 stdin stdout; \
+ cmd_lz4_with_size = { cat $(real-prereqs) | $(LZ4) -l -9 - -; \
$(size_append); } > $@
# U-Boot mkimage
@@ -538,14 +470,17 @@ quiet_cmd_fit = FIT $@
# XZ
# ---------------------------------------------------------------------------
-# Use xzkern to compress the kernel image and xzmisc to compress other things.
+# Use xzkern or xzkern_with_size to compress the kernel image and xzmisc to
+# compress other things.
#
# xzkern uses a big LZMA2 dictionary since it doesn't increase memory usage
# of the kernel decompressor. A BCJ filter is used if it is available for
-# the target architecture. xzkern also appends uncompressed size of the data
-# using size_append. The .xz format has the size information available at
-# the end of the file too, but it's in more complex format and it's good to
-# avoid changing the part of the boot code that reads the uncompressed size.
+# the target architecture.
+#
+# xzkern_with_size also appends uncompressed size of the data using
+# size_append. The .xz format has the size information available at the end
+# of the file too, but it's in more complex format and it's good to avoid
+# changing the part of the boot code that reads the uncompressed size.
# Note that the bytes added by size_append will make the xz tool think that
# the file is corrupt. This is expected.
#
diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
index 1fa98b5e952b..542ba462ed3e 100644
--- a/scripts/Makefile.modfinal
+++ b/scripts/Makefile.modfinal
@@ -6,14 +6,12 @@
PHONY := __modfinal
__modfinal:
-include include/config/auto.conf
+include $(objtree)/include/config/auto.conf
include $(srctree)/scripts/Kbuild.include
-
-# for c_flags
include $(srctree)/scripts/Makefile.lib
# find all modules listed in modules.order
-modules := $(call read-file, $(MODORDER))
+modules := $(call read-file, modules.order)
__modfinal: $(modules:%.o=%.ko)
@:
@@ -22,27 +20,27 @@ __modfinal: $(modules:%.o=%.ko)
modname = $(notdir $(@:.mod.o=))
part-of-module = y
GCOV_PROFILE := n
-KCSAN_SANITIZE := n
-
-quiet_cmd_cc_o_c = CC [M] $@
- cmd_cc_o_c = $(CC) $(filter-out $(CC_FLAGS_CFI), $(c_flags)) -c -o $@ $<
+ccflags-remove-y := $(CC_FLAGS_CFI)
%.mod.o: %.mod.c FORCE
- $(call if_changed_dep,cc_o_c)
+ $(call if_changed_rule,cc_o_c)
+
+.module-common.o: $(srctree)/scripts/module-common.c FORCE
+ $(call if_changed_rule,cc_o_c)
quiet_cmd_ld_ko_o = LD [M] $@
- cmd_ld_ko_o += \
+ cmd_ld_ko_o = \
$(LD) -r $(KBUILD_LDFLAGS) \
$(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \
- -T scripts/module.lds -o $@ $(filter %.o, $^)
+ -T $(objtree)/scripts/module.lds -o $@ $(filter %.o, $^)
quiet_cmd_btf_ko = BTF [M] $@
cmd_btf_ko = \
- if [ ! -f vmlinux ]; then \
+ if [ ! -f $(objtree)/vmlinux ]; then \
printf "Skipping BTF generation for %s due to unavailability of vmlinux\n" $@ 1>&2; \
else \
- LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) $(MODULE_PAHOLE_FLAGS) --btf_base vmlinux $@; \
- $(RESOLVE_BTFIDS) -b vmlinux $@; \
+ LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) $(MODULE_PAHOLE_FLAGS) --btf_base $(objtree)/vmlinux $@; \
+ $(RESOLVE_BTFIDS) -b $(objtree)/vmlinux $@; \
fi;
# Same as newer-prereqs, but allows to exclude specified extra dependencies
@@ -54,15 +52,15 @@ if_changed_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check), \
printf '%s\n' 'savedcmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)
# Re-generate module BTFs if either module's .ko or vmlinux changed
-%.ko: %.o %.mod.o scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),vmlinux) FORCE
- +$(call if_changed_except,ld_ko_o,vmlinux)
+%.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_except,ld_ko_o,$(objtree)/vmlinux)
ifdef CONFIG_DEBUG_INFO_BTF_MODULES
+$(if $(newer-prereqs),$(call cmd,btf_ko))
endif
-targets += $(modules:%.o=%.ko) $(modules:%.o=%.mod.o)
+targets += $(modules:%.o=%.ko) $(modules:%.o=%.mod.o) .module-common.o
-# Add FORCE to the prequisites of a target to force it to be always rebuilt.
+# Add FORCE to the prerequisites of a target to force it to be always rebuilt.
# ---------------------------------------------------------------------------
PHONY += FORCE
diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst
index 0afd75472679..1628198f3e83 100644
--- a/scripts/Makefile.modinst
+++ b/scripts/Makefile.modinst
@@ -6,7 +6,7 @@
PHONY := __modinst
__modinst:
-include include/config/auto.conf
+include $(objtree)/include/config/auto.conf
include $(srctree)/scripts/Kbuild.include
install-y :=
@@ -30,15 +30,17 @@ $(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 modules.builtin(.modinfo,.ranges) even when CONFIG_MODULES is disabled.
install-y += $(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo)
-$(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): $(MODLIB)/%: % FORCE
$(call cmd,install)
endif
-modules := $(call read-file, $(MODORDER))
+modules := $(call read-file, modules.order)
ifeq ($(KBUILD_EXTMOD),)
dst := $(MODLIB)/kernel
@@ -51,11 +53,13 @@ $(foreach x, % :, $(if $(findstring $x, $(dst)), \
$(error module installation path cannot contain '$x')))
suffix-y :=
+ifdef CONFIG_MODULE_COMPRESS_ALL
suffix-$(CONFIG_MODULE_COMPRESS_GZIP) := .gz
suffix-$(CONFIG_MODULE_COMPRESS_XZ) := .xz
suffix-$(CONFIG_MODULE_COMPRESS_ZSTD) := .zst
+endif
-modules := $(patsubst $(extmod_prefix)%.o, $(dst)/%.ko$(suffix-y), $(modules))
+modules := $(patsubst %.o, $(dst)/%.ko$(suffix-y), $(modules))
install-$(CONFIG_MODULES) += $(modules)
__modinst: $(install-y)
@@ -101,7 +105,7 @@ else
sig-key := $(CONFIG_MODULE_SIG_KEY)
endif
quiet_cmd_sign = SIGN $@
- cmd_sign = scripts/sign-file $(CONFIG_MODULE_SIG_HASH) "$(sig-key)" certs/signing_key.x509 $@ \
+ cmd_sign = $(objtree)/scripts/sign-file $(CONFIG_MODULE_SIG_HASH) "$(sig-key)" $(objtree)/certs/signing_key.x509 $@ \
$(if $(KBUILD_EXTMOD),|| true)
ifeq ($(sign-only),)
@@ -115,7 +119,7 @@ endif
# Create necessary directories
$(foreach dir, $(sort $(dir $(install-y))), $(shell mkdir -p $(dir)))
-$(dst)/%.ko: $(extmod_prefix)%.ko FORCE
+$(dst)/%.ko: %.ko FORCE
$(call cmd,install)
$(call cmd,strip)
$(call cmd,sign)
@@ -146,7 +150,7 @@ quiet_cmd_gzip = GZIP $@
quiet_cmd_xz = XZ $@
cmd_xz = $(XZ) --check=crc32 --lzma2=dict=1MiB -f $<
quiet_cmd_zstd = ZSTD $@
- cmd_zstd = $(ZSTD) -T0 --rm -f -q $<
+ cmd_zstd = $(ZSTD) --rm -f -q $<
$(dst)/%.ko.gz: $(dst)/%.ko FORCE
$(call cmd,gzip)
diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost
index 44936ebad161..d7d45067d08b 100644
--- a/scripts/Makefile.modpost
+++ b/scripts/Makefile.modpost
@@ -35,18 +35,20 @@
PHONY := __modpost
__modpost:
-include include/config/auto.conf
+include $(objtree)/include/config/auto.conf
include $(srctree)/scripts/Kbuild.include
-MODPOST = scripts/mod/modpost
+MODPOST = $(objtree)/scripts/mod/modpost
modpost-args = \
$(if $(CONFIG_MODULES),-M) \
$(if $(CONFIG_MODVERSIONS),-m) \
+ $(if $(CONFIG_BASIC_MODVERSIONS),-b) \
+ $(if $(CONFIG_EXTENDED_MODVERSIONS),-x) \
$(if $(CONFIG_MODULE_SRCVERSION_ALL),-a) \
$(if $(CONFIG_SECTION_MISMATCH_WARN_ONLY),,-E) \
$(if $(KBUILD_MODPOST_WARN),-w) \
- $(if $(KBUILD_NSDEPS),-d $(MODULES_NSDEPS)) \
+ $(if $(KBUILD_NSDEPS),-d modules.nsdeps) \
$(if $(CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS)$(KBUILD_NSDEPS),-N) \
$(if $(findstring 1, $(KBUILD_EXTRA_WARN)),-W) \
-o $@
@@ -61,8 +63,8 @@ endif
# Read out modules.order to pass in modpost.
# Otherwise, allmodconfig would fail with "Argument list too long".
ifdef KBUILD_MODULES
-modpost-args += -T $(MODORDER)
-modpost-deps += $(MODORDER)
+modpost-args += -T modules.order
+modpost-deps += modules.order
endif
ifeq ($(KBUILD_EXTMOD),)
@@ -111,19 +113,19 @@ endif
else
# set src + obj - they may be used in the modules's Makefile
-obj := $(KBUILD_EXTMOD)
-src := $(if $(VPATH),$(VPATH)/)$(obj)
+obj := .
+src := $(srcroot)
# Include the module's Makefile to find KBUILD_EXTRA_SYMBOLS
include $(kbuild-file)
-output-symdump := $(KBUILD_EXTMOD)/Module.symvers
+output-symdump := Module.symvers
-ifeq ($(wildcard Module.symvers),)
-missing-input := Module.symvers
+ifeq ($(wildcard $(objtree)/Module.symvers),)
+missing-input := $(objtree)/Module.symvers
else
-modpost-args += -i Module.symvers
-modpost-deps += Module.symvers
+modpost-args += -i $(objtree)/Module.symvers
+modpost-deps += $(objtree)/Module.symvers
endif
modpost-args += -e $(addprefix -i , $(KBUILD_EXTRA_SYMBOLS))
diff --git a/scripts/Makefile.package b/scripts/Makefile.package
index 4a80584ec771..74bcb9e7f7a4 100644
--- a/scripts/Makefile.package
+++ b/scripts/Makefile.package
@@ -62,6 +62,10 @@ rpm-sources: linux.tar.gz
PHONY += rpm-pkg srcrpm-pkg binrpm-pkg
+ifneq ($(CC),$(HOSTCC))
+rpm-no-devel = --without=devel
+endif
+
rpm-pkg: private build-type := a
srcrpm-pkg: private build-type := s
binrpm-pkg: private build-type := b
@@ -72,7 +76,8 @@ rpm-pkg srcrpm-pkg binrpm-pkg: rpmbuild/SPECS/kernel.spec
--define='_topdir $(abspath rpmbuild)' \
$(if $(filter a b, $(build-type)), \
--target $(UTS_MACHINE)-linux --build-in-place --noprep --define='_smp_mflags %{nil}' \
- $$(rpm -q rpm >/dev/null 2>&1 || echo --nodeps)) \
+ $$(rpm -q rpm >/dev/null 2>&1 || echo --nodeps) \
+ $(rpm-no-devel)) \
$(RPMOPTS))
# deb-pkg srcdeb-pkg bindeb-pkg
@@ -147,8 +152,7 @@ snap-pkg:
PHONY += pacman-pkg
pacman-pkg:
@ln -srf $(srctree)/scripts/package/PKGBUILD $(objtree)/PKGBUILD
- +objtree="$(realpath $(objtree))" \
- BUILDDIR="$(realpath $(objtree))/pacman" \
+ +BUILDDIR="$(realpath $(objtree))/pacman" \
CARCH="$(UTS_MACHINE)" \
KBUILD_MAKEFLAGS="$(MAKEFLAGS)" \
KBUILD_REVISION="$(shell $(srctree)/scripts/build-version)" \
diff --git a/scripts/Makefile.propeller b/scripts/Makefile.propeller
new file mode 100644
index 000000000000..48a660128e25
--- /dev/null
+++ b/scripts/Makefile.propeller
@@ -0,0 +1,39 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# Enable available and selected Clang Propeller features.
+ifdef CLANG_PROPELLER_PROFILE_PREFIX
+ CFLAGS_PROPELLER_CLANG := -fbasic-block-sections=list=$(CLANG_PROPELLER_PROFILE_PREFIX)_cc_profile.txt -ffunction-sections
+ KBUILD_LDFLAGS += --symbol-ordering-file=$(CLANG_PROPELLER_PROFILE_PREFIX)_ld_profile.txt --no-warn-symbol-ordering
+else
+ # Starting with Clang v20, the '-fbasic-block-sections=labels' option is
+ # deprecated. Use the recommended '-fbasic-block-address-map' option.
+ # Link: https://github.com/llvm/llvm-project/pull/110039
+ ifeq ($(call clang-min-version, 200000),y)
+ CFLAGS_PROPELLER_CLANG := -fbasic-block-address-map
+ else
+ CFLAGS_PROPELLER_CLANG := -fbasic-block-sections=labels
+ endif
+endif
+
+# Propeller requires debug information to embed module names in the profiles.
+# If CONFIG_DEBUG_INFO is not enabled, set -gmlt option. Skip this for AutoFDO,
+# as the option should already be set.
+ifndef CONFIG_DEBUG_INFO
+ ifndef CONFIG_AUTOFDO_CLANG
+ CFLAGS_PROPELLER_CLANG += -gmlt
+ endif
+endif
+
+ifdef CONFIG_LTO_CLANG_THIN
+ ifdef CLANG_PROPELLER_PROFILE_PREFIX
+ KBUILD_LDFLAGS += --lto-basic-block-sections=$(CLANG_PROPELLER_PROFILE_PREFIX)_cc_profile.txt
+ else
+ ifeq ($(call test-ge, $(CONFIG_LLD_VERSION), 200000),y)
+ KBUILD_LDFLAGS += --lto-basic-block-address-map
+ else
+ KBUILD_LDFLAGS += --lto-basic-block-sections=labels
+ endif
+ endif
+endif
+
+export CFLAGS_PROPELLER_CLANG
diff --git a/scripts/Makefile.ubsan b/scripts/Makefile.ubsan
index b2d3b273b802..9e35198edbf0 100644
--- a/scripts/Makefile.ubsan
+++ b/scripts/Makefile.ubsan
@@ -14,5 +14,11 @@ ubsan-cflags-$(CONFIG_UBSAN_TRAP) += $(call cc-option,-fsanitize-trap=undefined
export CFLAGS_UBSAN := $(ubsan-cflags-y)
-ubsan-signed-wrap-cflags-$(CONFIG_UBSAN_SIGNED_WRAP) += -fsanitize=signed-integer-overflow
-export CFLAGS_UBSAN_SIGNED_WRAP := $(ubsan-signed-wrap-cflags-y)
+ubsan-integer-wrap-cflags-$(CONFIG_UBSAN_INTEGER_WRAP) += \
+ -fsanitize-undefined-ignore-overflow-pattern=all \
+ -fsanitize=signed-integer-overflow \
+ -fsanitize=unsigned-integer-overflow \
+ -fsanitize=implicit-signed-integer-truncation \
+ -fsanitize=implicit-unsigned-integer-truncation \
+ -fsanitize-ignorelist=$(srctree)/scripts/integer-wrap-ignore.scl
+export CFLAGS_UBSAN_INTEGER_WRAP := $(ubsan-integer-wrap-cflags-y)
diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
index 49946cb96844..b0a6cd5b818c 100644
--- a/scripts/Makefile.vmlinux
+++ b/scripts/Makefile.vmlinux
@@ -5,35 +5,117 @@ __default: vmlinux
include include/config/auto.conf
include $(srctree)/scripts/Kbuild.include
-
-# for c_flags
include $(srctree)/scripts/Makefile.lib
targets :=
-quiet_cmd_cc_o_c = CC $@
- cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
+ifdef CONFIG_ARCH_VMLINUX_NEEDS_RELOCS
+vmlinux-final := vmlinux.unstripped
+
+quiet_cmd_strip_relocs = RSTRIP $@
+ cmd_strip_relocs = $(OBJCOPY) --remove-section='.rel*' $< $@
+
+vmlinux: $(vmlinux-final) FORCE
+ $(call if_changed,strip_relocs)
+
+targets += vmlinux
+else
+vmlinux-final := vmlinux
+endif
%.o: %.c FORCE
- $(call if_changed_dep,cc_o_c)
+ $(call if_changed_rule,cc_o_c)
+
+%.o: %.S FORCE
+ $(call if_changed_rule,as_o_S)
+
+# Built-in dtb
+# ---------------------------------------------------------------------------
+
+quiet_cmd_wrap_dtbs = WRAP $@
+ cmd_wrap_dtbs = { \
+ echo '\#include <asm-generic/vmlinux.lds.h>'; \
+ echo '.section .dtb.init.rodata,"a"'; \
+ while read dtb; do \
+ symbase=__dtb_$$(basename -s .dtb "$${dtb}" | tr - _); \
+ echo '.balign STRUCT_ALIGNMENT'; \
+ echo ".global $${symbase}_begin"; \
+ echo "$${symbase}_begin:"; \
+ echo '.incbin "'$$dtb'" '; \
+ echo ".global $${symbase}_end"; \
+ echo "$${symbase}_end:"; \
+ done < $<; \
+ } > $@
+
+.builtin-dtbs.S: .builtin-dtbs-list FORCE
+ $(call if_changed,wrap_dtbs)
+
+quiet_cmd_gen_dtbs_list = GEN $@
+ cmd_gen_dtbs_list = \
+ $(if $(CONFIG_BUILTIN_DTB_NAME), echo "arch/$(SRCARCH)/boot/dts/$(CONFIG_BUILTIN_DTB_NAME).dtb",:) > $@
+
+.builtin-dtbs-list: arch/$(SRCARCH)/boot/dts/dtbs-list FORCE
+ $(call if_changed,$(if $(CONFIG_BUILTIN_DTB_ALL),copy,gen_dtbs_list))
+
+targets += .builtin-dtbs-list
+
+ifdef CONFIG_GENERIC_BUILTIN_DTB
+targets += .builtin-dtbs.S .builtin-dtbs.o
+$(vmlinux-final): .builtin-dtbs.o
+endif
+
+# vmlinux
+# ---------------------------------------------------------------------------
ifdef CONFIG_MODULES
targets += .vmlinux.export.o
-vmlinux: .vmlinux.export.o
+$(vmlinux-final): .vmlinux.export.o
+endif
+
+ifdef CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX
+$(vmlinux-final): arch/$(SRCARCH)/tools/vmlinux.arch.o
+
+arch/$(SRCARCH)/tools/vmlinux.arch.o: vmlinux.o FORCE
+ $(Q)$(MAKE) $(build)=arch/$(SRCARCH)/tools $@
endif
ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink)
# Final link of vmlinux with optional arch pass after final link
cmd_link_vmlinux = \
- $< "$(LD)" "$(KBUILD_LDFLAGS)" "$(LDFLAGS_vmlinux)"; \
+ $< "$(LD)" "$(KBUILD_LDFLAGS)" "$(LDFLAGS_vmlinux)" "$@"; \
$(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)
-targets += vmlinux
-vmlinux: scripts/link-vmlinux.sh vmlinux.o $(KBUILD_LDS) FORCE
+targets += $(vmlinux-final)
+$(vmlinux-final): scripts/link-vmlinux.sh vmlinux.o $(KBUILD_LDS) FORCE
+$(call if_changed_dep,link_vmlinux)
+ifdef CONFIG_DEBUG_INFO_BTF
+$(vmlinux-final): $(RESOLVE_BTFIDS)
+endif
+
+ifdef CONFIG_BUILDTIME_TABLE_SORT
+vmlinux: scripts/sorttable
+endif
+
+# module.builtin.ranges
+# ---------------------------------------------------------------------------
+ifdef CONFIG_BUILTIN_MODULE_RANGES
+__default: modules.builtin.ranges
+
+quiet_cmd_modules_builtin_ranges = GEN $@
+ cmd_modules_builtin_ranges = gawk -f $(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-final)
+ @:
+
+endif
-# Add FORCE to the prequisites of a target to force it to be always rebuilt.
+# Add FORCE to the prerequisites of a target to force it to be always rebuilt.
# ---------------------------------------------------------------------------
PHONY += FORCE
diff --git a/scripts/Makefile.vmlinux_o b/scripts/Makefile.vmlinux_o
index 6de297916ce6..938c7457717e 100644
--- a/scripts/Makefile.vmlinux_o
+++ b/scripts/Makefile.vmlinux_o
@@ -30,13 +30,20 @@ endif
# objtool for vmlinux.o
# ---------------------------------------------------------------------------
#
-# For LTO and IBT, objtool doesn't run on individual translation units.
-# Run everything on vmlinux instead.
+# For delay-objtool (IBT or LTO), objtool doesn't run on individual translation
+# units. Instead it runs on vmlinux.o.
+#
+# For !delay-objtool + CONFIG_NOINSTR_VALIDATION, it runs on both translation
+# units and vmlinux.o, with the latter only used for noinstr/unret validation.
objtool-enabled := $(or $(delay-objtool),$(CONFIG_NOINSTR_VALIDATION))
-vmlinux-objtool-args-$(delay-objtool) += $(objtool-args-y)
-vmlinux-objtool-args-$(CONFIG_GCOV_KERNEL) += --no-unreachable
+ifeq ($(delay-objtool),y)
+vmlinux-objtool-args-y += $(objtool-args-y)
+else
+vmlinux-objtool-args-$(CONFIG_OBJTOOL_WERROR) += --Werror
+endif
+
vmlinux-objtool-args-$(CONFIG_NOINSTR_VALIDATION) += --noinstr \
$(if $(or $(CONFIG_MITIGATION_UNRET_ENTRY),$(CONFIG_MITIGATION_SRSO)), --unret)
@@ -45,9 +52,12 @@ objtool-args = $(vmlinux-objtool-args-y) --link
# Link of vmlinux.o used for section mismatch analysis
# ---------------------------------------------------------------------------
+vmlinux-o-ld-args-$(CONFIG_BUILTIN_MODULE_RANGES) += -Map=$@.map
+
quiet_cmd_ld_vmlinux.o = LD $@
cmd_ld_vmlinux.o = \
$(LD) ${KBUILD_LDFLAGS} -r -o $@ \
+ $(vmlinux-o-ld-args-y) \
$(addprefix -T , $(initcalls-lds)) \
--whole-archive vmlinux.a --no-whole-archive \
--start-group $(KBUILD_VMLINUX_LIBS) --end-group \
@@ -87,7 +97,7 @@ targets += modules.builtin
modules.builtin: modules.builtin.modinfo FORCE
$(call if_changed,modules_builtin)
-# Add FORCE to the prequisites of a target to force it to be always rebuilt.
+# Add FORCE to the prerequisites of a target to force it to be always rebuilt.
# ---------------------------------------------------------------------------
PHONY += FORCE
diff --git a/scripts/bash-completion/make b/scripts/bash-completion/make
new file mode 100644
index 000000000000..42e8dcead25a
--- /dev/null
+++ b/scripts/bash-completion/make
@@ -0,0 +1,451 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# bash completion for GNU make with kbuild extension -*- shell-script -*-
+
+# Load the default completion script for make. It is typically located at
+# /usr/share/bash-completion/completions/make, but we do not rely on it.
+__kbuild_load_default_make_completion()
+{
+ local -a dirs=("${BASH_COMPLETION_USER_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion}/completions")
+ local ifs=$IFS IFS=: dir compfile this_dir
+
+ for dir in ${XDG_DATA_DIRS:-/usr/local/share:/usr/share}; do
+ dirs+=("$dir"/bash-completion/completions)
+ done
+ IFS=$ifs
+
+ this_dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
+
+ for dir in "${dirs[@]}"; do
+ if [[ ! -d ${dir} || ${dir} = "${this_dir}" ]]; then
+ continue
+ fi
+
+ for compfile in make make.bash _make; do
+ compfile=$dir/$compfile
+ # Avoid trying to source dirs; https://bugzilla.redhat.com/903540
+ if [[ -f ${compfile} ]] && . "${compfile}" &>/dev/null; then
+
+ __kbuild_default_make_completion=$(
+ # shellcheck disable=SC2046 # word splitting is the point here
+ set -- $(complete -p make)
+
+ while [[ $# -gt 1 && "$1" != -F ]]; do
+ shift
+ done
+
+ if [[ "$1" = -F ]]; then
+ echo "$2"
+ fi
+ )
+
+ return
+ fi
+ done
+ done
+}
+
+__kbuild_load_default_make_completion
+
+__kbuild_handle_variable()
+{
+ local var=${1%%=*}
+ local cur=${cur#"${var}"=}
+ local srctree=$2
+ local keywords=()
+
+ case $var in
+ ARCH)
+ # sub-directories under arch/
+ keywords+=($(find "${srctree}/arch" -mindepth 1 -maxdepth 1 -type d -printf '%P\n'))
+ # architectures hard-coded in the top Makefile
+ keywords+=(i386 x86_64 sparc32 sparc64 parisc64)
+ ;;
+ CROSS_COMPILE)
+ # toolchains with a full path
+ local cross_compile=()
+ local c c2
+ _filedir
+
+ for c in "${COMPREPLY[@]}"; do
+ # eval for tilde expansion
+ # suppress error, as this fails when it contains a space
+ eval "c2=${c}" 2>/dev/null || continue
+ if [[ ${c} == *-elfedit && ! -d ${c2} && -x ${c2} ]]; then
+ cross_compile+=("${c%elfedit}")
+ fi
+ done
+
+ # toolchains in the PATH environment
+ while read -r c; do
+ if [[ ${c} == *-elfedit ]]; then
+ keywords+=("${c%elfedit}")
+ fi
+ done < <(compgen -c)
+
+ COMPREPLY=()
+ _filedir -d
+
+ # Add cross_compile directly without passing it to compgen.
+ # Otherwise, toolchain paths with a tilde do not work.
+ # e.g.)
+ # CROSS_COMPILE=~/0day/gcc-14.2.0-nolibc/aarch64-linux/bin/aarch64-linux-
+ COMPREPLY+=("${cross_compile[@]}")
+ ;;
+ LLVM)
+ # LLVM=1 uses the default 'clang' etc.
+ keywords+=(1)
+
+ # suffix for a particular version. LLVM=-18 uses 'clang-18' etc.
+ while read -r c; do
+ if [[ ${c} == clang-[0-9]* ]]; then
+ keywords+=("${c#clang}")
+ fi
+ done < <(compgen -c)
+
+ # directory path to LLVM toolchains
+ _filedir -d
+ ;;
+ KCONFIG_ALLCONFIG)
+ # KCONFIG_ALLCONFIG=1 selects the default fragment
+ keywords+=(1)
+ # or the path to a fragment file
+ _filedir
+ ;;
+ C | KBUILD_CHECKSRC)
+ keywords+=(1 2)
+ ;;
+ V | KBUILD_VERBOSE)
+ keywords+=({,1}{,2})
+ ;;
+ W | KBUILD_EXTRA_WARN)
+ keywords+=({,1}{,2}{,3}{,c}{,e})
+ ;;
+ KBUILD_ABS_SRCTREE | KBUILD_MODPOST_NOFINAL | KBUILD_MODPOST_WARN | \
+ CLIPPY | KBUILD_CLIPPY | KCONFIG_NOSILENTUPDATE | \
+ KCONFIG_OVERWRITECONFIG | KCONFIG_WARN_UNKNOWN_SYMBOL | \
+ KCONFIG_WERROR )
+ keywords+=(1)
+ ;;
+ INSTALL_MOD_STRIP)
+ keywords+=(1 --strip-debug --strip-unneeded)
+ ;;
+ O | KBUILD_OUTPUT | M | KBUILD_EXTMOD | MO | KBUILD_EXTMOD_OUTPUT | *_PATH)
+ # variables that take a directory.
+ _filedir -d
+ return
+ ;;
+ KBUILD_EXTRA_SYMBOL | KBUILD_KCONFIG | KCONFIG_CONFIG)
+ # variables that take a file.
+ _filedir
+ return
+ esac
+
+ COMPREPLY+=($(compgen -W "${keywords[*]}" -- "${cur}"))
+}
+
+# Check the -C, -f options and 'source' symlink. Return the source tree we are
+# working in.
+__kbuild_get_srctree()
+{
+ local words=("$@")
+ local cwd makef_dir
+
+ # see if a path was specified with -C/--directory
+ for ((i = 1; i < ${#words[@]}; i++)); do
+ if [[ ${words[i]} == -@(C|-directory) ]]; then
+ # eval for tilde expansion.
+ # suppress error, as this fails when it contains a space
+ eval "cwd=${words[i + 1]}" 2>/dev/null
+ break
+ fi
+ done
+
+ if [[ -z ${cwd} ]]; then
+ cwd=.
+ fi
+
+ # see if a Makefile was specified with -f/--file/--makefile
+ for ((i = 1; i < ${#words[@]}; i++)); do
+ if [[ ${words[i]} == -@(f|-?(make)file) ]]; then
+ # eval for tilde expansion
+ # suppress error, as this fails when it contains a space
+ eval "makef_dir=${words[i + 1]%/*}" 2>/dev/null
+ break
+ fi
+ done
+
+ if [ -z "${makef_dir}" ]; then
+ makef_dir=${cwd}
+ elif [[ ${makef_dir} != /* ]]; then
+ makef_dir=${cwd}/${makef_dir}
+ fi
+
+ # If ${makef_dir} is a build directory created by the O= option, there
+ # is a symbolic link 'source', which points to the kernel source tree.
+ if [[ -L ${makef_dir}/source ]]; then
+ makef_dir=$(readlink "${makef_dir}/source")
+ fi
+
+ echo "${makef_dir}"
+}
+
+# Get SRCARCH to do a little more clever things
+__kbuild_get_srcarch()
+{
+ local words=("$@")
+ local arch srcarch uname_m
+
+ # see if ARCH= is explicitly specified
+ for ((i = 1; i < ${#words[@]}; i++)); do
+ if [[ ${words[i]} == ARCH=* ]]; then
+ arch=${words[i]#ARCH=}
+ break
+ fi
+ done
+
+ # If ARCH= is not specified, check the build marchine's architecture
+ if [[ -z ${arch} ]]; then
+ uname_m=$(uname -m)
+
+ # shellcheck disable=SC2209 # 'sh' is SuperH, not a shell command
+ case ${uname_m} in
+ arm64 | aarch64*) arch=arm64 ;;
+ arm* | sa110) arch=arm ;;
+ i?86 | x86_64) arch=x86 ;;
+ loongarch*) arch=loongarch ;;
+ mips*) arch=mips ;;
+ ppc*) arch=powerpc ;;
+ riscv*) arch=riscv ;;
+ s390x) arch=s390 ;;
+ sh[234]*) arch=sh ;;
+ sun4u) arch=sparc64 ;;
+ *) arch=${uname_m} ;;
+ esac
+ fi
+
+ case ${arch} in
+ parisc64) srcarch=parisc ;;
+ sparc32 | sparc64) srcarch=sparc ;;
+ i386 | x86_64) srcarch=x86 ;;
+ *) srcarch=${arch} ;;
+ esac
+
+ echo "$srcarch"
+}
+
+# small Makefile to parse obj-* syntax
+__kbuild_tmp_makefile()
+{
+cat <<'EOF'
+.PHONY: __default
+__default:
+ $(foreach m,$(obj-y) $(obj-m) $(obj-),$(foreach s, -objs -y -m -,$($(m:%.o=%$s))) $(m))
+EOF
+echo "include ${1}"
+}
+
+_make_for_kbuild ()
+{
+ # shellcheck disable=SC2034 # these are set by _init_completion
+ local cur prev words cword split
+ _init_completion -s || return
+
+ local srctree
+ srctree=$(__kbuild_get_srctree "${words[@]}")
+
+ # If 'kernel' and 'Documentation' directories are found, we assume this
+ # is a kernel tree. Otherwise, we fall back to the generic rule provided
+ # by the bash-completion project.
+ if [[ ! -d ${srctree}/kernel || ! -d ${srctree}/Documentation ]]; then
+ if [ -n "${__kbuild_default_make_completion}" ]; then
+ "${__kbuild_default_make_completion}" "$@"
+ fi
+ return
+ fi
+
+ # make options with a parameter (copied from the bash-completion project)
+ case ${prev} in
+ --file | --makefile | --old-file | --assume-old | --what-if | --new-file | \
+ --assume-new | -!(-*)[foW])
+ _filedir
+ return
+ ;;
+ --include-dir | --directory | -!(-*)[ICm])
+ _filedir -d
+ return
+ ;;
+ -!(-*)E)
+ COMPREPLY=($(compgen -v -- "$cur"))
+ return
+ ;;
+ --eval | -!(-*)[DVx])
+ return
+ ;;
+ --jobs | -!(-*)j)
+ COMPREPLY=($(compgen -W "{1..$(($(_ncpus) * 2))}" -- "$cur"))
+ return
+ ;;
+ esac
+
+ local keywords=()
+
+ case ${cur} in
+ -*)
+ # make options (copied from the bash-completion project)
+ local opts
+ opts="$(_parse_help "$1")"
+ COMPREPLY=($(compgen -W "${opts:-$(_parse_usage "$1")}" -- "$cur"))
+ if [[ ${COMPREPLY-} == *= ]]; then
+ compopt -o nospace
+ fi
+ return
+ ;;
+ *=*)
+ __kbuild_handle_variable "${cur}" "${srctree}"
+ return
+ ;;
+ KBUILD_*)
+ # There are many variables prefixed with 'KBUILD_'.
+ # Display them only when 'KBUILD_' is entered.
+ # shellcheck disable=SC2191 # '=' is appended for variables
+ keywords+=(
+ KBUILD_{CHECKSRC,EXTMOD,EXTMOD_OUTPUT,OUTPUT,VERBOSE,EXTRA_WARN,CLIPPY}=
+ KBUILD_BUILD_{USER,HOST,TIMESTAMP}=
+ KBUILD_MODPOST_{NOFINAL,WARN}=
+ KBUILD_{ABS_SRCTREE,EXTRA_SYMBOLS,KCONFIG}=
+ )
+ ;;
+ KCONFIG_*)
+ # There are many variables prefixed with 'KCONFIG_'.
+ # Display them only when 'KCONFIG_' is entered.
+ # shellcheck disable=SC2191 # '=' is appended for variables
+ keywords+=(
+ KCONFIG_{CONFIG,ALLCONFIG,NOSILENTUPDATE,OVERWRITECONFIG}=
+ KCONFIG_{SEED,PROBABILITY}=
+ KCONFIG_WARN_UNKNOWN_SYMBOL=
+ KCONFIG_WERROR=
+ )
+ ;;
+ *)
+ # By default, hide KBUILD_* and KCONFIG_* variables.
+ # Instead, display only the prefix parts.
+ keywords+=(KBUILD_ KCONFIG_)
+ ;;
+ esac
+
+ if [[ ${cur} != /* && ${cur} != *//* ]]; then
+ local dir srcarch kbuild_file tmp
+ srcarch=$(__kbuild_get_srcarch "${words[@]}")
+
+ # single build
+ dir=${cur}
+ while true; do
+ if [[ ${dir} == */* ]]; then
+ dir=${dir%/*}
+ else
+ dir=.
+ fi
+
+ # Search for 'Kbuild' or 'Makefile' in the parent
+ # directories (may not be a direct parent)
+ if [[ -f ${srctree}/${dir}/Kbuild ]]; then
+ kbuild_file=${srctree}/${dir}/Kbuild
+ break
+ fi
+ if [[ -f ${srctree}/${dir}/Makefile ]]; then
+ kbuild_file=${srctree}/${dir}/Makefile
+ break
+ fi
+
+ if [[ ${dir} == . ]]; then
+ break
+ fi
+ done
+
+ if [[ -n ${kbuild_file} ]]; then
+ tmp=($(__kbuild_tmp_makefile "${kbuild_file}" |
+ SRCARCH=${srcarch} obj=${dir} src=${srctree}/${dir} \
+ "${1}" -n -f - 2>/dev/null))
+
+ # Add $(obj)/ prefix
+ if [[ ${dir} != . ]]; then
+ tmp=("${tmp[@]/#/${dir}\/}")
+ fi
+
+ keywords+=("${tmp[@]}")
+ fi
+
+ # *_defconfig and *.config files. These might be grouped into
+ # subdirectories, e.g., arch/powerpc/configs/*/*_defconfig.
+ if [[ ${cur} == */* ]]; then
+ dir=${cur%/*}
+ else
+ dir=.
+ fi
+
+ tmp=($(find "${srctree}/arch/${srcarch}/configs/${dir}" \
+ "${srctree}/kernel/configs/${dir}" \
+ -mindepth 1 -maxdepth 1 -type d -printf '%P/\n' \
+ -o -printf '%P\n' 2>/dev/null))
+
+ if [[ ${dir} != . ]]; then
+ tmp=("${tmp[@]/#/${dir}\/}")
+ fi
+
+ keywords+=("${tmp[@]}")
+ fi
+
+ # shellcheck disable=SC2191 # '=' is appended for variables
+ keywords+=(
+ #
+ # variables (append =)
+ #
+ ARCH=
+ CROSS_COMPILE=
+ LLVM=
+ C= M= MO= O= V= W=
+ INSTALL{,_MOD,_HDR,_DTBS}_PATH=
+ KERNELRELEASE=
+
+ #
+ # targets
+ #
+ all help
+ clean mrproper distclean
+ clang-{tidy,analyzer} compile_commands.json
+ coccicheck
+ dtbs{,_check,_install} dt_binding_{check,schemas}
+ headers{,_install}
+ vmlinux install
+ modules{,_prepare,_install,_sign}
+ vdso_install
+ tags TAGS cscope gtags
+ rust{available,fmt,fmtcheck}
+ kernel{version,release} image_name
+ kselftest{,-all,-install,-clean,-merge}
+
+ # configuration
+ {,old,olddef,sync,def,savedef,rand,listnew,helpnew,test,tiny}config
+ {,build_}{menu,n,g,x}config
+ local{mod,yes}config
+ all{no,yes,mod,def}config
+ {yes2mod,mod2yes,mod2no}config
+
+ # docs
+ {html,textinfo,info,latex,pdf,epub,xml,linkcheck,refcheck,clean}docs
+
+ # package
+ {,bin,src}{rpm,deb}-pkg
+ {pacman,dir,tar}-pkg
+ tar{,gz,bz2,xz,zst}-pkg
+ perf-tar{,gz,bz2,xz,zst}-src-pkg
+ )
+
+ COMPREPLY=($(compgen -W "${keywords[*]}" -- "${cur}"))
+
+ # Do not append a space for variables, subdirs, "KBUILD_", "KCONFIG_".
+ if [[ ${COMPREPLY-} == *[=/] || ${COMPREPLY-} =~ ^(KBUILD|KCONFIG)_$ ]]; then
+ compopt -o nospace
+ fi
+
+} && complete -F _make_for_kbuild make
diff --git a/scripts/basic/fixdep.c b/scripts/basic/fixdep.c
index 84b6efa849f4..cdd5da7e009b 100644
--- a/scripts/basic/fixdep.c
+++ b/scripts/basic/fixdep.c
@@ -99,6 +99,8 @@
#include <stdio.h>
#include <ctype.h>
+#include <xalloc.h>
+
static void usage(void)
{
fprintf(stderr, "Usage: fixdep <depfile> <target> <cmdline>\n");
@@ -131,12 +133,9 @@ static unsigned int strhash(const char *str, unsigned int sz)
static void add_to_hashtable(const char *name, int len, unsigned int hash,
struct item *hashtab[])
{
- struct item *aux = malloc(sizeof(*aux) + len);
+ struct item *aux;
- if (!aux) {
- perror("fixdep:malloc");
- exit(1);
- }
+ aux = xmalloc(sizeof(*aux) + len);
memcpy(aux->name, name, len);
aux->len = len;
aux->hash = hash;
@@ -228,11 +227,7 @@ static void *read_file(const char *filename)
perror(filename);
exit(2);
}
- buf = malloc(st.st_size + 1);
- if (!buf) {
- perror("fixdep: malloc");
- exit(2);
- }
+ buf = xmalloc(st.st_size + 1);
if (read(fd, buf, st.st_size) != st.st_size) {
perror("fixdep: read");
exit(2);
diff --git a/scripts/bpf_doc.py b/scripts/bpf_doc.py
index c55878bddfdd..e74a01a85070 100755
--- a/scripts/bpf_doc.py
+++ b/scripts/bpf_doc.py
@@ -37,10 +37,11 @@ class APIElement(object):
@desc: textual description of the symbol
@ret: (optional) description of any associated return value
"""
- def __init__(self, proto='', desc='', ret=''):
+ def __init__(self, proto='', desc='', ret='', attrs=[]):
self.proto = proto
self.desc = desc
self.ret = ret
+ self.attrs = attrs
class Helper(APIElement):
@@ -81,6 +82,11 @@ class Helper(APIElement):
return res
+ATTRS = {
+ '__bpf_fastcall': 'bpf_fastcall'
+}
+
+
class HeaderParser(object):
"""
An object used to parse a file in order to extract the documentation of a
@@ -111,7 +117,8 @@ class HeaderParser(object):
proto = self.parse_proto()
desc = self.parse_desc(proto)
ret = self.parse_ret(proto)
- return Helper(proto=proto, desc=desc, ret=ret)
+ attrs = self.parse_attrs(proto)
+ return Helper(proto=proto, desc=desc, ret=ret, attrs=attrs)
def parse_symbol(self):
p = re.compile(r' \* ?(BPF\w+)$')
@@ -192,6 +199,28 @@ class HeaderParser(object):
raise Exception("No return found for " + proto)
return ret
+ def parse_attrs(self, proto):
+ p = re.compile(r' \* ?(?:\t| {5,8})Attributes$')
+ capture = p.match(self.line)
+ if not capture:
+ return []
+ # Expect a single line with mnemonics for attributes separated by spaces
+ self.line = self.reader.readline()
+ p = re.compile(r' \* ?(?:\t| {5,8})(?:\t| {8})(.*)')
+ capture = p.match(self.line)
+ if not capture:
+ raise Exception("Incomplete 'Attributes' section for " + proto)
+ attrs = capture.group(1).split(' ')
+ for attr in attrs:
+ if attr not in ATTRS:
+ raise Exception("Unexpected attribute '" + attr + "' specified for " + proto)
+ self.line = self.reader.readline()
+ if self.line != ' *\n':
+ raise Exception("Expecting empty line after 'Attributes' section for " + proto)
+ # Prepare a line for next self.parse_* to consume
+ self.line = self.reader.readline()
+ return attrs
+
def seek_to(self, target, help_message, discard_lines = 1):
self.reader.seek(0)
offset = self.reader.read().find(target)
@@ -789,6 +818,21 @@ class PrinterHelpers(Printer):
print('%s;' % fwd)
print('')
+ used_attrs = set()
+ for helper in self.elements:
+ for attr in helper.attrs:
+ used_attrs.add(attr)
+ for attr in sorted(used_attrs):
+ print('#ifndef %s' % attr)
+ print('#if __has_attribute(%s)' % ATTRS[attr])
+ print('#define %s __attribute__((%s))' % (attr, ATTRS[attr]))
+ print('#else')
+ print('#define %s' % attr)
+ print('#endif')
+ print('#endif')
+ if used_attrs:
+ print('')
+
def print_footer(self):
footer = ''
print(footer)
@@ -827,7 +871,10 @@ class PrinterHelpers(Printer):
print(' *{}{}'.format(' \t' if line else '', line))
print(' */')
- print('static %s %s(* const %s)(' % (self.map_type(proto['ret_type']),
+ print('static ', end='')
+ if helper.attrs:
+ print('%s ' % (" ".join(helper.attrs)), end='')
+ print('%s %s(* const %s)(' % (self.map_type(proto['ret_type']),
proto['ret_star'], proto['name']), end='')
comma = ''
for i, a in enumerate(proto['args']):
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 39032224d504..3d22bf863eec 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -113,7 +113,8 @@ Options:
--max-line-length=n set the maximum line length, (default $max_line_length)
if exceeded, warn on patches
requires --strict for use with --file
- --min-conf-desc-length=n set the min description length, if shorter, warn
+ --min-conf-desc-length=n set the minimum description length for config symbols
+ in lines, if shorter, warn (default $min_conf_desc_length)
--tab-size=n set the number of spaces for tab (default $tabsize)
--root=PATH PATH to the kernel tree root
--no-summary suppress the per-file summary
@@ -834,16 +835,6 @@ foreach my $entry (@mode_permission_funcs) {
$mode_perms_search = "(?:${mode_perms_search})";
our %deprecated_apis = (
- "synchronize_rcu_bh" => "synchronize_rcu",
- "synchronize_rcu_bh_expedited" => "synchronize_rcu_expedited",
- "call_rcu_bh" => "call_rcu",
- "rcu_barrier_bh" => "rcu_barrier",
- "synchronize_sched" => "synchronize_rcu",
- "synchronize_sched_expedited" => "synchronize_rcu_expedited",
- "call_rcu_sched" => "call_rcu",
- "rcu_barrier_sched" => "rcu_barrier",
- "get_state_synchronize_sched" => "get_state_synchronize_rcu",
- "cond_synchronize_sched" => "cond_synchronize_rcu",
"kmap" => "kmap_local_page",
"kunmap" => "kunmap_local",
"kmap_atomic" => "kmap_local_page",
@@ -2875,7 +2866,7 @@ sub process {
if ($realfile =~ m@^include/asm/@) {
ERROR("MODIFIED_INCLUDE_ASM",
- "do not modify files in include/asm, change architecture specific files in include/asm-<architecture>\n" . "$here$rawline\n");
+ "do not modify files in include/asm, change architecture specific files in arch/<architecture>/include/asm\n" . "$here$rawline\n");
}
$found_file = 1;
}
@@ -3209,45 +3200,40 @@ sub process {
# Check Fixes: styles is correct
if (!$in_header_lines &&
- $line =~ /^\s*fixes:?\s*(?:commit\s*)?[0-9a-f]{5,}\b/i) {
- my $orig_commit = "";
- my $id = "0123456789ab";
- my $title = "commit title";
- my $tag_case = 1;
- my $tag_space = 1;
- my $id_length = 1;
- my $id_case = 1;
+ $line =~ /^\s*(fixes:?)\s*(?:commit\s*)?([0-9a-f]{5,40})(?:\s*($balanced_parens))?/i) {
+ my $tag = $1;
+ my $orig_commit = $2;
+ my $title;
my $title_has_quotes = 0;
$fixes_tag = 1;
-
- if ($line =~ /(\s*fixes:?)\s+([0-9a-f]{5,})\s+($balanced_parens)/i) {
- my $tag = $1;
- $orig_commit = $2;
- $title = $3;
-
- $tag_case = 0 if $tag eq "Fixes:";
- $tag_space = 0 if ($line =~ /^fixes:? [0-9a-f]{5,} ($balanced_parens)/i);
-
- $id_length = 0 if ($orig_commit =~ /^[0-9a-f]{12}$/i);
- $id_case = 0 if ($orig_commit !~ /[A-F]/);
-
+ if (defined $3) {
# Always strip leading/trailing parens then double quotes if existing
- $title = substr($title, 1, -1);
+ $title = substr($3, 1, -1);
if ($title =~ /^".*"$/) {
$title = substr($title, 1, -1);
$title_has_quotes = 1;
}
+ } else {
+ $title = "commit title"
}
+
+ my $tag_case = not ($tag eq "Fixes:");
+ my $tag_space = not ($line =~ /^fixes:? [0-9a-f]{5,40} ($balanced_parens)/i);
+
+ my $id_length = not ($orig_commit =~ /^[0-9a-f]{12,40}$/i);
+ my $id_case = not ($orig_commit !~ /[A-F]/);
+
+ my $id = "0123456789ab";
my ($cid, $ctitle) = git_commit_info($orig_commit, $id,
$title);
- if ($ctitle ne $title || $tag_case || $tag_space ||
- $id_length || $id_case || !$title_has_quotes) {
+ if (defined($cid) && ($ctitle ne $title || $tag_case || $tag_space || $id_length || $id_case || !$title_has_quotes)) {
+ my $fixed = "Fixes: $cid (\"$ctitle\")";
if (WARN("BAD_FIXES_TAG",
- "Please use correct Fixes: style 'Fixes: <12 chars of sha1> (\"<title line>\")' - ie: 'Fixes: $cid (\"$ctitle\")'\n" . $herecurr) &&
+ "Please use correct Fixes: style 'Fixes: <12+ chars of sha1> (\"<title line>\")' - ie: '$fixed'\n" . $herecurr) &&
$fix) {
- $fixed[$fixlinenr] = "Fixes: $cid (\"$ctitle\")";
+ $fixed[$fixlinenr] = $fixed;
}
}
}
@@ -3660,7 +3646,7 @@ sub process {
$help_length < $min_conf_desc_length) {
my $stat_real = get_stat_real($linenr, $ln - 1);
WARN("CONFIG_DESCRIPTION",
- "please write a help paragraph that fully describes the config symbol\n" . "$here\n$stat_real\n");
+ "please write a help paragraph that fully describes the config symbol with at least $min_conf_desc_length lines\n" . "$here\n$stat_real\n");
}
}
@@ -3704,20 +3690,6 @@ sub process {
}
}
- if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) &&
- ($line =~ /\+(EXTRA_[A-Z]+FLAGS).*/)) {
- my $flag = $1;
- my $replacement = {
- 'EXTRA_AFLAGS' => 'asflags-y',
- 'EXTRA_CFLAGS' => 'ccflags-y',
- 'EXTRA_CPPFLAGS' => 'cppflags-y',
- 'EXTRA_LDFLAGS' => 'ldflags-y',
- };
-
- WARN("DEPRECATED_VARIABLE",
- "Use of $flag is deprecated, please use \`$replacement->{$flag} instead.\n" . $herecurr) if ($replacement->{$flag});
- }
-
# check for DT compatible documentation
if (defined $root &&
(($realfile =~ /\.dtsi?$/ && $line =~ /^\+\s*compatible\s*=\s*\"/) ||
@@ -4015,16 +3987,6 @@ sub process {
}
}
-# Block comment styles
-# Networking with an initial /*
- if ($realfile =~ m@^(drivers/net/|net/)@ &&
- $prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ &&
- $rawline =~ /^\+[ \t]*\*/ &&
- $realline > 3) { # Do not warn about the initial copyright comment block after SPDX-License-Identifier
- WARN("NETWORKING_BLOCK_COMMENT_STYLE",
- "networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev);
- }
-
# Block comments use * on subsequent lines
if ($prevline =~ /$;[ \t]*$/ && #ends in comment
$prevrawline =~ /^\+.*?\/\*/ && #starting /*
@@ -5528,9 +5490,9 @@ sub process {
}
}
-# check for unnecessary parentheses around comparisons in if uses
-# when !drivers/staging or command-line uses --strict
- if (($realfile !~ m@^(?:drivers/staging/)@ || $check_orig) &&
+# check for unnecessary parentheses around comparisons
+# except in drivers/staging
+ if (($realfile !~ m@^(?:drivers/staging/)@) &&
$perl_version_ok && defined($stat) &&
$stat =~ /(^.\s*if\s*($balanced_parens))/) {
my $if_stat = $1;
@@ -5858,6 +5820,8 @@ sub process {
#CamelCase
if ($var !~ /^$Constant$/ &&
$var =~ /[A-Z][a-z]|[a-z][A-Z]/ &&
+#Ignore C keywords
+ $var !~ /^_Generic$/ &&
#Ignore some autogenerated defines and enum values
$var !~ /^(?:[A-Z]+_){1,5}[A-Z]{1,3}[a-z]/ &&
#Ignore Page<foo> variants
@@ -6607,11 +6571,11 @@ sub process {
# ignore udelay's < 10, however
if (! ($delay < 10) ) {
CHK("USLEEP_RANGE",
- "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.rst\n" . $herecurr);
+ "usleep_range is preferred over udelay; see function description of usleep_range() and udelay().\n" . $herecurr);
}
if ($delay > 2000) {
WARN("LONG_UDELAY",
- "long udelay - prefer mdelay; see arch/arm/include/asm/delay.h\n" . $herecurr);
+ "long udelay - prefer mdelay; see function description of mdelay().\n" . $herecurr);
}
}
@@ -6619,7 +6583,7 @@ sub process {
if ($line =~ /\bmsleep\s*\((\d+)\);/) {
if ($1 < 20) {
WARN("MSLEEP",
- "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.rst\n" . $herecurr);
+ "msleep < 20ms can sleep for up to 20ms; see function description of msleep().\n" . $herecurr);
}
}
@@ -7087,11 +7051,11 @@ sub process {
my $max = $7;
if ($min eq $max) {
WARN("USLEEP_RANGE",
- "usleep_range should not use min == max args; see Documentation/timers/timers-howto.rst\n" . "$here\n$stat\n");
+ "usleep_range should not use min == max args; see function description of usleep_range().\n" . "$here\n$stat\n");
} elsif ($min =~ /^\d+$/ && $max =~ /^\d+$/ &&
$min > $max) {
WARN("USLEEP_RANGE",
- "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.rst\n" . "$here\n$stat\n");
+ "usleep_range args reversed, use min then max; see function description of usleep_range().\n" . "$here\n$stat\n");
}
}
diff --git a/scripts/checktransupdate.py b/scripts/checktransupdate.py
index 5a0fc99e3f93..578c3fecfdfd 100755
--- a/scripts/checktransupdate.py
+++ b/scripts/checktransupdate.py
@@ -10,31 +10,28 @@ differences occur, report the file and commits that need to be updated.
The usage is as follows:
- ./scripts/checktransupdate.py -l zh_CN
-This will print all the files that need to be updated in the zh_CN locale.
+This will print all the files that need to be updated or translated in the zh_CN locale.
- ./scripts/checktransupdate.py Documentation/translations/zh_CN/dev-tools/testing-overview.rst
This will only print the status of the specified file.
The output is something like:
-Documentation/translations/zh_CN/dev-tools/testing-overview.rst (1 commits)
+Documentation/dev-tools/kfence.rst
+No translation in the locale of zh_CN
+
+Documentation/translations/zh_CN/dev-tools/testing-overview.rst
commit 42fb9cfd5b18 ("Documentation: dev-tools: Add link to RV docs")
+1 commits needs resolving in total
"""
import os
-from argparse import ArgumentParser, BooleanOptionalAction
+import time
+import logging
+from argparse import ArgumentParser, ArgumentTypeError, BooleanOptionalAction
from datetime import datetime
-flag_p_c = False
-flag_p_uf = False
-flag_debug = False
-
-
-def dprint(*args, **kwargs):
- if flag_debug:
- print("[DEBUG] ", end="")
- print(*args, **kwargs)
-
def get_origin_path(file_path):
+ """Get the origin path from the translation path"""
paths = file_path.split("/")
tidx = paths.index("translations")
opaths = paths[:tidx]
@@ -43,17 +40,16 @@ def get_origin_path(file_path):
def get_latest_commit_from(file_path, commit):
- command = "git log --pretty=format:%H%n%aD%n%cD%n%n%B {} -1 -- {}".format(
- commit, file_path
- )
- dprint(command)
+ """Get the latest commit from the specified commit for the specified file"""
+ command = f"git log --pretty=format:%H%n%aD%n%cD%n%n%B {commit} -1 -- {file_path}"
+ logging.debug(command)
pipe = os.popen(command)
result = pipe.read()
result = result.split("\n")
if len(result) <= 1:
return None
- dprint("Result: {}".format(result[0]))
+ logging.debug("Result: %s", result[0])
return {
"hash": result[0],
@@ -64,17 +60,19 @@ def get_latest_commit_from(file_path, commit):
def get_origin_from_trans(origin_path, t_from_head):
+ """Get the latest origin commit from the translation commit"""
o_from_t = get_latest_commit_from(origin_path, t_from_head["hash"])
while o_from_t is not None and o_from_t["author_date"] > t_from_head["author_date"]:
o_from_t = get_latest_commit_from(origin_path, o_from_t["hash"] + "^")
if o_from_t is not None:
- dprint("tracked origin commit id: {}".format(o_from_t["hash"]))
+ logging.debug("tracked origin commit id: %s", o_from_t["hash"])
return o_from_t
def get_commits_count_between(opath, commit1, commit2):
- command = "git log --pretty=format:%H {}...{} -- {}".format(commit1, commit2, opath)
- dprint(command)
+ """Get the commits count between two commits for the specified file"""
+ command = f"git log --pretty=format:%H {commit1}...{commit2} -- {opath}"
+ logging.debug(command)
pipe = os.popen(command)
result = pipe.read().split("\n")
# filter out empty lines
@@ -83,50 +81,120 @@ def get_commits_count_between(opath, commit1, commit2):
def pretty_output(commit):
- command = "git log --pretty='format:%h (\"%s\")' -1 {}".format(commit)
- dprint(command)
+ """Pretty print the commit message"""
+ command = f"git log --pretty='format:%h (\"%s\")' -1 {commit}"
+ logging.debug(command)
pipe = os.popen(command)
return pipe.read()
+def valid_commit(commit):
+ """Check if the commit is valid or not"""
+ msg = pretty_output(commit)
+ return "Merge tag" not in msg
+
def check_per_file(file_path):
+ """Check the translation status for the specified file"""
opath = get_origin_path(file_path)
if not os.path.isfile(opath):
- dprint("Error: Cannot find the origin path for {}".format(file_path))
+ logging.error("Cannot find the origin path for {file_path}")
return
o_from_head = get_latest_commit_from(opath, "HEAD")
t_from_head = get_latest_commit_from(file_path, "HEAD")
if o_from_head is None or t_from_head is None:
- print("Error: Cannot find the latest commit for {}".format(file_path))
+ logging.error("Cannot find the latest commit for %s", file_path)
return
o_from_t = get_origin_from_trans(opath, t_from_head)
if o_from_t is None:
- print("Error: Cannot find the latest origin commit for {}".format(file_path))
+ logging.error("Error: Cannot find the latest origin commit for %s", file_path)
return
if o_from_head["hash"] == o_from_t["hash"]:
- if flag_p_uf:
- print("No update needed for {}".format(file_path))
- return
+ logging.debug("No update needed for %s", file_path)
else:
- print("{}".format(file_path), end="\t")
+ logging.info(file_path)
commits = get_commits_count_between(
opath, o_from_t["hash"], o_from_head["hash"]
)
- print("({} commits)".format(len(commits)))
- if flag_p_c:
- for commit in commits:
- msg = pretty_output(commit)
- if "Merge tag" not in msg:
- print("commit", msg)
+ count = 0
+ for commit in commits:
+ if valid_commit(commit):
+ logging.info("commit %s", pretty_output(commit))
+ count += 1
+ logging.info("%d commits needs resolving in total\n", count)
+
+
+def valid_locales(locale):
+ """Check if the locale is valid or not"""
+ script_path = os.path.dirname(os.path.abspath(__file__))
+ linux_path = os.path.join(script_path, "..")
+ if not os.path.isdir(f"{linux_path}/Documentation/translations/{locale}"):
+ raise ArgumentTypeError("Invalid locale: {locale}")
+ return locale
+
+
+def list_files_with_excluding_folders(folder, exclude_folders, include_suffix):
+ """List all files with the specified suffix in the folder and its subfolders"""
+ files = []
+ stack = [folder]
+
+ while stack:
+ pwd = stack.pop()
+ # filter out the exclude folders
+ if os.path.basename(pwd) in exclude_folders:
+ continue
+ # list all files and folders
+ for item in os.listdir(pwd):
+ ab_item = os.path.join(pwd, item)
+ if os.path.isdir(ab_item):
+ stack.append(ab_item)
+ else:
+ if ab_item.endswith(include_suffix):
+ files.append(ab_item)
+
+ return files
+
+
+class DmesgFormatter(logging.Formatter):
+ """Custom dmesg logging formatter"""
+ def format(self, record):
+ timestamp = time.time()
+ formatted_time = f"[{timestamp:>10.6f}]"
+ log_message = f"{formatted_time} {record.getMessage()}"
+ return log_message
+
+
+def config_logging(log_level, log_file="checktransupdate.log"):
+ """configure logging based on the log level"""
+ # set up the root logger
+ logger = logging.getLogger()
+ logger.setLevel(log_level)
+
+ # Create console handler
+ console_handler = logging.StreamHandler()
+ console_handler.setLevel(log_level)
+
+ # Create file handler
+ file_handler = logging.FileHandler(log_file)
+ file_handler.setLevel(log_level)
+
+ # Create formatter and add it to the handlers
+ formatter = DmesgFormatter()
+ console_handler.setFormatter(formatter)
+ file_handler.setFormatter(formatter)
+
+ # Add the handler to the logger
+ logger.addHandler(console_handler)
+ logger.addHandler(file_handler)
def main():
+ """Main function of the script"""
script_path = os.path.dirname(os.path.abspath(__file__))
linux_path = os.path.join(script_path, "..")
@@ -134,62 +202,62 @@ def main():
parser.add_argument(
"-l",
"--locale",
+ default="zh_CN",
+ type=valid_locales,
help="Locale to check when files are not specified",
)
+
parser.add_argument(
- "--print-commits",
+ "--print-missing-translations",
action=BooleanOptionalAction,
default=True,
- help="Print commits between the origin and the translation",
+ help="Print files that do not have translations",
)
parser.add_argument(
- "--print-updated-files",
- action=BooleanOptionalAction,
- default=False,
- help="Print files that do no need to be updated",
- )
+ '--log',
+ default='INFO',
+ choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
+ help='Set the logging level')
parser.add_argument(
- "--debug",
- action=BooleanOptionalAction,
- help="Print debug information",
- default=False,
- )
+ '--logfile',
+ default='checktransupdate.log',
+ help='Set the logging file (default: checktransupdate.log)')
parser.add_argument(
"files", nargs="*", help="Files to check, if not specified, check all files"
)
args = parser.parse_args()
- global flag_p_c, flag_p_uf, flag_debug
- flag_p_c = args.print_commits
- flag_p_uf = args.print_updated_files
- flag_debug = args.debug
+ # Configure logging based on the --log argument
+ log_level = getattr(logging, args.log.upper(), logging.INFO)
+ config_logging(log_level)
- # get files related to linux path
+ # Get files related to linux path
files = args.files
if len(files) == 0:
- if args.locale is not None:
- files = (
- os.popen(
- "find {}/Documentation/translations/{} -type f".format(
- linux_path, args.locale
- )
- )
- .read()
- .split("\n")
- )
- else:
- files = (
- os.popen(
- "find {}/Documentation/translations -type f".format(linux_path)
- )
- .read()
- .split("\n")
- )
-
- files = list(filter(lambda x: x != "", files))
+ offical_files = list_files_with_excluding_folders(
+ os.path.join(linux_path, "Documentation"), ["translations", "output"], "rst"
+ )
+
+ for file in offical_files:
+ # split the path into parts
+ path_parts = file.split(os.sep)
+ # find the index of the "Documentation" directory
+ kindex = path_parts.index("Documentation")
+ # insert the translations and locale after the Documentation directory
+ new_path_parts = path_parts[:kindex + 1] + ["translations", args.locale] \
+ + path_parts[kindex + 1 :]
+ # join the path parts back together
+ new_file = os.sep.join(new_path_parts)
+ if os.path.isfile(new_file):
+ files.append(new_file)
+ else:
+ if args.print_missing_translations:
+ logging.info(os.path.relpath(os.path.abspath(file), linux_path))
+ logging.info("No translation in the locale of %s\n", args.locale)
+
files = list(map(lambda x: os.path.relpath(os.path.abspath(x), linux_path), files))
# cd to linux root directory
diff --git a/scripts/clang-tools/gen_compile_commands.py b/scripts/clang-tools/gen_compile_commands.py
index e4fb686dfaa9..96e6e46ad1a7 100755
--- a/scripts/clang-tools/gen_compile_commands.py
+++ b/scripts/clang-tools/gen_compile_commands.py
@@ -167,10 +167,10 @@ def process_line(root_directory, command_prefix, file_path):
root_directory or file_directory.
"""
# The .cmd files are intended to be included directly by Make, so they
- # escape the pound sign '#', either as '\#' or '$(pound)' (depending on the
- # kernel version). The compile_commands.json file is not interepreted
- # by Make, so this code replaces the escaped version with '#'.
- prefix = command_prefix.replace(r'\#', '#').replace('$(pound)', '#')
+ # escape the pound sign '#' as '$(pound)'. The compile_commands.json file
+ # is not interepreted by Make, so this code replaces the escaped version
+ # with '#'.
+ prefix = command_prefix.replace('$(pound)', '#')
# Return the canonical path, eliminating any symbolic links encountered in the path.
abs_path = os.path.realpath(os.path.join(root_directory, file_path))
diff --git a/scripts/coccicheck b/scripts/coccicheck
index e52cb43fede6..0e6bc5a10320 100755
--- a/scripts/coccicheck
+++ b/scripts/coccicheck
@@ -80,11 +80,7 @@ command results in a shift count error.'
NPROC=1
else
ONLINE=0
- if [ "$KBUILD_EXTMOD" = "" ] ; then
- OPTIONS="--dir $srctree $COCCIINCLUDE"
- else
- OPTIONS="--dir $KBUILD_EXTMOD $COCCIINCLUDE"
- fi
+ OPTIONS="--dir $srcroot $COCCIINCLUDE"
# Use only one thread per core by default if hyperthreading is enabled
THREADS_PER_CORE=$(LANG=C lscpu | grep "Thread(s) per core: " | tr -cd "[:digit:]")
diff --git a/scripts/coccinelle/api/stream_open.cocci b/scripts/coccinelle/api/stream_open.cocci
index df00d6619b06..50ab60c81f13 100644
--- a/scripts/coccinelle/api/stream_open.cocci
+++ b/scripts/coccinelle/api/stream_open.cocci
@@ -131,7 +131,6 @@ identifier llseek_f;
identifier fops0.fops;
@@
struct file_operations fops = {
- .llseek = no_llseek,
};
@ has_noop_llseek @
diff --git a/scripts/coccinelle/api/string_choices.cocci b/scripts/coccinelle/api/string_choices.cocci
index a71966c0494e..375045086912 100644
--- a/scripts/coccinelle/api/string_choices.cocci
+++ b/scripts/coccinelle/api/string_choices.cocci
@@ -14,23 +14,18 @@ expression E;
- ((E == 1) ? "" : "s")
+ str_plural(E)
|
-- ((E != 1) ? "s" : "")
-+ str_plural(E)
-|
- ((E > 1) ? "s" : "")
+ str_plural(E)
)
-@str_plural_r depends on !patch exists@
+@str_plural_r depends on !patch@
expression E;
position P;
@@
(
-* ((E@P == 1) ? "" : "s")
-|
-* ((E@P != 1) ? "s" : "")
+* (E@P == 1) ? "" : "s"
|
-* ((E@P > 1) ? "s" : "")
+* (E@P > 1) ? "s" : ""
)
@script:python depends on report@
@@ -39,3 +34,269 @@ e << str_plural_r.E;
@@
coccilib.report.print_report(p[0], "opportunity for str_plural(%s)" % e)
+
+@str_up_down depends on patch disable neg_if_exp@
+expression E;
+@@
+- ((E) ? "up" : "down")
++ str_up_down(E)
+
+@str_up_down_r depends on !patch disable neg_if_exp@
+expression E;
+position P;
+@@
+* E@P ? "up" : "down"
+
+@script:python depends on report@
+p << str_up_down_r.P;
+e << str_up_down_r.E;
+@@
+
+coccilib.report.print_report(p[0], "opportunity for str_up_down(%s)" % e)
+
+@str_down_up depends on patch disable neg_if_exp@
+expression E;
+@@
+- ((E) ? "down" : "up")
++ str_down_up(E)
+
+@str_down_up_r depends on !patch disable neg_if_exp@
+expression E;
+position P;
+@@
+* E@P ? "down" : "up"
+
+@script:python depends on report@
+p << str_down_up_r.P;
+e << str_down_up_r.E;
+@@
+
+coccilib.report.print_report(p[0], "opportunity for str_down_up(%s)" % e)
+
+@str_true_false depends on patch disable neg_if_exp@
+expression E;
+@@
+- ((E) ? "true" : "false")
++ str_true_false(E)
+
+@str_true_false_r depends on !patch disable neg_if_exp@
+expression E;
+position P;
+@@
+* E@P ? "true" : "false"
+
+@script:python depends on report@
+p << str_true_false_r.P;
+e << str_true_false_r.E;
+@@
+
+coccilib.report.print_report(p[0], "opportunity for str_true_false(%s)" % e)
+
+@str_false_true depends on patch disable neg_if_exp@
+expression E;
+@@
+- ((E) ? "false" : "true")
++ str_false_true(E)
+
+@str_false_true_r depends on !patch disable neg_if_exp@
+expression E;
+position P;
+@@
+* E@P ? "false" : "true"
+
+@script:python depends on report@
+p << str_false_true_r.P;
+e << str_false_true_r.E;
+@@
+
+coccilib.report.print_report(p[0], "opportunity for str_false_true(%s)" % e)
+
+@str_hi_lo depends on patch disable neg_if_exp@
+expression E;
+@@
+- ((E) ? "hi" : "lo")
++ str_hi_lo(E)
+
+@str_hi_lo_r depends on !patch disable neg_if_exp@
+expression E;
+position P;
+@@
+* E@P ? "hi" : "lo"
+
+@script:python depends on report@
+p << str_hi_lo_r.P;
+e << str_hi_lo_r.E;
+@@
+
+coccilib.report.print_report(p[0], "opportunity for str_hi_lo(%s)" % e)
+
+@str_high_low depends on patch disable neg_if_exp@
+expression E;
+@@
+- ((E) ? "high" : "low")
++ str_high_low(E)
+
+@str_high_low_r depends on !patch disable neg_if_exp@
+expression E;
+position P;
+@@
+* E@P ? "high" : "low"
+
+@script:python depends on report@
+p << str_high_low_r.P;
+e << str_high_low_r.E;
+@@
+
+coccilib.report.print_report(p[0], "opportunity for str_high_low(%s)" % e)
+
+@str_lo_hi depends on patch disable neg_if_exp@
+expression E;
+@@
+- ((E) ? "lo" : "hi")
++ str_lo_hi(E)
+
+@str_lo_hi_r depends on !patch disable neg_if_exp@
+expression E;
+position P;
+@@
+* E@P ? "lo" : "hi"
+
+@script:python depends on report@
+p << str_lo_hi_r.P;
+e << str_lo_hi_r.E;
+@@
+
+coccilib.report.print_report(p[0], "opportunity for str_lo_hi(%s)" % e)
+
+@str_low_high depends on patch disable neg_if_exp@
+expression E;
+@@
+- ((E) ? "low" : "high")
++ str_low_high(E)
+
+@str_low_high_r depends on !patch disable neg_if_exp@
+expression E;
+position P;
+@@
+* E@P ? "low" : "high"
+
+@script:python depends on report@
+p << str_low_high_r.P;
+e << str_low_high_r.E;
+@@
+
+coccilib.report.print_report(p[0], "opportunity for str_low_high(%s)" % e)
+
+@str_enable_disable depends on patch@
+expression E;
+@@
+- ((E) ? "enable" : "disable")
++ str_enable_disable(E)
+
+@str_enable_disable_r depends on !patch@
+expression E;
+position P;
+@@
+* E@P ? "enable" : "disable"
+
+@script:python depends on report@
+p << str_enable_disable_r.P;
+e << str_enable_disable_r.E;
+@@
+
+coccilib.report.print_report(p[0], "opportunity for str_enable_disable(%s)" % e)
+
+@str_enabled_disabled depends on patch@
+expression E;
+@@
+- ((E) ? "enabled" : "disabled")
++ str_enabled_disabled(E)
+
+@str_enabled_disabled_r depends on !patch@
+expression E;
+position P;
+@@
+* E@P ? "enabled" : "disabled"
+
+@script:python depends on report@
+p << str_enabled_disabled_r.P;
+e << str_enabled_disabled_r.E;
+@@
+
+coccilib.report.print_report(p[0], "opportunity for str_enabled_disabled(%s)" % e)
+
+@str_read_write depends on patch disable neg_if_exp@
+expression E;
+@@
+- ((E) ? "read" : "write")
++ str_read_write(E)
+
+@str_read_write_r depends on !patch disable neg_if_exp@
+expression E;
+position P;
+@@
+* E@P ? "read" : "write"
+
+@script:python depends on report@
+p << str_read_write_r.P;
+e << str_read_write_r.E;
+@@
+
+coccilib.report.print_report(p[0], "opportunity for str_read_write(%s)" % e)
+
+@str_write_read depends on patch disable neg_if_exp@
+expression E;
+@@
+- ((E) ? "write" : "read")
++ str_write_read(E)
+
+@str_write_read_r depends on !patch disable neg_if_exp@
+expression E;
+position P;
+@@
+* E@P ? "write" : "read"
+
+@script:python depends on report@
+p << str_write_read_r.P;
+e << str_write_read_r.E;
+@@
+
+coccilib.report.print_report(p[0], "opportunity for str_write_read(%s)" % e)
+
+@str_on_off depends on patch@
+expression E;
+@@
+- ((E) ? "on" : "off")
++ str_on_off(E)
+
+@str_on_off_r depends on !patch@
+expression E;
+position P;
+@@
+* E@P ? "on" : "off"
+
+@script:python depends on report@
+p << str_on_off_r.P;
+e << str_on_off_r.E;
+@@
+
+coccilib.report.print_report(p[0], "opportunity for str_on_off(%s)" % e)
+
+@str_yes_no depends on patch@
+expression E;
+@@
+- ((E) ? "yes" : "no")
++ str_yes_no(E)
+
+@str_yes_no_r depends on !patch@
+expression E;
+position P;
+@@
+* E@P ? "yes" : "no"
+
+@script:python depends on report@
+p << str_yes_no_r.P;
+e << str_yes_no_r.E;
+@@
+
+coccilib.report.print_report(p[0], "opportunity for str_yes_no(%s)" % e)
diff --git a/scripts/coccinelle/misc/newline_in_nl_msg.cocci b/scripts/coccinelle/misc/newline_in_nl_msg.cocci
index 9baffe55d917..2814f6b205b9 100644
--- a/scripts/coccinelle/misc/newline_in_nl_msg.cocci
+++ b/scripts/coccinelle/misc/newline_in_nl_msg.cocci
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
///
-/// Catch strings ending in newline with GENL_SET_ERR_MSG, NL_SET_ERR_MSG,
-/// NL_SET_ERR_MSG_MOD.
+/// Catch strings ending in newline with (GE)NL_SET_ERR_MSG*.
///
// Confidence: Very High
// Copyright: (C) 2020 Intel Corporation
@@ -17,7 +16,11 @@ expression e;
constant m;
position p;
@@
- \(GENL_SET_ERR_MSG\|NL_SET_ERR_MSG\|NL_SET_ERR_MSG_MOD\)(e,m@p)
+ \(GENL_SET_ERR_MSG\|GENL_SET_ERR_MSG_FMT\|NL_SET_ERR_MSG\|NL_SET_ERR_MSG_MOD\|
+ NL_SET_ERR_MSG_FMT\|NL_SET_ERR_MSG_FMT_MOD\|NL_SET_ERR_MSG_WEAK\|
+ NL_SET_ERR_MSG_WEAK_MOD\|NL_SET_ERR_MSG_ATTR_POL\|
+ NL_SET_ERR_MSG_ATTR_POL_FMT\|NL_SET_ERR_MSG_ATTR\|
+ NL_SET_ERR_MSG_ATTR_FMT\)(e,m@p,...)
@script:python@
m << r.m;
@@ -32,7 +35,7 @@ expression r.e;
constant r.m;
position r.p;
@@
- fname(e,m@p)
+ fname(e,m@p,...)
//----------------------------------------------------------
// For context mode
@@ -43,7 +46,7 @@ identifier r1.fname;
expression r.e;
constant r.m;
@@
-* fname(e,m)
+* fname(e,m,...)
//----------------------------------------------------------
// For org mode
diff --git a/scripts/coccinelle/misc/secs_to_jiffies.cocci b/scripts/coccinelle/misc/secs_to_jiffies.cocci
new file mode 100644
index 000000000000..416f348174ca
--- /dev/null
+++ b/scripts/coccinelle/misc/secs_to_jiffies.cocci
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0-only
+///
+/// Find usages of:
+/// - msecs_to_jiffies(value*1000)
+/// - msecs_to_jiffies(value*MSEC_PER_SEC)
+///
+// Confidence: High
+// Copyright: (C) 2024 Easwar Hariharan, Microsoft
+// Keywords: secs, seconds, jiffies
+//
+
+virtual patch
+
+@depends on patch@ constant C; @@
+
+- msecs_to_jiffies(C * 1000)
++ secs_to_jiffies(C)
+
+@depends on patch@ constant C; @@
+
+- msecs_to_jiffies(C * MSEC_PER_SEC)
++ secs_to_jiffies(C)
+
+@depends on patch@ expression E; @@
+
+- msecs_to_jiffies(E * 1000)
++ secs_to_jiffies(E)
+
+@depends on patch@ expression E; @@
+
+- msecs_to_jiffies(E * MSEC_PER_SEC)
++ secs_to_jiffies(E)
diff --git a/scripts/config b/scripts/config
index ff88e2faefd3..ea475c07de28 100755
--- a/scripts/config
+++ b/scripts/config
@@ -32,6 +32,7 @@ commands:
Disable option directly after other option
--module-after|-M beforeopt option
Turn option into module directly after other option
+ --refresh Refresh the config using old settings
commands can be repeated multiple times
@@ -124,16 +125,22 @@ undef_var() {
txt_delete "^# $name is not set" "$FN"
}
-if [ "$1" = "--file" ]; then
- FN="$2"
- if [ "$FN" = "" ] ; then
- usage
+FN=.config
+CMDS=()
+while [[ $# -gt 0 ]]; do
+ if [ "$1" = "--file" ]; then
+ if [ "$2" = "" ]; then
+ usage
+ fi
+ FN="$2"
+ shift 2
+ else
+ CMDS+=("$1")
+ shift
fi
- shift 2
-else
- FN=.config
-fi
+done
+set -- "${CMDS[@]}"
if [ "$1" = "" ] ; then
usage
fi
@@ -217,9 +224,8 @@ while [ "$1" != "" ] ; do
set_var "${CONFIG_}$B" "${CONFIG_}$B=m" "${CONFIG_}$A"
;;
- # undocumented because it ignores --file (fixme)
--refresh)
- yes "" | make oldconfig
+ yes "" | make oldconfig KCONFIG_CONFIG=$FN
;;
*)
diff --git a/scripts/const_structs.checkpatch b/scripts/const_structs.checkpatch
index 014b3bfe3237..e8609a03c3d8 100644
--- a/scripts/const_structs.checkpatch
+++ b/scripts/const_structs.checkpatch
@@ -6,6 +6,7 @@ bus_type
clk_ops
comedi_lrange
component_ops
+ctl_table
dentry_operations
dev_pm_ops
device_type
diff --git a/scripts/decode_stacktrace.sh b/scripts/decode_stacktrace.sh
index a0f50a5b4f7c..17abc4e7a985 100755
--- a/scripts/decode_stacktrace.sh
+++ b/scripts/decode_stacktrace.sh
@@ -1,11 +1,13 @@
-#!/bin/bash
+#!/usr/bin/env bash
# SPDX-License-Identifier: GPL-2.0
# (c) 2014, Sasha Levin <sasha.levin@oracle.com>
#set -x
usage() {
echo "Usage:"
- echo " $0 -r <release> | <vmlinux> [<base path>|auto] [<modules path>]"
+ echo " $0 -r <release>"
+ echo " $0 [<vmlinux> [<base_path>|auto [<modules_path>]]]"
+ echo " $0 -h"
}
# Try to find a Rust demangler
@@ -32,7 +34,10 @@ READELF=${UTIL_PREFIX}readelf${UTIL_SUFFIX}
ADDR2LINE=${UTIL_PREFIX}addr2line${UTIL_SUFFIX}
NM=${UTIL_PREFIX}nm${UTIL_SUFFIX}
-if [[ $1 == "-r" ]] ; then
+if [[ $1 == "-h" ]] ; then
+ usage
+ exit 0
+elif [[ $1 == "-r" ]] ; then
vmlinux=""
basepath="auto"
modpath=""
@@ -89,31 +94,32 @@ find_module() {
fi
fi
- if [[ "$modpath" != "" ]] ; then
- for fn in $(find "$modpath" -name "${module//_/[-_]}.ko*") ; do
- if ${READELF} -WS "$fn" | grep -qwF .debug_line ; then
- echo $fn
- return
- fi
- done
- return 1
- fi
-
- modpath=$(dirname "$vmlinux")
- find_module && return
-
- if [[ $release == "" ]] ; then
+ if [ -z $release ] ; then
release=$(gdb -ex 'print init_uts_ns.name.release' -ex 'quit' -quiet -batch "$vmlinux" 2>/dev/null | sed -n 's/\$1 = "\(.*\)".*/\1/p')
fi
+ if [ -n "${release}" ] ; then
+ release_dirs="/usr/lib/debug/lib/modules/$release /lib/modules/$release"
+ fi
- for dn in {/usr/lib/debug,}/lib/modules/$release ; do
- if [ -e "$dn" ] ; then
- modpath="$dn"
- find_module && return
+ found_without_debug_info=false
+ for dir in "$modpath" "$(dirname "$vmlinux")" ${release_dirs}; do
+ if [ -n "${dir}" ] && [ -e "${dir}" ]; then
+ for fn in $(find "$dir" -name "${module//_/[-_]}.ko*") ; do
+ if ${READELF} -WS "$fn" | grep -qwF .debug_line ; then
+ echo $fn
+ return
+ fi
+ found_without_debug_info=true
+ done
fi
done
- modpath=""
+ if [[ ${found_without_debug_info} == true ]]; then
+ echo "WARNING! No debugging info in module ${module}, rebuild with DEBUG_KERNEL and DEBUG_INFO" >&2
+ else
+ echo "WARNING! Cannot find .ko for module ${module}, please pass a valid module path" >&2
+ fi
+
return 1
}
@@ -131,7 +137,6 @@ parse_symbol() {
else
local objfile=$(find_module)
if [[ $objfile == "" ]] ; then
- echo "WARNING! Modules path isn't set, but is needed to parse this symbol" >&2
return
fi
if [[ $aarray_support == true ]]; then
@@ -281,6 +286,18 @@ handle_line() {
last=$(( $last - 1 ))
fi
+ # Extract info after the symbol if present. E.g.:
+ # func_name+0x54/0x80 (P)
+ # ^^^
+ # The regex assumes only uppercase letters will be used. To be
+ # extended if needed.
+ local info_str=""
+ if [[ ${words[$last]} =~ \([A-Z]*\) ]]; then
+ info_str=${words[$last]}
+ unset words[$last]
+ last=$(( $last - 1 ))
+ fi
+
if [[ ${words[$last]} =~ \[([^]]+)\] ]]; then
module=${words[$last]}
# some traces format is "(%pS)", which like "(foo+0x0/0x1 [bar])"
@@ -306,7 +323,12 @@ handle_line() {
parse_symbol # modifies $symbol
# Add up the line number to the symbol
- echo "${words[@]}" "$symbol $module"
+ if [[ -z ${module} ]]
+ then
+ echo "${words[@]}" "$symbol ${info_str}"
+ else
+ echo "${words[@]}" "$symbol $module ${info_str}"
+ fi
}
while read line; do
diff --git a/scripts/depmod.sh b/scripts/depmod.sh
index e22da27fe13e..3c34fecacbc8 100755
--- a/scripts/depmod.sh
+++ b/scripts/depmod.sh
@@ -12,7 +12,7 @@ KERNELRELEASE=$1
: ${DEPMOD:=depmod}
-if ! test -r System.map ; then
+if ! test -r "${objtree}/System.map" ; then
echo "Warning: modules_install: missing 'System.map' file. Skipping depmod." >&2
exit 0
fi
@@ -25,7 +25,7 @@ if [ -z $(command -v $DEPMOD) ]; then
exit 0
fi
-set -- -ae -F System.map
+set -- -ae -F "${objtree}/System.map"
if test -n "$INSTALL_MOD_PATH"; then
set -- "$@" -b "$INSTALL_MOD_PATH"
fi
diff --git a/scripts/documentation-file-ref-check b/scripts/documentation-file-ref-check
index 68083f2f1122..408b1dbe7884 100755
--- a/scripts/documentation-file-ref-check
+++ b/scripts/documentation-file-ref-check
@@ -92,7 +92,7 @@ while (<IN>) {
next if ($f =~ m,^Next/,);
# Makefiles and scripts contain nasty expressions to parse docs
- next if ($f =~ m/Makefile/ || $f =~ m/\.sh$/);
+ next if ($f =~ m/Makefile/ || $f =~ m/\.(sh|py|pl|~|rej|org|orig)$/);
# It doesn't make sense to parse hidden files
next if ($f =~ m#/\.#);
diff --git a/scripts/dtc/checks.c b/scripts/dtc/checks.c
index 10fb63894369..6e06aeab5503 100644
--- a/scripts/dtc/checks.c
+++ b/scripts/dtc/checks.c
@@ -1826,10 +1826,14 @@ static void check_graph_port(struct check *c, struct dt_info *dti,
if (node->bus != &graph_port_bus)
return;
+ check_graph_reg(c, dti, node);
+
+ /* skip checks below for overlays */
+ if (dti->dtsflags & DTSF_PLUGIN)
+ return;
+
if (!strprefixeq(node->name, node->basenamelen, "port"))
FAIL(c, dti, node, "graph port node name should be 'port'");
-
- check_graph_reg(c, dti, node);
}
WARNING(graph_port, check_graph_port, NULL, &graph_nodes);
@@ -1864,11 +1868,15 @@ static void check_graph_endpoint(struct check *c, struct dt_info *dti,
if (!node->parent || node->parent->bus != &graph_port_bus)
return;
+ check_graph_reg(c, dti, node);
+
+ /* skip checks below for overlays */
+ if (dti->dtsflags & DTSF_PLUGIN)
+ return;
+
if (!strprefixeq(node->name, node->basenamelen, "endpoint"))
FAIL(c, dti, node, "graph endpoint node name should be 'endpoint'");
- check_graph_reg(c, dti, node);
-
remote_node = get_remote_endpoint(c, dti, node);
if (!remote_node)
return;
diff --git a/scripts/dtc/dt-extract-compatibles b/scripts/dtc/dt-extract-compatibles
index 5ffb2364409b..6570efabaa64 100755
--- a/scripts/dtc/dt-extract-compatibles
+++ b/scripts/dtc/dt-extract-compatibles
@@ -46,6 +46,15 @@ def parse_of_match_table(data):
return match_table_list
+def parse_of_functions(data, func_name):
+ """ Find all compatibles in the last argument of a given function """
+ compat_list = []
+ for m in re.finditer(rf'{func_name}\(([a-zA-Z0-9_>\(\)"\-]+,\s)*"([a-zA-Z0-9_,-]+)"\)', data):
+ compat_list.append(m[2])
+
+ return compat_list
+
+
def parse_compatibles(file, compat_ignore_list):
with open(file, 'r', encoding='utf-8') as f:
data = f.read().replace('\n', '')
@@ -60,6 +69,10 @@ def parse_compatibles(file, compat_ignore_list):
else:
compat_list = parse_of_declare_macros(data)
compat_list += parse_of_device_id(data)
+ compat_list += parse_of_functions(data, "_is_compatible")
+ compat_list += parse_of_functions(data, "of_find_compatible_node")
+ compat_list += parse_of_functions(data, "for_each_compatible_node")
+ compat_list += parse_of_functions(data, "of_get_compatible_child")
return compat_list
diff --git a/scripts/dtc/fdtoverlay.c b/scripts/dtc/fdtoverlay.c
index 4eba0460f240..699b4f616502 100644
--- a/scripts/dtc/fdtoverlay.c
+++ b/scripts/dtc/fdtoverlay.c
@@ -48,7 +48,7 @@ static void *apply_one(char *base, const char *overlay, size_t *buf_len,
int ret;
/*
- * We take a copies first, because a failed apply can trash
+ * We take copies first, because a failed apply can trash
* both the base blob and the overlay
*/
tmpo = xmalloc(fdt_totalsize(overlay));
diff --git a/scripts/dtc/version_gen.h b/scripts/dtc/version_gen.h
index 4c5e17639d2b..bf81ce593685 100644
--- a/scripts/dtc/version_gen.h
+++ b/scripts/dtc/version_gen.h
@@ -1 +1 @@
-#define DTC_VERSION "DTC 1.7.0-g1df7b047"
+#define DTC_VERSION "DTC 1.7.0-gbcd02b52"
diff --git a/scripts/export_report.pl b/scripts/export_report.pl
deleted file mode 100755
index feb3d5542a62..000000000000
--- a/scripts/export_report.pl
+++ /dev/null
@@ -1,186 +0,0 @@
-#!/usr/bin/env perl
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# (C) Copyright IBM Corporation 2006.
-# Author : Ram Pai (linuxram@us.ibm.com)
-#
-# Usage: export_report.pl -k Module.symvers [-o report_file ] -f *.mod.c
-#
-
-use warnings;
-use Getopt::Std;
-use strict;
-
-sub numerically {
- my $no1 = (split /\s+/, $a)[1];
- my $no2 = (split /\s+/, $b)[1];
- return $no1 <=> $no2;
-}
-
-sub alphabetically {
- my ($module1, $value1) = @{$a};
- my ($module2, $value2) = @{$b};
- return $value1 <=> $value2 || $module2 cmp $module1;
-}
-
-sub print_depends_on {
- my ($href) = @_;
- print "\n";
- for my $mod (sort keys %$href) {
- my $list = $href->{$mod};
- print "\t$mod:\n";
- foreach my $sym (sort numerically @{$list}) {
- my ($symbol, $no) = split /\s+/, $sym;
- printf("\t\t%-25s\n", $symbol);
- }
- print "\n";
- }
- print "\n";
- print "~"x80 , "\n";
-}
-
-sub usage {
- print "Usage: @_ -h -k Module.symvers [ -o outputfile ] \n",
- "\t-f: treat all the non-option argument as .mod.c files. ",
- "Recommend using this as the last option\n",
- "\t-h: print detailed help\n",
- "\t-k: the path to Module.symvers file. By default uses ",
- "the file from the current directory\n",
- "\t-o outputfile: output the report to outputfile\n";
- exit 0;
-}
-
-sub collectcfiles {
- my @file;
- open my $fh, '< modules.order' or die "cannot open modules.order: $!\n";
- while (<$fh>) {
- s/\.ko$/.mod.c/;
- push (@file, $_)
- }
- close($fh);
- chomp @file;
- return @file;
-}
-
-my (%SYMBOL, %MODULE, %opt, @allcfiles);
-
-if (not getopts('hk:o:f',\%opt) or defined $opt{'h'}) {
- usage($0);
-}
-
-if (defined $opt{'f'}) {
- @allcfiles = @ARGV;
-} else {
- @allcfiles = collectcfiles();
-}
-
-if (not defined $opt{'k'}) {
- $opt{'k'} = "Module.symvers";
-}
-
-open (my $module_symvers, '<', $opt{'k'})
- or die "Sorry, cannot open $opt{'k'}: $!\n";
-
-if (defined $opt{'o'}) {
- open (my $out, '>', $opt{'o'})
- or die "Sorry, cannot open $opt{'o'} $!\n";
-
- select $out;
-}
-
-#
-# collect all the symbols and their attributes from the
-# Module.symvers file
-#
-while ( <$module_symvers> ) {
- chomp;
- my (undef, $symbol, $module, $gpl, $namespace) = split('\t');
- $SYMBOL { $symbol } = [ $module , "0" , $symbol, $gpl];
-}
-close($module_symvers);
-
-#
-# collect the usage count of each symbol.
-#
-my $modversion_warnings = 0;
-
-foreach my $thismod (@allcfiles) {
- my $module;
-
- unless (open ($module, '<', $thismod)) {
- warn "Sorry, cannot open $thismod: $!\n";
- next;
- }
-
- my $state=0;
- while ( <$module> ) {
- chomp;
- if ($state == 0) {
- $state = 1 if ($_ =~ /static const struct modversion_info/);
- next;
- }
- if ($state == 1) {
- $state = 2 if ($_ =~ /__attribute__\(\(section\("__versions"\)\)\)/);
- next;
- }
- if ($state == 2) {
- if ( $_ !~ /0x[0-9a-f]+,/ ) {
- next;
- }
- my $sym = (split /([,"])/,)[4];
- my ($module, $value, $symbol, $gpl) = @{$SYMBOL{$sym}};
- $SYMBOL{ $sym } = [ $module, $value+1, $symbol, $gpl];
- push(@{$MODULE{$thismod}} , $sym);
- }
- }
- if ($state != 2) {
- warn "WARNING:$thismod is not built with CONFIG_MODVERSIONS enabled\n";
- $modversion_warnings++;
- }
- close($module);
-}
-
-print "\tThis file reports the exported symbols usage patterns by in-tree\n",
- "\t\t\t\tmodules\n";
-printf("%s\n\n\n","x"x80);
-printf("\t\t\t\tINDEX\n\n\n");
-printf("SECTION 1: Usage counts of all exported symbols\n");
-printf("SECTION 2: List of modules and the exported symbols they use\n");
-printf("%s\n\n\n","x"x80);
-printf("SECTION 1:\tThe exported symbols and their usage count\n\n");
-printf("%-25s\t%-25s\t%-5s\t%-25s\n", "Symbol", "Module", "Usage count",
- "export type");
-
-#
-# print the list of unused exported symbols
-#
-foreach my $list (sort alphabetically values(%SYMBOL)) {
- my ($module, $value, $symbol, $gpl) = @{$list};
- printf("%-25s\t%-25s\t%-10s\t", $symbol, $module, $value);
- if (defined $gpl) {
- printf("%-25s\n",$gpl);
- } else {
- printf("\n");
- }
-}
-printf("%s\n\n\n","x"x80);
-
-printf("SECTION 2:\n\tThis section reports export-symbol-usage of in-kernel
-modules. Each module lists the modules, and the symbols from that module that
-it uses. Each listed symbol reports the number of modules using it\n");
-
-print "\nNOTE: Got $modversion_warnings CONFIG_MODVERSIONS warnings\n\n"
- if $modversion_warnings;
-
-print "~"x80 , "\n";
-for my $thismod (sort keys %MODULE) {
- my $list = $MODULE{$thismod};
- my %depends;
- $thismod =~ s/\.mod\.c/.ko/;
- print "\t\t\t$thismod\n";
- foreach my $symbol (@{$list}) {
- my ($module, $value, undef, $gpl) = @{$SYMBOL{$symbol}};
- push (@{$depends{"$module"}}, "$symbol $value");
- }
- print_depends_on(\%depends);
-}
diff --git a/scripts/extract-fwblobs b/scripts/extract-fwblobs
new file mode 100755
index 000000000000..53729124e5a0
--- /dev/null
+++ b/scripts/extract-fwblobs
@@ -0,0 +1,30 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# -----------------------------------------------------------------------------
+# Extracts the vmlinux built-in firmware blobs - requires a non-stripped image
+# -----------------------------------------------------------------------------
+
+if [ -z "$1" ]; then
+ echo "Must provide a non-stripped vmlinux as argument"
+ exit 1
+fi
+
+read -r RD_ADDR_HEX RD_OFF_HEX <<< "$( readelf -SW "$1" |\
+grep -w rodata | awk '{print "0x"$5" 0x"$6}' )"
+
+FW_SYMS="$(readelf -sW "$1" |\
+awk -n '/fw_end/ { end=$2 ; print name " 0x" start " 0x" end; } { start=$2; name=$8; }')"
+
+while IFS= read -r entry; do
+ read -r FW_NAME FW_ADDR_ST_HEX FW_ADDR_END_HEX <<< "$entry"
+
+ # Notice kernel prepends _fw_ and appends _bin to the FW name
+ # in rodata; hence we hereby filter that out.
+ FW_NAME=${FW_NAME:4:-4}
+
+ FW_OFFSET="$(printf "%d" $((FW_ADDR_ST_HEX - RD_ADDR_HEX + RD_OFF_HEX)))"
+ FW_SIZE="$(printf "%d" $((FW_ADDR_END_HEX - FW_ADDR_ST_HEX)))"
+
+ dd if="$1" of="./${FW_NAME}" bs="${FW_SIZE}" count=1 iflag=skip_bytes skip="${FW_OFFSET}"
+done <<< "${FW_SYMS}"
diff --git a/scripts/faddr2line b/scripts/faddr2line
index fe0cc45f03be..1fa6beef9f97 100755
--- a/scripts/faddr2line
+++ b/scripts/faddr2line
@@ -252,7 +252,7 @@ __faddr2line() {
found=2
break
fi
- done < <(echo "${ELF_SYMS}" | sed 's/\[.*\]//' | ${AWK} -v sec=$sym_sec '$7 == sec' | sort --key=2 | ${GREP} -A1 --no-group-separator " ${sym_name}$")
+ done < <(echo "${ELF_SYMS}" | sed 's/\[.*\]//' | ${AWK} -v sec=$sym_sec '$7 == sec' | sort --key=2)
if [[ $found = 0 ]]; then
warn "can't find symbol: sym_name: $sym_name sym_sec: $sym_sec sym_addr: $sym_addr sym_elf_size: $sym_elf_size"
diff --git a/scripts/gcc-plugins/randomize_layout_plugin.c b/scripts/gcc-plugins/randomize_layout_plugin.c
index 746ff2d272f2..5694df3da2e9 100644
--- a/scripts/gcc-plugins/randomize_layout_plugin.c
+++ b/scripts/gcc-plugins/randomize_layout_plugin.c
@@ -19,10 +19,6 @@
#include "gcc-common.h"
#include "randomize_layout_seed.h"
-#if BUILDING_GCC_MAJOR < 4 || (BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR < 7)
-#error "The RANDSTRUCT plugin requires GCC 4.7 or newer."
-#endif
-
#define ORIG_TYPE_NAME(node) \
(TYPE_NAME(TYPE_MAIN_VARIANT(node)) != NULL_TREE ? ((const unsigned char *)IDENTIFIER_POINTER(TYPE_NAME(TYPE_MAIN_VARIANT(node)))) : (const unsigned char *)"anonymous")
diff --git a/scripts/gcc-x86_32-has-stack-protector.sh b/scripts/gcc-x86_32-has-stack-protector.sh
deleted file mode 100755
index 9459ca4f0f11..000000000000
--- a/scripts/gcc-x86_32-has-stack-protector.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/sh
-# SPDX-License-Identifier: GPL-2.0
-
-# This requires GCC 8.1 or better. Specifically, we require
-# -mstack-protector-guard-reg, added by
-# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81708
-
-echo "int foo(void) { char X[200]; return 3; }" | $* -S -x c -m32 -O0 -fstack-protector -mstack-protector-guard-reg=fs -mstack-protector-guard-symbol=__stack_chk_guard - -o - 2> /dev/null | grep -q "%fs"
diff --git a/scripts/gcc-x86_64-has-stack-protector.sh b/scripts/gcc-x86_64-has-stack-protector.sh
deleted file mode 100755
index f680bb01aeeb..000000000000
--- a/scripts/gcc-x86_64-has-stack-protector.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-# SPDX-License-Identifier: GPL-2.0
-
-echo "int foo(void) { char X[200]; return 3; }" | $* -S -x c -m64 -O0 -mcmodel=kernel -fno-PIE -fstack-protector - -o - 2> /dev/null | grep -q "%gs"
diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py
index 2f11c4f9c345..f506965ea759 100644
--- a/scripts/gdb/linux/cpus.py
+++ b/scripts/gdb/linux/cpus.py
@@ -46,7 +46,7 @@ def per_cpu(var_ptr, cpu):
# !CONFIG_SMP case
offset = 0
pointer = var_ptr.cast(utils.get_long_type()) + offset
- return pointer.cast(var_ptr.type).dereference()
+ return pointer.cast(var_ptr.type)
cpu_mask = {}
@@ -149,11 +149,29 @@ Note that VAR has to be quoted as string."""
super(PerCpu, self).__init__("lx_per_cpu")
def invoke(self, var, cpu=-1):
- return per_cpu(var.address, cpu)
+ return per_cpu(var.address, cpu).dereference()
PerCpu()
+
+class PerCpuPtr(gdb.Function):
+ """Return per-cpu pointer.
+
+$lx_per_cpu_ptr("VAR"[, CPU]): Return the per-cpu pointer called VAR for the
+given CPU number. If CPU is omitted, the CPU of the current context is used.
+Note that VAR has to be quoted as string."""
+
+ def __init__(self):
+ super(PerCpuPtr, self).__init__("lx_per_cpu_ptr")
+
+ def invoke(self, var, cpu=-1):
+ return per_cpu(var, cpu)
+
+
+PerCpuPtr()
+
+
def get_current_task(cpu):
task_ptr_type = task_type.get_type().pointer()
@@ -164,10 +182,10 @@ def get_current_task(cpu):
var_ptr = gdb.parse_and_eval("(struct task_struct *)cpu_tasks[0].task")
return var_ptr.dereference()
else:
- var_ptr = gdb.parse_and_eval("&pcpu_hot.current_task")
+ var_ptr = gdb.parse_and_eval("&current_task")
return per_cpu(var_ptr, cpu).dereference()
elif utils.is_target_arch("aarch64"):
- current_task_addr = gdb.parse_and_eval("$SP_EL0")
+ current_task_addr = gdb.parse_and_eval("(unsigned long)$SP_EL0")
if (current_task_addr >> 63) != 0:
current_task = current_task_addr.cast(task_ptr_type)
return current_task.dereference()
diff --git a/scripts/gdb/linux/kasan.py b/scripts/gdb/linux/kasan.py
new file mode 100644
index 000000000000..56730b3fde0b
--- /dev/null
+++ b/scripts/gdb/linux/kasan.py
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright 2024 Canonical Ltd.
+#
+# Authors:
+# Kuan-Ying Lee <kuan-ying.lee@canonical.com>
+#
+
+import gdb
+from linux import constants, mm
+
+def help():
+ t = """Usage: lx-kasan_mem_to_shadow [Hex memory addr]
+ Example:
+ lx-kasan_mem_to_shadow 0xffff000008eca008\n"""
+ gdb.write("Unrecognized command\n")
+ raise gdb.GdbError(t)
+
+class KasanMemToShadow(gdb.Command):
+ """Translate memory address to kasan shadow address"""
+
+ p_ops = None
+
+ def __init__(self):
+ if constants.LX_CONFIG_KASAN_GENERIC or constants.LX_CONFIG_KASAN_SW_TAGS:
+ super(KasanMemToShadow, self).__init__("lx-kasan_mem_to_shadow", gdb.COMMAND_SUPPORT)
+
+ def invoke(self, args, from_tty):
+ if not constants.LX_CONFIG_KASAN_GENERIC or constants.LX_CONFIG_KASAN_SW_TAGS:
+ raise gdb.GdbError('CONFIG_KASAN_GENERIC or CONFIG_KASAN_SW_TAGS is not set')
+
+ argv = gdb.string_to_argv(args)
+ if len(argv) == 1:
+ if self.p_ops is None:
+ self.p_ops = mm.page_ops().ops
+ addr = int(argv[0], 16)
+ shadow_addr = self.kasan_mem_to_shadow(addr)
+ gdb.write('shadow addr: 0x%x\n' % shadow_addr)
+ else:
+ help()
+ def kasan_mem_to_shadow(self, addr):
+ return (addr >> self.p_ops.KASAN_SHADOW_SCALE_SHIFT) + self.p_ops.KASAN_SHADOW_OFFSET
+
+KasanMemToShadow()
diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py
index 298dfcc25eae..fa15f872ddbe 100644
--- a/scripts/gdb/linux/modules.py
+++ b/scripts/gdb/linux/modules.py
@@ -19,6 +19,9 @@ from linux import cpus, utils, lists, constants
module_type = utils.CachedType("struct module")
+def has_modules():
+ return utils.gdb_eval_or_none("modules") is not None
+
def module_list():
global module_type
modules = utils.gdb_eval_or_none("modules")
diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
index 43c687e7a69d..65dd1bd12964 100644
--- a/scripts/gdb/linux/proc.py
+++ b/scripts/gdb/linux/proc.py
@@ -18,6 +18,7 @@ from linux import utils
from linux import tasks
from linux import lists
from linux import vfs
+from linux import rbtree
from struct import *
@@ -172,8 +173,7 @@ values of that process namespace"""
gdb.write("{:^18} {:^15} {:>9} {} {} options\n".format(
"mount", "super_block", "devname", "pathname", "fstype"))
- for mnt in lists.list_for_each_entry(namespace['list'],
- mount_ptr_type, "mnt_list"):
+ for mnt in rbtree.rb_inorder_for_each_entry(namespace['mounts'], mount_ptr_type, "mnt_node"):
devname = mnt['mnt_devname'].string()
devname = devname if devname else "none"
diff --git a/scripts/gdb/linux/rbtree.py b/scripts/gdb/linux/rbtree.py
index fe462855eefd..fcbcc5f4153c 100644
--- a/scripts/gdb/linux/rbtree.py
+++ b/scripts/gdb/linux/rbtree.py
@@ -9,6 +9,18 @@ from linux import utils
rb_root_type = utils.CachedType("struct rb_root")
rb_node_type = utils.CachedType("struct rb_node")
+def rb_inorder_for_each(root):
+ def inorder(node):
+ if node:
+ yield from inorder(node['rb_left'])
+ yield node
+ yield from inorder(node['rb_right'])
+
+ yield from inorder(root['rb_node'])
+
+def rb_inorder_for_each_entry(root, gdbtype, member):
+ for node in rb_inorder_for_each(root):
+ yield utils.container_of(node, gdbtype, member)
def rb_first(root):
if root.type == rb_root_type.get_type():
diff --git a/scripts/gdb/linux/stackdepot.py b/scripts/gdb/linux/stackdepot.py
index bb3a0f843931..37313a5a51a0 100644
--- a/scripts/gdb/linux/stackdepot.py
+++ b/scripts/gdb/linux/stackdepot.py
@@ -13,6 +13,13 @@ if constants.LX_CONFIG_STACKDEPOT:
stack_record_type = utils.CachedType('struct stack_record')
DEPOT_STACK_ALIGN = 4
+def help():
+ t = """Usage: lx-stack_depot_lookup [Hex handle value]
+ Example:
+ lx-stack_depot_lookup 0x00c80300\n"""
+ gdb.write("Unrecognized command\n")
+ raise gdb.GdbError(t)
+
def stack_depot_fetch(handle):
global DEPOT_STACK_ALIGN
global stack_record_type
@@ -57,3 +64,23 @@ def stack_depot_print(handle):
gdb.execute("x /i 0x%x" % (int(entries[i])))
except Exception as e:
gdb.write("%s\n" % e)
+
+class StackDepotLookup(gdb.Command):
+ """Search backtrace by handle"""
+
+ def __init__(self):
+ if constants.LX_CONFIG_STACKDEPOT:
+ super(StackDepotLookup, self).__init__("lx-stack_depot_lookup", gdb.COMMAND_SUPPORT)
+
+ def invoke(self, args, from_tty):
+ if not constants.LX_CONFIG_STACKDEPOT:
+ raise gdb.GdbError('CONFIG_STACKDEPOT is not set')
+
+ argv = gdb.string_to_argv(args)
+ if len(argv) == 1:
+ handle = int(argv[0], 16)
+ stack_depot_print(gdb.Value(handle).cast(utils.get_uint_type()))
+ else:
+ help()
+
+StackDepotLookup()
diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py
index e8316beb17a7..b255177301e9 100644
--- a/scripts/gdb/linux/symbols.py
+++ b/scripts/gdb/linux/symbols.py
@@ -14,7 +14,9 @@
import gdb
import os
import re
+import struct
+from itertools import count
from linux import modules, utils, constants
@@ -53,6 +55,29 @@ if hasattr(gdb, 'Breakpoint'):
return False
+def get_vmcore_s390():
+ with utils.qemu_phy_mem_mode():
+ vmcore_info = 0x0e0c
+ paddr_vmcoreinfo_note = gdb.parse_and_eval("*(unsigned long long *)" +
+ hex(vmcore_info))
+ inferior = gdb.selected_inferior()
+ elf_note = inferior.read_memory(paddr_vmcoreinfo_note, 12)
+ n_namesz, n_descsz, n_type = struct.unpack(">III", elf_note)
+ desc_paddr = paddr_vmcoreinfo_note + len(elf_note) + n_namesz + 1
+ return gdb.parse_and_eval("(char *)" + hex(desc_paddr)).string()
+
+
+def get_kerneloffset():
+ if utils.is_target_arch('s390'):
+ try:
+ vmcore_str = get_vmcore_s390()
+ except gdb.error as e:
+ gdb.write("{}\n".format(e))
+ return None
+ return utils.parse_vmcore(vmcore_str).kerneloffset
+ return None
+
+
class LxSymbols(gdb.Command):
"""(Re-)load symbols of Linux kernel and currently loaded modules.
@@ -95,10 +120,14 @@ lx-symbols command."""
except gdb.error:
return str(module_addr)
- attrs = sect_attrs['attrs']
- section_name_to_address = {
- attrs[n]['battr']['attr']['name'].string(): attrs[n]['address']
- for n in range(int(sect_attrs['nsections']))}
+ section_name_to_address = {}
+ for i in count():
+ # this is a NULL terminated array
+ if sect_attrs['grp']['bin_attrs'][i] == 0x0:
+ break
+
+ attr = sect_attrs['grp']['bin_attrs'][i].dereference()
+ section_name_to_address[attr['attr']['name'].string()] = attr['private']
textaddr = section_name_to_address.get(".text", module_addr)
args = []
@@ -155,7 +184,12 @@ lx-symbols command."""
obj.filename.endswith('vmlinux.debug')):
orig_vmlinux = obj.filename
gdb.execute("symbol-file", to_string=True)
- gdb.execute("symbol-file {0}".format(orig_vmlinux))
+ kerneloffset = get_kerneloffset()
+ if kerneloffset is None:
+ offset_arg = ""
+ else:
+ offset_arg = " -o " + hex(kerneloffset)
+ gdb.execute("symbol-file {0}{1}".format(orig_vmlinux, offset_arg))
self.loaded_modules = []
module_list = modules.module_list()
@@ -178,6 +212,9 @@ lx-symbols command."""
self.load_all_symbols()
+ if not modules.has_modules():
+ return
+
if hasattr(gdb, 'Breakpoint'):
if self.breakpoint is not None:
self.breakpoint.delete()
diff --git a/scripts/gdb/linux/timerlist.py b/scripts/gdb/linux/timerlist.py
index 64bc87191003..98445671fe83 100644
--- a/scripts/gdb/linux/timerlist.py
+++ b/scripts/gdb/linux/timerlist.py
@@ -87,21 +87,22 @@ def print_cpu(hrtimer_bases, cpu, max_clock_bases):
text += "\n"
if constants.LX_CONFIG_TICK_ONESHOT:
- fmts = [(" .{} : {}", 'nohz_mode'),
- (" .{} : {} nsecs", 'last_tick'),
- (" .{} : {}", 'tick_stopped'),
- (" .{} : {}", 'idle_jiffies'),
- (" .{} : {}", 'idle_calls'),
- (" .{} : {}", 'idle_sleeps'),
- (" .{} : {} nsecs", 'idle_entrytime'),
- (" .{} : {} nsecs", 'idle_waketime'),
- (" .{} : {} nsecs", 'idle_exittime'),
- (" .{} : {} nsecs", 'idle_sleeptime'),
- (" .{}: {} nsecs", 'iowait_sleeptime'),
- (" .{} : {}", 'last_jiffies'),
- (" .{} : {}", 'next_timer'),
- (" .{} : {} nsecs", 'idle_expires')]
- text += "\n".join([s.format(f, ts[f]) for s, f in fmts])
+ TS_FLAG_STOPPED = 1 << 1
+ TS_FLAG_NOHZ = 1 << 4
+ text += f" .{'nohz':15s}: {int(bool(ts['flags'] & TS_FLAG_NOHZ))}\n"
+ text += f" .{'last_tick':15s}: {ts['last_tick']}\n"
+ text += f" .{'tick_stopped':15s}: {int(bool(ts['flags'] & TS_FLAG_STOPPED))}\n"
+ text += f" .{'idle_jiffies':15s}: {ts['idle_jiffies']}\n"
+ text += f" .{'idle_calls':15s}: {ts['idle_calls']}\n"
+ text += f" .{'idle_sleeps':15s}: {ts['idle_sleeps']}\n"
+ text += f" .{'idle_entrytime':15s}: {ts['idle_entrytime']} nsecs\n"
+ text += f" .{'idle_waketime':15s}: {ts['idle_waketime']} nsecs\n"
+ text += f" .{'idle_exittime':15s}: {ts['idle_exittime']} nsecs\n"
+ text += f" .{'idle_sleeptime':15s}: {ts['idle_sleeptime']} nsecs\n"
+ text += f" .{'iowait_sleeptime':15s}: {ts['iowait_sleeptime']} nsecs\n"
+ text += f" .{'last_jiffies':15s}: {ts['last_jiffies']}\n"
+ text += f" .{'next_timer':15s}: {ts['next_timer']}\n"
+ text += f" .{'idle_expires':15s}: {ts['idle_expires']} nsecs\n"
text += "\njiffies: {}\n".format(jiffies)
text += "\n"
diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py
index 245ab297ea84..03ebdccf5f69 100644
--- a/scripts/gdb/linux/utils.py
+++ b/scripts/gdb/linux/utils.py
@@ -11,6 +11,11 @@
# This work is licensed under the terms of the GNU GPL version 2.
#
+import contextlib
+import dataclasses
+import re
+import typing
+
import gdb
@@ -216,3 +221,33 @@ def gdb_eval_or_none(expresssion):
return gdb.parse_and_eval(expresssion)
except gdb.error:
return None
+
+
+@contextlib.contextmanager
+def qemu_phy_mem_mode():
+ connection = gdb.selected_inferior().connection
+ orig = connection.send_packet("qqemu.PhyMemMode")
+ if orig not in b"01":
+ raise gdb.error("Unexpected qemu.PhyMemMode")
+ orig = orig.decode()
+ if connection.send_packet("Qqemu.PhyMemMode:1") != b"OK":
+ raise gdb.error("Failed to set qemu.PhyMemMode")
+ try:
+ yield
+ finally:
+ if connection.send_packet("Qqemu.PhyMemMode:" + orig) != b"OK":
+ raise gdb.error("Failed to restore qemu.PhyMemMode")
+
+
+@dataclasses.dataclass
+class VmCore:
+ kerneloffset: typing.Optional[int]
+
+
+def parse_vmcore(s):
+ match = re.search(r"KERNELOFFSET=([0-9a-f]+)", s)
+ if match is None:
+ kerneloffset = None
+ else:
+ kerneloffset = int(match.group(1), 16)
+ return VmCore(kerneloffset=kerneloffset)
diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py
index fc53cdf286f1..d4eeed4506fd 100644
--- a/scripts/gdb/vmlinux-gdb.py
+++ b/scripts/gdb/vmlinux-gdb.py
@@ -49,3 +49,4 @@ else:
import linux.page_owner
import linux.slab
import linux.vmalloc
+ import linux.kasan
diff --git a/scripts/gen-crc-consts.py b/scripts/gen-crc-consts.py
new file mode 100755
index 000000000000..f9b44fc3a03f
--- /dev/null
+++ b/scripts/gen-crc-consts.py
@@ -0,0 +1,291 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Script that generates constants for computing the given CRC variant(s).
+#
+# Copyright 2025 Google LLC
+#
+# Author: Eric Biggers <ebiggers@google.com>
+
+import sys
+
+# XOR (add) an iterable of polynomials.
+def xor(iterable):
+ res = 0
+ for val in iterable:
+ res ^= val
+ return res
+
+# Multiply two polynomials.
+def clmul(a, b):
+ return xor(a << i for i in range(b.bit_length()) if (b & (1 << i)) != 0)
+
+# Polynomial division floor(a / b).
+def div(a, b):
+ q = 0
+ while a.bit_length() >= b.bit_length():
+ q ^= 1 << (a.bit_length() - b.bit_length())
+ a ^= b << (a.bit_length() - b.bit_length())
+ return q
+
+# Reduce the polynomial 'a' modulo the polynomial 'b'.
+def reduce(a, b):
+ return a ^ clmul(div(a, b), b)
+
+# Reflect the bits of a polynomial.
+def bitreflect(poly, num_bits):
+ assert poly.bit_length() <= num_bits
+ return xor(((poly >> i) & 1) << (num_bits - 1 - i) for i in range(num_bits))
+
+# Format a polynomial as hex. Bit-reflect it if the CRC is lsb-first.
+def fmt_poly(variant, poly, num_bits):
+ if variant.lsb:
+ poly = bitreflect(poly, num_bits)
+ return f'0x{poly:0{2*num_bits//8}x}'
+
+# Print a pair of 64-bit polynomial multipliers. They are always passed in the
+# order [HI64_TERMS, LO64_TERMS] but will be printed in the appropriate order.
+def print_mult_pair(variant, mults):
+ mults = list(mults if variant.lsb else reversed(mults))
+ terms = ['HI64_TERMS', 'LO64_TERMS'] if variant.lsb else ['LO64_TERMS', 'HI64_TERMS']
+ for i in range(2):
+ print(f'\t\t{fmt_poly(variant, mults[i]["val"], 64)},\t/* {terms[i]}: {mults[i]["desc"]} */')
+
+# Pretty-print a polynomial.
+def pprint_poly(prefix, poly):
+ terms = [f'x^{i}' for i in reversed(range(poly.bit_length()))
+ if (poly & (1 << i)) != 0]
+ j = 0
+ while j < len(terms):
+ s = prefix + terms[j] + (' +' if j < len(terms) - 1 else '')
+ j += 1
+ while j < len(terms) and len(s) < 73:
+ s += ' ' + terms[j] + (' +' if j < len(terms) - 1 else '')
+ j += 1
+ print(s)
+ prefix = ' * ' + (' ' * (len(prefix) - 3))
+
+# Print a comment describing constants generated for the given CRC variant.
+def print_header(variant, what):
+ print('/*')
+ s = f'{"least" if variant.lsb else "most"}-significant-bit-first CRC-{variant.bits}'
+ print(f' * {what} generated for {s} using')
+ pprint_poly(' * G(x) = ', variant.G)
+ print(' */')
+
+class CrcVariant:
+ def __init__(self, bits, generator_poly, bit_order):
+ self.bits = bits
+ if bit_order not in ['lsb', 'msb']:
+ raise ValueError('Invalid value for bit_order')
+ self.lsb = bit_order == 'lsb'
+ self.name = f'crc{bits}_{bit_order}_0x{generator_poly:0{(2*bits+7)//8}x}'
+ if self.lsb:
+ generator_poly = bitreflect(generator_poly, bits)
+ self.G = generator_poly ^ (1 << bits)
+
+# Generate tables for CRC computation using the "slice-by-N" method.
+# N=1 corresponds to the traditional byte-at-a-time table.
+def gen_slicebyN_tables(variants, n):
+ for v in variants:
+ print('')
+ print_header(v, f'Slice-by-{n} CRC table')
+ print(f'static const u{v.bits} __maybe_unused {v.name}_table[{256*n}] = {{')
+ s = ''
+ for i in range(256 * n):
+ # The i'th table entry is the CRC of the message consisting of byte
+ # i % 256 followed by i // 256 zero bytes.
+ poly = (bitreflect(i % 256, 8) if v.lsb else i % 256) << (v.bits + 8*(i//256))
+ next_entry = fmt_poly(v, reduce(poly, v.G), v.bits) + ','
+ if len(s + next_entry) > 71:
+ print(f'\t{s}')
+ s = ''
+ s += (' ' if s else '') + next_entry
+ if s:
+ print(f'\t{s}')
+ print('};')
+
+def print_riscv_const(v, bits_per_long, name, val, desc):
+ print(f'\t.{name} = {fmt_poly(v, val, bits_per_long)}, /* {desc} */')
+
+def do_gen_riscv_clmul_consts(v, bits_per_long):
+ (G, n, lsb) = (v.G, v.bits, v.lsb)
+
+ pow_of_x = 3 * bits_per_long - (1 if lsb else 0)
+ print_riscv_const(v, bits_per_long, 'fold_across_2_longs_const_hi',
+ reduce(1 << pow_of_x, G), f'x^{pow_of_x} mod G')
+ pow_of_x = 2 * bits_per_long - (1 if lsb else 0)
+ print_riscv_const(v, bits_per_long, 'fold_across_2_longs_const_lo',
+ reduce(1 << pow_of_x, G), f'x^{pow_of_x} mod G')
+
+ pow_of_x = bits_per_long - 1 + n
+ print_riscv_const(v, bits_per_long, 'barrett_reduction_const_1',
+ div(1 << pow_of_x, G), f'floor(x^{pow_of_x} / G)')
+
+ val = G - (1 << n)
+ desc = f'G - x^{n}'
+ if lsb:
+ val <<= bits_per_long - n
+ desc = f'({desc}) * x^{bits_per_long - n}'
+ print_riscv_const(v, bits_per_long, 'barrett_reduction_const_2', val, desc)
+
+def gen_riscv_clmul_consts(variants):
+ print('')
+ print('struct crc_clmul_consts {');
+ print('\tunsigned long fold_across_2_longs_const_hi;');
+ print('\tunsigned long fold_across_2_longs_const_lo;');
+ print('\tunsigned long barrett_reduction_const_1;');
+ print('\tunsigned long barrett_reduction_const_2;');
+ print('};');
+ for v in variants:
+ print('');
+ if v.bits > 32:
+ print_header(v, 'Constants')
+ print('#ifdef CONFIG_64BIT')
+ print(f'static const struct crc_clmul_consts {v.name}_consts __maybe_unused = {{')
+ do_gen_riscv_clmul_consts(v, 64)
+ print('};')
+ print('#endif')
+ else:
+ print_header(v, 'Constants')
+ print(f'static const struct crc_clmul_consts {v.name}_consts __maybe_unused = {{')
+ print('#ifdef CONFIG_64BIT')
+ do_gen_riscv_clmul_consts(v, 64)
+ print('#else')
+ do_gen_riscv_clmul_consts(v, 32)
+ print('#endif')
+ print('};')
+
+# Generate constants for carryless multiplication based CRC computation.
+def gen_x86_pclmul_consts(variants):
+ # These are the distances, in bits, to generate folding constants for.
+ FOLD_DISTANCES = [2048, 1024, 512, 256, 128]
+
+ for v in variants:
+ (G, n, lsb) = (v.G, v.bits, v.lsb)
+ print('')
+ print_header(v, 'CRC folding constants')
+ print('static const struct {')
+ if not lsb:
+ print('\tu8 bswap_mask[16];')
+ for i in FOLD_DISTANCES:
+ print(f'\tu64 fold_across_{i}_bits_consts[2];')
+ print('\tu8 shuf_table[48];')
+ print('\tu64 barrett_reduction_consts[2];')
+ print(f'}} {v.name}_consts ____cacheline_aligned __maybe_unused = {{')
+
+ # Byte-reflection mask, needed for msb-first CRCs
+ if not lsb:
+ print('\t.bswap_mask = {' + ', '.join(str(i) for i in reversed(range(16))) + '},')
+
+ # Fold constants for all distances down to 128 bits
+ for i in FOLD_DISTANCES:
+ print(f'\t.fold_across_{i}_bits_consts = {{')
+ # Given 64x64 => 128 bit carryless multiplication instructions, two
+ # 64-bit fold constants are needed per "fold distance" i: one for
+ # HI64_TERMS that is basically x^(i+64) mod G and one for LO64_TERMS
+ # that is basically x^i mod G. The exact values however undergo a
+ # couple adjustments, described below.
+ mults = []
+ for j in [64, 0]:
+ pow_of_x = i + j
+ if lsb:
+ # Each 64x64 => 128 bit carryless multiplication instruction
+ # actually generates a 127-bit product in physical bits 0
+ # through 126, which in the lsb-first case represent the
+ # coefficients of x^1 through x^127, not x^0 through x^126.
+ # Thus in the lsb-first case, each such instruction
+ # implicitly adds an extra factor of x. The below removes a
+ # factor of x from each constant to compensate for this.
+ # For n < 64 the x could be removed from either the reduced
+ # part or unreduced part, but for n == 64 the reduced part
+ # is the only option. Just always use the reduced part.
+ pow_of_x -= 1
+ # Make a factor of x^(64-n) be applied unreduced rather than
+ # reduced, to cause the product to use only the x^(64-n) and
+ # higher terms and always be zero in the lower terms. Usually
+ # this makes no difference as it does not affect the product's
+ # congruence class mod G and the constant remains 64-bit, but
+ # part of the final reduction from 128 bits does rely on this
+ # property when it reuses one of the constants.
+ pow_of_x -= 64 - n
+ mults.append({ 'val': reduce(1 << pow_of_x, G) << (64 - n),
+ 'desc': f'(x^{pow_of_x} mod G) * x^{64-n}' })
+ print_mult_pair(v, mults)
+ print('\t},')
+
+ # Shuffle table for handling 1..15 bytes at end
+ print('\t.shuf_table = {')
+ print('\t\t' + (16*'-1, ').rstrip())
+ print('\t\t' + ''.join(f'{i:2}, ' for i in range(16)).rstrip())
+ print('\t\t' + (16*'-1, ').rstrip())
+ print('\t},')
+
+ # Barrett reduction constants for reducing 128 bits to the final CRC
+ print('\t.barrett_reduction_consts = {')
+ mults = []
+
+ val = div(1 << (63+n), G)
+ desc = f'floor(x^{63+n} / G)'
+ if not lsb:
+ val = (val << 1) - (1 << 64)
+ desc = f'({desc} * x) - x^64'
+ mults.append({ 'val': val, 'desc': desc })
+
+ val = G - (1 << n)
+ desc = f'G - x^{n}'
+ if lsb and n == 64:
+ assert (val & 1) != 0 # The x^0 term should always be nonzero.
+ val >>= 1
+ desc = f'({desc} - x^0) / x'
+ else:
+ pow_of_x = 64 - n - (1 if lsb else 0)
+ val <<= pow_of_x
+ desc = f'({desc}) * x^{pow_of_x}'
+ mults.append({ 'val': val, 'desc': desc })
+
+ print_mult_pair(v, mults)
+ print('\t},')
+
+ print('};')
+
+def parse_crc_variants(vars_string):
+ variants = []
+ for var_string in vars_string.split(','):
+ bits, bit_order, generator_poly = var_string.split('_')
+ assert bits.startswith('crc')
+ bits = int(bits.removeprefix('crc'))
+ assert generator_poly.startswith('0x')
+ generator_poly = generator_poly.removeprefix('0x')
+ assert len(generator_poly) % 2 == 0
+ generator_poly = int(generator_poly, 16)
+ variants.append(CrcVariant(bits, generator_poly, bit_order))
+ return variants
+
+if len(sys.argv) != 3:
+ sys.stderr.write(f'Usage: {sys.argv[0]} CONSTS_TYPE[,CONSTS_TYPE]... CRC_VARIANT[,CRC_VARIANT]...\n')
+ sys.stderr.write(' CONSTS_TYPE can be sliceby[1-8], riscv_clmul, or x86_pclmul\n')
+ sys.stderr.write(' CRC_VARIANT is crc${num_bits}_${bit_order}_${generator_poly_as_hex}\n')
+ sys.stderr.write(' E.g. crc16_msb_0x8bb7 or crc32_lsb_0xedb88320\n')
+ sys.stderr.write(' Polynomial must use the given bit_order and exclude x^{num_bits}\n')
+ sys.exit(1)
+
+print('/* SPDX-License-Identifier: GPL-2.0-or-later */')
+print('/*')
+print(' * CRC constants generated by:')
+print(' *')
+print(f' *\t{sys.argv[0]} {" ".join(sys.argv[1:])}')
+print(' *')
+print(' * Do not edit manually.')
+print(' */')
+consts_types = sys.argv[1].split(',')
+variants = parse_crc_variants(sys.argv[2])
+for consts_type in consts_types:
+ if consts_type.startswith('sliceby'):
+ gen_slicebyN_tables(variants, int(consts_type.removeprefix('sliceby')))
+ elif consts_type == 'riscv_clmul':
+ gen_riscv_clmul_consts(variants)
+ elif consts_type == 'x86_pclmul':
+ gen_x86_pclmul_consts(variants)
+ else:
+ raise ValueError(f'Unknown consts_type: {consts_type}')
diff --git a/scripts/gen_packed_field_checks.c b/scripts/gen_packed_field_checks.c
new file mode 100644
index 000000000000..60042b7616ee
--- /dev/null
+++ b/scripts/gen_packed_field_checks.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2024, Intel Corporation
+#include <stdbool.h>
+#include <stdio.h>
+
+#define MAX_PACKED_FIELD_SIZE 50
+
+int main(int argc, char **argv)
+{
+ /* The first macro doesn't need a 'do {} while(0)' loop */
+ printf("#define CHECK_PACKED_FIELDS_1(fields) \\\n");
+ printf("\tCHECK_PACKED_FIELD(fields, 0)\n\n");
+
+ /* Remaining macros require a do/while loop, and are implemented
+ * recursively by calling the previous iteration's macro.
+ */
+ for (int i = 2; i <= MAX_PACKED_FIELD_SIZE; i++) {
+ printf("#define CHECK_PACKED_FIELDS_%d(fields) do { \\\n", i);
+ printf("\tCHECK_PACKED_FIELDS_%d(fields); \\\n", i - 1);
+ printf("\tCHECK_PACKED_FIELD(fields, %d); \\\n", i - 1);
+ printf("} while (0)\n\n");
+ }
+
+ printf("#define CHECK_PACKED_FIELDS(fields) \\\n");
+
+ for (int i = 1; i <= MAX_PACKED_FIELD_SIZE; i++)
+ printf("\t__builtin_choose_expr(ARRAY_SIZE(fields) == %d, ({ CHECK_PACKED_FIELDS_%d(fields); }), \\\n",
+ i, i);
+
+ printf("\t({ BUILD_BUG_ON_MSG(1, \"CHECK_PACKED_FIELDS() must be regenerated to support array sizes larger than %d.\"); }) \\\n",
+ MAX_PACKED_FIELD_SIZE);
+
+ for (int i = 1; i <= MAX_PACKED_FIELD_SIZE; i++)
+ printf(")");
+
+ printf("\n");
+}
diff --git a/scripts/gendwarfksyms/.gitignore b/scripts/gendwarfksyms/.gitignore
new file mode 100644
index 000000000000..0927f8d3cd96
--- /dev/null
+++ b/scripts/gendwarfksyms/.gitignore
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+/gendwarfksyms
diff --git a/scripts/gendwarfksyms/Makefile b/scripts/gendwarfksyms/Makefile
new file mode 100644
index 000000000000..6334c7d3c4d5
--- /dev/null
+++ b/scripts/gendwarfksyms/Makefile
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+hostprogs-always-y += gendwarfksyms
+
+gendwarfksyms-objs += gendwarfksyms.o
+gendwarfksyms-objs += cache.o
+gendwarfksyms-objs += die.o
+gendwarfksyms-objs += dwarf.o
+gendwarfksyms-objs += kabi.o
+gendwarfksyms-objs += symbols.o
+gendwarfksyms-objs += types.o
+
+HOSTLDLIBS_gendwarfksyms := -ldw -lelf -lz
diff --git a/scripts/gendwarfksyms/cache.c b/scripts/gendwarfksyms/cache.c
new file mode 100644
index 000000000000..c9c19b86a686
--- /dev/null
+++ b/scripts/gendwarfksyms/cache.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024 Google LLC
+ */
+
+#include "gendwarfksyms.h"
+
+struct cache_item {
+ unsigned long key;
+ int value;
+ struct hlist_node hash;
+};
+
+void cache_set(struct cache *cache, unsigned long key, int value)
+{
+ struct cache_item *ci;
+
+ ci = xmalloc(sizeof(struct cache_item));
+ ci->key = key;
+ ci->value = value;
+ hash_add(cache->cache, &ci->hash, hash_32(key));
+}
+
+int cache_get(struct cache *cache, unsigned long key)
+{
+ struct cache_item *ci;
+
+ hash_for_each_possible(cache->cache, ci, hash, hash_32(key)) {
+ if (ci->key == key)
+ return ci->value;
+ }
+
+ return -1;
+}
+
+void cache_init(struct cache *cache)
+{
+ hash_init(cache->cache);
+}
+
+void cache_free(struct cache *cache)
+{
+ struct hlist_node *tmp;
+ struct cache_item *ci;
+
+ hash_for_each_safe(cache->cache, ci, tmp, hash) {
+ free(ci);
+ }
+
+ hash_init(cache->cache);
+}
diff --git a/scripts/gendwarfksyms/die.c b/scripts/gendwarfksyms/die.c
new file mode 100644
index 000000000000..6183bbbe7b54
--- /dev/null
+++ b/scripts/gendwarfksyms/die.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024 Google LLC
+ */
+
+#include <string.h>
+#include "gendwarfksyms.h"
+
+#define DIE_HASH_BITS 16
+
+/* {die->addr, state} -> struct die * */
+static HASHTABLE_DEFINE(die_map, 1 << DIE_HASH_BITS);
+
+static unsigned int map_hits;
+static unsigned int map_misses;
+
+static inline unsigned int die_hash(uintptr_t addr, enum die_state state)
+{
+ return hash_32(addr_hash(addr) ^ (unsigned int)state);
+}
+
+static void init_die(struct die *cd)
+{
+ cd->state = DIE_INCOMPLETE;
+ cd->mapped = false;
+ cd->fqn = NULL;
+ cd->tag = -1;
+ cd->addr = 0;
+ INIT_LIST_HEAD(&cd->fragments);
+}
+
+static struct die *create_die(Dwarf_Die *die, enum die_state state)
+{
+ struct die *cd;
+
+ cd = xmalloc(sizeof(struct die));
+ init_die(cd);
+ cd->addr = (uintptr_t)die->addr;
+
+ hash_add(die_map, &cd->hash, die_hash(cd->addr, state));
+ return cd;
+}
+
+int __die_map_get(uintptr_t addr, enum die_state state, struct die **res)
+{
+ struct die *cd;
+
+ hash_for_each_possible(die_map, cd, hash, die_hash(addr, state)) {
+ if (cd->addr == addr && cd->state == state) {
+ *res = cd;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+struct die *die_map_get(Dwarf_Die *die, enum die_state state)
+{
+ struct die *cd;
+
+ if (__die_map_get((uintptr_t)die->addr, state, &cd) == 0) {
+ map_hits++;
+ return cd;
+ }
+
+ map_misses++;
+ return create_die(die, state);
+}
+
+static void reset_die(struct die *cd)
+{
+ struct die_fragment *tmp;
+ struct die_fragment *df;
+
+ list_for_each_entry_safe(df, tmp, &cd->fragments, list) {
+ if (df->type == FRAGMENT_STRING)
+ free(df->data.str);
+ free(df);
+ }
+
+ if (cd->fqn && *cd->fqn)
+ free(cd->fqn);
+ init_die(cd);
+}
+
+void die_map_for_each(die_map_callback_t func, void *arg)
+{
+ struct hlist_node *tmp;
+ struct die *cd;
+
+ hash_for_each_safe(die_map, cd, tmp, hash) {
+ func(cd, arg);
+ }
+}
+
+void die_map_free(void)
+{
+ struct hlist_node *tmp;
+ unsigned int stats[DIE_LAST + 1];
+ struct die *cd;
+ int i;
+
+ memset(stats, 0, sizeof(stats));
+
+ hash_for_each_safe(die_map, cd, tmp, hash) {
+ stats[cd->state]++;
+ reset_die(cd);
+ free(cd);
+ }
+ hash_init(die_map);
+
+ if (map_hits + map_misses > 0)
+ debug("hits %u, misses %u (hit rate %.02f%%)", map_hits,
+ map_misses,
+ (100.0f * map_hits) / (map_hits + map_misses));
+
+ for (i = 0; i <= DIE_LAST; i++)
+ debug("%s: %u entries", die_state_name(i), stats[i]);
+}
+
+static struct die_fragment *append_item(struct die *cd)
+{
+ struct die_fragment *df;
+
+ df = xmalloc(sizeof(struct die_fragment));
+ df->type = FRAGMENT_EMPTY;
+ list_add_tail(&df->list, &cd->fragments);
+ return df;
+}
+
+void die_map_add_string(struct die *cd, const char *str)
+{
+ struct die_fragment *df;
+
+ if (!cd)
+ return;
+
+ df = append_item(cd);
+ df->data.str = xstrdup(str);
+ df->type = FRAGMENT_STRING;
+}
+
+void die_map_add_linebreak(struct die *cd, int linebreak)
+{
+ struct die_fragment *df;
+
+ if (!cd)
+ return;
+
+ df = append_item(cd);
+ df->data.linebreak = linebreak;
+ df->type = FRAGMENT_LINEBREAK;
+}
+
+void die_map_add_die(struct die *cd, struct die *child)
+{
+ struct die_fragment *df;
+
+ if (!cd)
+ return;
+
+ df = append_item(cd);
+ df->data.addr = child->addr;
+ df->type = FRAGMENT_DIE;
+}
diff --git a/scripts/gendwarfksyms/dwarf.c b/scripts/gendwarfksyms/dwarf.c
new file mode 100644
index 000000000000..eed247d8abfc
--- /dev/null
+++ b/scripts/gendwarfksyms/dwarf.c
@@ -0,0 +1,1171 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024 Google LLC
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include "gendwarfksyms.h"
+
+/* See get_union_kabi_status */
+#define KABI_PREFIX "__kabi_"
+#define KABI_PREFIX_LEN (sizeof(KABI_PREFIX) - 1)
+#define KABI_RESERVED_PREFIX "reserved"
+#define KABI_RESERVED_PREFIX_LEN (sizeof(KABI_RESERVED_PREFIX) - 1)
+#define KABI_RENAMED_PREFIX "renamed"
+#define KABI_RENAMED_PREFIX_LEN (sizeof(KABI_RENAMED_PREFIX) - 1)
+#define KABI_IGNORED_PREFIX "ignored"
+#define KABI_IGNORED_PREFIX_LEN (sizeof(KABI_IGNORED_PREFIX) - 1)
+
+static inline bool is_kabi_prefix(const char *name)
+{
+ return name && !strncmp(name, KABI_PREFIX, KABI_PREFIX_LEN);
+}
+
+enum kabi_status {
+ /* >0 to stop DIE processing */
+ KABI_NORMAL = 1,
+ KABI_RESERVED,
+ KABI_IGNORED,
+};
+
+static bool do_linebreak;
+static int indentation_level;
+
+/* Line breaks and indentation for pretty-printing */
+static void process_linebreak(struct die *cache, int n)
+{
+ indentation_level += n;
+ do_linebreak = true;
+ die_map_add_linebreak(cache, n);
+}
+
+#define DEFINE_GET_ATTR(attr, type) \
+ static bool get_##attr##_attr(Dwarf_Die *die, unsigned int id, \
+ type *value) \
+ { \
+ Dwarf_Attribute da; \
+ return dwarf_attr(die, id, &da) && \
+ !dwarf_form##attr(&da, value); \
+ }
+
+DEFINE_GET_ATTR(flag, bool)
+DEFINE_GET_ATTR(udata, Dwarf_Word)
+
+static bool get_ref_die_attr(Dwarf_Die *die, unsigned int id, Dwarf_Die *value)
+{
+ Dwarf_Attribute da;
+
+ /* dwarf_formref_die returns a pointer instead of an error value. */
+ return dwarf_attr(die, id, &da) && dwarf_formref_die(&da, value);
+}
+
+#define DEFINE_GET_STRING_ATTR(attr) \
+ static const char *get_##attr##_attr(Dwarf_Die *die) \
+ { \
+ Dwarf_Attribute da; \
+ if (dwarf_attr(die, DW_AT_##attr, &da)) \
+ return dwarf_formstring(&da); \
+ return NULL; \
+ }
+
+DEFINE_GET_STRING_ATTR(name)
+DEFINE_GET_STRING_ATTR(linkage_name)
+
+static const char *get_symbol_name(Dwarf_Die *die)
+{
+ const char *name;
+
+ /* rustc uses DW_AT_linkage_name for exported symbols */
+ name = get_linkage_name_attr(die);
+ if (!name)
+ name = get_name_attr(die);
+
+ return name;
+}
+
+static bool match_export_symbol(struct state *state, Dwarf_Die *die)
+{
+ Dwarf_Die *source = die;
+ Dwarf_Die origin;
+
+ /* If the DIE has an abstract origin, use it for type information. */
+ if (get_ref_die_attr(die, DW_AT_abstract_origin, &origin))
+ source = &origin;
+
+ state->sym = symbol_get(get_symbol_name(die));
+
+ /* Look up using the origin name if there are no matches. */
+ if (!state->sym && source != die)
+ state->sym = symbol_get(get_symbol_name(source));
+
+ state->die = *source;
+ return !!state->sym;
+}
+
+/* DW_AT_decl_file -> struct srcfile */
+static struct cache srcfile_cache;
+
+static bool is_definition_private(Dwarf_Die *die)
+{
+ Dwarf_Word filenum;
+ Dwarf_Files *files;
+ Dwarf_Die cudie;
+ const char *s;
+ int res;
+
+ /*
+ * Definitions in .c files cannot change the public ABI,
+ * so consider them private.
+ */
+ if (!get_udata_attr(die, DW_AT_decl_file, &filenum))
+ return false;
+
+ res = cache_get(&srcfile_cache, filenum);
+ if (res >= 0)
+ return !!res;
+
+ if (!dwarf_cu_die(die->cu, &cudie, NULL, NULL, NULL, NULL, NULL, NULL))
+ error("dwarf_cu_die failed: '%s'", dwarf_errmsg(-1));
+
+ if (dwarf_getsrcfiles(&cudie, &files, NULL))
+ error("dwarf_getsrcfiles failed: '%s'", dwarf_errmsg(-1));
+
+ s = dwarf_filesrc(files, filenum, NULL, NULL);
+ if (!s)
+ error("dwarf_filesrc failed: '%s'", dwarf_errmsg(-1));
+
+ s = strrchr(s, '.');
+ res = s && !strcmp(s, ".c");
+ cache_set(&srcfile_cache, filenum, res);
+
+ return !!res;
+}
+
+static bool is_kabi_definition(struct die *cache, Dwarf_Die *die)
+{
+ bool value;
+
+ if (get_flag_attr(die, DW_AT_declaration, &value) && value)
+ return false;
+
+ if (kabi_is_declonly(cache->fqn))
+ return false;
+
+ return !is_definition_private(die);
+}
+
+/*
+ * Type string processing
+ */
+static void process(struct die *cache, const char *s)
+{
+ s = s ?: "<null>";
+
+ if (dump_dies && do_linebreak) {
+ fputs("\n", stderr);
+ for (int i = 0; i < indentation_level; i++)
+ fputs(" ", stderr);
+ do_linebreak = false;
+ }
+ if (dump_dies)
+ fputs(s, stderr);
+
+ if (cache)
+ die_debug_r("cache %p string '%s'", cache, s);
+ die_map_add_string(cache, s);
+}
+
+#define MAX_FMT_BUFFER_SIZE 128
+
+static void process_fmt(struct die *cache, const char *fmt, ...)
+{
+ char buf[MAX_FMT_BUFFER_SIZE];
+ va_list args;
+
+ va_start(args, fmt);
+
+ if (checkp(vsnprintf(buf, sizeof(buf), fmt, args)) >= sizeof(buf))
+ error("vsnprintf overflow: increase MAX_FMT_BUFFER_SIZE");
+
+ process(cache, buf);
+ va_end(args);
+}
+
+static void update_fqn(struct die *cache, Dwarf_Die *die)
+{
+ struct die *fqn;
+
+ if (!cache->fqn) {
+ if (!__die_map_get((uintptr_t)die->addr, DIE_FQN, &fqn) &&
+ *fqn->fqn)
+ cache->fqn = xstrdup(fqn->fqn);
+ else
+ cache->fqn = "";
+ }
+}
+
+static void process_fqn(struct die *cache, Dwarf_Die *die)
+{
+ update_fqn(cache, die);
+ if (*cache->fqn)
+ process(cache, " ");
+ process(cache, cache->fqn);
+}
+
+#define DEFINE_PROCESS_UDATA_ATTRIBUTE(attribute) \
+ static void process_##attribute##_attr(struct die *cache, \
+ Dwarf_Die *die) \
+ { \
+ Dwarf_Word value; \
+ if (get_udata_attr(die, DW_AT_##attribute, &value)) \
+ process_fmt(cache, " " #attribute "(%" PRIu64 ")", \
+ value); \
+ }
+
+DEFINE_PROCESS_UDATA_ATTRIBUTE(accessibility)
+DEFINE_PROCESS_UDATA_ATTRIBUTE(alignment)
+DEFINE_PROCESS_UDATA_ATTRIBUTE(bit_size)
+DEFINE_PROCESS_UDATA_ATTRIBUTE(byte_size)
+DEFINE_PROCESS_UDATA_ATTRIBUTE(encoding)
+DEFINE_PROCESS_UDATA_ATTRIBUTE(data_bit_offset)
+DEFINE_PROCESS_UDATA_ATTRIBUTE(data_member_location)
+DEFINE_PROCESS_UDATA_ATTRIBUTE(discr_value)
+
+/* Match functions -- die_match_callback_t */
+#define DEFINE_MATCH(type) \
+ static bool match_##type##_type(Dwarf_Die *die) \
+ { \
+ return dwarf_tag(die) == DW_TAG_##type##_type; \
+ }
+
+DEFINE_MATCH(enumerator)
+DEFINE_MATCH(formal_parameter)
+DEFINE_MATCH(member)
+DEFINE_MATCH(subrange)
+
+bool match_all(Dwarf_Die *die)
+{
+ return true;
+}
+
+int process_die_container(struct state *state, struct die *cache,
+ Dwarf_Die *die, die_callback_t func,
+ die_match_callback_t match)
+{
+ Dwarf_Die current;
+ int res;
+
+ /* Track the first item in lists. */
+ if (state)
+ state->first_list_item = true;
+
+ res = checkp(dwarf_child(die, &current));
+ while (!res) {
+ if (match(&current)) {
+ /* <0 = error, 0 = continue, >0 = stop */
+ res = checkp(func(state, cache, &current));
+ if (res)
+ goto out;
+ }
+
+ res = checkp(dwarf_siblingof(&current, &current));
+ }
+
+ res = 0;
+out:
+ if (state)
+ state->first_list_item = false;
+
+ return res;
+}
+
+static int process_type(struct state *state, struct die *parent,
+ Dwarf_Die *die);
+
+static void process_type_attr(struct state *state, struct die *cache,
+ Dwarf_Die *die)
+{
+ Dwarf_Die type;
+
+ if (get_ref_die_attr(die, DW_AT_type, &type)) {
+ check(process_type(state, cache, &type));
+ return;
+ }
+
+ /* Compilers can omit DW_AT_type -- print out 'void' to clarify */
+ process(cache, "base_type void");
+}
+
+static void process_list_comma(struct state *state, struct die *cache)
+{
+ if (state->first_list_item) {
+ state->first_list_item = false;
+ } else {
+ process(cache, " ,");
+ process_linebreak(cache, 0);
+ }
+}
+
+/* Comma-separated with DW_AT_type */
+static void __process_list_type(struct state *state, struct die *cache,
+ Dwarf_Die *die, const char *type)
+{
+ const char *name = get_name_attr(die);
+
+ if (stable) {
+ if (is_kabi_prefix(name))
+ name = NULL;
+ state->kabi.orig_name = NULL;
+ }
+
+ process_list_comma(state, cache);
+ process(cache, type);
+ process_type_attr(state, cache, die);
+
+ if (stable && state->kabi.orig_name)
+ name = state->kabi.orig_name;
+ if (name) {
+ process(cache, " ");
+ process(cache, name);
+ }
+
+ process_accessibility_attr(cache, die);
+ process_bit_size_attr(cache, die);
+ process_data_bit_offset_attr(cache, die);
+ process_data_member_location_attr(cache, die);
+}
+
+#define DEFINE_PROCESS_LIST_TYPE(type) \
+ static void process_##type##_type(struct state *state, \
+ struct die *cache, Dwarf_Die *die) \
+ { \
+ __process_list_type(state, cache, die, #type " "); \
+ }
+
+DEFINE_PROCESS_LIST_TYPE(formal_parameter)
+DEFINE_PROCESS_LIST_TYPE(member)
+
+/* Container types with DW_AT_type */
+static void __process_type(struct state *state, struct die *cache,
+ Dwarf_Die *die, const char *type)
+{
+ process(cache, type);
+ process_fqn(cache, die);
+ process(cache, " {");
+ process_linebreak(cache, 1);
+ process_type_attr(state, cache, die);
+ process_linebreak(cache, -1);
+ process(cache, "}");
+ process_byte_size_attr(cache, die);
+ process_alignment_attr(cache, die);
+}
+
+#define DEFINE_PROCESS_TYPE(type) \
+ static void process_##type##_type(struct state *state, \
+ struct die *cache, Dwarf_Die *die) \
+ { \
+ __process_type(state, cache, die, #type "_type"); \
+ }
+
+DEFINE_PROCESS_TYPE(atomic)
+DEFINE_PROCESS_TYPE(const)
+DEFINE_PROCESS_TYPE(immutable)
+DEFINE_PROCESS_TYPE(packed)
+DEFINE_PROCESS_TYPE(pointer)
+DEFINE_PROCESS_TYPE(reference)
+DEFINE_PROCESS_TYPE(restrict)
+DEFINE_PROCESS_TYPE(rvalue_reference)
+DEFINE_PROCESS_TYPE(shared)
+DEFINE_PROCESS_TYPE(template_type_parameter)
+DEFINE_PROCESS_TYPE(volatile)
+DEFINE_PROCESS_TYPE(typedef)
+
+static void process_subrange_type(struct state *state, struct die *cache,
+ Dwarf_Die *die)
+{
+ Dwarf_Word count = 0;
+
+ if (get_udata_attr(die, DW_AT_count, &count))
+ process_fmt(cache, "[%" PRIu64 "]", count);
+ else if (get_udata_attr(die, DW_AT_upper_bound, &count))
+ process_fmt(cache, "[%" PRIu64 "]", count + 1);
+ else
+ process(cache, "[]");
+}
+
+static void process_array_type(struct state *state, struct die *cache,
+ Dwarf_Die *die)
+{
+ process(cache, "array_type");
+ /* Array size */
+ check(process_die_container(state, cache, die, process_type,
+ match_subrange_type));
+ process(cache, " {");
+ process_linebreak(cache, 1);
+ process_type_attr(state, cache, die);
+ process_linebreak(cache, -1);
+ process(cache, "}");
+}
+
+static void __process_subroutine_type(struct state *state, struct die *cache,
+ Dwarf_Die *die, const char *type)
+{
+ process(cache, type);
+ process(cache, " (");
+ process_linebreak(cache, 1);
+ /* Parameters */
+ check(process_die_container(state, cache, die, process_type,
+ match_formal_parameter_type));
+ process_linebreak(cache, -1);
+ process(cache, ")");
+ process_linebreak(cache, 0);
+ /* Return type */
+ process(cache, "-> ");
+ process_type_attr(state, cache, die);
+}
+
+static void process_subroutine_type(struct state *state, struct die *cache,
+ Dwarf_Die *die)
+{
+ __process_subroutine_type(state, cache, die, "subroutine_type");
+}
+
+static void process_variant_type(struct state *state, struct die *cache,
+ Dwarf_Die *die)
+{
+ process_list_comma(state, cache);
+ process(cache, "variant {");
+ process_linebreak(cache, 1);
+ check(process_die_container(state, cache, die, process_type,
+ match_member_type));
+ process_linebreak(cache, -1);
+ process(cache, "}");
+ process_discr_value_attr(cache, die);
+}
+
+static void process_variant_part_type(struct state *state, struct die *cache,
+ Dwarf_Die *die)
+{
+ process_list_comma(state, cache);
+ process(cache, "variant_part {");
+ process_linebreak(cache, 1);
+ check(process_die_container(state, cache, die, process_type,
+ match_all));
+ process_linebreak(cache, -1);
+ process(cache, "}");
+}
+
+static int get_kabi_status(Dwarf_Die *die, const char **suffix)
+{
+ const char *name = get_name_attr(die);
+
+ if (suffix)
+ *suffix = NULL;
+
+ if (is_kabi_prefix(name)) {
+ name += KABI_PREFIX_LEN;
+
+ if (!strncmp(name, KABI_RESERVED_PREFIX,
+ KABI_RESERVED_PREFIX_LEN))
+ return KABI_RESERVED;
+ if (!strncmp(name, KABI_IGNORED_PREFIX,
+ KABI_IGNORED_PREFIX_LEN))
+ return KABI_IGNORED;
+
+ if (!strncmp(name, KABI_RENAMED_PREFIX,
+ KABI_RENAMED_PREFIX_LEN)) {
+ if (suffix) {
+ name += KABI_RENAMED_PREFIX_LEN;
+ *suffix = name;
+ }
+ return KABI_RESERVED;
+ }
+ }
+
+ return KABI_NORMAL;
+}
+
+static int check_struct_member_kabi_status(struct state *state,
+ struct die *__unused, Dwarf_Die *die)
+{
+ int res;
+
+ assert(dwarf_tag(die) == DW_TAG_member_type);
+
+ /*
+ * If the union member is a struct, expect the __kabi field to
+ * be the first member of the structure, i.e..:
+ *
+ * union {
+ * type new_member;
+ * struct {
+ * type __kabi_field;
+ * }
+ * };
+ */
+ res = get_kabi_status(die, &state->kabi.orig_name);
+
+ if (res == KABI_RESERVED &&
+ !get_ref_die_attr(die, DW_AT_type, &state->kabi.placeholder))
+ error("structure member missing a type?");
+
+ return res;
+}
+
+static int check_union_member_kabi_status(struct state *state,
+ struct die *__unused, Dwarf_Die *die)
+{
+ Dwarf_Die type;
+ int res;
+
+ assert(dwarf_tag(die) == DW_TAG_member_type);
+
+ if (!get_ref_die_attr(die, DW_AT_type, &type))
+ error("union member missing a type?");
+
+ /*
+ * We expect a union with two members. Check if either of them
+ * has a __kabi name prefix, i.e.:
+ *
+ * union {
+ * ...
+ * type memberN; // <- type, N = {0,1}
+ * ...
+ * };
+ *
+ * The member can also be a structure type, in which case we'll
+ * check the first structure member.
+ *
+ * In any case, stop processing after we've seen two members.
+ */
+ res = get_kabi_status(die, &state->kabi.orig_name);
+
+ if (res == KABI_RESERVED)
+ state->kabi.placeholder = type;
+ if (res != KABI_NORMAL)
+ return res;
+
+ if (dwarf_tag(&type) == DW_TAG_structure_type)
+ res = checkp(process_die_container(
+ state, NULL, &type, check_struct_member_kabi_status,
+ match_member_type));
+
+ if (res <= KABI_NORMAL && ++state->kabi.members < 2)
+ return 0; /* Continue */
+
+ return res;
+}
+
+static int get_union_kabi_status(Dwarf_Die *die, Dwarf_Die *placeholder,
+ const char **orig_name)
+{
+ struct state state;
+ int res;
+
+ if (!stable)
+ return KABI_NORMAL;
+
+ /*
+ * To maintain a stable kABI, distributions may choose to reserve
+ * space in structs for later use by adding placeholder members,
+ * for example:
+ *
+ * struct s {
+ * u32 a;
+ * // an 8-byte placeholder for future use
+ * u64 __kabi_reserved_0;
+ * };
+ *
+ * When the reserved member is taken into use, the type change
+ * would normally cause the symbol version to change as well, but
+ * if the replacement uses the following convention, gendwarfksyms
+ * continues to use the placeholder type for versioning instead,
+ * thus maintaining the same symbol version:
+ *
+ * struct s {
+ * u32 a;
+ * union {
+ * // placeholder replaced with a new member `b`
+ * struct t b;
+ * struct {
+ * // the placeholder type that is still
+ * // used for versioning
+ * u64 __kabi_reserved_0;
+ * };
+ * };
+ * };
+ *
+ * I.e., as long as the replaced member is in a union, and the
+ * placeholder has a __kabi_reserved name prefix, we'll continue
+ * to use the placeholder type (here u64) for version calculation
+ * instead of the union type.
+ *
+ * It's also possible to ignore new members from versioning if
+ * they've been added to alignment holes, for example, by
+ * including them in a union with another member that uses the
+ * __kabi_ignored name prefix:
+ *
+ * struct s {
+ * u32 a;
+ * // an alignment hole is used to add `n`
+ * union {
+ * u32 n;
+ * // hide the entire union member from versioning
+ * u8 __kabi_ignored_0;
+ * };
+ * u64 b;
+ * };
+ *
+ * Note that the user of this feature is responsible for ensuring
+ * that the structure actually remains ABI compatible.
+ */
+ memset(&state.kabi, 0, sizeof(struct kabi_state));
+
+ res = checkp(process_die_container(&state, NULL, die,
+ check_union_member_kabi_status,
+ match_member_type));
+
+ if (res == KABI_RESERVED) {
+ if (placeholder)
+ *placeholder = state.kabi.placeholder;
+ if (orig_name)
+ *orig_name = state.kabi.orig_name;
+ }
+
+ return res;
+}
+
+static bool is_kabi_ignored(Dwarf_Die *die)
+{
+ Dwarf_Die type;
+
+ if (!stable)
+ return false;
+
+ if (!get_ref_die_attr(die, DW_AT_type, &type))
+ error("member missing a type?");
+
+ return dwarf_tag(&type) == DW_TAG_union_type &&
+ checkp(get_union_kabi_status(&type, NULL, NULL)) == KABI_IGNORED;
+}
+
+static int ___process_structure_type(struct state *state, struct die *cache,
+ Dwarf_Die *die)
+{
+ switch (dwarf_tag(die)) {
+ case DW_TAG_member:
+ if (is_kabi_ignored(die))
+ return 0;
+ return check(process_type(state, cache, die));
+ case DW_TAG_variant_part:
+ return check(process_type(state, cache, die));
+ case DW_TAG_class_type:
+ case DW_TAG_enumeration_type:
+ case DW_TAG_structure_type:
+ case DW_TAG_template_type_parameter:
+ case DW_TAG_union_type:
+ case DW_TAG_subprogram:
+ /* Skip non-member types, including member functions */
+ return 0;
+ default:
+ error("unexpected structure_type child: %x", dwarf_tag(die));
+ }
+}
+
+static void __process_structure_type(struct state *state, struct die *cache,
+ Dwarf_Die *die, const char *type,
+ die_callback_t process_func,
+ die_match_callback_t match_func)
+{
+ bool expand;
+
+ process(cache, type);
+ process_fqn(cache, die);
+ process(cache, " {");
+ process_linebreak(cache, 1);
+
+ expand = state->expand.expand && is_kabi_definition(cache, die);
+
+ if (expand) {
+ state->expand.current_fqn = cache->fqn;
+ check(process_die_container(state, cache, die, process_func,
+ match_func));
+ }
+
+ process_linebreak(cache, -1);
+ process(cache, "}");
+
+ if (expand) {
+ process_byte_size_attr(cache, die);
+ process_alignment_attr(cache, die);
+ }
+}
+
+#define DEFINE_PROCESS_STRUCTURE_TYPE(structure) \
+ static void process_##structure##_type( \
+ struct state *state, struct die *cache, Dwarf_Die *die) \
+ { \
+ __process_structure_type(state, cache, die, \
+ #structure "_type", \
+ ___process_structure_type, \
+ match_all); \
+ }
+
+DEFINE_PROCESS_STRUCTURE_TYPE(class)
+DEFINE_PROCESS_STRUCTURE_TYPE(structure)
+
+static void process_union_type(struct state *state, struct die *cache,
+ Dwarf_Die *die)
+{
+ Dwarf_Die placeholder;
+
+ int res = checkp(get_union_kabi_status(die, &placeholder,
+ &state->kabi.orig_name));
+
+ if (res == KABI_RESERVED)
+ check(process_type(state, cache, &placeholder));
+ if (res > KABI_NORMAL)
+ return;
+
+ __process_structure_type(state, cache, die, "union_type",
+ ___process_structure_type, match_all);
+}
+
+static void process_enumerator_type(struct state *state, struct die *cache,
+ Dwarf_Die *die)
+{
+ bool overridden = false;
+ Dwarf_Word value;
+
+ if (stable) {
+ /* Get the fqn before we process anything */
+ update_fqn(cache, die);
+
+ if (kabi_is_enumerator_ignored(state->expand.current_fqn,
+ cache->fqn))
+ return;
+
+ overridden = kabi_get_enumerator_value(
+ state->expand.current_fqn, cache->fqn, &value);
+ }
+
+ process_list_comma(state, cache);
+ process(cache, "enumerator");
+ process_fqn(cache, die);
+
+ if (overridden || get_udata_attr(die, DW_AT_const_value, &value)) {
+ process(cache, " = ");
+ process_fmt(cache, "%" PRIu64, value);
+ }
+}
+
+static void process_enumeration_type(struct state *state, struct die *cache,
+ Dwarf_Die *die)
+{
+ __process_structure_type(state, cache, die, "enumeration_type",
+ process_type, match_enumerator_type);
+}
+
+static void process_base_type(struct state *state, struct die *cache,
+ Dwarf_Die *die)
+{
+ process(cache, "base_type");
+ process_fqn(cache, die);
+ process_byte_size_attr(cache, die);
+ process_encoding_attr(cache, die);
+ process_alignment_attr(cache, die);
+}
+
+static void process_unspecified_type(struct state *state, struct die *cache,
+ Dwarf_Die *die)
+{
+ /*
+ * These can be emitted for stand-alone assembly code, which means we
+ * might run into them in vmlinux.o.
+ */
+ process(cache, "unspecified_type");
+}
+
+static void process_cached(struct state *state, struct die *cache,
+ Dwarf_Die *die)
+{
+ struct die_fragment *df;
+ Dwarf_Die child;
+
+ list_for_each_entry(df, &cache->fragments, list) {
+ switch (df->type) {
+ case FRAGMENT_STRING:
+ die_debug_b("cache %p STRING '%s'", cache,
+ df->data.str);
+ process(NULL, df->data.str);
+ break;
+ case FRAGMENT_LINEBREAK:
+ process_linebreak(NULL, df->data.linebreak);
+ break;
+ case FRAGMENT_DIE:
+ if (!dwarf_die_addr_die(dwarf_cu_getdwarf(die->cu),
+ (void *)df->data.addr, &child))
+ error("dwarf_die_addr_die failed");
+ die_debug_b("cache %p DIE addr %" PRIxPTR " tag %x",
+ cache, df->data.addr, dwarf_tag(&child));
+ check(process_type(state, NULL, &child));
+ break;
+ default:
+ error("empty die_fragment");
+ }
+ }
+}
+
+static void state_init(struct state *state)
+{
+ state->expand.expand = true;
+ state->expand.current_fqn = NULL;
+ cache_init(&state->expansion_cache);
+}
+
+static void expansion_state_restore(struct expansion_state *state,
+ struct expansion_state *saved)
+{
+ state->expand = saved->expand;
+ state->current_fqn = saved->current_fqn;
+}
+
+static void expansion_state_save(struct expansion_state *state,
+ struct expansion_state *saved)
+{
+ expansion_state_restore(saved, state);
+}
+
+static bool is_expanded_type(int tag)
+{
+ return tag == DW_TAG_class_type || tag == DW_TAG_structure_type ||
+ tag == DW_TAG_union_type || tag == DW_TAG_enumeration_type;
+}
+
+#define PROCESS_TYPE(type) \
+ case DW_TAG_##type##_type: \
+ process_##type##_type(state, cache, die); \
+ break;
+
+static int process_type(struct state *state, struct die *parent, Dwarf_Die *die)
+{
+ enum die_state want_state = DIE_COMPLETE;
+ struct die *cache;
+ struct expansion_state saved;
+ int tag = dwarf_tag(die);
+
+ expansion_state_save(&state->expand, &saved);
+
+ /*
+ * Structures and enumeration types are expanded only once per
+ * exported symbol. This is sufficient for detecting ABI changes
+ * within the structure.
+ */
+ if (is_expanded_type(tag)) {
+ if (cache_was_expanded(&state->expansion_cache, die->addr))
+ state->expand.expand = false;
+
+ if (state->expand.expand)
+ cache_mark_expanded(&state->expansion_cache, die->addr);
+ else
+ want_state = DIE_UNEXPANDED;
+ }
+
+ /*
+ * If we have want_state already cached, use it instead of walking
+ * through DWARF.
+ */
+ cache = die_map_get(die, want_state);
+
+ if (cache->state == want_state) {
+ die_debug_g("cached addr %p tag %x -- %s", die->addr, tag,
+ die_state_name(cache->state));
+
+ process_cached(state, cache, die);
+ die_map_add_die(parent, cache);
+
+ expansion_state_restore(&state->expand, &saved);
+ return 0;
+ }
+
+ die_debug_g("addr %p tag %x -- %s -> %s", die->addr, tag,
+ die_state_name(cache->state), die_state_name(want_state));
+
+ switch (tag) {
+ /* Type modifiers */
+ PROCESS_TYPE(atomic)
+ PROCESS_TYPE(const)
+ PROCESS_TYPE(immutable)
+ PROCESS_TYPE(packed)
+ PROCESS_TYPE(pointer)
+ PROCESS_TYPE(reference)
+ PROCESS_TYPE(restrict)
+ PROCESS_TYPE(rvalue_reference)
+ PROCESS_TYPE(shared)
+ PROCESS_TYPE(volatile)
+ /* Container types */
+ PROCESS_TYPE(class)
+ PROCESS_TYPE(structure)
+ PROCESS_TYPE(union)
+ PROCESS_TYPE(enumeration)
+ /* Subtypes */
+ PROCESS_TYPE(enumerator)
+ PROCESS_TYPE(formal_parameter)
+ PROCESS_TYPE(member)
+ PROCESS_TYPE(subrange)
+ PROCESS_TYPE(template_type_parameter)
+ PROCESS_TYPE(variant)
+ PROCESS_TYPE(variant_part)
+ /* Other types */
+ PROCESS_TYPE(array)
+ PROCESS_TYPE(base)
+ PROCESS_TYPE(subroutine)
+ PROCESS_TYPE(typedef)
+ PROCESS_TYPE(unspecified)
+ default:
+ error("unexpected type: %x", tag);
+ }
+
+ die_debug_r("parent %p cache %p die addr %p tag %x", parent, cache,
+ die->addr, tag);
+
+ /* Update cache state and append to the parent (if any) */
+ cache->tag = tag;
+ cache->state = want_state;
+ die_map_add_die(parent, cache);
+
+ expansion_state_restore(&state->expand, &saved);
+ return 0;
+}
+
+/*
+ * Exported symbol processing
+ */
+static struct die *get_symbol_cache(struct state *state, Dwarf_Die *die)
+{
+ struct die *cache;
+
+ cache = die_map_get(die, DIE_SYMBOL);
+
+ if (cache->state != DIE_INCOMPLETE)
+ return NULL; /* We already processed a symbol for this DIE */
+
+ cache->tag = dwarf_tag(die);
+ return cache;
+}
+
+static void process_symbol(struct state *state, Dwarf_Die *die,
+ die_callback_t process_func)
+{
+ struct die *cache;
+
+ symbol_set_die(state->sym, die);
+
+ cache = get_symbol_cache(state, die);
+ if (!cache)
+ return;
+
+ debug("%s", state->sym->name);
+ check(process_func(state, cache, die));
+ cache->state = DIE_SYMBOL;
+ if (dump_dies)
+ fputs("\n", stderr);
+}
+
+static int __process_subprogram(struct state *state, struct die *cache,
+ Dwarf_Die *die)
+{
+ __process_subroutine_type(state, cache, die, "subprogram");
+ return 0;
+}
+
+static void process_subprogram(struct state *state, Dwarf_Die *die)
+{
+ process_symbol(state, die, __process_subprogram);
+}
+
+static int __process_variable(struct state *state, struct die *cache,
+ Dwarf_Die *die)
+{
+ process(cache, "variable ");
+ process_type_attr(state, cache, die);
+ return 0;
+}
+
+static void process_variable(struct state *state, Dwarf_Die *die)
+{
+ process_symbol(state, die, __process_variable);
+}
+
+static void save_symbol_ptr(struct state *state)
+{
+ Dwarf_Die ptr_type;
+ Dwarf_Die type;
+
+ if (!get_ref_die_attr(&state->die, DW_AT_type, &ptr_type) ||
+ dwarf_tag(&ptr_type) != DW_TAG_pointer_type)
+ error("%s must be a pointer type!",
+ get_symbol_name(&state->die));
+
+ if (!get_ref_die_attr(&ptr_type, DW_AT_type, &type))
+ error("%s pointer missing a type attribute?",
+ get_symbol_name(&state->die));
+
+ /*
+ * Save the symbol pointer DIE in case the actual symbol is
+ * missing from the DWARF. Clang, for example, intentionally
+ * omits external symbols from the debugging information.
+ */
+ if (dwarf_tag(&type) == DW_TAG_subroutine_type)
+ symbol_set_ptr(state->sym, &type);
+ else
+ symbol_set_ptr(state->sym, &ptr_type);
+}
+
+static int process_exported_symbols(struct state *unused, struct die *cache,
+ Dwarf_Die *die)
+{
+ int tag = dwarf_tag(die);
+
+ switch (tag) {
+ /* Possible containers of exported symbols */
+ case DW_TAG_namespace:
+ case DW_TAG_class_type:
+ case DW_TAG_structure_type:
+ return check(process_die_container(
+ NULL, cache, die, process_exported_symbols, match_all));
+
+ /* Possible exported symbols */
+ case DW_TAG_subprogram:
+ case DW_TAG_variable: {
+ struct state state;
+
+ if (!match_export_symbol(&state, die))
+ return 0;
+
+ state_init(&state);
+
+ if (is_symbol_ptr(get_symbol_name(&state.die)))
+ save_symbol_ptr(&state);
+ else if (tag == DW_TAG_subprogram)
+ process_subprogram(&state, &state.die);
+ else
+ process_variable(&state, &state.die);
+
+ cache_free(&state.expansion_cache);
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
+static void process_symbol_ptr(struct symbol *sym, void *arg)
+{
+ struct state state;
+ Dwarf *dwarf = arg;
+
+ if (sym->state != SYMBOL_UNPROCESSED || !sym->ptr_die_addr)
+ return;
+
+ debug("%s", sym->name);
+ state_init(&state);
+ state.sym = sym;
+
+ if (!dwarf_die_addr_die(dwarf, (void *)sym->ptr_die_addr, &state.die))
+ error("dwarf_die_addr_die failed for symbol ptr: '%s'",
+ sym->name);
+
+ if (dwarf_tag(&state.die) == DW_TAG_subroutine_type)
+ process_subprogram(&state, &state.die);
+ else
+ process_variable(&state, &state.die);
+
+ cache_free(&state.expansion_cache);
+}
+
+static int resolve_fqns(struct state *parent, struct die *unused,
+ Dwarf_Die *die)
+{
+ struct state state;
+ struct die *cache;
+ const char *name;
+ bool use_prefix;
+ char *prefix = NULL;
+ char *fqn = "";
+ int tag;
+
+ if (!__die_map_get((uintptr_t)die->addr, DIE_FQN, &cache))
+ return 0;
+
+ tag = dwarf_tag(die);
+
+ /*
+ * Only namespaces and structures need to pass a prefix to the next
+ * scope.
+ */
+ use_prefix = tag == DW_TAG_namespace || tag == DW_TAG_class_type ||
+ tag == DW_TAG_structure_type;
+
+ state.expand.current_fqn = NULL;
+ name = get_name_attr(die);
+
+ if (parent && parent->expand.current_fqn && (use_prefix || name)) {
+ /*
+ * The fqn for the current DIE, and if needed, a prefix for the
+ * next scope.
+ */
+ if (asprintf(&prefix, "%s::%s", parent->expand.current_fqn,
+ name ? name : "<anonymous>") < 0)
+ error("asprintf failed");
+
+ if (use_prefix)
+ state.expand.current_fqn = prefix;
+
+ /*
+ * Use fqn only if the DIE has a name. Otherwise fqn will
+ * remain empty.
+ */
+ if (name) {
+ fqn = prefix;
+ /* prefix will be freed by die_map. */
+ prefix = NULL;
+ }
+ } else if (name) {
+ /* No prefix from the previous scope. Use only the name. */
+ fqn = xstrdup(name);
+
+ if (use_prefix)
+ state.expand.current_fqn = fqn;
+ }
+
+ /* If the DIE has a non-empty name, cache it. */
+ if (*fqn) {
+ cache = die_map_get(die, DIE_FQN);
+ /* Move ownership of fqn to die_map. */
+ cache->fqn = fqn;
+ cache->state = DIE_FQN;
+ }
+
+ check(process_die_container(&state, NULL, die, resolve_fqns,
+ match_all));
+
+ free(prefix);
+ return 0;
+}
+
+void process_cu(Dwarf_Die *cudie)
+{
+ check(process_die_container(NULL, NULL, cudie, resolve_fqns,
+ match_all));
+
+ check(process_die_container(NULL, NULL, cudie, process_exported_symbols,
+ match_all));
+
+ symbol_for_each(process_symbol_ptr, dwarf_cu_getdwarf(cudie->cu));
+
+ cache_free(&srcfile_cache);
+}
diff --git a/scripts/gendwarfksyms/examples/kabi.h b/scripts/gendwarfksyms/examples/kabi.h
new file mode 100644
index 000000000000..97a5669b083d
--- /dev/null
+++ b/scripts/gendwarfksyms/examples/kabi.h
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2024 Google LLC
+ *
+ * Example macros for maintaining kABI stability.
+ *
+ * This file is based on android_kabi.h, which has the following notice:
+ *
+ * Heavily influenced by rh_kabi.h which came from the RHEL/CENTOS kernel
+ * and was:
+ * Copyright (c) 2014 Don Zickus
+ * Copyright (c) 2015-2018 Jiri Benc
+ * Copyright (c) 2015 Sabrina Dubroca, Hannes Frederic Sowa
+ * Copyright (c) 2016-2018 Prarit Bhargava
+ * Copyright (c) 2017 Paolo Abeni, Larry Woodman
+ */
+
+#ifndef __KABI_H__
+#define __KABI_H__
+
+/* Kernel macros for userspace testing. */
+#ifndef __aligned
+#define __aligned(x) __attribute__((__aligned__(x)))
+#endif
+#ifndef __used
+#define __used __attribute__((__used__))
+#endif
+#ifndef __section
+#define __section(section) __attribute__((__section__(section)))
+#endif
+#ifndef __PASTE
+#define ___PASTE(a, b) a##b
+#define __PASTE(a, b) ___PASTE(a, b)
+#endif
+#ifndef __stringify
+#define __stringify_1(x...) #x
+#define __stringify(x...) __stringify_1(x)
+#endif
+
+#define __KABI_RULE(hint, target, value) \
+ static const char __PASTE(__gendwarfksyms_rule_, \
+ __COUNTER__)[] __used __aligned(1) \
+ __section(".discard.gendwarfksyms.kabi_rules") = \
+ "1\0" #hint "\0" #target "\0" #value
+
+#define __KABI_NORMAL_SIZE_ALIGN(_orig, _new) \
+ union { \
+ _Static_assert( \
+ sizeof(struct { _new; }) <= sizeof(struct { _orig; }), \
+ __FILE__ ":" __stringify(__LINE__) ": " __stringify( \
+ _new) " is larger than " __stringify(_orig)); \
+ _Static_assert( \
+ __alignof__(struct { _new; }) <= \
+ __alignof__(struct { _orig; }), \
+ __FILE__ ":" __stringify(__LINE__) ": " __stringify( \
+ _orig) " is not aligned the same as " __stringify(_new)); \
+ }
+
+#define __KABI_REPLACE(_orig, _new) \
+ union { \
+ _new; \
+ struct { \
+ _orig; \
+ }; \
+ __KABI_NORMAL_SIZE_ALIGN(_orig, _new); \
+ }
+
+/*
+ * KABI_DECLONLY(fqn)
+ * Treat the struct/union/enum fqn as a declaration, i.e. even if
+ * a definition is available, don't expand the contents.
+ */
+#define KABI_DECLONLY(fqn) __KABI_RULE(declonly, fqn, )
+
+/*
+ * KABI_ENUMERATOR_IGNORE(fqn, field)
+ * When expanding enum fqn, skip the provided field. This makes it
+ * possible to hide added enum fields from versioning.
+ */
+#define KABI_ENUMERATOR_IGNORE(fqn, field) \
+ __KABI_RULE(enumerator_ignore, fqn field, )
+
+/*
+ * KABI_ENUMERATOR_VALUE(fqn, field, value)
+ * When expanding enum fqn, use the provided value for the
+ * specified field. This makes it possible to override enumerator
+ * values when calculating versions.
+ */
+#define KABI_ENUMERATOR_VALUE(fqn, field, value) \
+ __KABI_RULE(enumerator_value, fqn field, value)
+
+/*
+ * KABI_RESERVE
+ * Reserve some "padding" in a structure for use by LTS backports.
+ * This is normally placed at the end of a structure.
+ * number: the "number" of the padding variable in the structure. Start with
+ * 1 and go up.
+ */
+#define KABI_RESERVE(n) unsigned long __kabi_reserved##n
+
+/*
+ * KABI_RESERVE_ARRAY
+ * Same as _BACKPORT_RESERVE but allocates an array with the specified
+ * size in bytes.
+ */
+#define KABI_RESERVE_ARRAY(n, s) \
+ unsigned char __aligned(8) __kabi_reserved##n[s]
+
+/*
+ * KABI_IGNORE
+ * Add a new field that's ignored in versioning.
+ */
+#define KABI_IGNORE(n, _new) \
+ union { \
+ _new; \
+ unsigned char __kabi_ignored##n; \
+ }
+
+/*
+ * KABI_REPLACE
+ * Replace a field with a compatible new field.
+ */
+#define KABI_REPLACE(_oldtype, _oldname, _new) \
+ __KABI_REPLACE(_oldtype __kabi_renamed##_oldname, struct { _new; })
+
+/*
+ * KABI_USE(number, _new)
+ * Use a previous padding entry that was defined with KABI_RESERVE
+ * number: the previous "number" of the padding variable
+ * _new: the variable to use now instead of the padding variable
+ */
+#define KABI_USE(number, _new) __KABI_REPLACE(KABI_RESERVE(number), _new)
+
+/*
+ * KABI_USE2(number, _new1, _new2)
+ * Use a previous padding entry that was defined with KABI_RESERVE for
+ * two new variables that fit into 64 bits. This is good for when you do not
+ * want to "burn" a 64bit padding variable for a smaller variable size if not
+ * needed.
+ */
+#define KABI_USE2(number, _new1, _new2) \
+ __KABI_REPLACE( \
+ KABI_RESERVE(number), struct { \
+ _new1; \
+ _new2; \
+ })
+/*
+ * KABI_USE_ARRAY(number, bytes, _new)
+ * Use a previous padding entry that was defined with KABI_RESERVE_ARRAY
+ * number: the previous "number" of the padding variable
+ * bytes: the size in bytes reserved for the array
+ * _new: the variable to use now instead of the padding variable
+ */
+#define KABI_USE_ARRAY(number, bytes, _new) \
+ __KABI_REPLACE(KABI_RESERVE_ARRAY(number, bytes), _new)
+
+#endif /* __KABI_H__ */
diff --git a/scripts/gendwarfksyms/examples/kabi_ex.c b/scripts/gendwarfksyms/examples/kabi_ex.c
new file mode 100644
index 000000000000..0b7ffd830541
--- /dev/null
+++ b/scripts/gendwarfksyms/examples/kabi_ex.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * kabi_ex.c
+ *
+ * Copyright (C) 2024 Google LLC
+ *
+ * Examples for kABI stability features with --stable. See kabi_ex.h
+ * for details.
+ */
+
+#include "kabi_ex.h"
+
+struct s e0;
+enum e e1;
+
+struct ex0a ex0a;
+struct ex0b ex0b;
+struct ex0c ex0c;
+
+struct ex1a ex1a;
+struct ex1b ex1b;
+struct ex1c ex1c;
+
+struct ex2a ex2a;
+struct ex2b ex2b;
+struct ex2c ex2c;
+
+struct ex3a ex3a;
+struct ex3b ex3b;
+struct ex3c ex3c;
diff --git a/scripts/gendwarfksyms/examples/kabi_ex.h b/scripts/gendwarfksyms/examples/kabi_ex.h
new file mode 100644
index 000000000000..1736e0f65208
--- /dev/null
+++ b/scripts/gendwarfksyms/examples/kabi_ex.h
@@ -0,0 +1,263 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * kabi_ex.h
+ *
+ * Copyright (C) 2024 Google LLC
+ *
+ * Examples for kABI stability features with --stable.
+ */
+
+/*
+ * The comments below each example contain the expected gendwarfksyms
+ * output, which can be verified using LLVM's FileCheck tool:
+ *
+ * https://llvm.org/docs/CommandGuide/FileCheck.html
+ *
+ * Usage:
+ *
+ * $ gcc -g -c examples/kabi_ex.c -o examples/kabi_ex.o
+ *
+ * $ nm examples/kabi_ex.o | awk '{ print $NF }' | \
+ * ./gendwarfksyms --stable --dump-dies \
+ * examples/kabi_ex.o 2>&1 >/dev/null | \
+ * FileCheck examples/kabi_ex.h --check-prefix=STABLE
+ */
+
+#ifndef __KABI_EX_H__
+#define __KABI_EX_H__
+
+#include "kabi.h"
+
+/*
+ * Example: kABI rules
+ */
+
+struct s {
+ int a;
+};
+
+KABI_DECLONLY(s);
+
+/*
+ * STABLE: variable structure_type s {
+ * STABLE-NEXT: }
+ */
+
+enum e {
+ A,
+ B,
+ C,
+ D,
+};
+
+KABI_ENUMERATOR_IGNORE(e, B);
+KABI_ENUMERATOR_IGNORE(e, C);
+KABI_ENUMERATOR_VALUE(e, D, 123456789);
+
+/*
+ * STABLE: variable enumeration_type e {
+ * STABLE-NEXT: enumerator A = 0 ,
+ * STABLE-NEXT: enumerator D = 123456789
+ * STABLE-NEXT: } byte_size(4)
+*/
+
+/*
+ * Example: Reserved fields
+ */
+struct ex0a {
+ int a;
+ KABI_RESERVE(0);
+ KABI_RESERVE(1);
+};
+
+/*
+ * STABLE: variable structure_type ex0a {
+ * STABLE-NEXT: member base_type int byte_size(4) encoding(5) a data_member_location(0) ,
+ * STABLE-NEXT: member base_type [[ULONG:long unsigned int|unsigned long]] byte_size(8) encoding(7) data_member_location(8) ,
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) data_member_location(16)
+ * STABLE-NEXT: } byte_size(24)
+ */
+
+struct ex0b {
+ int a;
+ KABI_RESERVE(0);
+ KABI_USE2(1, int b, int c);
+};
+
+/*
+ * STABLE: variable structure_type ex0b {
+ * STABLE-NEXT: member base_type int byte_size(4) encoding(5) a data_member_location(0) ,
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) data_member_location(8) ,
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) data_member_location(16)
+ * STABLE-NEXT: } byte_size(24)
+ */
+
+struct ex0c {
+ int a;
+ KABI_USE(0, void *p);
+ KABI_USE2(1, int b, int c);
+};
+
+/*
+ * STABLE: variable structure_type ex0c {
+ * STABLE-NEXT: member base_type int byte_size(4) encoding(5) a data_member_location(0) ,
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) data_member_location(8) ,
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) data_member_location(16)
+ * STABLE-NEXT: } byte_size(24)
+ */
+
+/*
+ * Example: A reserved array
+ */
+
+struct ex1a {
+ unsigned int a;
+ KABI_RESERVE_ARRAY(0, 64);
+};
+
+/*
+ * STABLE: variable structure_type ex1a {
+ * STABLE-NEXT: member base_type unsigned int byte_size(4) encoding(7) a data_member_location(0) ,
+ * STABLE-NEXT: member array_type[64] {
+ * STABLE-NEXT: base_type unsigned char byte_size(1) encoding(8)
+ * STABLE-NEXT: } data_member_location(8)
+ * STABLE-NEXT: } byte_size(72)
+ */
+
+struct ex1b {
+ unsigned int a;
+ KABI_USE_ARRAY(
+ 0, 64, struct {
+ void *p;
+ KABI_RESERVE_ARRAY(1, 56);
+ });
+};
+
+/*
+ * STABLE: variable structure_type ex1b {
+ * STABLE-NEXT: member base_type unsigned int byte_size(4) encoding(7) a data_member_location(0) ,
+ * STABLE-NEXT: member array_type[64] {
+ * STABLE-NEXT: base_type unsigned char byte_size(1) encoding(8)
+ * STABLE-NEXT: } data_member_location(8)
+ * STABLE-NEXT: } byte_size(72)
+ */
+
+struct ex1c {
+ unsigned int a;
+ KABI_USE_ARRAY(0, 64, void *p[8]);
+};
+
+/*
+ * STABLE: variable structure_type ex1c {
+ * STABLE-NEXT: member base_type unsigned int byte_size(4) encoding(7) a data_member_location(0) ,
+ * STABLE-NEXT: member array_type[64] {
+ * STABLE-NEXT: base_type unsigned char byte_size(1) encoding(8)
+ * STABLE-NEXT: } data_member_location(8)
+ * STABLE-NEXT: } byte_size(72)
+ */
+
+/*
+ * Example: An ignored field added to an alignment hole
+ */
+
+struct ex2a {
+ int a;
+ unsigned long b;
+ int c;
+ unsigned long d;
+};
+
+/*
+ * STABLE: variable structure_type ex2a {
+ * STABLE-NEXT: member base_type int byte_size(4) encoding(5) a data_member_location(0) ,
+ * STABLE-NEXT: member base_type [[ULONG:long unsigned int|unsigned long]] byte_size(8) encoding(7) b data_member_location(8)
+ * STABLE-NEXT: member base_type int byte_size(4) encoding(5) c data_member_location(16) ,
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) d data_member_location(24)
+ * STABLE-NEXT: } byte_size(32)
+ */
+
+struct ex2b {
+ int a;
+ KABI_IGNORE(0, unsigned int n);
+ unsigned long b;
+ int c;
+ unsigned long d;
+};
+
+_Static_assert(sizeof(struct ex2a) == sizeof(struct ex2b), "ex2a size doesn't match ex2b");
+
+/*
+ * STABLE: variable structure_type ex2b {
+ * STABLE-NEXT: member base_type int byte_size(4) encoding(5) a data_member_location(0) ,
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) b data_member_location(8)
+ * STABLE-NEXT: member base_type int byte_size(4) encoding(5) c data_member_location(16) ,
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) d data_member_location(24)
+ * STABLE-NEXT: } byte_size(32)
+ */
+
+struct ex2c {
+ int a;
+ KABI_IGNORE(0, unsigned int n);
+ unsigned long b;
+ int c;
+ KABI_IGNORE(1, unsigned int m);
+ unsigned long d;
+};
+
+_Static_assert(sizeof(struct ex2a) == sizeof(struct ex2c), "ex2a size doesn't match ex2c");
+
+/*
+ * STABLE: variable structure_type ex2c {
+ * STABLE-NEXT: member base_type int byte_size(4) encoding(5) a data_member_location(0) ,
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) b data_member_location(8)
+ * STABLE-NEXT: member base_type int byte_size(4) encoding(5) c data_member_location(16) ,
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) d data_member_location(24)
+ * STABLE-NEXT: } byte_size(32)
+ */
+
+
+/*
+ * Example: A replaced field
+ */
+
+struct ex3a {
+ unsigned long a;
+ unsigned long unused;
+};
+
+/*
+ * STABLE: variable structure_type ex3a {
+ * STABLE-NEXT: member base_type [[ULONG:long unsigned int|unsigned long]] byte_size(8) encoding(7) a data_member_location(0)
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) unused data_member_location(8)
+ * STABLE-NEXT: } byte_size(16)
+ */
+
+struct ex3b {
+ unsigned long a;
+ KABI_REPLACE(unsigned long, unused, unsigned long renamed);
+};
+
+_Static_assert(sizeof(struct ex3a) == sizeof(struct ex3b), "ex3a size doesn't match ex3b");
+
+/*
+ * STABLE: variable structure_type ex3b {
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) a data_member_location(0)
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) unused data_member_location(8)
+ * STABLE-NEXT: } byte_size(16)
+ */
+
+struct ex3c {
+ unsigned long a;
+ KABI_REPLACE(unsigned long, unused, long replaced);
+};
+
+_Static_assert(sizeof(struct ex3a) == sizeof(struct ex3c), "ex3a size doesn't match ex3c");
+
+/*
+ * STABLE: variable structure_type ex3c {
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) a data_member_location(0)
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) unused data_member_location(8)
+ * STABLE-NEXT: } byte_size(16)
+ */
+
+#endif /* __KABI_EX_H__ */
diff --git a/scripts/gendwarfksyms/examples/symbolptr.c b/scripts/gendwarfksyms/examples/symbolptr.c
new file mode 100644
index 000000000000..88bc1bd60da8
--- /dev/null
+++ b/scripts/gendwarfksyms/examples/symbolptr.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024 Google LLC
+ *
+ * Example for symbol pointers. When compiled with Clang, gendwarfkyms
+ * uses a symbol pointer for `f`.
+ *
+ * $ clang -g -c examples/symbolptr.c -o examples/symbolptr.o
+ * $ echo -e "f\ng\np" | ./gendwarfksyms -d examples/symbolptr.o
+ */
+
+/* Kernel macros for userspace testing. */
+#ifndef __used
+#define __used __attribute__((__used__))
+#endif
+#ifndef __section
+#define __section(section) __attribute__((__section__(section)))
+#endif
+
+#define __GENDWARFKSYMS_EXPORT(sym) \
+ static typeof(sym) *__gendwarfksyms_ptr_##sym __used \
+ __section(".discard.gendwarfksyms") = &sym;
+
+extern void f(unsigned int arg);
+void g(int *arg);
+void g(int *arg) {}
+
+struct s;
+extern struct s *p;
+
+__GENDWARFKSYMS_EXPORT(f);
+__GENDWARFKSYMS_EXPORT(g);
+__GENDWARFKSYMS_EXPORT(p);
diff --git a/scripts/gendwarfksyms/gendwarfksyms.c b/scripts/gendwarfksyms/gendwarfksyms.c
new file mode 100644
index 000000000000..08ae61eb327e
--- /dev/null
+++ b/scripts/gendwarfksyms/gendwarfksyms.c
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024 Google LLC
+ */
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include "gendwarfksyms.h"
+
+/*
+ * Options
+ */
+
+/* Print debugging information to stderr */
+int debug;
+/* Dump DIE contents */
+int dump_dies;
+/* Print debugging information about die_map changes */
+int dump_die_map;
+/* Print out type strings (i.e. type_map) */
+int dump_types;
+/* Print out expanded type strings used for symbol versions */
+int dump_versions;
+/* Support kABI stability features */
+int stable;
+/* Write a symtypes file */
+int symtypes;
+static const char *symtypes_file;
+
+static void usage(void)
+{
+ fputs("Usage: gendwarfksyms [options] elf-object-file ... < symbol-list\n\n"
+ "Options:\n"
+ " -d, --debug Print debugging information\n"
+ " --dump-dies Dump DWARF DIE contents\n"
+ " --dump-die-map Print debugging information about die_map changes\n"
+ " --dump-types Dump type strings\n"
+ " --dump-versions Dump expanded type strings used for symbol versions\n"
+ " -s, --stable Support kABI stability features\n"
+ " -T, --symtypes file Write a symtypes file\n"
+ " -h, --help Print this message\n"
+ "\n",
+ stderr);
+}
+
+static int process_module(Dwfl_Module *mod, void **userdata, const char *name,
+ Dwarf_Addr base, void *arg)
+{
+ Dwarf_Addr dwbias;
+ Dwarf_Die cudie;
+ Dwarf_CU *cu = NULL;
+ Dwarf *dbg;
+ FILE *symfile = arg;
+ int res;
+
+ debug("%s", name);
+ dbg = dwfl_module_getdwarf(mod, &dwbias);
+
+ /*
+ * Look for exported symbols in each CU, follow the DIE tree, and add
+ * the entries to die_map.
+ */
+ do {
+ res = dwarf_get_units(dbg, cu, &cu, NULL, NULL, &cudie, NULL);
+ if (res < 0)
+ error("dwarf_get_units failed: no debugging information?");
+ if (res == 1)
+ break; /* No more units */
+
+ process_cu(&cudie);
+ } while (cu);
+
+ /*
+ * Use die_map to expand type strings, write them to `symfile`, and
+ * calculate symbol versions.
+ */
+ generate_symtypes_and_versions(symfile);
+ die_map_free();
+
+ return DWARF_CB_OK;
+}
+
+static const Dwfl_Callbacks callbacks = {
+ .section_address = dwfl_offline_section_address,
+ .find_debuginfo = dwfl_standard_find_debuginfo,
+};
+
+int main(int argc, char **argv)
+{
+ FILE *symfile = NULL;
+ unsigned int n;
+ int opt;
+
+ static const struct option opts[] = {
+ { "debug", 0, NULL, 'd' },
+ { "dump-dies", 0, &dump_dies, 1 },
+ { "dump-die-map", 0, &dump_die_map, 1 },
+ { "dump-types", 0, &dump_types, 1 },
+ { "dump-versions", 0, &dump_versions, 1 },
+ { "stable", 0, NULL, 's' },
+ { "symtypes", 1, NULL, 'T' },
+ { "help", 0, NULL, 'h' },
+ { 0, 0, NULL, 0 }
+ };
+
+ while ((opt = getopt_long(argc, argv, "dsT:h", opts, NULL)) != EOF) {
+ switch (opt) {
+ case 0:
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 's':
+ stable = 1;
+ break;
+ case 'T':
+ symtypes = 1;
+ symtypes_file = optarg;
+ break;
+ case 'h':
+ usage();
+ return 0;
+ default:
+ usage();
+ return 1;
+ }
+ }
+
+ if (dump_die_map)
+ dump_dies = 1;
+
+ if (optind >= argc) {
+ usage();
+ error("no input files?");
+ }
+
+ symbol_read_exports(stdin);
+
+ if (symtypes_file) {
+ symfile = fopen(symtypes_file, "w");
+ if (!symfile)
+ error("fopen failed for '%s': %s", symtypes_file,
+ strerror(errno));
+ }
+
+ for (n = optind; n < argc; n++) {
+ Dwfl *dwfl;
+ int fd;
+
+ fd = open(argv[n], O_RDONLY);
+ if (fd == -1)
+ error("open failed for '%s': %s", argv[n],
+ strerror(errno));
+
+ symbol_read_symtab(fd);
+ kabi_read_rules(fd);
+
+ dwfl = dwfl_begin(&callbacks);
+ if (!dwfl)
+ error("dwfl_begin failed for '%s': %s", argv[n],
+ dwarf_errmsg(-1));
+
+ if (!dwfl_report_offline(dwfl, argv[n], argv[n], fd))
+ error("dwfl_report_offline failed for '%s': %s",
+ argv[n], dwarf_errmsg(-1));
+
+ dwfl_report_end(dwfl, NULL, NULL);
+
+ if (dwfl_getmodules(dwfl, &process_module, symfile, 0))
+ error("dwfl_getmodules failed for '%s'", argv[n]);
+
+ dwfl_end(dwfl);
+ kabi_free();
+ }
+
+ if (symfile)
+ check(fclose(symfile));
+
+ symbol_print_versions();
+ symbol_free();
+
+ return 0;
+}
diff --git a/scripts/gendwarfksyms/gendwarfksyms.h b/scripts/gendwarfksyms/gendwarfksyms.h
new file mode 100644
index 000000000000..2feec168bf73
--- /dev/null
+++ b/scripts/gendwarfksyms/gendwarfksyms.h
@@ -0,0 +1,298 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2024 Google LLC
+ */
+
+#include <dwarf.h>
+#include <elfutils/libdw.h>
+#include <elfutils/libdwfl.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <hash.h>
+#include <hashtable.h>
+#include <xalloc.h>
+
+#ifndef __GENDWARFKSYMS_H
+#define __GENDWARFKSYMS_H
+
+/*
+ * Options -- in gendwarfksyms.c
+ */
+extern int debug;
+extern int dump_dies;
+extern int dump_die_map;
+extern int dump_types;
+extern int dump_versions;
+extern int stable;
+extern int symtypes;
+
+/*
+ * Output helpers
+ */
+#define __PREFIX "gendwarfksyms: "
+#define __println(prefix, format, ...) \
+ fprintf(stderr, prefix __PREFIX "%s: " format "\n", __func__, \
+ ##__VA_ARGS__)
+
+#define debug(format, ...) \
+ do { \
+ if (debug) \
+ __println("", format, ##__VA_ARGS__); \
+ } while (0)
+
+#define warn(format, ...) __println("warning: ", format, ##__VA_ARGS__)
+#define error(format, ...) \
+ do { \
+ __println("error: ", format, ##__VA_ARGS__); \
+ exit(1); \
+ } while (0)
+
+#define __die_debug(color, format, ...) \
+ do { \
+ if (dump_dies && dump_die_map) \
+ fprintf(stderr, \
+ "\033[" #color "m<" format ">\033[39m", \
+ __VA_ARGS__); \
+ } while (0)
+
+#define die_debug_r(format, ...) __die_debug(91, format, __VA_ARGS__)
+#define die_debug_g(format, ...) __die_debug(92, format, __VA_ARGS__)
+#define die_debug_b(format, ...) __die_debug(94, format, __VA_ARGS__)
+
+/*
+ * Error handling helpers
+ */
+#define __check(expr, test) \
+ ({ \
+ int __res = expr; \
+ if (test) \
+ error("`%s` failed: %d", #expr, __res); \
+ __res; \
+ })
+
+/* Error == non-zero values */
+#define check(expr) __check(expr, __res)
+/* Error == negative values */
+#define checkp(expr) __check(expr, __res < 0)
+
+/* Consistent aliases (DW_TAG_<type>_type) for DWARF tags */
+#define DW_TAG_enumerator_type DW_TAG_enumerator
+#define DW_TAG_formal_parameter_type DW_TAG_formal_parameter
+#define DW_TAG_member_type DW_TAG_member
+#define DW_TAG_template_type_parameter_type DW_TAG_template_type_parameter
+#define DW_TAG_typedef_type DW_TAG_typedef
+#define DW_TAG_variant_part_type DW_TAG_variant_part
+#define DW_TAG_variant_type DW_TAG_variant
+
+/*
+ * symbols.c
+ */
+
+/* See symbols.c:is_symbol_ptr */
+#define SYMBOL_PTR_PREFIX "__gendwarfksyms_ptr_"
+#define SYMBOL_PTR_PREFIX_LEN (sizeof(SYMBOL_PTR_PREFIX) - 1)
+
+static inline unsigned int addr_hash(uintptr_t addr)
+{
+ return hash_ptr((const void *)addr);
+}
+
+enum symbol_state {
+ SYMBOL_UNPROCESSED,
+ SYMBOL_MAPPED,
+ SYMBOL_PROCESSED
+};
+
+struct symbol_addr {
+ uint32_t section;
+ Elf64_Addr address;
+};
+
+struct symbol {
+ const char *name;
+ struct symbol_addr addr;
+ struct hlist_node addr_hash;
+ struct hlist_node name_hash;
+ enum symbol_state state;
+ uintptr_t die_addr;
+ uintptr_t ptr_die_addr;
+ unsigned long crc;
+};
+
+typedef void (*symbol_callback_t)(struct symbol *, void *arg);
+
+bool is_symbol_ptr(const char *name);
+void symbol_read_exports(FILE *file);
+void symbol_read_symtab(int fd);
+struct symbol *symbol_get(const char *name);
+void symbol_set_ptr(struct symbol *sym, Dwarf_Die *ptr);
+void symbol_set_die(struct symbol *sym, Dwarf_Die *die);
+void symbol_set_crc(struct symbol *sym, unsigned long crc);
+void symbol_for_each(symbol_callback_t func, void *arg);
+void symbol_print_versions(void);
+void symbol_free(void);
+
+/*
+ * die.c
+ */
+
+enum die_state {
+ DIE_INCOMPLETE,
+ DIE_FQN,
+ DIE_UNEXPANDED,
+ DIE_COMPLETE,
+ DIE_SYMBOL,
+ DIE_LAST = DIE_SYMBOL
+};
+
+enum die_fragment_type {
+ FRAGMENT_EMPTY,
+ FRAGMENT_STRING,
+ FRAGMENT_LINEBREAK,
+ FRAGMENT_DIE
+};
+
+struct die_fragment {
+ enum die_fragment_type type;
+ union {
+ char *str;
+ int linebreak;
+ uintptr_t addr;
+ } data;
+ struct list_head list;
+};
+
+#define CASE_CONST_TO_STR(name) \
+ case name: \
+ return #name;
+
+static inline const char *die_state_name(enum die_state state)
+{
+ switch (state) {
+ CASE_CONST_TO_STR(DIE_INCOMPLETE)
+ CASE_CONST_TO_STR(DIE_FQN)
+ CASE_CONST_TO_STR(DIE_UNEXPANDED)
+ CASE_CONST_TO_STR(DIE_COMPLETE)
+ CASE_CONST_TO_STR(DIE_SYMBOL)
+ }
+
+ error("unexpected die_state: %d", state);
+}
+
+struct die {
+ enum die_state state;
+ bool mapped;
+ char *fqn;
+ int tag;
+ uintptr_t addr;
+ struct list_head fragments;
+ struct hlist_node hash;
+};
+
+typedef void (*die_map_callback_t)(struct die *, void *arg);
+
+int __die_map_get(uintptr_t addr, enum die_state state, struct die **res);
+struct die *die_map_get(Dwarf_Die *die, enum die_state state);
+void die_map_add_string(struct die *pd, const char *str);
+void die_map_add_linebreak(struct die *pd, int linebreak);
+void die_map_for_each(die_map_callback_t func, void *arg);
+void die_map_add_die(struct die *pd, struct die *child);
+void die_map_free(void);
+
+/*
+ * cache.c
+ */
+
+#define CACHE_HASH_BITS 10
+
+/* A cache for addresses we've already seen. */
+struct cache {
+ HASHTABLE_DECLARE(cache, 1 << CACHE_HASH_BITS);
+};
+
+void cache_set(struct cache *cache, unsigned long key, int value);
+int cache_get(struct cache *cache, unsigned long key);
+void cache_init(struct cache *cache);
+void cache_free(struct cache *cache);
+
+static inline void __cache_mark_expanded(struct cache *cache, uintptr_t addr)
+{
+ cache_set(cache, addr, 1);
+}
+
+static inline bool __cache_was_expanded(struct cache *cache, uintptr_t addr)
+{
+ return cache_get(cache, addr) == 1;
+}
+
+static inline void cache_mark_expanded(struct cache *cache, void *addr)
+{
+ __cache_mark_expanded(cache, (uintptr_t)addr);
+}
+
+static inline bool cache_was_expanded(struct cache *cache, void *addr)
+{
+ return __cache_was_expanded(cache, (uintptr_t)addr);
+}
+
+/*
+ * dwarf.c
+ */
+
+struct expansion_state {
+ bool expand;
+ const char *current_fqn;
+};
+
+struct kabi_state {
+ int members;
+ Dwarf_Die placeholder;
+ const char *orig_name;
+};
+
+struct state {
+ struct symbol *sym;
+ Dwarf_Die die;
+
+ /* List expansion */
+ bool first_list_item;
+
+ /* Structure expansion */
+ struct expansion_state expand;
+ struct cache expansion_cache;
+
+ /* Reserved or ignored members */
+ struct kabi_state kabi;
+};
+
+typedef int (*die_callback_t)(struct state *state, struct die *cache,
+ Dwarf_Die *die);
+typedef bool (*die_match_callback_t)(Dwarf_Die *die);
+bool match_all(Dwarf_Die *die);
+
+int process_die_container(struct state *state, struct die *cache,
+ Dwarf_Die *die, die_callback_t func,
+ die_match_callback_t match);
+
+void process_cu(Dwarf_Die *cudie);
+
+/*
+ * types.c
+ */
+
+void generate_symtypes_and_versions(FILE *file);
+
+/*
+ * kabi.c
+ */
+
+bool kabi_is_enumerator_ignored(const char *fqn, const char *field);
+bool kabi_get_enumerator_value(const char *fqn, const char *field,
+ unsigned long *value);
+bool kabi_is_declonly(const char *fqn);
+
+void kabi_read_rules(int fd);
+void kabi_free(void);
+
+#endif /* __GENDWARFKSYMS_H */
diff --git a/scripts/gendwarfksyms/kabi.c b/scripts/gendwarfksyms/kabi.c
new file mode 100644
index 000000000000..66f01fcd1607
--- /dev/null
+++ b/scripts/gendwarfksyms/kabi.c
@@ -0,0 +1,336 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024 Google LLC
+ */
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <stdio.h>
+
+#include "gendwarfksyms.h"
+
+#define KABI_RULE_SECTION ".discard.gendwarfksyms.kabi_rules"
+#define KABI_RULE_VERSION "1"
+
+/*
+ * The rule section consists of four null-terminated strings per
+ * entry:
+ *
+ * 1. version
+ * Entry format version. Must match KABI_RULE_VERSION.
+ *
+ * 2. type
+ * Type of the kABI rule. Must be one of the tags defined below.
+ *
+ * 3. target
+ * Rule-dependent target, typically the fully qualified name of
+ * the target DIE.
+ *
+ * 4. value
+ * Rule-dependent value.
+ */
+#define KABI_RULE_MIN_ENTRY_SIZE \
+ (/* version\0 */ 2 + /* type\0 */ 2 + /* target\0" */ 1 + \
+ /* value\0 */ 1)
+#define KABI_RULE_EMPTY_VALUE ""
+
+/*
+ * Rule: declonly
+ * - For the struct/enum/union in the target field, treat it as a
+ * declaration only even if a definition is available.
+ */
+#define KABI_RULE_TAG_DECLONLY "declonly"
+
+/*
+ * Rule: enumerator_ignore
+ * - For the enum_field in the target field, ignore the enumerator.
+ */
+#define KABI_RULE_TAG_ENUMERATOR_IGNORE "enumerator_ignore"
+
+/*
+ * Rule: enumerator_value
+ * - For the fqn_field in the target field, set the value to the
+ * unsigned integer in the value field.
+ */
+#define KABI_RULE_TAG_ENUMERATOR_VALUE "enumerator_value"
+
+enum kabi_rule_type {
+ KABI_RULE_TYPE_UNKNOWN,
+ KABI_RULE_TYPE_DECLONLY,
+ KABI_RULE_TYPE_ENUMERATOR_IGNORE,
+ KABI_RULE_TYPE_ENUMERATOR_VALUE,
+};
+
+#define RULE_HASH_BITS 7
+
+struct rule {
+ enum kabi_rule_type type;
+ const char *target;
+ const char *value;
+ struct hlist_node hash;
+};
+
+/* { type, target } -> struct rule */
+static HASHTABLE_DEFINE(rules, 1 << RULE_HASH_BITS);
+
+static inline unsigned int rule_values_hash(enum kabi_rule_type type,
+ const char *target)
+{
+ return hash_32(type) ^ hash_str(target);
+}
+
+static inline unsigned int rule_hash(const struct rule *rule)
+{
+ return rule_values_hash(rule->type, rule->target);
+}
+
+static inline const char *get_rule_field(const char **pos, ssize_t *left)
+{
+ const char *start = *pos;
+ size_t len;
+
+ if (*left <= 0)
+ error("unexpected end of kABI rules");
+
+ len = strnlen(start, *left) + 1;
+ *pos += len;
+ *left -= len;
+
+ return start;
+}
+
+void kabi_read_rules(int fd)
+{
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr;
+ Elf_Data *rule_data = NULL;
+ Elf_Scn *scn;
+ Elf *elf;
+ size_t shstrndx;
+ const char *rule_str;
+ ssize_t left;
+ int i;
+
+ const struct {
+ enum kabi_rule_type type;
+ const char *tag;
+ } rule_types[] = {
+ {
+ .type = KABI_RULE_TYPE_DECLONLY,
+ .tag = KABI_RULE_TAG_DECLONLY,
+ },
+ {
+ .type = KABI_RULE_TYPE_ENUMERATOR_IGNORE,
+ .tag = KABI_RULE_TAG_ENUMERATOR_IGNORE,
+ },
+ {
+ .type = KABI_RULE_TYPE_ENUMERATOR_VALUE,
+ .tag = KABI_RULE_TAG_ENUMERATOR_VALUE,
+ },
+ };
+
+ if (!stable)
+ return;
+
+ if (elf_version(EV_CURRENT) != EV_CURRENT)
+ error("elf_version failed: %s", elf_errmsg(-1));
+
+ elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
+ if (!elf)
+ error("elf_begin failed: %s", elf_errmsg(-1));
+
+ if (elf_getshdrstrndx(elf, &shstrndx) < 0)
+ error("elf_getshdrstrndx failed: %s", elf_errmsg(-1));
+
+ scn = elf_nextscn(elf, NULL);
+
+ while (scn) {
+ const char *sname;
+
+ shdr = gelf_getshdr(scn, &shdr_mem);
+ if (!shdr)
+ error("gelf_getshdr failed: %s", elf_errmsg(-1));
+
+ sname = elf_strptr(elf, shstrndx, shdr->sh_name);
+ if (!sname)
+ error("elf_strptr failed: %s", elf_errmsg(-1));
+
+ if (!strcmp(sname, KABI_RULE_SECTION)) {
+ rule_data = elf_getdata(scn, NULL);
+ if (!rule_data)
+ error("elf_getdata failed: %s", elf_errmsg(-1));
+ break;
+ }
+
+ scn = elf_nextscn(elf, scn);
+ }
+
+ if (!rule_data) {
+ debug("kABI rules not found");
+ check(elf_end(elf));
+ return;
+ }
+
+ rule_str = rule_data->d_buf;
+ left = shdr->sh_size;
+
+ if (left < KABI_RULE_MIN_ENTRY_SIZE)
+ error("kABI rule section too small: %zd bytes", left);
+
+ if (rule_str[left - 1] != '\0')
+ error("kABI rules are not null-terminated");
+
+ while (left > KABI_RULE_MIN_ENTRY_SIZE) {
+ enum kabi_rule_type type = KABI_RULE_TYPE_UNKNOWN;
+ const char *field;
+ struct rule *rule;
+
+ /* version */
+ field = get_rule_field(&rule_str, &left);
+
+ if (strcmp(field, KABI_RULE_VERSION))
+ error("unsupported kABI rule version: '%s'", field);
+
+ /* type */
+ field = get_rule_field(&rule_str, &left);
+
+ for (i = 0; i < ARRAY_SIZE(rule_types); i++) {
+ if (!strcmp(field, rule_types[i].tag)) {
+ type = rule_types[i].type;
+ break;
+ }
+ }
+
+ if (type == KABI_RULE_TYPE_UNKNOWN)
+ error("unsupported kABI rule type: '%s'", field);
+
+ rule = xmalloc(sizeof(struct rule));
+
+ rule->type = type;
+ rule->target = xstrdup(get_rule_field(&rule_str, &left));
+ rule->value = xstrdup(get_rule_field(&rule_str, &left));
+
+ hash_add(rules, &rule->hash, rule_hash(rule));
+
+ debug("kABI rule: type: '%s', target: '%s', value: '%s'", field,
+ rule->target, rule->value);
+ }
+
+ if (left > 0)
+ warn("unexpected data at the end of the kABI rules section");
+
+ check(elf_end(elf));
+}
+
+bool kabi_is_declonly(const char *fqn)
+{
+ struct rule *rule;
+
+ if (!stable)
+ return false;
+ if (!fqn || !*fqn)
+ return false;
+
+ hash_for_each_possible(rules, rule, hash,
+ rule_values_hash(KABI_RULE_TYPE_DECLONLY, fqn)) {
+ if (rule->type == KABI_RULE_TYPE_DECLONLY &&
+ !strcmp(fqn, rule->target))
+ return true;
+ }
+
+ return false;
+}
+
+static char *get_enumerator_target(const char *fqn, const char *field)
+{
+ char *target = NULL;
+
+ if (asprintf(&target, "%s %s", fqn, field) < 0)
+ error("asprintf failed for '%s %s'", fqn, field);
+
+ return target;
+}
+
+static unsigned long get_ulong_value(const char *value)
+{
+ unsigned long result = 0;
+ char *endptr = NULL;
+
+ errno = 0;
+ result = strtoul(value, &endptr, 10);
+
+ if (errno || *endptr)
+ error("invalid unsigned value '%s'", value);
+
+ return result;
+}
+
+bool kabi_is_enumerator_ignored(const char *fqn, const char *field)
+{
+ bool match = false;
+ struct rule *rule;
+ char *target;
+
+ if (!stable)
+ return false;
+ if (!fqn || !*fqn || !field || !*field)
+ return false;
+
+ target = get_enumerator_target(fqn, field);
+
+ hash_for_each_possible(
+ rules, rule, hash,
+ rule_values_hash(KABI_RULE_TYPE_ENUMERATOR_IGNORE, target)) {
+ if (rule->type == KABI_RULE_TYPE_ENUMERATOR_IGNORE &&
+ !strcmp(target, rule->target)) {
+ match = true;
+ break;
+ }
+ }
+
+ free(target);
+ return match;
+}
+
+bool kabi_get_enumerator_value(const char *fqn, const char *field,
+ unsigned long *value)
+{
+ bool match = false;
+ struct rule *rule;
+ char *target;
+
+ if (!stable)
+ return false;
+ if (!fqn || !*fqn || !field || !*field)
+ return false;
+
+ target = get_enumerator_target(fqn, field);
+
+ hash_for_each_possible(rules, rule, hash,
+ rule_values_hash(KABI_RULE_TYPE_ENUMERATOR_VALUE,
+ target)) {
+ if (rule->type == KABI_RULE_TYPE_ENUMERATOR_VALUE &&
+ !strcmp(target, rule->target)) {
+ *value = get_ulong_value(rule->value);
+ match = true;
+ break;
+ }
+ }
+
+ free(target);
+ return match;
+}
+
+void kabi_free(void)
+{
+ struct hlist_node *tmp;
+ struct rule *rule;
+
+ hash_for_each_safe(rules, rule, tmp, hash) {
+ free((void *)rule->target);
+ free((void *)rule->value);
+ free(rule);
+ }
+
+ hash_init(rules);
+}
diff --git a/scripts/gendwarfksyms/symbols.c b/scripts/gendwarfksyms/symbols.c
new file mode 100644
index 000000000000..327f87389c34
--- /dev/null
+++ b/scripts/gendwarfksyms/symbols.c
@@ -0,0 +1,341 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024 Google LLC
+ */
+
+#include "gendwarfksyms.h"
+
+#define SYMBOL_HASH_BITS 12
+
+/* struct symbol_addr -> struct symbol */
+static HASHTABLE_DEFINE(symbol_addrs, 1 << SYMBOL_HASH_BITS);
+/* name -> struct symbol */
+static HASHTABLE_DEFINE(symbol_names, 1 << SYMBOL_HASH_BITS);
+
+static inline unsigned int symbol_addr_hash(const struct symbol_addr *addr)
+{
+ return hash_32(addr->section ^ addr_hash(addr->address));
+}
+
+static unsigned int __for_each_addr(struct symbol *sym, symbol_callback_t func,
+ void *data)
+{
+ struct hlist_node *tmp;
+ struct symbol *match = NULL;
+ unsigned int processed = 0;
+
+ hash_for_each_possible_safe(symbol_addrs, match, tmp, addr_hash,
+ symbol_addr_hash(&sym->addr)) {
+ if (match == sym)
+ continue; /* Already processed */
+
+ if (match->addr.section == sym->addr.section &&
+ match->addr.address == sym->addr.address) {
+ func(match, data);
+ ++processed;
+ }
+ }
+
+ return processed;
+}
+
+/*
+ * For symbols without debugging information (e.g. symbols defined in other
+ * TUs), we also match __gendwarfksyms_ptr_<symbol_name> symbols, which the
+ * kernel uses to ensure type information is present in the TU that exports
+ * the symbol. A __gendwarfksyms_ptr pointer must have the same type as the
+ * exported symbol, e.g.:
+ *
+ * typeof(symname) *__gendwarf_ptr_symname = &symname;
+ */
+bool is_symbol_ptr(const char *name)
+{
+ return name && !strncmp(name, SYMBOL_PTR_PREFIX, SYMBOL_PTR_PREFIX_LEN);
+}
+
+static unsigned int for_each(const char *name, symbol_callback_t func,
+ void *data)
+{
+ struct hlist_node *tmp;
+ struct symbol *match;
+
+ if (!name || !*name)
+ return 0;
+ if (is_symbol_ptr(name))
+ name += SYMBOL_PTR_PREFIX_LEN;
+
+ hash_for_each_possible_safe(symbol_names, match, tmp, name_hash,
+ hash_str(name)) {
+ if (strcmp(match->name, name))
+ continue;
+
+ /* Call func for the match, and all address matches */
+ if (func)
+ func(match, data);
+
+ if (match->addr.section != SHN_UNDEF)
+ return __for_each_addr(match, func, data) + 1;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static void set_crc(struct symbol *sym, void *data)
+{
+ unsigned long *crc = data;
+
+ if (sym->state == SYMBOL_PROCESSED && sym->crc != *crc)
+ warn("overriding version for symbol %s (crc %lx vs. %lx)",
+ sym->name, sym->crc, *crc);
+
+ sym->state = SYMBOL_PROCESSED;
+ sym->crc = *crc;
+}
+
+void symbol_set_crc(struct symbol *sym, unsigned long crc)
+{
+ if (for_each(sym->name, set_crc, &crc) == 0)
+ error("no matching symbols: '%s'", sym->name);
+}
+
+static void set_ptr(struct symbol *sym, void *data)
+{
+ sym->ptr_die_addr = (uintptr_t)((Dwarf_Die *)data)->addr;
+}
+
+void symbol_set_ptr(struct symbol *sym, Dwarf_Die *ptr)
+{
+ if (for_each(sym->name, set_ptr, ptr) == 0)
+ error("no matching symbols: '%s'", sym->name);
+}
+
+static void set_die(struct symbol *sym, void *data)
+{
+ sym->die_addr = (uintptr_t)((Dwarf_Die *)data)->addr;
+ sym->state = SYMBOL_MAPPED;
+}
+
+void symbol_set_die(struct symbol *sym, Dwarf_Die *die)
+{
+ if (for_each(sym->name, set_die, die) == 0)
+ error("no matching symbols: '%s'", sym->name);
+}
+
+static bool is_exported(const char *name)
+{
+ return for_each(name, NULL, NULL) > 0;
+}
+
+void symbol_read_exports(FILE *file)
+{
+ struct symbol *sym;
+ char *line = NULL;
+ char *name = NULL;
+ size_t size = 0;
+ int nsym = 0;
+
+ while (getline(&line, &size, file) > 0) {
+ if (sscanf(line, "%ms\n", &name) != 1)
+ error("malformed input line: %s", line);
+
+ if (is_exported(name)) {
+ /* Ignore duplicates */
+ free(name);
+ continue;
+ }
+
+ sym = xcalloc(1, sizeof(struct symbol));
+ sym->name = name;
+ sym->addr.section = SHN_UNDEF;
+ sym->state = SYMBOL_UNPROCESSED;
+
+ hash_add(symbol_names, &sym->name_hash, hash_str(sym->name));
+ ++nsym;
+
+ debug("%s", sym->name);
+ }
+
+ free(line);
+ debug("%d exported symbols", nsym);
+}
+
+static void get_symbol(struct symbol *sym, void *arg)
+{
+ struct symbol **res = arg;
+
+ if (sym->state == SYMBOL_UNPROCESSED)
+ *res = sym;
+}
+
+struct symbol *symbol_get(const char *name)
+{
+ struct symbol *sym = NULL;
+
+ for_each(name, get_symbol, &sym);
+ return sym;
+}
+
+void symbol_for_each(symbol_callback_t func, void *arg)
+{
+ struct hlist_node *tmp;
+ struct symbol *sym;
+
+ hash_for_each_safe(symbol_names, sym, tmp, name_hash) {
+ func(sym, arg);
+ }
+}
+
+typedef void (*elf_symbol_callback_t)(const char *name, GElf_Sym *sym,
+ Elf32_Word xndx, void *arg);
+
+static void elf_for_each_global(int fd, elf_symbol_callback_t func, void *arg)
+{
+ size_t sym_size;
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr;
+ Elf_Data *xndx_data = NULL;
+ Elf_Scn *scn;
+ Elf *elf;
+
+ if (elf_version(EV_CURRENT) != EV_CURRENT)
+ error("elf_version failed: %s", elf_errmsg(-1));
+
+ elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
+ if (!elf)
+ error("elf_begin failed: %s", elf_errmsg(-1));
+
+ scn = elf_nextscn(elf, NULL);
+
+ while (scn) {
+ shdr = gelf_getshdr(scn, &shdr_mem);
+ if (!shdr)
+ error("gelf_getshdr failed: %s", elf_errmsg(-1));
+
+ if (shdr->sh_type == SHT_SYMTAB_SHNDX) {
+ xndx_data = elf_getdata(scn, NULL);
+ if (!xndx_data)
+ error("elf_getdata failed: %s", elf_errmsg(-1));
+ break;
+ }
+
+ scn = elf_nextscn(elf, scn);
+ }
+
+ sym_size = gelf_fsize(elf, ELF_T_SYM, 1, EV_CURRENT);
+ scn = elf_nextscn(elf, NULL);
+
+ while (scn) {
+ shdr = gelf_getshdr(scn, &shdr_mem);
+ if (!shdr)
+ error("gelf_getshdr failed: %s", elf_errmsg(-1));
+
+ if (shdr->sh_type == SHT_SYMTAB) {
+ unsigned int nsyms;
+ unsigned int n;
+ Elf_Data *data = elf_getdata(scn, NULL);
+
+ if (!data)
+ error("elf_getdata failed: %s", elf_errmsg(-1));
+
+ if (shdr->sh_entsize != sym_size)
+ error("expected sh_entsize (%lu) to be %zu",
+ shdr->sh_entsize, sym_size);
+
+ nsyms = shdr->sh_size / shdr->sh_entsize;
+
+ for (n = 1; n < nsyms; ++n) {
+ const char *name = NULL;
+ Elf32_Word xndx = 0;
+ GElf_Sym sym_mem;
+ GElf_Sym *sym;
+
+ sym = gelf_getsymshndx(data, xndx_data, n,
+ &sym_mem, &xndx);
+ if (!sym)
+ error("gelf_getsymshndx failed: %s",
+ elf_errmsg(-1));
+
+ if (GELF_ST_BIND(sym->st_info) == STB_LOCAL)
+ continue;
+
+ if (sym->st_shndx != SHN_XINDEX)
+ xndx = sym->st_shndx;
+
+ name = elf_strptr(elf, shdr->sh_link,
+ sym->st_name);
+ if (!name)
+ error("elf_strptr failed: %s",
+ elf_errmsg(-1));
+
+ /* Skip empty symbol names */
+ if (*name)
+ func(name, sym, xndx, arg);
+ }
+ }
+
+ scn = elf_nextscn(elf, scn);
+ }
+
+ check(elf_end(elf));
+}
+
+static void set_symbol_addr(struct symbol *sym, void *arg)
+{
+ struct symbol_addr *addr = arg;
+
+ if (sym->addr.section == SHN_UNDEF) {
+ sym->addr = *addr;
+ hash_add(symbol_addrs, &sym->addr_hash,
+ symbol_addr_hash(&sym->addr));
+
+ debug("%s -> { %u, %lx }", sym->name, sym->addr.section,
+ sym->addr.address);
+ } else if (sym->addr.section != addr->section ||
+ sym->addr.address != addr->address) {
+ warn("multiple addresses for symbol %s?", sym->name);
+ }
+}
+
+static void elf_set_symbol_addr(const char *name, GElf_Sym *sym,
+ Elf32_Word xndx, void *arg)
+{
+ struct symbol_addr addr = { .section = xndx, .address = sym->st_value };
+
+ /* Set addresses for exported symbols */
+ if (addr.section != SHN_UNDEF)
+ for_each(name, set_symbol_addr, &addr);
+}
+
+void symbol_read_symtab(int fd)
+{
+ elf_for_each_global(fd, elf_set_symbol_addr, NULL);
+}
+
+void symbol_print_versions(void)
+{
+ struct hlist_node *tmp;
+ struct symbol *sym;
+
+ hash_for_each_safe(symbol_names, sym, tmp, name_hash) {
+ if (sym->state != SYMBOL_PROCESSED)
+ warn("no information for symbol %s", sym->name);
+
+ printf("#SYMVER %s 0x%08lx\n", sym->name, sym->crc);
+ }
+}
+
+void symbol_free(void)
+{
+ struct hlist_node *tmp;
+ struct symbol *sym;
+
+ hash_for_each_safe(symbol_names, sym, tmp, name_hash) {
+ free((void *)sym->name);
+ free(sym);
+ }
+
+ hash_init(symbol_addrs);
+ hash_init(symbol_names);
+}
diff --git a/scripts/gendwarfksyms/types.c b/scripts/gendwarfksyms/types.c
new file mode 100644
index 000000000000..6f37289104ff
--- /dev/null
+++ b/scripts/gendwarfksyms/types.c
@@ -0,0 +1,481 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024 Google LLC
+ */
+
+#define _GNU_SOURCE
+#include <inttypes.h>
+#include <stdio.h>
+#include <zlib.h>
+
+#include "gendwarfksyms.h"
+
+static struct cache expansion_cache;
+
+/*
+ * A simple linked list of shared or owned strings to avoid copying strings
+ * around when not necessary.
+ */
+struct type_list_entry {
+ const char *str;
+ void *owned;
+ struct list_head list;
+};
+
+static void type_list_free(struct list_head *list)
+{
+ struct type_list_entry *entry;
+ struct type_list_entry *tmp;
+
+ list_for_each_entry_safe(entry, tmp, list, list) {
+ if (entry->owned)
+ free(entry->owned);
+ free(entry);
+ }
+
+ INIT_LIST_HEAD(list);
+}
+
+static int type_list_append(struct list_head *list, const char *s, void *owned)
+{
+ struct type_list_entry *entry;
+
+ if (!s)
+ return 0;
+
+ entry = xmalloc(sizeof(struct type_list_entry));
+ entry->str = s;
+ entry->owned = owned;
+ list_add_tail(&entry->list, list);
+
+ return strlen(entry->str);
+}
+
+static void type_list_write(struct list_head *list, FILE *file)
+{
+ struct type_list_entry *entry;
+
+ list_for_each_entry(entry, list, list) {
+ if (entry->str)
+ checkp(fputs(entry->str, file));
+ }
+}
+
+/*
+ * An expanded type string in symtypes format.
+ */
+struct type_expansion {
+ char *name;
+ size_t len;
+ struct list_head expanded;
+ struct hlist_node hash;
+};
+
+static void type_expansion_init(struct type_expansion *type)
+{
+ type->name = NULL;
+ type->len = 0;
+ INIT_LIST_HEAD(&type->expanded);
+}
+
+static inline void type_expansion_free(struct type_expansion *type)
+{
+ free(type->name);
+ type->name = NULL;
+ type->len = 0;
+ type_list_free(&type->expanded);
+}
+
+static void type_expansion_append(struct type_expansion *type, const char *s,
+ void *owned)
+{
+ type->len += type_list_append(&type->expanded, s, owned);
+}
+
+/*
+ * type_map -- the longest expansions for each type.
+ *
+ * const char *name -> struct type_expansion *
+ */
+#define TYPE_HASH_BITS 12
+static HASHTABLE_DEFINE(type_map, 1 << TYPE_HASH_BITS);
+
+static int type_map_get(const char *name, struct type_expansion **res)
+{
+ struct type_expansion *e;
+
+ hash_for_each_possible(type_map, e, hash, hash_str(name)) {
+ if (!strcmp(name, e->name)) {
+ *res = e;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static void type_map_add(const char *name, struct type_expansion *type)
+{
+ struct type_expansion *e;
+
+ if (type_map_get(name, &e)) {
+ e = xmalloc(sizeof(struct type_expansion));
+ type_expansion_init(e);
+ e->name = xstrdup(name);
+
+ hash_add(type_map, &e->hash, hash_str(e->name));
+
+ if (dump_types)
+ debug("adding %s", e->name);
+ } else {
+ /* Use the longest available expansion */
+ if (type->len <= e->len)
+ return;
+
+ type_list_free(&e->expanded);
+
+ if (dump_types)
+ debug("replacing %s", e->name);
+ }
+
+ /* Take ownership of type->expanded */
+ list_replace_init(&type->expanded, &e->expanded);
+ e->len = type->len;
+
+ if (dump_types) {
+ checkp(fputs(e->name, stderr));
+ checkp(fputs(" ", stderr));
+ type_list_write(&e->expanded, stderr);
+ checkp(fputs("\n", stderr));
+ }
+}
+
+static void type_map_write(FILE *file)
+{
+ struct type_expansion *e;
+ struct hlist_node *tmp;
+
+ if (!file)
+ return;
+
+ hash_for_each_safe(type_map, e, tmp, hash) {
+ checkp(fputs(e->name, file));
+ checkp(fputs(" ", file));
+ type_list_write(&e->expanded, file);
+ checkp(fputs("\n", file));
+ }
+}
+
+static void type_map_free(void)
+{
+ struct type_expansion *e;
+ struct hlist_node *tmp;
+
+ hash_for_each_safe(type_map, e, tmp, hash) {
+ type_expansion_free(e);
+ free(e);
+ }
+
+ hash_init(type_map);
+}
+
+/*
+ * CRC for a type, with an optional fully expanded type string for
+ * debugging.
+ */
+struct version {
+ struct type_expansion type;
+ unsigned long crc;
+};
+
+static void version_init(struct version *version)
+{
+ version->crc = crc32(0, NULL, 0);
+ type_expansion_init(&version->type);
+}
+
+static void version_free(struct version *version)
+{
+ type_expansion_free(&version->type);
+}
+
+static void version_add(struct version *version, const char *s)
+{
+ version->crc = crc32(version->crc, (void *)s, strlen(s));
+ if (dump_versions)
+ type_expansion_append(&version->type, s, NULL);
+}
+
+/*
+ * Type reference format: <prefix>#<name>, where prefix:
+ * s -> structure
+ * u -> union
+ * e -> enum
+ * t -> typedef
+ *
+ * Names with spaces are additionally wrapped in single quotes.
+ */
+static inline bool is_type_prefix(const char *s)
+{
+ return (s[0] == 's' || s[0] == 'u' || s[0] == 'e' || s[0] == 't') &&
+ s[1] == '#';
+}
+
+static char get_type_prefix(int tag)
+{
+ switch (tag) {
+ case DW_TAG_class_type:
+ case DW_TAG_structure_type:
+ return 's';
+ case DW_TAG_union_type:
+ return 'u';
+ case DW_TAG_enumeration_type:
+ return 'e';
+ case DW_TAG_typedef_type:
+ return 't';
+ default:
+ return 0;
+ }
+}
+
+static char *get_type_name(struct die *cache)
+{
+ const char *quote;
+ char prefix;
+ char *name;
+
+ if (cache->state == DIE_INCOMPLETE) {
+ warn("found incomplete cache entry: %p", cache);
+ return NULL;
+ }
+ if (cache->state == DIE_SYMBOL || cache->state == DIE_FQN)
+ return NULL;
+ if (!cache->fqn || !*cache->fqn)
+ return NULL;
+
+ prefix = get_type_prefix(cache->tag);
+ if (!prefix)
+ return NULL;
+
+ /* Wrap names with spaces in single quotes */
+ quote = strstr(cache->fqn, " ") ? "'" : "";
+
+ /* <prefix>#<type_name>\0 */
+ if (asprintf(&name, "%c#%s%s%s", prefix, quote, cache->fqn, quote) < 0)
+ error("asprintf failed for '%s'", cache->fqn);
+
+ return name;
+}
+
+static void __calculate_version(struct version *version, struct list_head *list)
+{
+ struct type_list_entry *entry;
+ struct type_expansion *e;
+
+ /* Calculate a CRC over an expanded type string */
+ list_for_each_entry(entry, list, list) {
+ if (is_type_prefix(entry->str)) {
+ check(type_map_get(entry->str, &e));
+
+ /*
+ * It's sufficient to expand each type reference just
+ * once to detect changes.
+ */
+ if (cache_was_expanded(&expansion_cache, e)) {
+ version_add(version, entry->str);
+ } else {
+ cache_mark_expanded(&expansion_cache, e);
+ __calculate_version(version, &e->expanded);
+ }
+ } else {
+ version_add(version, entry->str);
+ }
+ }
+}
+
+static void calculate_version(struct version *version, struct list_head *list)
+{
+ version_init(version);
+ __calculate_version(version, list);
+ cache_free(&expansion_cache);
+}
+
+static void __type_expand(struct die *cache, struct type_expansion *type,
+ bool recursive);
+
+static void type_expand_child(struct die *cache, struct type_expansion *type,
+ bool recursive)
+{
+ struct type_expansion child;
+ char *name;
+
+ name = get_type_name(cache);
+ if (!name) {
+ __type_expand(cache, type, recursive);
+ return;
+ }
+
+ if (recursive && !__cache_was_expanded(&expansion_cache, cache->addr)) {
+ __cache_mark_expanded(&expansion_cache, cache->addr);
+ type_expansion_init(&child);
+ __type_expand(cache, &child, true);
+ type_map_add(name, &child);
+ type_expansion_free(&child);
+ }
+
+ type_expansion_append(type, name, name);
+}
+
+static void __type_expand(struct die *cache, struct type_expansion *type,
+ bool recursive)
+{
+ struct die_fragment *df;
+ struct die *child;
+
+ list_for_each_entry(df, &cache->fragments, list) {
+ switch (df->type) {
+ case FRAGMENT_STRING:
+ type_expansion_append(type, df->data.str, NULL);
+ break;
+ case FRAGMENT_DIE:
+ /* Use a complete die_map expansion if available */
+ if (__die_map_get(df->data.addr, DIE_COMPLETE,
+ &child) &&
+ __die_map_get(df->data.addr, DIE_UNEXPANDED,
+ &child))
+ error("unknown child: %" PRIxPTR,
+ df->data.addr);
+
+ type_expand_child(child, type, recursive);
+ break;
+ case FRAGMENT_LINEBREAK:
+ /*
+ * Keep whitespace in the symtypes format, but avoid
+ * repeated spaces.
+ */
+ if (list_is_last(&df->list, &cache->fragments) ||
+ list_next_entry(df, list)->type !=
+ FRAGMENT_LINEBREAK)
+ type_expansion_append(type, " ", NULL);
+ break;
+ default:
+ error("empty die_fragment in %p", cache);
+ }
+ }
+}
+
+static void type_expand(struct die *cache, struct type_expansion *type,
+ bool recursive)
+{
+ type_expansion_init(type);
+ __type_expand(cache, type, recursive);
+ cache_free(&expansion_cache);
+}
+
+static void expand_type(struct die *cache, void *arg)
+{
+ struct type_expansion type;
+ char *name;
+
+ if (cache->mapped)
+ return;
+
+ cache->mapped = true;
+
+ /*
+ * Skip unexpanded die_map entries if there's a complete
+ * expansion available for this DIE.
+ */
+ if (cache->state == DIE_UNEXPANDED &&
+ !__die_map_get(cache->addr, DIE_COMPLETE, &cache)) {
+ if (cache->mapped)
+ return;
+
+ cache->mapped = true;
+ }
+
+ name = get_type_name(cache);
+ if (!name)
+ return;
+
+ debug("%s", name);
+ type_expand(cache, &type, true);
+ type_map_add(name, &type);
+
+ type_expansion_free(&type);
+ free(name);
+}
+
+static void expand_symbol(struct symbol *sym, void *arg)
+{
+ struct type_expansion type;
+ struct version version;
+ struct die *cache;
+
+ /*
+ * No need to expand again unless we want a symtypes file entry
+ * for the symbol. Note that this means `sym` has the same address
+ * as another symbol that was already processed.
+ */
+ if (!symtypes && sym->state == SYMBOL_PROCESSED)
+ return;
+
+ if (__die_map_get(sym->die_addr, DIE_SYMBOL, &cache))
+ return; /* We'll warn about missing CRCs later. */
+
+ type_expand(cache, &type, false);
+
+ /* If the symbol already has a version, don't calculate it again. */
+ if (sym->state != SYMBOL_PROCESSED) {
+ calculate_version(&version, &type.expanded);
+ symbol_set_crc(sym, version.crc);
+ debug("%s = %lx", sym->name, version.crc);
+
+ if (dump_versions) {
+ checkp(fputs(sym->name, stderr));
+ checkp(fputs(" ", stderr));
+ type_list_write(&version.type.expanded, stderr);
+ checkp(fputs("\n", stderr));
+ }
+
+ version_free(&version);
+ }
+
+ /* These aren't needed in type_map unless we want a symtypes file. */
+ if (symtypes)
+ type_map_add(sym->name, &type);
+
+ type_expansion_free(&type);
+}
+
+void generate_symtypes_and_versions(FILE *file)
+{
+ cache_init(&expansion_cache);
+
+ /*
+ * die_map processing:
+ *
+ * 1. die_map contains all types referenced in exported symbol
+ * signatures, but can contain duplicates just like the original
+ * DWARF, and some references may not be fully expanded depending
+ * on how far we processed the DIE tree for that specific symbol.
+ *
+ * For each die_map entry, find the longest available expansion,
+ * and add it to type_map.
+ */
+ die_map_for_each(expand_type, NULL);
+
+ /*
+ * 2. For each exported symbol, expand the die_map type, and use
+ * type_map expansions to calculate a symbol version from the
+ * fully expanded type string.
+ */
+ symbol_for_each(expand_symbol, NULL);
+
+ /*
+ * 3. If a symtypes file is requested, write type_map contents to
+ * the file.
+ */
+ type_map_write(file);
+ type_map_free();
+}
diff --git a/scripts/generate_builtin_ranges.awk b/scripts/generate_builtin_ranges.awk
new file mode 100755
index 000000000000..d4bd5c2b998c
--- /dev/null
+++ b/scripts/generate_builtin_ranges.awk
@@ -0,0 +1,513 @@
+#!/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 <kris.van.hees@oracle.com>
+#
+# Usage: generate_builtin_ranges.awk modules.builtin vmlinux.map \
+# vmlinux.o.map > 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 = 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 = sprintf("%016x", (soff + sect_base[osect]) * 2 + 1);
+ entries[idx] = sprintf("%s %08x-%08x %s", sect, soff, eoff, mod);
+ count[sect]++;
+}
+
+# (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) {
+ # There are a few sections with constant data (without symbols) that
+ # can get resized during linking, so it is best to ignore them.
+ if ($1 ~ /^\.rodata\.(cst|str)[0-9]/)
+ next;
+
+ 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) {
+ idx = sprintf("%016x", sect_base[sect] * 2);
+ entries[idx] = sect_anchor[sect];
+ }
+ }
+
+ n = asorti(entries, indices);
+ for (i = 1; i <= n; i++)
+ print entries[indices[i]];
+}
diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_analyzer.py
index f270c7b0cf34..cd41bc906fbd 100755
--- a/scripts/generate_rust_analyzer.py
+++ b/scripts/generate_rust_analyzer.py
@@ -8,6 +8,7 @@ import json
import logging
import os
import pathlib
+import subprocess
import sys
def args_crates_cfgs(cfgs):
@@ -35,8 +36,7 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs):
crates_cfgs = args_crates_cfgs(cfgs)
def append_crate(display_name, root_module, deps, cfg=[], is_workspace_member=True, is_proc_macro=False):
- crates_indexes[display_name] = len(crates)
- crates.append({
+ crate = {
"display_name": display_name,
"root_module": str(root_module),
"is_workspace_member": is_workspace_member,
@@ -47,16 +47,36 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs):
"env": {
"RUST_MODFILE": "This is only for rust-analyzer"
}
- })
-
- # First, the ones in `rust/` since they are a bit special.
- append_crate(
- "core",
- sysroot_src / "core" / "src" / "lib.rs",
- [],
- cfg=crates_cfgs.get("core", []),
- is_workspace_member=False,
- )
+ }
+ if is_proc_macro:
+ proc_macro_dylib_name = subprocess.check_output(
+ [os.environ["RUSTC"], "--print", "file-names", "--crate-name", display_name, "--crate-type", "proc-macro", "-"],
+ stdin=subprocess.DEVNULL,
+ ).decode('utf-8').strip()
+ crate["proc_macro_dylib_path"] = f"{objtree}/rust/{proc_macro_dylib_name}"
+ crates_indexes[display_name] = len(crates)
+ crates.append(crate)
+
+ def append_sysroot_crate(
+ display_name,
+ deps,
+ cfg=[],
+ ):
+ append_crate(
+ display_name,
+ sysroot_src / display_name / "src" / "lib.rs",
+ deps,
+ cfg,
+ is_workspace_member=False,
+ )
+
+ # NB: sysroot crates reexport items from one another so setting up our transitive dependencies
+ # here is important for ensuring that rust-analyzer can resolve symbols. The sources of truth
+ # for this dependency graph are `(sysroot_src / crate / "Cargo.toml" for crate in crates)`.
+ append_sysroot_crate("core", [], cfg=crates_cfgs.get("core", []))
+ append_sysroot_crate("alloc", ["core"])
+ append_sysroot_crate("std", ["alloc", "core"])
+ append_sysroot_crate("proc_macro", ["core", "std"])
append_crate(
"compiler_builtins",
@@ -65,19 +85,11 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs):
)
append_crate(
- "alloc",
- sysroot_src / "alloc" / "src" / "lib.rs",
- ["core", "compiler_builtins"],
- cfg=crates_cfgs.get("alloc", []),
- )
-
- append_crate(
"macros",
srctree / "rust" / "macros" / "lib.rs",
- [],
+ ["std", "proc_macro"],
is_proc_macro=True,
)
- crates[-1]["proc_macro_dylib_path"] = f"{objtree}/rust/libmacros.so"
append_crate(
"build_error",
@@ -86,26 +98,42 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs):
)
append_crate(
- "bindings",
- srctree / "rust"/ "bindings" / "lib.rs",
- ["core"],
- cfg=cfg,
+ "pin_init_internal",
+ srctree / "rust" / "pin-init" / "internal" / "src" / "lib.rs",
+ [],
+ cfg=["kernel"],
+ is_proc_macro=True,
)
- crates[-1]["env"]["OBJTREE"] = str(objtree.resolve(True))
append_crate(
- "kernel",
- srctree / "rust" / "kernel" / "lib.rs",
- ["core", "alloc", "macros", "build_error", "bindings"],
- cfg=cfg,
+ "pin_init",
+ srctree / "rust" / "pin-init" / "src" / "lib.rs",
+ ["core", "pin_init_internal", "macros"],
+ cfg=["kernel"],
)
- crates[-1]["source"] = {
- "include_dirs": [
- str(srctree / "rust" / "kernel"),
- str(objtree / "rust")
- ],
- "exclude_dirs": [],
- }
+
+ def append_crate_with_generated(
+ display_name,
+ deps,
+ ):
+ append_crate(
+ display_name,
+ srctree / "rust"/ display_name / "lib.rs",
+ deps,
+ cfg=cfg,
+ )
+ crates[-1]["env"]["OBJTREE"] = str(objtree.resolve(True))
+ crates[-1]["source"] = {
+ "include_dirs": [
+ str(srctree / "rust" / display_name),
+ str(objtree / "rust")
+ ],
+ "exclude_dirs": [],
+ }
+
+ append_crate_with_generated("bindings", ["core"])
+ append_crate_with_generated("uapi", ["core"])
+ append_crate_with_generated("kernel", ["core", "macros", "build_error", "pin_init", "bindings", "uapi"])
def is_root_crate(build_file, target):
try:
@@ -133,7 +161,7 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs):
append_crate(
name,
path,
- ["core", "alloc", "kernel"],
+ ["core", "kernel"],
cfg=cfg,
)
@@ -145,6 +173,7 @@ def main():
parser.add_argument('--cfgs', action='append', default=[])
parser.add_argument("srctree", type=pathlib.Path)
parser.add_argument("objtree", type=pathlib.Path)
+ parser.add_argument("sysroot", type=pathlib.Path)
parser.add_argument("sysroot_src", type=pathlib.Path)
parser.add_argument("exttree", type=pathlib.Path, nargs="?")
args = parser.parse_args()
@@ -154,9 +183,12 @@ def main():
level=logging.INFO if args.verbose else logging.WARNING
)
+ # Making sure that the `sysroot` and `sysroot_src` belong to the same toolchain.
+ assert args.sysroot in args.sysroot_src.parents
+
rust_project = {
"crates": generate_crates(args.srctree, args.objtree, args.sysroot_src, args.exttree, args.cfgs),
- "sysroot_src": str(args.sysroot_src),
+ "sysroot": str(args.sysroot),
}
json.dump(rust_project, sys.stdout, sort_keys=True, indent=4)
diff --git a/scripts/generate_rust_target.rs b/scripts/generate_rust_target.rs
index 87f34925eb7b..8667d0ae3c82 100644
--- a/scripts/generate_rust_target.rs
+++ b/scripts/generate_rust_target.rs
@@ -20,12 +20,28 @@ enum Value {
Boolean(bool),
Number(i32),
String(String),
+ Array(Vec<Value>),
Object(Object),
}
type Object = Vec<(String, Value)>;
-/// Minimal "almost JSON" generator (e.g. no `null`s, no arrays, no escaping),
+fn comma_sep<T>(
+ seq: &[T],
+ formatter: &mut Formatter<'_>,
+ f: impl Fn(&mut Formatter<'_>, &T) -> Result,
+) -> Result {
+ if let [ref rest @ .., ref last] = seq[..] {
+ for v in rest {
+ f(formatter, v)?;
+ formatter.write_str(",")?;
+ }
+ f(formatter, last)?;
+ }
+ Ok(())
+}
+
+/// Minimal "almost JSON" generator (e.g. no `null`s, no escaping),
/// enough for this purpose.
impl Display for Value {
fn fmt(&self, formatter: &mut Formatter<'_>) -> Result {
@@ -33,59 +49,67 @@ impl Display for Value {
Value::Boolean(boolean) => write!(formatter, "{}", boolean),
Value::Number(number) => write!(formatter, "{}", number),
Value::String(string) => write!(formatter, "\"{}\"", string),
+ Value::Array(values) => {
+ formatter.write_str("[")?;
+ comma_sep(&values[..], formatter, |formatter, v| v.fmt(formatter))?;
+ formatter.write_str("]")
+ }
Value::Object(object) => {
formatter.write_str("{")?;
- if let [ref rest @ .., ref last] = object[..] {
- for (key, value) in rest {
- write!(formatter, "\"{}\": {},", key, value)?;
- }
- write!(formatter, "\"{}\": {}", last.0, last.1)?;
- }
+ comma_sep(&object[..], formatter, |formatter, v| {
+ write!(formatter, "\"{}\": {}", v.0, v.1)
+ })?;
formatter.write_str("}")
}
}
}
}
-struct TargetSpec(Object);
-
-impl TargetSpec {
- fn new() -> TargetSpec {
- TargetSpec(Vec::new())
+impl From<bool> for Value {
+ fn from(value: bool) -> Self {
+ Self::Boolean(value)
}
}
-trait Push<T> {
- fn push(&mut self, key: &str, value: T);
+impl From<i32> for Value {
+ fn from(value: i32) -> Self {
+ Self::Number(value)
+ }
}
-impl Push<bool> for TargetSpec {
- fn push(&mut self, key: &str, value: bool) {
- self.0.push((key.to_string(), Value::Boolean(value)));
+impl From<String> for Value {
+ fn from(value: String) -> Self {
+ Self::String(value)
}
}
-impl Push<i32> for TargetSpec {
- fn push(&mut self, key: &str, value: i32) {
- self.0.push((key.to_string(), Value::Number(value)));
+impl From<&str> for Value {
+ fn from(value: &str) -> Self {
+ Self::String(value.to_string())
}
}
-impl Push<String> for TargetSpec {
- fn push(&mut self, key: &str, value: String) {
- self.0.push((key.to_string(), Value::String(value)));
+impl From<Object> for Value {
+ fn from(object: Object) -> Self {
+ Self::Object(object)
}
}
-impl Push<&str> for TargetSpec {
- fn push(&mut self, key: &str, value: &str) {
- self.push(key, value.to_string());
+impl<T: Into<Value>, const N: usize> From<[T; N]> for Value {
+ fn from(i: [T; N]) -> Self {
+ Self::Array(i.into_iter().map(|v| v.into()).collect())
}
}
-impl Push<Object> for TargetSpec {
- fn push(&mut self, key: &str, value: Object) {
- self.0.push((key.to_string(), Value::Object(value)));
+struct TargetSpec(Object);
+
+impl TargetSpec {
+ fn new() -> TargetSpec {
+ TargetSpec(Vec::new())
+ }
+
+ fn push(&mut self, key: &str, value: impl Into<Value>) {
+ self.0.push((key.to_string(), value.into()));
}
}
@@ -141,6 +165,18 @@ impl KernelConfig {
let option = "CONFIG_".to_owned() + option;
self.0.contains_key(&option)
}
+
+ /// Is the rustc version at least `major.minor.patch`?
+ fn rustc_version_atleast(&self, major: u32, minor: u32, patch: u32) -> bool {
+ let check_version = 100000 * major + 100 * minor + patch;
+ let actual_version = self
+ .0
+ .get("CONFIG_RUSTC_VERSION")
+ .unwrap()
+ .parse::<u32>()
+ .unwrap();
+ check_version <= actual_version
+ }
}
fn main() {
@@ -148,7 +184,9 @@ fn main() {
let mut ts = TargetSpec::new();
// `llvm-target`s are taken from `scripts/Makefile.clang`.
- if cfg.has("ARM64") {
+ if cfg.has("ARM") {
+ panic!("arm uses the builtin rustc target");
+ } else if cfg.has("ARM64") {
panic!("arm64 uses the builtin rustc aarch64-unknown-none target");
} else if cfg.has("RISCV") {
if cfg.has("64BIT") {
@@ -158,16 +196,35 @@ fn main() {
}
} else if cfg.has("X86_64") {
ts.push("arch", "x86_64");
+ if cfg.rustc_version_atleast(1, 86, 0) {
+ ts.push("rustc-abi", "x86-softfloat");
+ }
ts.push(
"data-layout",
"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
);
- let mut features = "-3dnow,-3dnowa,-mmx,+soft-float".to_string();
+ let mut features = "-mmx,+soft-float".to_string();
if cfg.has("MITIGATION_RETPOLINE") {
+ // The kernel uses `-mretpoline-external-thunk` (for Clang), which Clang maps to the
+ // target feature of the same name plus the other two target features in
+ // `clang/lib/Driver/ToolChains/Arch/X86.cpp`. These should be eventually enabled via
+ // `-Ctarget-feature` when `rustc` starts recognizing them (or via a new dedicated
+ // flag); see https://github.com/rust-lang/rust/issues/116852.
features += ",+retpoline-external-thunk";
+ features += ",+retpoline-indirect-branches";
+ features += ",+retpoline-indirect-calls";
+ }
+ if cfg.has("MITIGATION_SLS") {
+ // The kernel uses `-mharden-sls=all`, which Clang maps to both these target features in
+ // `clang/lib/Driver/ToolChains/Arch/X86.cpp`. These should be eventually enabled via
+ // `-Ctarget-feature` when `rustc` starts recognizing them (or via a new dedicated
+ // flag); see https://github.com/rust-lang/rust/issues/116851.
+ features += ",+harden-sls-ijmp";
+ features += ",+harden-sls-ret";
}
ts.push("features", features);
ts.push("llvm-target", "x86_64-linux-gnu");
+ ts.push("supported-sanitizers", ["kcfi", "kernel-address"]);
ts.push("target-pointer-width", "64");
} else if cfg.has("X86_32") {
// This only works on UML, as i386 otherwise needs regparm support in rustc
@@ -175,11 +232,14 @@ fn main() {
panic!("32-bit x86 only works under UML");
}
ts.push("arch", "x86");
+ if cfg.rustc_version_atleast(1, 86, 0) {
+ ts.push("rustc-abi", "x86-softfloat");
+ }
ts.push(
"data-layout",
"e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128",
);
- let mut features = "-3dnow,-3dnowa,-mmx,+soft-float".to_string();
+ let mut features = "-mmx,+soft-float".to_string();
if cfg.has("MITIGATION_RETPOLINE") {
features += ",+retpoline-external-thunk";
}
diff --git a/scripts/genksyms/Makefile b/scripts/genksyms/Makefile
index 312edccda736..4350311fb7b3 100644
--- a/scripts/genksyms/Makefile
+++ b/scripts/genksyms/Makefile
@@ -4,24 +4,6 @@ hostprogs-always-y += genksyms
genksyms-objs := genksyms.o parse.tab.o lex.lex.o
-# FIXME: fix the ambiguous grammar in parse.y and delete this hack
-#
-# Suppress shift/reduce, reduce/reduce conflicts warnings
-# unless W=1 is specified.
-#
-# Just in case, run "$(YACC) --version" without suppressing stderr
-# so that 'bison: not found' will be displayed if it is missing.
-ifeq ($(findstring 1,$(KBUILD_EXTRA_WARN)),)
-
-quiet_cmd_bison_no_warn = $(quiet_cmd_bison)
- cmd_bison_no_warn = $(YACC) --version >/dev/null; \
- $(cmd_bison) 2>/dev/null
-
-$(obj)/pars%.tab.c $(obj)/pars%.tab.h: $(src)/pars%.y FORCE
- $(call if_changed,bison_no_warn)
-
-endif
-
# -I needed for generated C source to include headers in source tree
HOSTCFLAGS_parse.tab.o := -I $(src)
HOSTCFLAGS_lex.lex.o := -I $(src)
diff --git a/scripts/genksyms/genksyms.c b/scripts/genksyms/genksyms.c
index f3901c55df23..8b0d7ac73dbb 100644
--- a/scripts/genksyms/genksyms.c
+++ b/scripts/genksyms/genksyms.c
@@ -12,18 +12,19 @@
#include <stdio.h>
#include <string.h>
+#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <stdarg.h>
#include <getopt.h>
+#include <hashtable.h>
+
#include "genksyms.h"
/*----------------------------------------------------------------------*/
-#define HASH_BUCKETS 4096
-
-static struct symbol *symtab[HASH_BUCKETS];
+static HASHTABLE_DEFINE(symbol_hashtable, 1U << 12);
static FILE *debugfile;
int cur_line = 1;
@@ -60,7 +61,7 @@ static void print_type_name(enum symbol_type type, const char *name);
/*----------------------------------------------------------------------*/
-static const unsigned int crctab32[] = {
+static const uint32_t crctab32[] = {
0x00000000U, 0x77073096U, 0xee0e612cU, 0x990951baU, 0x076dc419U,
0x706af48fU, 0xe963a535U, 0x9e6495a3U, 0x0edb8832U, 0x79dcb8a4U,
0xe0d5e91eU, 0x97d2d988U, 0x09b64c2bU, 0x7eb17cbdU, 0xe7b82d07U,
@@ -115,19 +116,19 @@ static const unsigned int crctab32[] = {
0x2d02ef8dU
};
-static unsigned long partial_crc32_one(unsigned char c, unsigned long crc)
+static uint32_t partial_crc32_one(uint8_t c, uint32_t crc)
{
return crctab32[(crc ^ c) & 0xff] ^ (crc >> 8);
}
-static unsigned long partial_crc32(const char *s, unsigned long crc)
+static uint32_t partial_crc32(const char *s, uint32_t crc)
{
while (*s)
crc = partial_crc32_one(*s++, crc);
return crc;
}
-static unsigned long crc32(const char *s)
+static uint32_t crc32(const char *s)
{
return partial_crc32(s, 0xffffffff) ^ 0xffffffff;
}
@@ -151,14 +152,14 @@ static enum symbol_type map_to_ns(enum symbol_type t)
struct symbol *find_symbol(const char *name, enum symbol_type ns, int exact)
{
- unsigned long h = crc32(name) % HASH_BUCKETS;
struct symbol *sym;
- for (sym = symtab[h]; sym; sym = sym->hash_next)
+ hash_for_each_possible(symbol_hashtable, sym, hnode, crc32(name)) {
if (map_to_ns(sym->type) == map_to_ns(ns) &&
strcmp(name, sym->name) == 0 &&
sym->is_declared)
break;
+ }
if (exact && sym && sym->type != ns)
return NULL;
@@ -224,64 +225,56 @@ static struct symbol *__add_symbol(const char *name, enum symbol_type type,
return NULL;
}
- h = crc32(name) % HASH_BUCKETS;
- for (sym = symtab[h]; sym; sym = sym->hash_next) {
- if (map_to_ns(sym->type) == map_to_ns(type) &&
- strcmp(name, sym->name) == 0) {
- if (is_reference)
- /* fall through */ ;
- else if (sym->type == type &&
- equal_list(sym->defn, defn)) {
- if (!sym->is_declared && sym->is_override) {
- print_location();
- print_type_name(type, name);
- fprintf(stderr, " modversion is "
- "unchanged\n");
- }
- sym->is_declared = 1;
- return sym;
- } else if (!sym->is_declared) {
- if (sym->is_override && flag_preserve) {
- print_location();
- fprintf(stderr, "ignoring ");
- print_type_name(type, name);
- fprintf(stderr, " modversion change\n");
- sym->is_declared = 1;
- return sym;
- } else {
- status = is_unknown_symbol(sym) ?
- STATUS_DEFINED : STATUS_MODIFIED;
- }
- } else {
- error_with_pos("redefinition of %s", name);
- return sym;
+ h = crc32(name);
+ hash_for_each_possible(symbol_hashtable, sym, hnode, h) {
+ if (map_to_ns(sym->type) != map_to_ns(type) ||
+ strcmp(name, sym->name))
+ continue;
+
+ if (is_reference) {
+ break;
+ } else if (sym->type == type && equal_list(sym->defn, defn)) {
+ if (!sym->is_declared && sym->is_override) {
+ print_location();
+ print_type_name(type, name);
+ fprintf(stderr, " modversion is unchanged\n");
}
+ sym->is_declared = 1;
+ } else if (sym->is_declared) {
+ error_with_pos("redefinition of %s", name);
+ } else if (sym->is_override && flag_preserve) {
+ print_location();
+ fprintf(stderr, "ignoring ");
+ print_type_name(type, name);
+ fprintf(stderr, " modversion change\n");
+ sym->is_declared = 1;
+ } else {
+ status = is_unknown_symbol(sym) ?
+ STATUS_DEFINED : STATUS_MODIFIED;
break;
}
+ free_list(defn, NULL);
+ return sym;
}
if (sym) {
- struct symbol **psym;
+ hash_del(&sym->hnode);
- for (psym = &symtab[h]; *psym; psym = &(*psym)->hash_next) {
- if (*psym == sym) {
- *psym = sym->hash_next;
- break;
- }
- }
+ free_list(sym->defn, NULL);
+ free(sym->name);
+ free(sym);
--nsyms;
}
sym = xmalloc(sizeof(*sym));
- sym->name = name;
+ sym->name = xstrdup(name);
sym->type = type;
sym->defn = defn;
sym->expansion_trail = NULL;
sym->visited = NULL;
sym->is_extern = is_extern;
- sym->hash_next = symtab[h];
- symtab[h] = sym;
+ hash_add(symbol_hashtable, &sym->hnode, h);
sym->is_declared = !is_reference;
sym->status = status;
@@ -480,7 +473,7 @@ static void read_reference(FILE *f)
defn = def;
def = read_node(f);
}
- subsym = add_reference_symbol(xstrdup(sym->string), sym->tag,
+ subsym = add_reference_symbol(sym->string, sym->tag,
defn, is_extern);
subsym->is_override = is_override;
free_node(sym);
@@ -525,7 +518,7 @@ static void print_list(FILE * f, struct string_list *list)
}
}
-static unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc)
+static uint32_t expand_and_crc_sym(struct symbol *sym, uint32_t crc)
{
struct string_list *list = sym->defn;
struct string_list **e, **b;
@@ -632,54 +625,55 @@ static unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc)
void export_symbol(const char *name)
{
struct symbol *sym;
+ uint32_t crc;
+ int has_changed = 0;
sym = find_symbol(name, SYM_NORMAL, 0);
- if (!sym)
+ if (!sym) {
error_with_pos("export undefined symbol %s", name);
- else {
- unsigned long crc;
- int has_changed = 0;
-
- if (flag_dump_defs)
- fprintf(debugfile, "Export %s == <", name);
-
- expansion_trail = (struct symbol *)-1L;
-
- sym->expansion_trail = expansion_trail;
- expansion_trail = sym;
- crc = expand_and_crc_sym(sym, 0xffffffff) ^ 0xffffffff;
-
- sym = expansion_trail;
- while (sym != (struct symbol *)-1L) {
- struct symbol *n = sym->expansion_trail;
-
- if (sym->status != STATUS_UNCHANGED) {
- if (!has_changed) {
- print_location();
- fprintf(stderr, "%s: %s: modversion "
- "changed because of changes "
- "in ", flag_preserve ? "error" :
- "warning", name);
- } else
- fprintf(stderr, ", ");
- print_type_name(sym->type, sym->name);
- if (sym->status == STATUS_DEFINED)
- fprintf(stderr, " (became defined)");
- has_changed = 1;
- if (flag_preserve)
- errors++;
+ return;
+ }
+
+ if (flag_dump_defs)
+ fprintf(debugfile, "Export %s == <", name);
+
+ expansion_trail = (struct symbol *)-1L;
+
+ sym->expansion_trail = expansion_trail;
+ expansion_trail = sym;
+ crc = expand_and_crc_sym(sym, 0xffffffff) ^ 0xffffffff;
+
+ sym = expansion_trail;
+ while (sym != (struct symbol *)-1L) {
+ struct symbol *n = sym->expansion_trail;
+
+ if (sym->status != STATUS_UNCHANGED) {
+ if (!has_changed) {
+ print_location();
+ fprintf(stderr,
+ "%s: %s: modversion changed because of changes in ",
+ flag_preserve ? "error" : "warning",
+ name);
+ } else {
+ fprintf(stderr, ", ");
}
- sym->expansion_trail = 0;
- sym = n;
+ print_type_name(sym->type, sym->name);
+ if (sym->status == STATUS_DEFINED)
+ fprintf(stderr, " (became defined)");
+ has_changed = 1;
+ if (flag_preserve)
+ errors++;
}
- if (has_changed)
- fprintf(stderr, "\n");
+ sym->expansion_trail = 0;
+ sym = n;
+ }
+ if (has_changed)
+ fprintf(stderr, "\n");
- if (flag_dump_defs)
- fputs(">\n", debugfile);
+ if (flag_dump_defs)
+ fputs(">\n", debugfile);
- printf("#SYMVER %s 0x%08lx\n", name, crc);
- }
+ printf("#SYMVER %s 0x%08lx\n", name, (unsigned long)crc);
}
/*----------------------------------------------------------------------*/
@@ -831,9 +825,9 @@ int main(int argc, char **argv)
}
if (flag_debug) {
- fprintf(debugfile, "Hash table occupancy %d/%d = %g\n",
- nsyms, HASH_BUCKETS,
- (double)nsyms / (double)HASH_BUCKETS);
+ fprintf(debugfile, "Hash table occupancy %d/%zd = %g\n",
+ nsyms, HASH_SIZE(symbol_hashtable),
+ (double)nsyms / HASH_SIZE(symbol_hashtable));
}
if (dumpfile)
diff --git a/scripts/genksyms/genksyms.h b/scripts/genksyms/genksyms.h
index 21ed2ec2d98c..0c355075f0e6 100644
--- a/scripts/genksyms/genksyms.h
+++ b/scripts/genksyms/genksyms.h
@@ -12,8 +12,11 @@
#ifndef MODUTILS_GENKSYMS_H
#define MODUTILS_GENKSYMS_H 1
+#include <stdbool.h>
#include <stdio.h>
+#include <list_types.h>
+
enum symbol_type {
SYM_NORMAL, SYM_TYPEDEF, SYM_ENUM, SYM_STRUCT, SYM_UNION,
SYM_ENUM_CONST
@@ -31,8 +34,8 @@ struct string_list {
};
struct symbol {
- struct symbol *hash_next;
- const char *name;
+ struct hlist_node hnode;
+ char *name;
enum symbol_type type;
struct string_list *defn;
struct symbol *expansion_trail;
@@ -64,6 +67,8 @@ struct string_list *copy_list_range(struct string_list *start,
int yylex(void);
int yyparse(void);
+extern bool dont_want_type_specifier;
+
void error_with_pos(const char *, ...) __attribute__ ((format(printf, 1, 2)));
/*----------------------------------------------------------------------*/
diff --git a/scripts/genksyms/lex.l b/scripts/genksyms/lex.l
index a4d7495eaf75..f81033af1528 100644
--- a/scripts/genksyms/lex.l
+++ b/scripts/genksyms/lex.l
@@ -12,6 +12,7 @@
%{
#include <limits.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
@@ -50,6 +51,7 @@ MC_TOKEN ([~%^&*+=|<>/-]=)|(&&)|("||")|(->)|(<<)|(>>)
%%
+u?int(8|16|32|64)x(1|2|4|8|16)_t return BUILTIN_INT_KEYW;
/* Keep track of our location in the original source files. */
^#[ \t]+{INT}[ \t]+\"[^\"\n]+\".*\n return FILENAME;
@@ -113,6 +115,12 @@ MC_TOKEN ([~%^&*+=|<>/-]=)|(&&)|("||")|(->)|(<<)|(>>)
/* The second stage lexer. Here we incorporate knowledge of the state
of the parser to tailor the tokens that are returned. */
+/*
+ * The lexer cannot distinguish whether a typedef'ed string is a TYPE or an
+ * IDENT. We need a hint from the parser to handle this accurately.
+ */
+bool dont_want_type_specifier;
+
int
yylex(void)
{
@@ -168,10 +176,10 @@ repeat:
switch (lexstate)
{
case ST_NORMAL:
+ APP;
switch (token)
{
case IDENT:
- APP;
{
int r = is_reserved_word(yytext, yyleng);
if (r >= 0)
@@ -207,7 +215,7 @@ repeat:
goto repeat;
}
}
- if (!suppress_type_lookup)
+ if (!suppress_type_lookup && !dont_want_type_specifier)
{
if (find_symbol(yytext, SYM_TYPEDEF, 1))
token = TYPE;
@@ -216,13 +224,11 @@ repeat:
break;
case '[':
- APP;
lexstate = ST_BRACKET;
count = 1;
goto repeat;
case '{':
- APP;
if (dont_want_brace_phrase)
break;
lexstate = ST_BRACE;
@@ -230,12 +236,10 @@ repeat:
goto repeat;
case '=': case ':':
- APP;
lexstate = ST_EXPRESSION;
break;
default:
- APP;
break;
}
break;
@@ -431,7 +435,12 @@ fini:
if (suppress_type_lookup > 0)
--suppress_type_lookup;
- if (dont_want_brace_phrase > 0)
+
+ /*
+ * __attribute__() can be placed immediately after the 'struct' keyword.
+ * e.g.) struct __attribute__((__packed__)) foo { ... };
+ */
+ if (token != ATTRIBUTE_PHRASE && dont_want_brace_phrase > 0)
--dont_want_brace_phrase;
yylval = &next_node->next;
diff --git a/scripts/genksyms/parse.y b/scripts/genksyms/parse.y
index 8e9b5e69e8f0..ee600a804fa1 100644
--- a/scripts/genksyms/parse.y
+++ b/scripts/genksyms/parse.y
@@ -12,6 +12,7 @@
%{
#include <assert.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "genksyms.h"
@@ -148,32 +149,45 @@ simple_declaration:
current_name = NULL;
}
$$ = $3;
+ dont_want_type_specifier = false;
}
;
init_declarator_list_opt:
- /* empty */ { $$ = NULL; }
- | init_declarator_list
+ /* empty */ { $$ = NULL; }
+ | init_declarator_list { free_list(decl_spec, NULL); $$ = $1; }
;
init_declarator_list:
init_declarator
{ struct string_list *decl = *$1;
*$1 = NULL;
+
+ /* avoid sharing among multiple init_declarators */
+ if (decl_spec)
+ decl_spec = copy_list_range(decl_spec, NULL);
+
add_symbol(current_name,
is_typedef ? SYM_TYPEDEF : SYM_NORMAL, decl, is_extern);
current_name = NULL;
$$ = $1;
+ dont_want_type_specifier = true;
}
- | init_declarator_list ',' init_declarator
- { struct string_list *decl = *$3;
- *$3 = NULL;
+ | init_declarator_list ',' attribute_opt init_declarator
+ { struct string_list *decl = *$4;
+ *$4 = NULL;
free_list(*$2, NULL);
*$2 = decl_spec;
+
+ /* avoid sharing among multiple init_declarators */
+ if (decl_spec)
+ decl_spec = copy_list_range(decl_spec, NULL);
+
add_symbol(current_name,
is_typedef ? SYM_TYPEDEF : SYM_NORMAL, decl, is_extern);
current_name = NULL;
- $$ = $3;
+ $$ = $4;
+ dont_want_type_specifier = true;
}
;
@@ -189,8 +203,9 @@ decl_specifier_seq_opt:
;
decl_specifier_seq:
- decl_specifier { decl_spec = *$1; }
+ attribute_opt decl_specifier { decl_spec = *$2; }
| decl_specifier_seq decl_specifier { decl_spec = *$2; }
+ | decl_specifier_seq ATTRIBUTE_PHRASE { decl_spec = *$2; }
;
decl_specifier:
@@ -200,7 +215,8 @@ decl_specifier:
remove_node($1);
$$ = $1;
}
- | type_specifier
+ | type_specifier { dont_want_type_specifier = true; $$ = $1; }
+ | type_qualifier
;
storage_class_specifier:
@@ -213,24 +229,23 @@ storage_class_specifier:
type_specifier:
simple_type_specifier
- | cvar_qualifier
| TYPEOF_KEYW '(' parameter_declaration ')'
| TYPEOF_PHRASE
/* References to s/u/e's defined elsewhere. Rearrange things
so that it is easier to expand the definition fully later. */
- | STRUCT_KEYW IDENT
- { remove_node($1); (*$2)->tag = SYM_STRUCT; $$ = $2; }
- | UNION_KEYW IDENT
- { remove_node($1); (*$2)->tag = SYM_UNION; $$ = $2; }
+ | STRUCT_KEYW attribute_opt IDENT
+ { remove_node($1); (*$3)->tag = SYM_STRUCT; $$ = $3; }
+ | UNION_KEYW attribute_opt IDENT
+ { remove_node($1); (*$3)->tag = SYM_UNION; $$ = $3; }
| ENUM_KEYW IDENT
{ remove_node($1); (*$2)->tag = SYM_ENUM; $$ = $2; }
/* Full definitions of an s/u/e. Record it. */
- | STRUCT_KEYW IDENT class_body
- { record_compound($1, $2, $3, SYM_STRUCT); $$ = $3; }
- | UNION_KEYW IDENT class_body
- { record_compound($1, $2, $3, SYM_UNION); $$ = $3; }
+ | STRUCT_KEYW attribute_opt IDENT class_body
+ { record_compound($1, $3, $4, SYM_STRUCT); $$ = $4; }
+ | UNION_KEYW attribute_opt IDENT class_body
+ { record_compound($1, $3, $4, SYM_UNION); $$ = $4; }
| ENUM_KEYW IDENT enum_body
{ record_compound($1, $2, $3, SYM_ENUM); $$ = $3; }
/*
@@ -239,8 +254,8 @@ type_specifier:
| ENUM_KEYW enum_body
{ add_symbol(NULL, SYM_ENUM, NULL, 0); $$ = $2; }
/* Anonymous s/u definitions. Nothing needs doing. */
- | STRUCT_KEYW class_body { $$ = $2; }
- | UNION_KEYW class_body { $$ = $2; }
+ | STRUCT_KEYW attribute_opt class_body { $$ = $3; }
+ | UNION_KEYW attribute_opt class_body { $$ = $3; }
;
simple_type_specifier:
@@ -260,22 +275,24 @@ simple_type_specifier:
;
ptr_operator:
- '*' cvar_qualifier_seq_opt
+ '*' type_qualifier_seq_opt
{ $$ = $2 ? $2 : $1; }
;
-cvar_qualifier_seq_opt:
+type_qualifier_seq_opt:
/* empty */ { $$ = NULL; }
- | cvar_qualifier_seq
+ | type_qualifier_seq
;
-cvar_qualifier_seq:
- cvar_qualifier
- | cvar_qualifier_seq cvar_qualifier { $$ = $2; }
+type_qualifier_seq:
+ type_qualifier
+ | ATTRIBUTE_PHRASE
+ | type_qualifier_seq type_qualifier { $$ = $2; }
+ | type_qualifier_seq ATTRIBUTE_PHRASE { $$ = $2; }
;
-cvar_qualifier:
- CONST_KEYW | VOLATILE_KEYW | ATTRIBUTE_PHRASE
+type_qualifier:
+ CONST_KEYW | VOLATILE_KEYW
| RESTRICT_KEYW
{ /* restrict has no effect in prototypes so ignore it */
remove_node($1);
@@ -297,15 +314,7 @@ direct_declarator:
current_name = (*$1)->string;
$$ = $1;
}
- }
- | TYPE
- { if (current_name != NULL) {
- error_with_pos("unexpected second declaration name");
- YYERROR;
- } else {
- current_name = (*$1)->string;
- $$ = $1;
- }
+ dont_want_type_specifier = false;
}
| direct_declarator '(' parameter_declaration_clause ')'
{ $$ = $4; }
@@ -325,16 +334,19 @@ nested_declarator:
;
direct_nested_declarator:
- IDENT
- | TYPE
- | direct_nested_declarator '(' parameter_declaration_clause ')'
+ direct_nested_declarator1
+ | direct_nested_declarator1 '(' parameter_declaration_clause ')'
{ $$ = $4; }
- | direct_nested_declarator '(' error ')'
+ ;
+
+direct_nested_declarator1:
+ IDENT { $$ = $1; dont_want_type_specifier = false; }
+ | direct_nested_declarator1 '(' error ')'
{ $$ = $4; }
- | direct_nested_declarator BRACKET_PHRASE
+ | direct_nested_declarator1 BRACKET_PHRASE
{ $$ = $2; }
- | '(' nested_declarator ')'
- { $$ = $3; }
+ | '(' attribute_opt nested_declarator ')'
+ { $$ = $4; }
| '(' error ')'
{ $$ = $3; }
;
@@ -352,45 +364,57 @@ parameter_declaration_list_opt:
parameter_declaration_list:
parameter_declaration
+ { $$ = $1; dont_want_type_specifier = false; }
| parameter_declaration_list ',' parameter_declaration
- { $$ = $3; }
+ { $$ = $3; dont_want_type_specifier = false; }
;
parameter_declaration:
- decl_specifier_seq m_abstract_declarator
+ decl_specifier_seq abstract_declarator_opt
{ $$ = $2 ? $2 : $1; }
;
-m_abstract_declarator:
- ptr_operator m_abstract_declarator
+abstract_declarator_opt:
+ /* empty */ { $$ = NULL; }
+ | abstract_declarator
+ ;
+
+abstract_declarator:
+ ptr_operator
+ | ptr_operator abstract_declarator
{ $$ = $2 ? $2 : $1; }
- | direct_m_abstract_declarator
+ | direct_abstract_declarator attribute_opt
+ { $$ = $2; dont_want_type_specifier = false; }
;
-direct_m_abstract_declarator:
- /* empty */ { $$ = NULL; }
- | IDENT
+direct_abstract_declarator:
+ direct_abstract_declarator1
+ | direct_abstract_declarator1 open_paren parameter_declaration_clause ')'
+ { $$ = $4; }
+ | open_paren parameter_declaration_clause ')'
+ { $$ = $3; }
+ ;
+
+direct_abstract_declarator1:
+ IDENT
{ /* For version 2 checksums, we don't want to remember
private parameter names. */
remove_node($1);
$$ = $1;
}
- /* This wasn't really a typedef name but an identifier that
- shadows one. */
- | TYPE
- { remove_node($1);
- $$ = $1;
- }
- | direct_m_abstract_declarator '(' parameter_declaration_clause ')'
- { $$ = $4; }
- | direct_m_abstract_declarator '(' error ')'
+ | direct_abstract_declarator1 open_paren error ')'
{ $$ = $4; }
- | direct_m_abstract_declarator BRACKET_PHRASE
+ | direct_abstract_declarator1 BRACKET_PHRASE
{ $$ = $2; }
- | '(' m_abstract_declarator ')'
- { $$ = $3; }
- | '(' error ')'
+ | open_paren attribute_opt abstract_declarator ')'
+ { $$ = $4; }
+ | open_paren error ')'
{ $$ = $3; }
+ | BRACKET_PHRASE
+ ;
+
+open_paren:
+ '(' { $$ = $1; dont_want_type_specifier = false; }
;
function_definition:
@@ -430,9 +454,9 @@ member_specification:
member_declaration:
decl_specifier_seq_opt member_declarator_list_opt ';'
- { $$ = $3; }
+ { $$ = $3; dont_want_type_specifier = false; }
| error ';'
- { $$ = $2; }
+ { $$ = $2; dont_want_type_specifier = false; }
;
member_declarator_list_opt:
@@ -442,7 +466,9 @@ member_declarator_list_opt:
member_declarator_list:
member_declarator
- | member_declarator_list ',' member_declarator { $$ = $3; }
+ { $$ = $1; dont_want_type_specifier = true; }
+ | member_declarator_list ',' member_declarator
+ { $$ = $3; dont_want_type_specifier = true; }
;
member_declarator:
@@ -457,7 +483,7 @@ member_bitfield_declarator:
attribute_opt:
/* empty */ { $$ = NULL; }
- | attribute_opt ATTRIBUTE_PHRASE
+ | attribute_opt ATTRIBUTE_PHRASE { $$ = $2; }
;
enum_body:
@@ -472,12 +498,12 @@ enumerator_list:
enumerator:
IDENT
{
- const char *name = strdup((*$1)->string);
+ const char *name = (*$1)->string;
add_symbol(name, SYM_ENUM_CONST, NULL, 0);
}
| IDENT '=' EXPRESSION_PHRASE
{
- const char *name = strdup((*$1)->string);
+ const char *name = (*$1)->string;
struct string_list *expr = copy_list_range(*$3, *$2);
add_symbol(name, SYM_ENUM_CONST, expr, 0);
}
diff --git a/scripts/get_abi.pl b/scripts/get_abi.pl
deleted file mode 100755
index de1c0354b50c..000000000000
--- a/scripts/get_abi.pl
+++ /dev/null
@@ -1,1103 +0,0 @@
-#!/usr/bin/env perl
-# SPDX-License-Identifier: GPL-2.0
-
-BEGIN { $Pod::Usage::Formatter = 'Pod::Text::Termcap'; }
-
-use strict;
-use warnings;
-use utf8;
-use Pod::Usage qw(pod2usage);
-use Getopt::Long;
-use File::Find;
-use IO::Handle;
-use Fcntl ':mode';
-use Cwd 'abs_path';
-use Data::Dumper;
-
-my $help = 0;
-my $hint = 0;
-my $man = 0;
-my $debug = 0;
-my $enable_lineno = 0;
-my $show_warnings = 1;
-my $prefix="Documentation/ABI";
-my $sysfs_prefix="/sys";
-my $search_string;
-
-# Debug options
-my $dbg_what_parsing = 1;
-my $dbg_what_open = 2;
-my $dbg_dump_abi_structs = 4;
-my $dbg_undefined = 8;
-
-$Data::Dumper::Indent = 1;
-$Data::Dumper::Terse = 1;
-
-#
-# If true, assumes that the description is formatted with ReST
-#
-my $description_is_rst = 1;
-
-GetOptions(
- "debug=i" => \$debug,
- "enable-lineno" => \$enable_lineno,
- "rst-source!" => \$description_is_rst,
- "dir=s" => \$prefix,
- 'help|?' => \$help,
- "show-hints" => \$hint,
- "search-string=s" => \$search_string,
- man => \$man
-) or pod2usage(2);
-
-pod2usage(1) if $help;
-pod2usage(-exitstatus => 0, -noperldoc, -verbose => 2) if $man;
-
-pod2usage(2) if (scalar @ARGV < 1 || @ARGV > 2);
-
-my ($cmd, $arg) = @ARGV;
-
-pod2usage(2) if ($cmd ne "search" && $cmd ne "rest" && $cmd ne "validate" && $cmd ne "undefined");
-pod2usage(2) if ($cmd eq "search" && !$arg);
-
-require Data::Dumper if ($debug & $dbg_dump_abi_structs);
-
-my %data;
-my %symbols;
-
-#
-# Displays an error message, printing file name and line
-#
-sub parse_error($$$$) {
- my ($file, $ln, $msg, $data) = @_;
-
- return if (!$show_warnings);
-
- $data =~ s/\s+$/\n/;
-
- print STDERR "Warning: file $file#$ln:\n\t$msg";
-
- if ($data ne "") {
- print STDERR ". Line\n\t\t$data";
- } else {
- print STDERR "\n";
- }
-}
-
-#
-# Parse an ABI file, storing its contents at %data
-#
-sub parse_abi {
- my $file = $File::Find::name;
-
- my $mode = (stat($file))[2];
- return if ($mode & S_IFDIR);
- return if ($file =~ m,/README,);
- return if ($file =~ m,/\.,);
- return if ($file =~ m,\.(rej|org|orig|bak)$,);
-
- my $name = $file;
- $name =~ s,.*/,,;
-
- my $fn = $file;
- $fn =~ s,.*Documentation/ABI/,,;
-
- my $nametag = "File $fn";
- $data{$nametag}->{what} = "File $name";
- $data{$nametag}->{type} = "File";
- $data{$nametag}->{file} = $name;
- $data{$nametag}->{filepath} = $file;
- $data{$nametag}->{is_file} = 1;
- $data{$nametag}->{line_no} = 1;
-
- my $type = $file;
- $type =~ s,.*/(.*)/.*,$1,;
-
- my $what;
- my $new_what;
- my $tag = "";
- my $ln;
- my $xrefs;
- my $space;
- my @labels;
- my $label = "";
-
- print STDERR "Opening $file\n" if ($debug & $dbg_what_open);
- open IN, $file;
- while(<IN>) {
- $ln++;
- if (m/^(\S+)(:\s*)(.*)/i) {
- my $new_tag = lc($1);
- my $sep = $2;
- my $content = $3;
-
- if (!($new_tag =~ m/(what|where|date|kernelversion|contact|description|users)/)) {
- if ($tag eq "description") {
- # New "tag" is actually part of
- # description. Don't consider it a tag
- $new_tag = "";
- } elsif ($tag ne "") {
- parse_error($file, $ln, "tag '$tag' is invalid", $_);
- }
- }
-
- # Invalid, but it is a common mistake
- if ($new_tag eq "where") {
- parse_error($file, $ln, "tag 'Where' is invalid. Should be 'What:' instead", "");
- $new_tag = "what";
- }
-
- if ($new_tag =~ m/what/) {
- $space = "";
- $content =~ s/[,.;]$//;
-
- push @{$symbols{$content}->{file}}, " $file:" . ($ln - 1);
-
- if ($tag =~ m/what/) {
- $what .= "\xac" . $content;
- } else {
- if ($what) {
- parse_error($file, $ln, "What '$what' doesn't have a description", "") if (!$data{$what}->{description});
-
- foreach my $w(split /\xac/, $what) {
- $symbols{$w}->{xref} = $what;
- };
- }
-
- $what = $content;
- $label = $content;
- $new_what = 1;
- }
- push @labels, [($content, $label)];
- $tag = $new_tag;
-
- push @{$data{$nametag}->{symbols}}, $content if ($data{$nametag}->{what});
- next;
- }
-
- if ($tag ne "" && $new_tag) {
- $tag = $new_tag;
-
- if ($new_what) {
- @{$data{$what}->{label_list}} = @labels if ($data{$nametag}->{what});
- @labels = ();
- $label = "";
- $new_what = 0;
-
- $data{$what}->{type} = $type;
- if (!defined($data{$what}->{file})) {
- $data{$what}->{file} = $name;
- $data{$what}->{filepath} = $file;
- } else {
- $data{$what}->{description} .= "\n\n" if (defined($data{$what}->{description}));
- if ($name ne $data{$what}->{file}) {
- $data{$what}->{file} .= " " . $name;
- $data{$what}->{filepath} .= " " . $file;
- }
- }
- print STDERR "\twhat: $what\n" if ($debug & $dbg_what_parsing);
- $data{$what}->{line_no} = $ln;
- } else {
- $data{$what}->{line_no} = $ln if (!defined($data{$what}->{line_no}));
- }
-
- if (!$what) {
- parse_error($file, $ln, "'What:' should come first:", $_);
- next;
- }
- if ($new_tag eq "description") {
- $sep =~ s,:, ,;
- $content = ' ' x length($new_tag) . $sep . $content;
- while ($content =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {}
- if ($content =~ m/^(\s*)(\S.*)$/) {
- # Preserve initial spaces for the first line
- $space = $1;
- $content = "$2\n";
- $data{$what}->{$tag} .= $content;
- } else {
- undef($space);
- }
-
- } else {
- $data{$what}->{$tag} = $content;
- }
- next;
- }
- }
-
- # Store any contents before tags at the database
- if (!$tag && $data{$nametag}->{what}) {
- $data{$nametag}->{description} .= $_;
- next;
- }
-
- if ($tag eq "description") {
- my $content = $_;
- while ($content =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {}
- if (m/^\s*\n/) {
- $data{$what}->{$tag} .= "\n";
- next;
- }
-
- if (!defined($space)) {
- # Preserve initial spaces for the first line
- if ($content =~ m/^(\s*)(\S.*)$/) {
- $space = $1;
- $content = "$2\n";
- }
- } else {
- $space = "" if (!($content =~ s/^($space)//));
- }
- $data{$what}->{$tag} .= $content;
-
- next;
- }
- if (m/^\s*(.*)/) {
- $data{$what}->{$tag} .= "\n$1";
- $data{$what}->{$tag} =~ s/\n+$//;
- next;
- }
-
- # Everything else is error
- parse_error($file, $ln, "Unexpected content", $_);
- }
- $data{$nametag}->{description} =~ s/^\n+// if ($data{$nametag}->{description});
- if ($what) {
- parse_error($file, $ln, "What '$what' doesn't have a description", "") if (!$data{$what}->{description});
-
- foreach my $w(split /\xac/,$what) {
- $symbols{$w}->{xref} = $what;
- };
- }
- close IN;
-}
-
-sub create_labels {
- my %labels;
-
- foreach my $what (keys %data) {
- next if ($data{$what}->{file} eq "File");
-
- foreach my $p (@{$data{$what}->{label_list}}) {
- my ($content, $label) = @{$p};
- $label = "abi_" . $label . " ";
- $label =~ tr/A-Z/a-z/;
-
- # Convert special chars to "_"
- $label =~s/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\xff])/_/g;
- $label =~ s,_+,_,g;
- $label =~ s,_$,,;
-
- # Avoid duplicated labels
- while (defined($labels{$label})) {
- my @chars = ("A".."Z", "a".."z");
- $label .= $chars[rand @chars];
- }
- $labels{$label} = 1;
-
- $data{$what}->{label} = $label;
-
- # only one label is enough
- last;
- }
- }
-}
-
-#
-# Outputs the book on ReST format
-#
-
-# \b doesn't work well with paths. So, we need to define something else:
-# Boundaries are punct characters, spaces and end-of-line
-my $start = qr {(^|\s|\() }x;
-my $bondary = qr { ([,.:;\)\s]|\z) }x;
-my $xref_match = qr { $start(\/(sys|config|proc|dev|kvd)\/[^,.:;\)\s]+)$bondary }x;
-my $symbols = qr { ([\x01-\x08\x0e-\x1f\x21-\x2f\x3a-\x40\x7b-\xff]) }x;
-
-sub output_rest {
- create_labels();
-
- my $part = "";
-
- foreach my $what (sort {
- ($data{$a}->{type} eq "File") cmp ($data{$b}->{type} eq "File") ||
- $a cmp $b
- } keys %data) {
- my $type = $data{$what}->{type};
-
- my @file = split / /, $data{$what}->{file};
- my @filepath = split / /, $data{$what}->{filepath};
-
- if ($enable_lineno) {
- printf ".. LINENO %s%s#%s\n\n",
- $prefix, $file[0],
- $data{$what}->{line_no};
- }
-
- my $w = $what;
-
- if ($type ne "File") {
- my $cur_part = $what;
- if ($what =~ '/') {
- if ($what =~ m#^(\/?(?:[\w\-]+\/?){1,2})#) {
- $cur_part = "Symbols under $1";
- $cur_part =~ s,/$,,;
- }
- }
-
- if ($cur_part ne "" && $part ne $cur_part) {
- $part = $cur_part;
- my $bar = $part;
- $bar =~ s/./-/g;
- print "$part\n$bar\n\n";
- }
-
- printf ".. _%s:\n\n", $data{$what}->{label};
-
- my @names = split /\xac/,$w;
- my $len = 0;
-
- foreach my $name (@names) {
- $name =~ s/$symbols/\\$1/g;
- $name = "**$name**";
- $len = length($name) if (length($name) > $len);
- }
-
- print "+-" . "-" x $len . "-+\n";
- foreach my $name (@names) {
- printf "| %s", $name . " " x ($len - length($name)) . " |\n";
- print "+-" . "-" x $len . "-+\n";
- }
-
- print "\n";
- }
-
- for (my $i = 0; $i < scalar(@filepath); $i++) {
- my $path = $filepath[$i];
- my $f = $file[$i];
-
- $path =~ s,.*/(.*/.*),$1,;;
- $path =~ s,[/\-],_,g;;
- my $fileref = "abi_file_".$path;
-
- if ($type eq "File") {
- print ".. _$fileref:\n\n";
- } else {
- print "Defined on file :ref:`$f <$fileref>`\n\n";
- }
- }
-
- if ($type eq "File") {
- my $bar = $w;
- $bar =~ s/./-/g;
- print "$w\n$bar\n\n";
- }
-
- my $desc = "";
- $desc = $data{$what}->{description} if (defined($data{$what}->{description}));
- $desc =~ s/\s+$/\n/;
-
- if (!($desc =~ /^\s*$/)) {
- if ($description_is_rst) {
- # Remove title markups from the description
- # Having titles inside ABI files will only work if extra
- # care would be taken in order to strictly follow the same
- # level order for each markup.
- $desc =~ s/\n[\-\*\=\^\~]+\n/\n\n/g;
-
- # Enrich text by creating cross-references
-
- my $new_desc = "";
- my $init_indent = -1;
- my $literal_indent = -1;
-
- open(my $fh, "+<", \$desc);
- while (my $d = <$fh>) {
- my $indent = $d =~ m/^(\s+)/;
- my $spaces = length($indent);
- $init_indent = $indent if ($init_indent < 0);
- if ($literal_indent >= 0) {
- if ($spaces > $literal_indent) {
- $new_desc .= $d;
- next;
- } else {
- $literal_indent = -1;
- }
- } else {
- if ($d =~ /()::$/ && !($d =~ /^\s*\.\./)) {
- $literal_indent = $spaces;
- }
- }
-
- $d =~ s,Documentation/(?!devicetree)(\S+)\.rst,:doc:`/$1`,g;
-
- my @matches = $d =~ m,Documentation/ABI/([\w\/\-]+),g;
- foreach my $f (@matches) {
- my $xref = $f;
- my $path = $f;
- $path =~ s,.*/(.*/.*),$1,;;
- $path =~ s,[/\-],_,g;;
- $xref .= " <abi_file_" . $path . ">";
- $d =~ s,\bDocumentation/ABI/$f\b,:ref:`$xref`,g;
- }
-
- # Seek for cross reference symbols like /sys/...
- @matches = $d =~ m/$xref_match/g;
-
- foreach my $s (@matches) {
- next if (!($s =~ m,/,));
- if (defined($data{$s}) && defined($data{$s}->{label})) {
- my $xref = $s;
-
- $xref =~ s/$symbols/\\$1/g;
- $xref = ":ref:`$xref <" . $data{$s}->{label} . ">`";
-
- $d =~ s,$start$s$bondary,$1$xref$2,g;
- }
- }
- $new_desc .= $d;
- }
- close $fh;
-
-
- print "$new_desc\n\n";
- } else {
- $desc =~ s/^\s+//;
-
- # Remove title markups from the description, as they won't work
- $desc =~ s/\n[\-\*\=\^\~]+\n/\n\n/g;
-
- if ($desc =~ m/\:\n/ || $desc =~ m/\n[\t ]+/ || $desc =~ m/[\x00-\x08\x0b-\x1f\x7b-\xff]/) {
- # put everything inside a code block
- $desc =~ s/\n/\n /g;
-
- print "::\n\n";
- print " $desc\n\n";
- } else {
- # Escape any special chars from description
- $desc =~s/([\x00-\x08\x0b-\x1f\x21-\x2a\x2d\x2f\x3c-\x40\x5c\x5e-\x60\x7b-\xff])/\\$1/g;
- print "$desc\n\n";
- }
- }
- } else {
- print "DESCRIPTION MISSING for $what\n\n" if (!$data{$what}->{is_file});
- }
-
- if ($data{$what}->{symbols}) {
- printf "Has the following ABI:\n\n";
-
- foreach my $content(@{$data{$what}->{symbols}}) {
- my $label = $data{$symbols{$content}->{xref}}->{label};
-
- # Escape special chars from content
- $content =~s/([\x00-\x1f\x21-\x2f\x3a-\x40\x7b-\xff])/\\$1/g;
-
- print "- :ref:`$content <$label>`\n\n";
- }
- }
-
- if (defined($data{$what}->{users})) {
- my $users = $data{$what}->{users};
-
- $users =~ s/\n/\n\t/g;
- printf "Users:\n\t%s\n\n", $users if ($users ne "");
- }
-
- }
-}
-
-#
-# Searches for ABI symbols
-#
-sub search_symbols {
- foreach my $what (sort keys %data) {
- next if (!($what =~ m/($arg)/));
-
- my $type = $data{$what}->{type};
- next if ($type eq "File");
-
- my $file = $data{$what}->{filepath};
-
- $what =~ s/\xac/, /g;
- my $bar = $what;
- $bar =~ s/./-/g;
-
- print "\n$what\n$bar\n\n";
-
- my $kernelversion = $data{$what}->{kernelversion} if (defined($data{$what}->{kernelversion}));
- my $contact = $data{$what}->{contact} if (defined($data{$what}->{contact}));
- my $users = $data{$what}->{users} if (defined($data{$what}->{users}));
- my $date = $data{$what}->{date} if (defined($data{$what}->{date}));
- my $desc = $data{$what}->{description} if (defined($data{$what}->{description}));
-
- $kernelversion =~ s/^\s+// if ($kernelversion);
- $contact =~ s/^\s+// if ($contact);
- if ($users) {
- $users =~ s/^\s+//;
- $users =~ s/\n//g;
- }
- $date =~ s/^\s+// if ($date);
- $desc =~ s/^\s+// if ($desc);
-
- printf "Kernel version:\t\t%s\n", $kernelversion if ($kernelversion);
- printf "Date:\t\t\t%s\n", $date if ($date);
- printf "Contact:\t\t%s\n", $contact if ($contact);
- printf "Users:\t\t\t%s\n", $users if ($users);
- print "Defined on file(s):\t$file\n\n";
- print "Description:\n\n$desc";
- }
-}
-
-# Exclude /sys/kernel/debug and /sys/kernel/tracing from the search path
-sub dont_parse_special_attributes {
- if (($File::Find::dir =~ m,^/sys/kernel,)) {
- return grep {!/(debug|tracing)/ } @_;
- }
-
- if (($File::Find::dir =~ m,^/sys/fs,)) {
- return grep {!/(pstore|bpf|fuse)/ } @_;
- }
-
- return @_
-}
-
-my %leaf;
-my %aliases;
-my @files;
-my %root;
-
-sub graph_add_file {
- my $file = shift;
- my $type = shift;
-
- my $dir = $file;
- $dir =~ s,^(.*/).*,$1,;
- $file =~ s,.*/,,;
-
- my $name;
- my $file_ref = \%root;
- foreach my $edge(split "/", $dir) {
- $name .= "$edge/";
- if (!defined ${$file_ref}{$edge}) {
- ${$file_ref}{$edge} = { };
- }
- $file_ref = \%{$$file_ref{$edge}};
- ${$file_ref}{"__name"} = [ $name ];
- }
- $name .= "$file";
- ${$file_ref}{$file} = {
- "__name" => [ $name ]
- };
-
- return \%{$$file_ref{$file}};
-}
-
-sub graph_add_link {
- my $file = shift;
- my $link = shift;
-
- # Traverse graph to find the reference
- my $file_ref = \%root;
- foreach my $edge(split "/", $file) {
- $file_ref = \%{$$file_ref{$edge}} || die "Missing node!";
- }
-
- # do a BFS
-
- my @queue;
- my %seen;
- my $st;
-
- push @queue, $file_ref;
- $seen{$start}++;
-
- while (@queue) {
- my $v = shift @queue;
- my @child = keys(%{$v});
-
- foreach my $c(@child) {
- next if $seen{$$v{$c}};
- next if ($c eq "__name");
-
- if (!defined($$v{$c}{"__name"})) {
- printf STDERR "Error: Couldn't find a non-empty name on a children of $file/.*: ";
- print STDERR Dumper(%{$v});
- exit;
- }
-
- # Add new name
- my $name = @{$$v{$c}{"__name"}}[0];
- if ($name =~ s#^$file/#$link/#) {
- push @{$$v{$c}{"__name"}}, $name;
- }
- # Add child to the queue and mark as seen
- push @queue, $$v{$c};
- $seen{$c}++;
- }
- }
-}
-
-my $escape_symbols = qr { ([\x01-\x08\x0e-\x1f\x21-\x29\x2b-\x2d\x3a-\x40\x7b-\xfe]) }x;
-sub parse_existing_sysfs {
- my $file = $File::Find::name;
-
- my $mode = (lstat($file))[2];
- my $abs_file = abs_path($file);
-
- my @tmp;
- push @tmp, $file;
- push @tmp, $abs_file if ($abs_file ne $file);
-
- foreach my $f(@tmp) {
- # Ignore cgroup, as this is big and has zero docs under ABI
- return if ($f =~ m#^/sys/fs/cgroup/#);
-
- # Ignore firmware as it is documented elsewhere
- # Either ACPI or under Documentation/devicetree/bindings/
- return if ($f =~ m#^/sys/firmware/#);
-
- # Ignore some sysfs nodes that aren't actually part of ABI
- return if ($f =~ m#/sections|notes/#);
-
- # Would need to check at
- # Documentation/admin-guide/kernel-parameters.txt, but this
- # is not easily parseable.
- return if ($f =~ m#/parameters/#);
- }
-
- if (S_ISLNK($mode)) {
- $aliases{$file} = $abs_file;
- return;
- }
-
- return if (S_ISDIR($mode));
-
- # Trivial: file is defined exactly the same way at ABI What:
- return if (defined($data{$file}));
- return if (defined($data{$abs_file}));
-
- push @files, graph_add_file($abs_file, "file");
-}
-
-sub get_leave($)
-{
- my $what = shift;
- my $leave;
-
- my $l = $what;
- my $stop = 1;
-
- $leave = $l;
- $leave =~ s,/$,,;
- $leave =~ s,.*/,,;
- $leave =~ s/[\(\)]//g;
-
- # $leave is used to improve search performance at
- # check_undefined_symbols, as the algorithm there can seek
- # for a small number of "what". It also allows giving a
- # hint about a leave with the same name somewhere else.
- # However, there are a few occurences where the leave is
- # either a wildcard or a number. Just group such cases
- # altogether.
- if ($leave =~ m/\.\*/ || $leave eq "" || $leave =~ /\\d/) {
- $leave = "others";
- }
-
- return $leave;
-}
-
-my @not_found;
-
-sub check_file($$)
-{
- my $file_ref = shift;
- my $names_ref = shift;
- my @names = @{$names_ref};
- my $file = $names[0];
-
- my $found_string;
-
- my $leave = get_leave($file);
- if (!defined($leaf{$leave})) {
- $leave = "others";
- }
- my @expr = @{$leaf{$leave}->{expr}};
- die ("\rmissing rules for $leave") if (!defined($leaf{$leave}));
-
- my $path = $file;
- $path =~ s,(.*/).*,$1,;
-
- if ($search_string) {
- return if (!($file =~ m#$search_string#));
- $found_string = 1;
- }
-
- for (my $i = 0; $i < @names; $i++) {
- if ($found_string && $hint) {
- if (!$i) {
- print STDERR "--> $names[$i]\n";
- } else {
- print STDERR " $names[$i]\n";
- }
- }
- foreach my $re (@expr) {
- print STDERR "$names[$i] =~ /^$re\$/\n" if ($debug && $dbg_undefined);
- if ($names[$i] =~ $re) {
- return;
- }
- }
- }
-
- if ($leave ne "others") {
- my @expr = @{$leaf{"others"}->{expr}};
- for (my $i = 0; $i < @names; $i++) {
- foreach my $re (@expr) {
- print STDERR "$names[$i] =~ /^$re\$/\n" if ($debug && $dbg_undefined);
- if ($names[$i] =~ $re) {
- return;
- }
- }
- }
- }
-
- push @not_found, $file if (!$search_string || $found_string);
-
- if ($hint && (!$search_string || $found_string)) {
- my $what = $leaf{$leave}->{what};
- $what =~ s/\xac/\n\t/g;
- if ($leave ne "others") {
- print STDERR "\r more likely regexes:\n\t$what\n";
- } else {
- print STDERR "\r tested regexes:\n\t$what\n";
- }
- }
-}
-
-sub check_undefined_symbols {
- my $num_files = scalar @files;
- my $next_i = 0;
- my $start_time = times;
-
- @files = sort @files;
-
- my $last_time = $start_time;
-
- # When either debug or hint is enabled, there's no sense showing
- # progress, as the progress will be overriden.
- if ($hint || ($debug && $dbg_undefined)) {
- $next_i = $num_files;
- }
-
- my $is_console;
- $is_console = 1 if (-t STDERR);
-
- for (my $i = 0; $i < $num_files; $i++) {
- my $file_ref = $files[$i];
- my @names = @{$$file_ref{"__name"}};
-
- check_file($file_ref, \@names);
-
- my $cur_time = times;
-
- if ($i == $next_i || $cur_time > $last_time + 1) {
- my $percent = $i * 100 / $num_files;
-
- my $tm = $cur_time - $start_time;
- my $time = sprintf "%d:%02d", int($tm), 60 * ($tm - int($tm));
-
- printf STDERR "\33[2K\r", if ($is_console);
- printf STDERR "%s: processing sysfs files... %i%%: $names[0]", $time, $percent;
- printf STDERR "\n", if (!$is_console);
- STDERR->flush();
-
- $next_i = int (($percent + 1) * $num_files / 100);
- $last_time = $cur_time;
- }
- }
-
- my $cur_time = times;
- my $tm = $cur_time - $start_time;
- my $time = sprintf "%d:%02d", int($tm), 60 * ($tm - int($tm));
-
- printf STDERR "\33[2K\r", if ($is_console);
- printf STDERR "%s: processing sysfs files... done\n", $time;
-
- foreach my $file (@not_found) {
- print "$file not found.\n";
- }
-}
-
-sub undefined_symbols {
- print STDERR "Reading $sysfs_prefix directory contents...";
- find({
- wanted =>\&parse_existing_sysfs,
- preprocess =>\&dont_parse_special_attributes,
- no_chdir => 1
- }, $sysfs_prefix);
- print STDERR "done.\n";
-
- $leaf{"others"}->{what} = "";
-
- print STDERR "Converting ABI What fields into regexes...";
- foreach my $w (sort keys %data) {
- foreach my $what (split /\xac/,$w) {
- next if (!($what =~ m/^$sysfs_prefix/));
-
- # Convert what into regular expressions
-
- # Escape dot characters
- $what =~ s/\./\xf6/g;
-
- # Temporarily change [0-9]+ type of patterns
- $what =~ s/\[0\-9\]\+/\xff/g;
-
- # Temporarily change [\d+-\d+] type of patterns
- $what =~ s/\[0\-\d+\]/\xff/g;
- $what =~ s/\[(\d+)\]/\xf4$1\xf5/g;
-
- # Temporarily change [0-9] type of patterns
- $what =~ s/\[(\d)\-(\d)\]/\xf4$1-$2\xf5/g;
-
- # Handle multiple option patterns
- $what =~ s/[\{\<\[]([\w_]+)(?:[,|]+([\w_]+)){1,}[\}\>\]]/($1|$2)/g;
-
- # Handle wildcards
- $what =~ s,\*,.*,g;
- $what =~ s,/\xf6..,/.*,g;
- $what =~ s/\<[^\>]+\>/.*/g;
- $what =~ s/\{[^\}]+\}/.*/g;
- $what =~ s/\[[^\]]+\]/.*/g;
-
- $what =~ s/[XYZ]/.*/g;
-
- # Recover [0-9] type of patterns
- $what =~ s/\xf4/[/g;
- $what =~ s/\xf5/]/g;
-
- # Remove duplicated spaces
- $what =~ s/\s+/ /g;
-
- # Special case: this ABI has a parenthesis on it
- $what =~ s/sqrt\(x^2\+y^2\+z^2\)/sqrt\(x^2\+y^2\+z^2\)/;
-
- # Special case: drop comparition as in:
- # What: foo = <something>
- # (this happens on a few IIO definitions)
- $what =~ s,\s*\=.*$,,;
-
- # Escape all other symbols
- $what =~ s/$escape_symbols/\\$1/g;
- $what =~ s/\\\\/\\/g;
- $what =~ s/\\([\[\]\(\)\|])/$1/g;
- $what =~ s/(\d+)\\(-\d+)/$1$2/g;
-
- $what =~ s/\xff/\\d+/g;
-
- # Special case: IIO ABI which a parenthesis.
- $what =~ s/sqrt(.*)/sqrt\(.*\)/;
-
- # Simplify regexes with multiple .*
- $what =~ s#(?:\.\*){2,}##g;
-# $what =~ s#\.\*/\.\*#.*#g;
-
- # Recover dot characters
- $what =~ s/\xf6/\./g;
-
- my $leave = get_leave($what);
-
- my $added = 0;
- foreach my $l (split /\|/, $leave) {
- if (defined($leaf{$l})) {
- next if ($leaf{$l}->{what} =~ m/\b$what\b/);
- $leaf{$l}->{what} .= "\xac" . $what;
- $added = 1;
- } else {
- $leaf{$l}->{what} = $what;
- $added = 1;
- }
- }
- if ($search_string && $added) {
- print STDERR "What: $what\n" if ($what =~ m#$search_string#);
- }
-
- }
- }
- # Compile regexes
- foreach my $l (sort keys %leaf) {
- my @expr;
- foreach my $w(sort split /\xac/, $leaf{$l}->{what}) {
- push @expr, qr /^$w$/;
- }
- $leaf{$l}->{expr} = \@expr;
- }
-
- # Take links into account
- foreach my $link (sort keys %aliases) {
- my $abs_file = $aliases{$link};
- graph_add_link($abs_file, $link);
- }
- print STDERR "done.\n";
-
- check_undefined_symbols;
-}
-
-# Ensure that the prefix will always end with a slash
-# While this is not needed for find, it makes the patch nicer
-# with --enable-lineno
-$prefix =~ s,/?$,/,;
-
-if ($cmd eq "undefined" || $cmd eq "search") {
- $show_warnings = 0;
-}
-#
-# Parses all ABI files located at $prefix dir
-#
-find({wanted =>\&parse_abi, no_chdir => 1}, $prefix);
-
-print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug & $dbg_dump_abi_structs);
-
-#
-# Handles the command
-#
-if ($cmd eq "undefined") {
- undefined_symbols;
-} elsif ($cmd eq "search") {
- search_symbols;
-} else {
- if ($cmd eq "rest") {
- output_rest;
- }
-
- # Warn about duplicated ABI entries
- foreach my $what(sort keys %symbols) {
- my @files = @{$symbols{$what}->{file}};
-
- next if (scalar(@files) == 1);
-
- printf STDERR "Warning: $what is defined %d times: @files\n",
- scalar(@files);
- }
-}
-
-__END__
-
-=head1 NAME
-
-get_abi.pl - parse the Linux ABI files and produce a ReST book.
-
-=head1 SYNOPSIS
-
-B<get_abi.pl> [--debug <level>] [--enable-lineno] [--man] [--help]
- [--(no-)rst-source] [--dir=<dir>] [--show-hints]
- [--search-string <regex>]
- <COMMAND> [<ARGUMENT>]
-
-Where B<COMMAND> can be:
-
-=over 8
-
-B<search> I<SEARCH_REGEX> - search for I<SEARCH_REGEX> inside ABI
-
-B<rest> - output the ABI in ReST markup language
-
-B<validate> - validate the ABI contents
-
-B<undefined> - existing symbols at the system that aren't
- defined at Documentation/ABI
-
-=back
-
-=head1 OPTIONS
-
-=over 8
-
-=item B<--dir>
-
-Changes the location of the ABI search. By default, it uses
-the Documentation/ABI directory.
-
-=item B<--rst-source> and B<--no-rst-source>
-
-The input file may be using ReST syntax or not. Those two options allow
-selecting between a rst-compliant source ABI (B<--rst-source>), or a
-plain text that may be violating ReST spec, so it requres some escaping
-logic (B<--no-rst-source>).
-
-=item B<--enable-lineno>
-
-Enable output of .. LINENO lines.
-
-=item B<--debug> I<debug level>
-
-Print debug information according with the level, which is given by the
-following bitmask:
-
- - 1: Debug parsing What entries from ABI files;
- - 2: Shows what files are opened from ABI files;
- - 4: Dump the structs used to store the contents of the ABI files.
-
-=item B<--show-hints>
-
-Show hints about possible definitions for the missing ABI symbols.
-Used only when B<undefined>.
-
-=item B<--search-string> I<regex string>
-
-Show only occurences that match a search string.
-Used only when B<undefined>.
-
-=item B<--help>
-
-Prints a brief help message and exits.
-
-=item B<--man>
-
-Prints the manual page and exits.
-
-=back
-
-=head1 DESCRIPTION
-
-Parse the Linux ABI files from ABI DIR (usually located at Documentation/ABI),
-allowing to search for ABI symbols or to produce a ReST book containing
-the Linux ABI documentation.
-
-=head1 EXAMPLES
-
-Search for all stable symbols with the word "usb":
-
-=over 8
-
-$ scripts/get_abi.pl search usb --dir Documentation/ABI/stable
-
-=back
-
-Search for all symbols that match the regex expression "usb.*cap":
-
-=over 8
-
-$ scripts/get_abi.pl search usb.*cap
-
-=back
-
-Output all obsoleted symbols in ReST format
-
-=over 8
-
-$ scripts/get_abi.pl rest --dir Documentation/ABI/obsolete
-
-=back
-
-=head1 BUGS
-
-Report bugs to Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
-
-=head1 COPYRIGHT
-
-Copyright (c) 2016-2021 by Mauro Carvalho Chehab <mchehab+huawei@kernel.org>.
-
-License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html>.
-
-This is free software: you are free to change and redistribute it.
-There is NO WARRANTY, to the extent permitted by law.
-
-=cut
diff --git a/scripts/get_abi.py b/scripts/get_abi.py
new file mode 100755
index 000000000000..7ce4748a46d2
--- /dev/null
+++ b/scripts/get_abi.py
@@ -0,0 +1,214 @@
+#!/usr/bin/env python3
+# pylint: disable=R0903
+# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
+# SPDX-License-Identifier: GPL-2.0
+
+"""
+Parse ABI documentation and produce results from it.
+"""
+
+import argparse
+import logging
+import os
+import sys
+
+# Import Python modules
+
+LIB_DIR = "lib/abi"
+SRC_DIR = os.path.dirname(os.path.realpath(__file__))
+
+sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR))
+
+from abi_parser import AbiParser # pylint: disable=C0413
+from abi_regex import AbiRegex # pylint: disable=C0413
+from helpers import ABI_DIR, DEBUG_HELP # pylint: disable=C0413
+from system_symbols import SystemSymbols # pylint: disable=C0413
+
+# Command line classes
+
+
+REST_DESC = """
+Produce output in ReST format.
+
+The output is done on two sections:
+
+- Symbols: show all parsed symbols in alphabetic order;
+- Files: cross reference the content of each file with the symbols on it.
+"""
+
+class AbiRest:
+ """Initialize an argparse subparser for rest output"""
+
+ def __init__(self, subparsers):
+ """Initialize argparse subparsers"""
+
+ parser = subparsers.add_parser("rest",
+ formatter_class=argparse.RawTextHelpFormatter,
+ description=REST_DESC)
+
+ parser.add_argument("--enable-lineno", action="store_true",
+ help="enable lineno")
+ parser.add_argument("--raw", action="store_true",
+ help="output text as contained in the ABI files. "
+ "It not used, output will contain dynamically"
+ " generated cross references when possible.")
+ parser.add_argument("--no-file", action="store_true",
+ help="Don't the files section")
+ parser.add_argument("--show-hints", help="Show-hints")
+
+ parser.set_defaults(func=self.run)
+
+ def run(self, args):
+ """Run subparser"""
+
+ parser = AbiParser(args.dir, debug=args.debug)
+ parser.parse_abi()
+ parser.check_issues()
+
+ for t in parser.doc(args.raw, not args.no_file):
+ if args.enable_lineno:
+ print (f".. LINENO {t[1]}#{t[2]}\n\n")
+
+ print(t[0])
+
+class AbiValidate:
+ """Initialize an argparse subparser for ABI validation"""
+
+ def __init__(self, subparsers):
+ """Initialize argparse subparsers"""
+
+ parser = subparsers.add_parser("validate",
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+ description="list events")
+
+ parser.set_defaults(func=self.run)
+
+ def run(self, args):
+ """Run subparser"""
+
+ parser = AbiParser(args.dir, debug=args.debug)
+ parser.parse_abi()
+ parser.check_issues()
+
+
+class AbiSearch:
+ """Initialize an argparse subparser for ABI search"""
+
+ def __init__(self, subparsers):
+ """Initialize argparse subparsers"""
+
+ parser = subparsers.add_parser("search",
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+ description="Search ABI using a regular expression")
+
+ parser.add_argument("expression",
+ help="Case-insensitive search pattern for the ABI symbol")
+
+ parser.set_defaults(func=self.run)
+
+ def run(self, args):
+ """Run subparser"""
+
+ parser = AbiParser(args.dir, debug=args.debug)
+ parser.parse_abi()
+ parser.search_symbols(args.expression)
+
+UNDEFINED_DESC="""
+Check undefined ABIs on local machine.
+
+Read sysfs devnodes and check if the devnodes there are defined inside
+ABI documentation.
+
+The search logic tries to minimize the number of regular expressions to
+search per each symbol.
+
+By default, it runs on a single CPU, as Python support for CPU threads
+is still experimental, and multi-process runs on Python is very slow.
+
+On experimental tests, if the number of ABI symbols to search per devnode
+is contained on a limit of ~150 regular expressions, using a single CPU
+is a lot faster than using multiple processes. However, if the number of
+regular expressions to check is at the order of ~30000, using multiple
+CPUs speeds up the check.
+"""
+
+class AbiUndefined:
+ """
+ Initialize an argparse subparser for logic to check undefined ABI at
+ the current machine's sysfs
+ """
+
+ def __init__(self, subparsers):
+ """Initialize argparse subparsers"""
+
+ parser = subparsers.add_parser("undefined",
+ formatter_class=argparse.RawTextHelpFormatter,
+ description=UNDEFINED_DESC)
+
+ parser.add_argument("-S", "--sysfs-dir", default="/sys",
+ help="directory where sysfs is mounted")
+ parser.add_argument("-s", "--search-string",
+ help="search string regular expression to limit symbol search")
+ parser.add_argument("-H", "--show-hints", action="store_true",
+ help="Hints about definitions for missing ABI symbols.")
+ parser.add_argument("-j", "--jobs", "--max-workers", type=int, default=1,
+ help="If bigger than one, enables multiprocessing.")
+ parser.add_argument("-c", "--max-chunk-size", type=int, default=50,
+ help="Maximum number of chunk size")
+ parser.add_argument("-f", "--found", action="store_true",
+ help="Also show found items. "
+ "Helpful to debug the parser."),
+ parser.add_argument("-d", "--dry-run", action="store_true",
+ help="Don't actually search for undefined. "
+ "Helpful to debug the parser."),
+
+ parser.set_defaults(func=self.run)
+
+ def run(self, args):
+ """Run subparser"""
+
+ abi = AbiRegex(args.dir, debug=args.debug,
+ search_string=args.search_string)
+
+ abi_symbols = SystemSymbols(abi=abi, hints=args.show_hints,
+ sysfs=args.sysfs_dir)
+
+ abi_symbols.check_undefined_symbols(dry_run=args.dry_run,
+ found=args.found,
+ max_workers=args.jobs,
+ chunk_size=args.max_chunk_size)
+
+
+def main():
+ """Main program"""
+
+ parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
+
+ parser.add_argument("-d", "--debug", type=int, default=0, help="debug level")
+ parser.add_argument("-D", "--dir", default=ABI_DIR, help=DEBUG_HELP)
+
+ subparsers = parser.add_subparsers()
+
+ AbiRest(subparsers)
+ AbiValidate(subparsers)
+ AbiSearch(subparsers)
+ AbiUndefined(subparsers)
+
+ args = parser.parse_args()
+
+ if args.debug:
+ level = logging.DEBUG
+ else:
+ level = logging.INFO
+
+ logging.basicConfig(level=level, format="[%(levelname)s] %(message)s")
+
+ if "func" in args:
+ args.func(args)
+ else:
+ sys.exit(f"Please specify a valid command for {sys.argv[0]}")
+
+
+# Call main method
+if __name__ == "__main__":
+ main()
diff --git a/scripts/get_feat.pl b/scripts/get_feat.pl
index 5c5397eeb237..40fb28c8424e 100755
--- a/scripts/get_feat.pl
+++ b/scripts/get_feat.pl
@@ -512,13 +512,13 @@ print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug);
# Handles the command
#
if ($cmd eq "current") {
- $arch = qx(uname -m | sed 's/x86_64/x86/' | sed 's/i386/x86/');
+ $arch = qx(uname -m | sed 's/x86_64/x86/' | sed 's/i386/x86/' | sed 's/s390x/s390/');
$arch =~s/\s+$//;
}
if ($cmd eq "ls" or $cmd eq "list") {
if (!$arch) {
- $arch = qx(uname -m | sed 's/x86_64/x86/' | sed 's/i386/x86/');
+ $arch = qx(uname -m | sed 's/x86_64/x86/' | sed 's/i386/x86/' | sed 's/s390x/s390/');
$arch =~s/\s+$//;
}
diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl
index ee1aed7e090c..4414194bedcf 100755
--- a/scripts/get_maintainer.pl
+++ b/scripts/get_maintainer.pl
@@ -50,10 +50,12 @@ my $output_multiline = 1;
my $output_separator = ", ";
my $output_roles = 0;
my $output_rolestats = 1;
+my $output_substatus = undef;
my $output_section_maxlen = 50;
my $scm = 0;
my $tree = 1;
my $web = 0;
+my $bug = 0;
my $subsystem = 0;
my $status = 0;
my $letters = "";
@@ -268,9 +270,11 @@ if (!GetOptions(
'separator=s' => \$output_separator,
'subsystem!' => \$subsystem,
'status!' => \$status,
+ 'substatus!' => \$output_substatus,
'scm!' => \$scm,
'tree!' => \$tree,
'web!' => \$web,
+ 'bug!' => \$bug,
'letters=s' => \$letters,
'pattern-depth=i' => \$pattern_depth,
'k|keywords!' => \$keywords,
@@ -312,6 +316,10 @@ $output_multiline = 0 if ($output_separator ne ", ");
$output_rolestats = 1 if ($interactive);
$output_roles = 1 if ($output_rolestats);
+if (!defined $output_substatus) {
+ $output_substatus = $email && $output_roles && -t STDOUT;
+}
+
if ($sections || $letters ne "") {
$sections = 1;
$email = 0;
@@ -320,13 +328,14 @@ if ($sections || $letters ne "") {
$status = 0;
$subsystem = 0;
$web = 0;
+ $bug = 0;
$keywords = 0;
$keywords_in_file = 0;
$interactive = 0;
} else {
- my $selections = $email + $scm + $status + $subsystem + $web;
+ my $selections = $email + $scm + $status + $subsystem + $web + $bug;
if ($selections == 0) {
- die "$P: Missing required option: email, scm, status, subsystem or web\n";
+ die "$P: Missing required option: email, scm, status, subsystem, web or bug\n";
}
}
@@ -631,8 +640,10 @@ my %hash_list_to;
my @list_to = ();
my @scm = ();
my @web = ();
+my @bug = ();
my @subsystem = ();
my @status = ();
+my @substatus = ();
my %deduplicate_name_hash = ();
my %deduplicate_address_hash = ();
@@ -647,6 +658,11 @@ if ($scm) {
output(@scm);
}
+if ($output_substatus) {
+ @substatus = uniq(@substatus);
+ output(@substatus);
+}
+
if ($status) {
@status = uniq(@status);
output(@status);
@@ -662,6 +678,11 @@ if ($web) {
output(@web);
}
+if ($bug) {
+ @bug = uniq(@bug);
+ output(@bug);
+}
+
exit($exit);
sub self_test {
@@ -847,8 +868,10 @@ sub get_maintainers {
@list_to = ();
@scm = ();
@web = ();
+ @bug = ();
@subsystem = ();
@status = ();
+ @substatus = ();
%deduplicate_name_hash = ();
%deduplicate_address_hash = ();
if ($email_git_all_signature_types) {
@@ -1061,14 +1084,16 @@ MAINTAINER field selection options:
--moderated => include moderated lists(s) if any (default: true)
--s => include subscriber only list(s) if any (default: false)
--remove-duplicates => minimize duplicate email names/addresses
- --roles => show roles (status:subsystem, git-signer, list, etc...)
+ --roles => show roles (role:subsystem, git-signer, list, etc...)
--rolestats => show roles and statistics (commits/total_commits, %)
+ --substatus => show subsystem status if not Maintained (default: match --roles when output is tty)"
--file-emails => add email addresses found in -f file (default: 0 (off))
--fixes => for patches, add signatures of commits with 'Fixes: <commit>' (default: 1 (on))
--scm => print SCM tree(s) if any
--status => print status if any
--subsystem => print subsystem name if any
--web => print website(s) if any
+ --bug => print bug reporting info if any
Output type options:
--separator [, ] => separator for multiple entries on 1 line
@@ -1273,8 +1298,9 @@ sub get_maintainer_role {
my $start = find_starting_index($index);
my $end = find_ending_index($index);
- my $role = "unknown";
+ my $role = "maintainer";
my $subsystem = get_subsystem_name($index);
+ my $status = "unknown";
for ($i = $start + 1; $i < $end; $i++) {
my $tv = $typevalue[$i];
@@ -1282,23 +1308,13 @@ sub get_maintainer_role {
my $ptype = $1;
my $pvalue = $2;
if ($ptype eq "S") {
- $role = $pvalue;
+ $status = $pvalue;
}
}
}
- $role = lc($role);
- if ($role eq "supported") {
- $role = "supporter";
- } elsif ($role eq "maintained") {
- $role = "maintainer";
- } elsif ($role eq "odd fixes") {
- $role = "odd fixer";
- } elsif ($role eq "orphan") {
- $role = "orphan minder";
- } elsif ($role eq "obsolete") {
- $role = "obsolete minder";
- } elsif ($role eq "buried alive in reporters") {
+ $status = lc($status);
+ if ($status eq "buried alive in reporters") {
$role = "chief penguin";
}
@@ -1324,7 +1340,9 @@ sub add_categories {
my $start = find_starting_index($index);
my $end = find_ending_index($index);
- push(@subsystem, $typevalue[$start]);
+ my $subsystem = $typevalue[$start];
+ push(@subsystem, $subsystem);
+ my $status = "Unknown";
for ($i = $start + 1; $i < $end; $i++) {
my $tv = $typevalue[$i];
@@ -1375,18 +1393,25 @@ sub add_categories {
}
} elsif ($ptype eq "R") {
if ($email_reviewer) {
- my $subsystem = get_subsystem_name($i);
- push_email_addresses($pvalue, "reviewer:$subsystem" . $suffix);
+ my $subs = get_subsystem_name($i);
+ push_email_addresses($pvalue, "reviewer:$subs" . $suffix);
}
} elsif ($ptype eq "T") {
push(@scm, $pvalue . $suffix);
} elsif ($ptype eq "W") {
push(@web, $pvalue . $suffix);
+ } elsif ($ptype eq "B") {
+ push(@bug, $pvalue . $suffix);
} elsif ($ptype eq "S") {
push(@status, $pvalue . $suffix);
+ $status = $pvalue;
}
}
}
+
+ if ($subsystem ne "THE REST" and $status ne "Maintained") {
+ push(@substatus, $subsystem . " status: " . $status . $suffix)
+ }
}
sub email_inuse {
@@ -1890,6 +1915,7 @@ EOT
$done = 1;
$output_rolestats = 0;
$output_roles = 0;
+ $output_substatus = 0;
last;
} elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
$selected{$nr - 1} = !$selected{$nr - 1};
diff --git a/scripts/gfp-translate b/scripts/gfp-translate
index 6c9aed17cf56..8385ae0d5af9 100755
--- a/scripts/gfp-translate
+++ b/scripts/gfp-translate
@@ -62,25 +62,57 @@ if [ "$GFPMASK" = "none" ]; then
fi
# Extract GFP flags from the kernel source
-TMPFILE=`mktemp -t gfptranslate-XXXXXX` || exit 1
-grep -q ___GFP $SOURCE/include/linux/gfp_types.h
-if [ $? -eq 0 ]; then
- grep "^#define ___GFP" $SOURCE/include/linux/gfp_types.h | sed -e 's/u$//' | grep -v GFP_BITS > $TMPFILE
-else
- grep "^#define __GFP" $SOURCE/include/linux/gfp_types.h | sed -e 's/(__force gfp_t)//' | sed -e 's/u)/)/' | grep -v GFP_BITS | sed -e 's/)\//) \//' > $TMPFILE
-fi
+TMPFILE=`mktemp -t gfptranslate-XXXXXX.c` || exit 1
-# Parse the flags
-IFS="
-"
echo Source: $SOURCE
echo Parsing: $GFPMASK
-for LINE in `cat $TMPFILE`; do
- MASK=`echo $LINE | awk '{print $3}'`
- if [ $(($GFPMASK&$MASK)) -ne 0 ]; then
- echo $LINE
- fi
-done
-rm -f $TMPFILE
+(
+ cat <<EOF
+#include <stdint.h>
+#include <stdio.h>
+
+// Try to fool compiler.h into not including extra stuff
+#define __ASSEMBLY__ 1
+
+#include <generated/autoconf.h>
+#include <linux/gfp_types.h>
+
+static const char *masks[] = {
+EOF
+
+ sed -nEe 's/^[[:space:]]+(___GFP_.*)_BIT,.*$/\1/p' $SOURCE/include/linux/gfp_types.h |
+ while read b; do
+ cat <<EOF
+#if defined($b) && ($b > 0)
+ [${b}_BIT] = "$b",
+#endif
+EOF
+ done
+
+ cat <<EOF
+};
+
+int main(int argc, char *argv[])
+{
+ unsigned long long mask = $GFPMASK;
+
+ for (int i = 0; i < sizeof(mask) * 8; i++) {
+ unsigned long long bit = 1ULL << i;
+ if (mask & bit)
+ printf("\t%-25s0x%llx\n",
+ (i < ___GFP_LAST_BIT && masks[i]) ?
+ masks[i] : "*** INVALID ***",
+ bit);
+ }
+
+ return 0;
+}
+EOF
+) > $TMPFILE
+
+${CC:-gcc} -Wall -o ${TMPFILE}.bin -I $SOURCE/include $TMPFILE && ${TMPFILE}.bin
+
+rm -f $TMPFILE ${TMPFILE}.bin
+
exit 0
diff --git a/scripts/head-object-list.txt b/scripts/head-object-list.txt
index fd5d00bac447..7274dfc65af6 100644
--- a/scripts/head-object-list.txt
+++ b/scripts/head-object-list.txt
@@ -23,9 +23,7 @@ arch/m68k/coldfire/head.o
arch/m68k/kernel/head.o
arch/m68k/kernel/sun3-head.o
arch/microblaze/kernel/head.o
-arch/mips/kernel/head.o
arch/nios2/kernel/head.o
-arch/openrisc/kernel/head.o
arch/parisc/kernel/head.o
arch/powerpc/kernel/head_44x.o
arch/powerpc/kernel/head_64.o
diff --git a/scripts/include/hash.h b/scripts/include/hash.h
new file mode 100644
index 000000000000..efa904368a62
--- /dev/null
+++ b/scripts/include/hash.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef HASH_H
+#define HASH_H
+
+static inline unsigned int hash_str(const char *s)
+{
+ /* fnv32 hash */
+ unsigned int hash = 2166136261U;
+
+ for (; *s; s++)
+ hash = (hash ^ *s) * 0x01000193;
+ return hash;
+}
+
+/* simplified version of functions from include/linux/hash.h */
+#define GOLDEN_RATIO_32 0x61C88647
+
+static inline unsigned int hash_32(unsigned int val)
+{
+ return 0x61C88647 * val;
+}
+
+static inline unsigned int hash_ptr(const void *ptr)
+{
+ return hash_32((unsigned int)(unsigned long)ptr);
+}
+
+#endif /* HASH_H */
diff --git a/scripts/include/hashtable.h b/scripts/include/hashtable.h
index a0a2c8f5f639..45abcb12bfce 100644
--- a/scripts/include/hashtable.h
+++ b/scripts/include/hashtable.h
@@ -15,6 +15,23 @@
#define hash_head(table, key) (&(table)[(key) % HASH_SIZE(table)])
+static inline void __hash_init(struct hlist_head *ht, unsigned int sz)
+{
+ unsigned int i;
+
+ for (i = 0; i < sz; i++)
+ INIT_HLIST_HEAD(&ht[i]);
+}
+
+/**
+ * hash_init - initialize a hash table
+ * @table: hashtable to be initialized
+ *
+ * This has to be a macro since HASH_SIZE() will not work on pointers since
+ * it calculates the size during preprocessing.
+ */
+#define hash_init(table) __hash_init(table, HASH_SIZE(table))
+
/**
* hash_add - add an object to a hashtable
* @table: hashtable to add to
@@ -25,6 +42,15 @@
hlist_add_head(node, hash_head(table, key))
/**
+ * hash_del - remove an object from a hashtable
+ * @node: &struct hlist_node of the object to remove
+ */
+static inline void hash_del(struct hlist_node *node)
+{
+ hlist_del_init(node);
+}
+
+/**
* hash_for_each - iterate over a hashtable
* @table: hashtable to iterate
* @obj: the type * to use as a loop cursor for each entry
@@ -35,6 +61,18 @@
hlist_for_each_entry(obj, &table[_bkt], member)
/**
+ * hash_for_each_safe - iterate over a hashtable safe against removal of
+ * hash entry
+ * @table: hashtable to iterate
+ * @obj: the type * to use as a loop cursor for each entry
+ * @tmp: a &struct hlist_node used for temporary storage
+ * @member: the name of the hlist_node within the struct
+ */
+#define hash_for_each_safe(table, obj, tmp, member) \
+ for (int _bkt = 0; _bkt < HASH_SIZE(table); _bkt++) \
+ hlist_for_each_entry_safe(obj, tmp, &table[_bkt], member)
+
+/**
* hash_for_each_possible - iterate over all possible objects hashing to the
* same bucket
* @table: hashtable to iterate
@@ -45,4 +83,16 @@
#define hash_for_each_possible(table, obj, member, key) \
hlist_for_each_entry(obj, hash_head(table, key), member)
+/**
+ * hash_for_each_possible_safe - iterate over all possible objects hashing to the
+ * same bucket safe against removals
+ * @table: hashtable to iterate
+ * @obj: the type * to use as a loop cursor for each entry
+ * @tmp: a &struct hlist_node used for temporary storage
+ * @member: the name of the hlist_node within the struct
+ * @key: the key of the objects to iterate over
+ */
+#define hash_for_each_possible_safe(table, obj, tmp, member, key) \
+ hlist_for_each_entry_safe(obj, tmp, hash_head(table, key), member)
+
#endif /* HASHTABLE_H */
diff --git a/scripts/include/list.h b/scripts/include/list.h
index 409201cd495b..8bdcaadca709 100644
--- a/scripts/include/list.h
+++ b/scripts/include/list.h
@@ -128,6 +128,36 @@ static inline void list_del(struct list_head *entry)
}
/**
+ * list_replace - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * If @old was empty, it will be overwritten.
+ */
+static inline void list_replace(struct list_head *old,
+ struct list_head *new)
+{
+ new->next = old->next;
+ new->next->prev = new;
+ new->prev = old->prev;
+ new->prev->next = new;
+}
+
+/**
+ * list_replace_init - replace old entry by new one and initialize the old one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * If @old was empty, it will be overwritten.
+ */
+static inline void list_replace_init(struct list_head *old,
+ struct list_head *new)
+{
+ list_replace(old, new);
+ INIT_LIST_HEAD(old);
+}
+
+/**
* list_move - delete from one list and add as another's head
* @list: the entry to move
* @head: the head that will precede our entry
@@ -151,6 +181,26 @@ static inline void list_move_tail(struct list_head *list,
}
/**
+ * list_is_first -- tests whether @list is the first entry in list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_first(const struct list_head *list, const struct list_head *head)
+{
+ return list->prev == head;
+}
+
+/**
+ * list_is_last - tests whether @list is the last entry in list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_last(const struct list_head *list, const struct list_head *head)
+{
+ return list->next == head;
+}
+
+/**
* list_is_head - tests whether @list is the list @head
* @list: the entry to test
* @head: the head of the list
@@ -268,6 +318,63 @@ static inline int list_empty(const struct list_head *head)
*/
#define HLIST_HEAD_INIT { .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+static inline void INIT_HLIST_NODE(struct hlist_node *h)
+{
+ h->next = NULL;
+ h->pprev = NULL;
+}
+
+/**
+ * hlist_unhashed - Has node been removed from list and reinitialized?
+ * @h: Node to be checked
+ *
+ * Not that not all removal functions will leave a node in unhashed
+ * state. For example, hlist_nulls_del_init_rcu() does leave the
+ * node in unhashed state, but hlist_nulls_del() does not.
+ */
+static inline int hlist_unhashed(const struct hlist_node *h)
+{
+ return !h->pprev;
+}
+
+static inline void __hlist_del(struct hlist_node *n)
+{
+ struct hlist_node *next = n->next;
+ struct hlist_node **pprev = n->pprev;
+
+ *pprev = next;
+ if (next)
+ next->pprev = pprev;
+}
+
+/**
+ * hlist_del - Delete the specified hlist_node from its list
+ * @n: Node to delete.
+ *
+ * Note that this function leaves the node in hashed state. Use
+ * hlist_del_init() or similar instead to unhash @n.
+ */
+static inline void hlist_del(struct hlist_node *n)
+{
+ __hlist_del(n);
+ n->next = LIST_POISON1;
+ n->pprev = LIST_POISON2;
+}
+
+/**
+ * hlist_del_init - Delete the specified hlist_node from its list and initialize
+ * @n: Node to delete.
+ *
+ * Note that this function leaves the node in unhashed state.
+ */
+static inline void hlist_del_init(struct hlist_node *n)
+{
+ if (!hlist_unhashed(n)) {
+ __hlist_del(n);
+ INIT_HLIST_NODE(n);
+ }
+}
/**
* hlist_add_head - add a new entry at the beginning of the hlist
@@ -306,4 +413,16 @@ static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
pos; \
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
+/**
+ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos: the type * to use as a loop cursor.
+ * @n: a &struct hlist_node to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_safe(pos, n, head, member) \
+ for (pos = hlist_entry_safe((head)->first, typeof(*pos), member);\
+ pos && ({ n = pos->member.next; 1; }); \
+ pos = hlist_entry_safe(n, typeof(*pos), member))
+
#endif /* LIST_H */
diff --git a/scripts/include/xalloc.h b/scripts/include/xalloc.h
new file mode 100644
index 000000000000..cdadb07d0592
--- /dev/null
+++ b/scripts/include/xalloc.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef XALLOC_H
+#define XALLOC_H
+
+#include <stdlib.h>
+#include <string.h>
+
+static inline void *xmalloc(size_t size)
+{
+ void *p = malloc(size);
+
+ if (!p)
+ exit(1);
+ return p;
+}
+
+static inline void *xcalloc(size_t nmemb, size_t size)
+{
+ void *p = calloc(nmemb, size);
+
+ if (!p)
+ exit(1);
+ return p;
+}
+
+static inline void *xrealloc(void *p, size_t size)
+{
+ p = realloc(p, size);
+ if (!p)
+ exit(1);
+ return p;
+}
+
+static inline char *xstrdup(const char *s)
+{
+ char *p = strdup(s);
+
+ if (!p)
+ exit(1);
+ return p;
+}
+
+static inline char *xstrndup(const char *s, size_t n)
+{
+ char *p = strndup(s, n);
+
+ if (!p)
+ exit(1);
+ return p;
+}
+
+#endif /* XALLOC_H */
diff --git a/scripts/integer-wrap-ignore.scl b/scripts/integer-wrap-ignore.scl
new file mode 100644
index 000000000000..431c3053a4a2
--- /dev/null
+++ b/scripts/integer-wrap-ignore.scl
@@ -0,0 +1,3 @@
+[{unsigned-integer-overflow,signed-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation}]
+type:*
+type:size_t=sanitize
diff --git a/scripts/ipe/Makefile b/scripts/ipe/Makefile
new file mode 100644
index 000000000000..e87553fbb8d6
--- /dev/null
+++ b/scripts/ipe/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+subdir-y := polgen
diff --git a/scripts/selinux/genheaders/.gitignore b/scripts/ipe/polgen/.gitignore
index 5fcadd307908..b6f05cf3dc0e 100644
--- a/scripts/selinux/genheaders/.gitignore
+++ b/scripts/ipe/polgen/.gitignore
@@ -1,2 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
-genheaders
+polgen
diff --git a/scripts/ipe/polgen/Makefile b/scripts/ipe/polgen/Makefile
new file mode 100644
index 000000000000..c20456a2f2e9
--- /dev/null
+++ b/scripts/ipe/polgen/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+hostprogs-always-y := polgen
+HOST_EXTRACFLAGS += \
+ -I$(srctree)/include \
+ -I$(srctree)/include/uapi \
diff --git a/scripts/ipe/polgen/polgen.c b/scripts/ipe/polgen/polgen.c
new file mode 100644
index 000000000000..01134cf895d0
--- /dev/null
+++ b/scripts/ipe/polgen/polgen.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+static void usage(const char *const name)
+{
+ printf("Usage: %s OutputFile (PolicyFile)\n", name);
+ exit(EINVAL);
+}
+
+static int policy_to_buffer(const char *pathname, char **buffer, size_t *size)
+{
+ size_t fsize;
+ size_t read;
+ char *lbuf;
+ int rc = 0;
+ FILE *fd;
+
+ fd = fopen(pathname, "r");
+ if (!fd) {
+ rc = errno;
+ goto out;
+ }
+
+ fseek(fd, 0, SEEK_END);
+ fsize = ftell(fd);
+ rewind(fd);
+
+ lbuf = malloc(fsize);
+ if (!lbuf) {
+ rc = ENOMEM;
+ goto out_close;
+ }
+
+ read = fread((void *)lbuf, sizeof(*lbuf), fsize, fd);
+ if (read != fsize) {
+ rc = -1;
+ goto out_free;
+ }
+
+ *buffer = lbuf;
+ *size = fsize;
+ fclose(fd);
+
+ return rc;
+
+out_free:
+ free(lbuf);
+out_close:
+ fclose(fd);
+out:
+ return rc;
+}
+
+static int write_boot_policy(const char *pathname, const char *buf, size_t size)
+{
+ FILE *fd;
+ size_t i;
+
+ fd = fopen(pathname, "w");
+ if (!fd)
+ return errno;
+
+ fprintf(fd, "/* This file is automatically generated.");
+ fprintf(fd, " Do not edit. */\n");
+ fprintf(fd, "#include <linux/stddef.h>\n");
+ fprintf(fd, "\nextern const char *const ipe_boot_policy;\n\n");
+ fprintf(fd, "const char *const ipe_boot_policy =\n");
+
+ if (!buf || size == 0) {
+ fprintf(fd, "\tNULL;\n");
+ fclose(fd);
+ return 0;
+ }
+
+ fprintf(fd, "\t\"");
+
+ for (i = 0; i < size; ++i) {
+ switch (buf[i]) {
+ case '"':
+ fprintf(fd, "\\\"");
+ break;
+ case '\'':
+ fprintf(fd, "'");
+ break;
+ case '\n':
+ fprintf(fd, "\\n\"\n\t\"");
+ break;
+ case '\\':
+ fprintf(fd, "\\\\");
+ break;
+ case '\t':
+ fprintf(fd, "\\t");
+ break;
+ case '\?':
+ fprintf(fd, "\\?");
+ break;
+ default:
+ fprintf(fd, "%c", buf[i]);
+ }
+ }
+ fprintf(fd, "\";\n");
+ fclose(fd);
+
+ return 0;
+}
+
+int main(int argc, const char *const argv[])
+{
+ char *policy = NULL;
+ size_t len = 0;
+ int rc = 0;
+
+ if (argc < 2)
+ usage(argv[0]);
+
+ if (argc > 2) {
+ rc = policy_to_buffer(argv[2], &policy, &len);
+ if (rc != 0)
+ goto cleanup;
+ }
+
+ rc = write_boot_policy(argv[1], policy, len);
+cleanup:
+ if (policy)
+ free(policy);
+ if (rc != 0)
+ perror("An error occurred during policy conversion: ");
+ return rc;
+}
diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
index 0ed873491bf5..4b0234e4b12f 100644
--- a/scripts/kallsyms.c
+++ b/scripts/kallsyms.c
@@ -5,8 +5,7 @@
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
- * Usage: kallsyms [--all-symbols] [--absolute-percpu]
- * [--lto-clang] in.map > out.S
+ * Usage: kallsyms [--all-symbols] in.map > out.S
*
* Table compression uses all the unused char codes on the symbols and
* maps these to the most used substrings (tokens). For instance, it might
@@ -28,6 +27,8 @@
#include <ctype.h>
#include <limits.h>
+#include <xalloc.h>
+
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
#define KSYM_NAME_LEN 512
@@ -36,7 +37,6 @@ struct sym_entry {
unsigned long long addr;
unsigned int len;
unsigned int seq;
- bool percpu_absolute;
unsigned char sym[];
};
@@ -54,15 +54,9 @@ static struct addr_range text_ranges[] = {
#define text_range_text (&text_ranges[0])
#define text_range_inittext (&text_ranges[1])
-static struct addr_range percpu_range = {
- "__per_cpu_start", "__per_cpu_end", -1ULL, 0
-};
-
static struct sym_entry **table;
static unsigned int table_size, table_cnt;
static int all_symbols;
-static int absolute_percpu;
-static int lto_clang;
static int token_profit[0x10000];
@@ -73,8 +67,7 @@ static unsigned char best_table_len[256];
static void usage(void)
{
- fprintf(stderr, "Usage: kallsyms [--all-symbols] [--absolute-percpu] "
- "[--lto-clang] in.map > out.S\n");
+ fprintf(stderr, "Usage: kallsyms [--all-symbols] in.map > out.S\n");
exit(1);
}
@@ -165,23 +158,16 @@ static struct sym_entry *read_symbol(FILE *in, char **buf, size_t *buf_len)
return NULL;
check_symbol_range(name, addr, text_ranges, ARRAY_SIZE(text_ranges));
- check_symbol_range(name, addr, &percpu_range, 1);
/* include the type field in the symbol name, so that it gets
* compressed together */
len++;
- sym = malloc(sizeof(*sym) + len + 1);
- if (!sym) {
- fprintf(stderr, "kallsyms failure: "
- "unable to allocate required amount of memory\n");
- exit(EXIT_FAILURE);
- }
+ sym = xmalloc(sizeof(*sym) + len + 1);
sym->addr = addr;
sym->len = len;
sym->sym[0] = type;
strcpy(sym_name(sym), name);
- sym->percpu_absolute = false;
return sym;
}
@@ -281,12 +267,7 @@ static void read_map(const char *in)
if (table_cnt >= table_size) {
table_size += 10000;
- table = realloc(table, sizeof(*table) * table_size);
- if (!table) {
- fprintf(stderr, "out of memory\n");
- fclose(fp);
- exit (1);
- }
+ table = xrealloc(table, sizeof(*table) * table_size);
}
table[table_cnt++] = sym;
@@ -303,15 +284,6 @@ static void output_label(const char *label)
printf("%s:\n", label);
}
-/* Provide proper symbols relocatability by their '_text' relativeness. */
-static void output_address(unsigned long long addr)
-{
- if (_text <= addr)
- printf("\tPTR\t_text + %#llx\n", addr - _text);
- else
- printf("\tPTR\t_text - %#llx\n", _text - addr);
-}
-
/* uncompress a compressed symbol. When this function is called, the best table
* might still be compressed itself, so the function needs to be recursive */
static int expand_symbol(const unsigned char *data, int len, char *result)
@@ -339,30 +311,6 @@ static int expand_symbol(const unsigned char *data, int len, char *result)
return total;
}
-static bool symbol_absolute(const struct sym_entry *s)
-{
- return s->percpu_absolute;
-}
-
-static void cleanup_symbol_name(char *s)
-{
- char *p;
-
- /*
- * ASCII[.] = 2e
- * ASCII[0-9] = 30,39
- * ASCII[A-Z] = 41,5a
- * ASCII[_] = 5f
- * ASCII[a-z] = 61,7a
- *
- * As above, replacing the first '.' in ".llvm." with '\0' does not
- * affect the main sorting, but it helps us with subsorting.
- */
- p = strstr(s, ".llvm.");
- if (p)
- *p = '\0';
-}
-
static int compare_names(const void *a, const void *b)
{
int ret;
@@ -413,12 +361,7 @@ static void write_src(void)
/* table of offset markers, that give the offset in the compressed stream
* every 256 symbols */
markers_cnt = (table_cnt + 255) / 256;
- markers = malloc(sizeof(*markers) * markers_cnt);
- if (!markers) {
- fprintf(stderr, "kallsyms failure: "
- "unable to allocate required memory\n");
- exit(EXIT_FAILURE);
- }
+ markers = xmalloc(sizeof(*markers) * markers_cnt);
output_label("kallsyms_names");
off = 0;
@@ -499,22 +442,11 @@ static void write_src(void)
*/
long long offset;
- int overflow;
-
- if (!absolute_percpu) {
- offset = table[i]->addr - relative_base;
- overflow = (offset < 0 || offset > UINT_MAX);
- } else if (symbol_absolute(table[i])) {
- offset = table[i]->addr;
- overflow = (offset < 0 || offset > INT_MAX);
- } else {
- offset = relative_base - table[i]->addr - 1;
- overflow = (offset < INT_MIN || offset >= 0);
- }
- if (overflow) {
+
+ offset = table[i]->addr - relative_base;
+ if (offset < 0 || offset > UINT_MAX) {
fprintf(stderr, "kallsyms failure: "
- "%s symbol value %#llx out of range in relative mode\n",
- symbol_absolute(table[i]) ? "absolute" : "relative",
+ "relative symbol value %#llx out of range\n",
table[i]->addr);
exit(EXIT_FAILURE);
}
@@ -523,13 +455,13 @@ static void write_src(void)
printf("\n");
output_label("kallsyms_relative_base");
- output_address(relative_base);
+ /* Provide proper symbols relocatability by their '_text' relativeness. */
+ if (_text <= relative_base)
+ printf("\tPTR\t_text + %#llx\n", relative_base - _text);
+ else
+ printf("\tPTR\t_text - %#llx\n", _text - relative_base);
printf("\n");
- if (lto_clang)
- for (i = 0; i < table_cnt; i++)
- cleanup_symbol_name((char *)table[i]->sym);
-
sort_symbols_by_name();
output_label("kallsyms_seqs_of_names");
for (i = 0; i < table_cnt; i++)
@@ -769,36 +701,15 @@ static void sort_symbols(void)
qsort(table, table_cnt, sizeof(table[0]), compare_symbols);
}
-static void make_percpus_absolute(void)
-{
- unsigned int i;
-
- for (i = 0; i < table_cnt; i++)
- if (symbol_in_range(table[i], &percpu_range, 1)) {
- /*
- * Keep the 'A' override for percpu symbols to
- * ensure consistent behavior compared to older
- * versions of this tool.
- */
- table[i]->sym[0] = 'A';
- table[i]->percpu_absolute = true;
- }
-}
-
/* find the minimum non-absolute symbol address */
static void record_relative_base(void)
{
- unsigned int i;
-
- for (i = 0; i < table_cnt; i++)
- if (!symbol_absolute(table[i])) {
- /*
- * The table is sorted by address.
- * Take the first non-absolute symbol value.
- */
- relative_base = table[i]->addr;
- return;
- }
+ /*
+ * The table is sorted by address.
+ * Take the first symbol value.
+ */
+ if (table_cnt)
+ relative_base = table[0]->addr;
}
int main(int argc, char **argv)
@@ -806,8 +717,6 @@ int main(int argc, char **argv)
while (1) {
static const struct option long_options[] = {
{"all-symbols", no_argument, &all_symbols, 1},
- {"absolute-percpu", no_argument, &absolute_percpu, 1},
- {"lto-clang", no_argument, &lto_clang, 1},
{},
};
@@ -824,8 +733,6 @@ int main(int argc, char **argv)
read_map(argv[optind]);
shrink_table();
- if (absolute_percpu)
- make_percpus_absolute();
sort_symbols();
record_relative_base();
optimize_token_table();
diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
index a0a0be38cbdc..fb50bd4f4103 100644
--- a/scripts/kconfig/Makefile
+++ b/scripts/kconfig/Makefile
@@ -105,9 +105,11 @@ configfiles = $(wildcard $(srctree)/kernel/configs/$(1) $(srctree)/arch/$(SRCARC
all-config-fragments = $(call configfiles,*.config)
config-fragments = $(call configfiles,$@)
+cmd_merge_fragments = $(srctree)/scripts/kconfig/merge_config.sh -m $(KCONFIG_CONFIG) $(config-fragments)
+
%.config: $(obj)/conf
$(if $(config-fragments),, $(error $@ fragment does not exists on this architecture))
- $(Q)$(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh -m $(KCONFIG_CONFIG) $(config-fragments)
+ $(call cmd,merge_fragments)
$(Q)$(MAKE) -f $(srctree)/Makefile olddefconfig
PHONY += tinyconfig
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index 3d7d454c54da..8abe57041955 100644
--- a/scripts/kconfig/conf.c
+++ b/scripts/kconfig/conf.c
@@ -628,7 +628,7 @@ static const struct option long_opts[] = {
static void conf_usage(const char *progname)
{
- printf("Usage: %s [options] <kconfig-file>\n", progname);
+ printf("Usage: %s [options] kconfig_file\n", progname);
printf("\n");
printf("Generic options:\n");
printf(" -h, --help Print this message and exit.\n");
@@ -653,6 +653,9 @@ static void conf_usage(const char *progname)
printf(" --mod2yesconfig Change answers from mod to yes if possible\n");
printf(" --mod2noconfig Change answers from mod to no if possible\n");
printf(" (If none of the above is given, --oldaskconfig is the default)\n");
+ printf("\n");
+ printf("Arguments:\n");
+ printf(" kconfig_file Top-level Kconfig file.\n");
}
int main(int ac, char **av)
diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index 76193ce5a792..ac95661a1c9d 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -18,6 +18,7 @@
#include <time.h>
#include <unistd.h>
+#include <xalloc.h>
#include "internal.h"
#include "lkc.h"
@@ -359,10 +360,12 @@ int conf_read_simple(const char *name, int def)
*p = '\0';
- in = zconf_fopen(env);
+ name = env;
+
+ in = zconf_fopen(name);
if (in) {
conf_message("using defaults found in %s",
- env);
+ name);
goto load;
}
@@ -382,7 +385,7 @@ load:
def_flags = SYMBOL_DEF << def;
for_all_symbols(sym) {
- sym->flags &= ~(def_flags|SYMBOL_VALID);
+ sym->flags &= ~def_flags;
switch (sym->type) {
case S_INT:
case S_HEX:
@@ -395,6 +398,12 @@ load:
}
}
+ if (def == S_DEF_USER) {
+ for_all_symbols(sym)
+ sym->flags &= ~SYMBOL_VALID;
+ expr_invalidate_all();
+ }
+
while (getline_stripped(&line, &line_asize, in) != -1) {
struct menu *choice;
@@ -459,6 +468,9 @@ load:
if (conf_set_sym_val(sym, def, def_flags, val))
continue;
+ if (def != S_DEF_USER)
+ continue;
+
/*
* If this is a choice member, give it the highest priority.
* If conflicting CONFIG options are given from an input file,
@@ -962,10 +974,8 @@ static int conf_touch_deps(void)
depfile_path[depfile_prefix_len] = 0;
conf_read_simple(name, S_DEF_AUTO);
- sym_calc_value(modules_sym);
for_all_symbols(sym) {
- sym_calc_value(sym);
if (sym_is_choice(sym))
continue;
if (sym->flags & SYMBOL_WRITE) {
@@ -1079,12 +1089,12 @@ int conf_write_autoconf(int overwrite)
if (ret)
return -1;
- if (conf_touch_deps())
- return 1;
-
for_all_symbols(sym)
sym_calc_value(sym);
+ if (conf_touch_deps())
+ return 1;
+
ret = __conf_write_autoconf(conf_get_autoheader_name(),
print_symbol_for_c,
&comment_style_c);
diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c
index c349da7fe3f8..16f92c4a775a 100644
--- a/scripts/kconfig/expr.c
+++ b/scripts/kconfig/expr.c
@@ -9,44 +9,69 @@
#include <stdlib.h>
#include <string.h>
+#include <hash.h>
+#include <xalloc.h>
+#include "internal.h"
#include "lkc.h"
#define DEBUG_EXPR 0
+HASHTABLE_DEFINE(expr_hashtable, EXPR_HASHSIZE);
+
static struct expr *expr_eliminate_yn(struct expr *e);
-struct expr *expr_alloc_symbol(struct symbol *sym)
+/**
+ * expr_lookup - return the expression with the given type and sub-nodes
+ * This looks up an expression with the specified type and sub-nodes. If such
+ * an expression is found in the hash table, it is returned. Otherwise, a new
+ * expression node is allocated and added to the hash table.
+ * @type: expression type
+ * @l: left node
+ * @r: right node
+ * return: expression
+ */
+static struct expr *expr_lookup(enum expr_type type, void *l, void *r)
{
- struct expr *e = xcalloc(1, sizeof(*e));
- e->type = E_SYMBOL;
- e->left.sym = sym;
+ struct expr *e;
+ int hash;
+
+ hash = hash_32((unsigned int)type ^ hash_ptr(l) ^ hash_ptr(r));
+
+ hash_for_each_possible(expr_hashtable, e, node, hash) {
+ if (e->type == type && e->left._initdata == l &&
+ e->right._initdata == r)
+ return e;
+ }
+
+ e = xmalloc(sizeof(*e));
+ e->type = type;
+ e->left._initdata = l;
+ e->right._initdata = r;
+ e->val_is_valid = false;
+
+ hash_add(expr_hashtable, &e->node, hash);
+
return e;
}
+struct expr *expr_alloc_symbol(struct symbol *sym)
+{
+ return expr_lookup(E_SYMBOL, sym, NULL);
+}
+
struct expr *expr_alloc_one(enum expr_type type, struct expr *ce)
{
- struct expr *e = xcalloc(1, sizeof(*e));
- e->type = type;
- e->left.expr = ce;
- return e;
+ return expr_lookup(type, ce, NULL);
}
struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2)
{
- struct expr *e = xcalloc(1, sizeof(*e));
- e->type = type;
- e->left.expr = e1;
- e->right.expr = e2;
- return e;
+ return expr_lookup(type, e1, e2);
}
struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2)
{
- struct expr *e = xcalloc(1, sizeof(*e));
- e->type = type;
- e->left.sym = s1;
- e->right.sym = s2;
- return e;
+ return expr_lookup(type, s1, s2);
}
struct expr *expr_alloc_and(struct expr *e1, struct expr *e2)
@@ -63,76 +88,6 @@ struct expr *expr_alloc_or(struct expr *e1, struct expr *e2)
return e2 ? expr_alloc_two(E_OR, e1, e2) : e1;
}
-struct expr *expr_copy(const struct expr *org)
-{
- struct expr *e;
-
- if (!org)
- return NULL;
-
- e = xmalloc(sizeof(*org));
- memcpy(e, org, sizeof(*org));
- switch (org->type) {
- case E_SYMBOL:
- e->left = org->left;
- break;
- case E_NOT:
- e->left.expr = expr_copy(org->left.expr);
- break;
- case E_EQUAL:
- case E_GEQ:
- case E_GTH:
- case E_LEQ:
- case E_LTH:
- case E_UNEQUAL:
- e->left.sym = org->left.sym;
- e->right.sym = org->right.sym;
- break;
- case E_AND:
- case E_OR:
- e->left.expr = expr_copy(org->left.expr);
- e->right.expr = expr_copy(org->right.expr);
- break;
- default:
- fprintf(stderr, "can't copy type %d\n", e->type);
- free(e);
- e = NULL;
- break;
- }
-
- return e;
-}
-
-void expr_free(struct expr *e)
-{
- if (!e)
- return;
-
- switch (e->type) {
- case E_SYMBOL:
- break;
- case E_NOT:
- expr_free(e->left.expr);
- break;
- case E_EQUAL:
- case E_GEQ:
- case E_GTH:
- case E_LEQ:
- case E_LTH:
- case E_UNEQUAL:
- break;
- case E_OR:
- case E_AND:
- expr_free(e->left.expr);
- expr_free(e->right.expr);
- break;
- default:
- fprintf(stderr, "how to free type %d?\n", e->type);
- break;
- }
- free(e);
-}
-
static int trans_count;
/*
@@ -145,16 +100,24 @@ static int trans_count;
*/
static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2)
{
+ struct expr *l, *r;
+
/* Recurse down to leaves */
if ((*ep1)->type == type) {
- __expr_eliminate_eq(type, &(*ep1)->left.expr, ep2);
- __expr_eliminate_eq(type, &(*ep1)->right.expr, ep2);
+ l = (*ep1)->left.expr;
+ r = (*ep1)->right.expr;
+ __expr_eliminate_eq(type, &l, ep2);
+ __expr_eliminate_eq(type, &r, ep2);
+ *ep1 = expr_alloc_two(type, l, r);
return;
}
if ((*ep2)->type == type) {
- __expr_eliminate_eq(type, ep1, &(*ep2)->left.expr);
- __expr_eliminate_eq(type, ep1, &(*ep2)->right.expr);
+ l = (*ep2)->left.expr;
+ r = (*ep2)->right.expr;
+ __expr_eliminate_eq(type, ep1, &l);
+ __expr_eliminate_eq(type, ep1, &r);
+ *ep2 = expr_alloc_two(type, l, r);
return;
}
@@ -170,7 +133,6 @@ static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct e
/* *ep1 and *ep2 are equal leaves. Prepare them for elimination. */
trans_count++;
- expr_free(*ep1); expr_free(*ep2);
switch (type) {
case E_OR:
*ep1 = expr_alloc_symbol(&symbol_no);
@@ -242,9 +204,10 @@ void expr_eliminate_eq(struct expr **ep1, struct expr **ep2)
* equals some operand in the other (operands do not need to appear in the same
* order), recursively.
*/
-int expr_eq(struct expr *e1, struct expr *e2)
+bool expr_eq(struct expr *e1, struct expr *e2)
{
- int res, old_count;
+ int old_count;
+ bool res;
/*
* A NULL expr is taken to be yes, but there's also a different way to
@@ -254,7 +217,7 @@ int expr_eq(struct expr *e1, struct expr *e2)
return expr_is_yes(e1) && expr_is_yes(e2);
if (e1->type != e2->type)
- return 0;
+ return false;
switch (e1->type) {
case E_EQUAL:
case E_GEQ:
@@ -269,14 +232,10 @@ int expr_eq(struct expr *e1, struct expr *e2)
return expr_eq(e1->left.expr, e2->left.expr);
case E_AND:
case E_OR:
- e1 = expr_copy(e1);
- e2 = expr_copy(e2);
old_count = trans_count;
expr_eliminate_eq(&e1, &e2);
res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
e1->left.sym == e2->left.sym);
- expr_free(e1);
- expr_free(e2);
trans_count = old_count;
return res;
case E_RANGE:
@@ -291,11 +250,11 @@ int expr_eq(struct expr *e1, struct expr *e2)
printf(" ?\n");
}
- return 0;
+ return false;
}
/*
- * Recursively performs the following simplifications in-place (as well as the
+ * Recursively performs the following simplifications (as well as the
* corresponding simplifications with swapped operands):
*
* expr && n -> n
@@ -307,79 +266,39 @@ int expr_eq(struct expr *e1, struct expr *e2)
*/
static struct expr *expr_eliminate_yn(struct expr *e)
{
- struct expr *tmp;
+ struct expr *l, *r;
if (e) switch (e->type) {
case E_AND:
- e->left.expr = expr_eliminate_yn(e->left.expr);
- e->right.expr = expr_eliminate_yn(e->right.expr);
- if (e->left.expr->type == E_SYMBOL) {
- if (e->left.expr->left.sym == &symbol_no) {
- expr_free(e->left.expr);
- expr_free(e->right.expr);
- e->type = E_SYMBOL;
- e->left.sym = &symbol_no;
- e->right.expr = NULL;
- return e;
- } else if (e->left.expr->left.sym == &symbol_yes) {
- free(e->left.expr);
- tmp = e->right.expr;
- *e = *(e->right.expr);
- free(tmp);
- return e;
- }
+ l = expr_eliminate_yn(e->left.expr);
+ r = expr_eliminate_yn(e->right.expr);
+ if (l->type == E_SYMBOL) {
+ if (l->left.sym == &symbol_no)
+ return l;
+ else if (l->left.sym == &symbol_yes)
+ return r;
}
- if (e->right.expr->type == E_SYMBOL) {
- if (e->right.expr->left.sym == &symbol_no) {
- expr_free(e->left.expr);
- expr_free(e->right.expr);
- e->type = E_SYMBOL;
- e->left.sym = &symbol_no;
- e->right.expr = NULL;
- return e;
- } else if (e->right.expr->left.sym == &symbol_yes) {
- free(e->right.expr);
- tmp = e->left.expr;
- *e = *(e->left.expr);
- free(tmp);
- return e;
- }
+ if (r->type == E_SYMBOL) {
+ if (r->left.sym == &symbol_no)
+ return r;
+ else if (r->left.sym == &symbol_yes)
+ return l;
}
break;
case E_OR:
- e->left.expr = expr_eliminate_yn(e->left.expr);
- e->right.expr = expr_eliminate_yn(e->right.expr);
- if (e->left.expr->type == E_SYMBOL) {
- if (e->left.expr->left.sym == &symbol_no) {
- free(e->left.expr);
- tmp = e->right.expr;
- *e = *(e->right.expr);
- free(tmp);
- return e;
- } else if (e->left.expr->left.sym == &symbol_yes) {
- expr_free(e->left.expr);
- expr_free(e->right.expr);
- e->type = E_SYMBOL;
- e->left.sym = &symbol_yes;
- e->right.expr = NULL;
- return e;
- }
+ l = expr_eliminate_yn(e->left.expr);
+ r = expr_eliminate_yn(e->right.expr);
+ if (l->type == E_SYMBOL) {
+ if (l->left.sym == &symbol_no)
+ return r;
+ else if (l->left.sym == &symbol_yes)
+ return l;
}
- if (e->right.expr->type == E_SYMBOL) {
- if (e->right.expr->left.sym == &symbol_no) {
- free(e->right.expr);
- tmp = e->left.expr;
- *e = *(e->left.expr);
- free(tmp);
- return e;
- } else if (e->right.expr->left.sym == &symbol_yes) {
- expr_free(e->left.expr);
- expr_free(e->right.expr);
- e->type = E_SYMBOL;
- e->left.sym = &symbol_yes;
- e->right.expr = NULL;
- return e;
- }
+ if (r->type == E_SYMBOL) {
+ if (r->left.sym == &symbol_no)
+ return l;
+ else if (r->left.sym == &symbol_yes)
+ return r;
}
break;
default:
@@ -397,7 +316,7 @@ static struct expr *expr_join_or(struct expr *e1, struct expr *e2)
struct symbol *sym1, *sym2;
if (expr_eq(e1, e2))
- return expr_copy(e1);
+ return e1;
if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
return NULL;
if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
@@ -440,6 +359,7 @@ static struct expr *expr_join_or(struct expr *e1, struct expr *e2)
}
}
if (sym1->type == S_BOOLEAN) {
+ // a || !a -> y
if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) ||
(e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL))
return expr_alloc_symbol(&symbol_yes);
@@ -461,7 +381,7 @@ static struct expr *expr_join_and(struct expr *e1, struct expr *e2)
struct symbol *sym1, *sym2;
if (expr_eq(e1, e2))
- return expr_copy(e1);
+ return e1;
if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
return NULL;
if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
@@ -558,38 +478,33 @@ static struct expr *expr_join_and(struct expr *e1, struct expr *e2)
*/
static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2)
{
- struct expr *tmp;
+ struct expr *tmp, *l, *r;
/* Recurse down to leaves */
if ((*ep1)->type == type) {
- expr_eliminate_dups1(type, &(*ep1)->left.expr, ep2);
- expr_eliminate_dups1(type, &(*ep1)->right.expr, ep2);
+ l = (*ep1)->left.expr;
+ r = (*ep1)->right.expr;
+ expr_eliminate_dups1(type, &l, ep2);
+ expr_eliminate_dups1(type, &r, ep2);
+ *ep1 = expr_alloc_two(type, l, r);
return;
}
if ((*ep2)->type == type) {
- expr_eliminate_dups1(type, ep1, &(*ep2)->left.expr);
- expr_eliminate_dups1(type, ep1, &(*ep2)->right.expr);
+ l = (*ep2)->left.expr;
+ r = (*ep2)->right.expr;
+ expr_eliminate_dups1(type, ep1, &l);
+ expr_eliminate_dups1(type, ep1, &r);
+ *ep2 = expr_alloc_two(type, l, r);
return;
}
/* *ep1 and *ep2 are leaves. Compare and process them. */
- if (*ep1 == *ep2)
- return;
-
- switch ((*ep1)->type) {
- case E_OR: case E_AND:
- expr_eliminate_dups1((*ep1)->type, ep1, ep1);
- default:
- ;
- }
-
switch (type) {
case E_OR:
tmp = expr_join_or(*ep1, *ep2);
if (tmp) {
- expr_free(*ep1); expr_free(*ep2);
*ep1 = expr_alloc_symbol(&symbol_no);
*ep2 = tmp;
trans_count++;
@@ -598,7 +513,6 @@ static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct
case E_AND:
tmp = expr_join_and(*ep1, *ep2);
if (tmp) {
- expr_free(*ep1); expr_free(*ep2);
*ep1 = expr_alloc_symbol(&symbol_yes);
*ep2 = tmp;
trans_count++;
@@ -628,10 +542,15 @@ struct expr *expr_eliminate_dups(struct expr *e)
oldcount = trans_count;
do {
+ struct expr *l, *r;
+
trans_count = 0;
switch (e->type) {
case E_OR: case E_AND:
- expr_eliminate_dups1(e->type, &e, &e);
+ l = expr_eliminate_dups(e->left.expr);
+ r = expr_eliminate_dups(e->right.expr);
+ expr_eliminate_dups1(e->type, &l, &r);
+ e = expr_alloc_two(e->type, l, r);
default:
;
}
@@ -645,12 +564,34 @@ struct expr *expr_eliminate_dups(struct expr *e)
* Performs various simplifications involving logical operators and
* comparisons.
*
+ * For bool type:
+ * A=n -> !A
+ * A=m -> n
+ * A=y -> A
+ * A!=n -> A
+ * A!=m -> y
+ * A!=y -> !A
+ *
+ * For any type:
+ * !!A -> A
+ * !(A=B) -> A!=B
+ * !(A!=B) -> A=B
+ * !(A<=B) -> A>B
+ * !(A>=B) -> A<B
+ * !(A<B) -> A>=B
+ * !(A>B) -> A<=B
+ * !(A || B) -> !A && !B
+ * !(A && B) -> !A || !B
+ *
+ * For constant:
+ * !y -> n
+ * !m -> m
+ * !n -> y
+ *
* Allocates and returns a new expression.
*/
struct expr *expr_transform(struct expr *e)
{
- struct expr *tmp;
-
if (!e)
return NULL;
switch (e->type) {
@@ -663,8 +604,9 @@ struct expr *expr_transform(struct expr *e)
case E_SYMBOL:
break;
default:
- e->left.expr = expr_transform(e->left.expr);
- e->right.expr = expr_transform(e->right.expr);
+ e = expr_alloc_two(e->type,
+ expr_transform(e->left.expr),
+ expr_transform(e->right.expr));
}
switch (e->type) {
@@ -672,21 +614,19 @@ struct expr *expr_transform(struct expr *e)
if (e->left.sym->type != S_BOOLEAN)
break;
if (e->right.sym == &symbol_no) {
- e->type = E_NOT;
- e->left.expr = expr_alloc_symbol(e->left.sym);
- e->right.sym = NULL;
+ // A=n -> !A
+ e = expr_alloc_one(E_NOT, expr_alloc_symbol(e->left.sym));
break;
}
if (e->right.sym == &symbol_mod) {
+ // A=m -> n
printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name);
- e->type = E_SYMBOL;
- e->left.sym = &symbol_no;
- e->right.sym = NULL;
+ e = expr_alloc_symbol(&symbol_no);
break;
}
if (e->right.sym == &symbol_yes) {
- e->type = E_SYMBOL;
- e->right.sym = NULL;
+ // A=y -> A
+ e = expr_alloc_symbol(e->left.sym);
break;
}
break;
@@ -694,104 +634,71 @@ struct expr *expr_transform(struct expr *e)
if (e->left.sym->type != S_BOOLEAN)
break;
if (e->right.sym == &symbol_no) {
- e->type = E_SYMBOL;
- e->right.sym = NULL;
+ // A!=n -> A
+ e = expr_alloc_symbol(e->left.sym);
break;
}
if (e->right.sym == &symbol_mod) {
+ // A!=m -> y
printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name);
- e->type = E_SYMBOL;
- e->left.sym = &symbol_yes;
- e->right.sym = NULL;
+ e = expr_alloc_symbol(&symbol_yes);
break;
}
if (e->right.sym == &symbol_yes) {
- e->type = E_NOT;
- e->left.expr = expr_alloc_symbol(e->left.sym);
- e->right.sym = NULL;
+ // A!=y -> !A
+ e = expr_alloc_one(E_NOT, e->left.expr);
break;
}
break;
case E_NOT:
switch (e->left.expr->type) {
case E_NOT:
- // !!a -> a
- tmp = e->left.expr->left.expr;
- free(e->left.expr);
- free(e);
- e = tmp;
- e = expr_transform(e);
+ // !!A -> A
+ e = e->left.expr->left.expr;
break;
case E_EQUAL:
case E_UNEQUAL:
- // !a='x' -> a!='x'
- tmp = e->left.expr;
- free(e);
- e = tmp;
- e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL;
+ // !(A=B) -> A!=B
+ e = expr_alloc_comp(e->left.expr->type == E_EQUAL ? E_UNEQUAL : E_EQUAL,
+ e->left.expr->left.sym,
+ e->left.expr->right.sym);
break;
case E_LEQ:
case E_GEQ:
- // !a<='x' -> a>'x'
- tmp = e->left.expr;
- free(e);
- e = tmp;
- e->type = e->type == E_LEQ ? E_GTH : E_LTH;
+ // !(A<=B) -> A>B
+ e = expr_alloc_comp(e->left.expr->type == E_LEQ ? E_GTH : E_LTH,
+ e->left.expr->left.sym,
+ e->left.expr->right.sym);
break;
case E_LTH:
case E_GTH:
- // !a<'x' -> a>='x'
- tmp = e->left.expr;
- free(e);
- e = tmp;
- e->type = e->type == E_LTH ? E_GEQ : E_LEQ;
+ // !(A<B) -> A>=B
+ e = expr_alloc_comp(e->left.expr->type == E_LTH ? E_GEQ : E_LEQ,
+ e->left.expr->left.sym,
+ e->left.expr->right.sym);
break;
case E_OR:
- // !(a || b) -> !a && !b
- tmp = e->left.expr;
- e->type = E_AND;
- e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
- tmp->type = E_NOT;
- tmp->right.expr = NULL;
+ // !(A || B) -> !A && !B
+ e = expr_alloc_and(expr_alloc_one(E_NOT, e->left.expr->left.expr),
+ expr_alloc_one(E_NOT, e->left.expr->right.expr));
e = expr_transform(e);
break;
case E_AND:
- // !(a && b) -> !a || !b
- tmp = e->left.expr;
- e->type = E_OR;
- e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
- tmp->type = E_NOT;
- tmp->right.expr = NULL;
+ // !(A && B) -> !A || !B
+ e = expr_alloc_or(expr_alloc_one(E_NOT, e->left.expr->left.expr),
+ expr_alloc_one(E_NOT, e->left.expr->right.expr));
e = expr_transform(e);
break;
case E_SYMBOL:
- if (e->left.expr->left.sym == &symbol_yes) {
+ if (e->left.expr->left.sym == &symbol_yes)
// !'y' -> 'n'
- tmp = e->left.expr;
- free(e);
- e = tmp;
- e->type = E_SYMBOL;
- e->left.sym = &symbol_no;
- break;
- }
- if (e->left.expr->left.sym == &symbol_mod) {
+ e = expr_alloc_symbol(&symbol_no);
+ else if (e->left.expr->left.sym == &symbol_mod)
// !'m' -> 'm'
- tmp = e->left.expr;
- free(e);
- e = tmp;
- e->type = E_SYMBOL;
- e->left.sym = &symbol_mod;
- break;
- }
- if (e->left.expr->left.sym == &symbol_no) {
+ e = expr_alloc_symbol(&symbol_mod);
+ else if (e->left.expr->left.sym == &symbol_no)
// !'n' -> 'y'
- tmp = e->left.expr;
- free(e);
- e = tmp;
- e->type = E_SYMBOL;
- e->left.sym = &symbol_yes;
- break;
- }
+ e = expr_alloc_symbol(&symbol_yes);
break;
default:
;
@@ -803,10 +710,10 @@ struct expr *expr_transform(struct expr *e)
return e;
}
-int expr_contains_symbol(struct expr *dep, struct symbol *sym)
+bool expr_contains_symbol(struct expr *dep, struct symbol *sym)
{
if (!dep)
- return 0;
+ return false;
switch (dep->type) {
case E_AND:
@@ -828,7 +735,7 @@ int expr_contains_symbol(struct expr *dep, struct symbol *sym)
default:
;
}
- return 0;
+ return false;
}
bool expr_depends_symbol(struct expr *dep, struct symbol *sym)
@@ -915,18 +822,18 @@ struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symb
case E_EQUAL:
if (type == E_EQUAL) {
if (sym == &symbol_yes)
- return expr_copy(e);
+ return e;
if (sym == &symbol_mod)
return expr_alloc_symbol(&symbol_no);
if (sym == &symbol_no)
- return expr_alloc_one(E_NOT, expr_copy(e));
+ return expr_alloc_one(E_NOT, e);
} else {
if (sym == &symbol_yes)
- return expr_alloc_one(E_NOT, expr_copy(e));
+ return expr_alloc_one(E_NOT, e);
if (sym == &symbol_mod)
return expr_alloc_symbol(&symbol_yes);
if (sym == &symbol_no)
- return expr_copy(e);
+ return e;
}
break;
case E_SYMBOL:
@@ -981,7 +888,7 @@ static enum string_value_kind expr_parse_string(const char *str,
? kind : k_string;
}
-tristate expr_calc_value(struct expr *e)
+static tristate __expr_calc_value(struct expr *e)
{
tristate val1, val2;
const char *str1, *str2;
@@ -989,9 +896,6 @@ tristate expr_calc_value(struct expr *e)
union string_value lval = {}, rval = {};
int res;
- if (!e)
- return yes;
-
switch (e->type) {
case E_SYMBOL:
sym_calc_value(e->left.sym);
@@ -1055,6 +959,35 @@ tristate expr_calc_value(struct expr *e)
}
}
+/**
+ * expr_calc_value - return the tristate value of the given expression
+ * @e: expression
+ * return: tristate value of the expression
+ */
+tristate expr_calc_value(struct expr *e)
+{
+ if (!e)
+ return yes;
+
+ if (!e->val_is_valid) {
+ e->val = __expr_calc_value(e);
+ e->val_is_valid = true;
+ }
+
+ return e->val;
+}
+
+/**
+ * expr_invalidate_all - invalidate all cached expression values
+ */
+void expr_invalidate_all(void)
+{
+ struct expr *e;
+
+ hash_for_each(expr_hashtable, e, node)
+ e->val_is_valid = false;
+}
+
static int expr_compare_type(enum expr_type t1, enum expr_type t2)
{
if (t1 == t2)
diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h
index 2bc96cd28253..21578dcd4292 100644
--- a/scripts/kconfig/expr.h
+++ b/scripts/kconfig/expr.h
@@ -29,12 +29,26 @@ enum expr_type {
};
union expr_data {
- struct expr *expr;
- struct symbol *sym;
+ struct expr * const expr;
+ struct symbol * const sym;
+ void *_initdata;
};
+/**
+ * struct expr - expression
+ *
+ * @node: link node for the hash table
+ * @type: expressoin type
+ * @val: calculated tristate value
+ * @val_is_valid: indicate whether the value is valid
+ * @left: left node
+ * @right: right node
+ */
struct expr {
+ struct hlist_node node;
enum expr_type type;
+ tristate val;
+ bool val_is_valid;
union expr_data left, right;
};
@@ -168,7 +182,6 @@ enum prop_type {
P_SELECT, /* select BAR */
P_IMPLY, /* imply BAR */
P_RANGE, /* range 7..100 (for a symbol) */
- P_SYMBOL, /* where a symbol is defined */
};
struct property {
@@ -276,14 +289,12 @@ struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e
struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2);
struct expr *expr_alloc_and(struct expr *e1, struct expr *e2);
struct expr *expr_alloc_or(struct expr *e1, struct expr *e2);
-struct expr *expr_copy(const struct expr *org);
-void expr_free(struct expr *e);
void expr_eliminate_eq(struct expr **ep1, struct expr **ep2);
-int expr_eq(struct expr *e1, struct expr *e2);
+bool expr_eq(struct expr *e1, struct expr *e2);
tristate expr_calc_value(struct expr *e);
struct expr *expr_eliminate_dups(struct expr *e);
struct expr *expr_transform(struct expr *e);
-int expr_contains_symbol(struct expr *dep, struct symbol *sym);
+bool expr_contains_symbol(struct expr *dep, struct symbol *sym);
bool expr_depends_symbol(struct expr *dep, struct symbol *sym);
struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym);
@@ -293,7 +304,7 @@ void expr_gstr_print(const struct expr *e, struct gstr *gs);
void expr_gstr_print_revdep(struct expr *e, struct gstr *gs,
tristate pr_type, const char *title);
-static inline int expr_is_yes(const struct expr *e)
+static inline bool expr_is_yes(const struct expr *e)
{
return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes);
}
diff --git a/scripts/kconfig/internal.h b/scripts/kconfig/internal.h
index 02106eb7815e..d0ffce2dfbba 100644
--- a/scripts/kconfig/internal.h
+++ b/scripts/kconfig/internal.h
@@ -11,6 +11,12 @@ extern HASHTABLE_DECLARE(sym_hashtable, SYMBOL_HASHSIZE);
#define for_all_symbols(sym) \
hash_for_each(sym_hashtable, sym, node)
+#define EXPR_HASHSIZE (1U << 14)
+
+extern HASHTABLE_DECLARE(expr_hashtable, EXPR_HASHSIZE);
+
+void expr_invalidate_all(void);
+
struct menu;
extern struct menu *current_menu, *current_entry;
diff --git a/scripts/kconfig/lexer.l b/scripts/kconfig/lexer.l
index 8dd597c4710d..9c2cdfc33c6f 100644
--- a/scripts/kconfig/lexer.l
+++ b/scripts/kconfig/lexer.l
@@ -13,6 +13,7 @@
#include <stdlib.h>
#include <string.h>
+#include <xalloc.h>
#include "lkc.h"
#include "preprocess.h"
diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h
index 401bdf36323a..b8ebc3094a23 100644
--- a/scripts/kconfig/lkc.h
+++ b/scripts/kconfig/lkc.h
@@ -51,13 +51,7 @@ static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out)
}
/* util.c */
-unsigned int strhash(const char *s);
const char *file_lookup(const char *name);
-void *xmalloc(size_t size);
-void *xcalloc(size_t nmemb, size_t size);
-void *xrealloc(void *p, size_t size);
-char *xstrdup(const char *s);
-char *xstrndup(const char *s, size_t n);
/* lexer.l */
int yylex(void);
diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h
index 63519cd24bc7..8914b4e8f2a8 100644
--- a/scripts/kconfig/lkc_proto.h
+++ b/scripts/kconfig/lkc_proto.h
@@ -34,6 +34,7 @@ bool sym_string_valid(struct symbol *sym, const char *newval);
bool sym_string_within_range(struct symbol *sym, const char *str);
bool sym_set_string_value(struct symbol *sym, const char *newval);
bool sym_is_changeable(const struct symbol *sym);
+struct menu *sym_get_prompt_menu(const struct symbol *sym);
struct menu *sym_get_choice_menu(const struct symbol *sym);
const char * sym_get_string_value(struct symbol *sym);
diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c
index 3887eac75289..84ea9215c0a7 100644
--- a/scripts/kconfig/mconf.c
+++ b/scripts/kconfig/mconf.c
@@ -20,6 +20,7 @@
#include <unistd.h>
#include <list.h>
+#include <xalloc.h>
#include "lkc.h"
#include "lxdialog/dialog.h"
#include "mnconf-common.h"
diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c
index 323cc0b62be6..6587ac86d0d5 100644
--- a/scripts/kconfig/menu.c
+++ b/scripts/kconfig/menu.c
@@ -9,6 +9,7 @@
#include <string.h>
#include <list.h>
+#include <xalloc.h>
#include "lkc.h"
#include "internal.h"
@@ -78,10 +79,8 @@ void menu_add_entry(struct symbol *sym)
*last_entry_ptr = menu;
last_entry_ptr = &menu->next;
current_entry = menu;
- if (sym) {
- menu_add_symbol(P_SYMBOL, sym, NULL);
+ if (sym)
list_add_tail(&menu->link, &sym->menus);
- }
}
struct menu *menu_add_menu(void)
@@ -108,12 +107,13 @@ static struct expr *rewrite_m(struct expr *e)
switch (e->type) {
case E_NOT:
- e->left.expr = rewrite_m(e->left.expr);
+ e = expr_alloc_one(E_NOT, rewrite_m(e->left.expr));
break;
case E_OR:
case E_AND:
- e->left.expr = rewrite_m(e->left.expr);
- e->right.expr = rewrite_m(e->right.expr);
+ e = expr_alloc_two(e->type,
+ rewrite_m(e->left.expr),
+ rewrite_m(e->right.expr));
break;
case E_SYMBOL:
/* change 'm' into 'm' && MODULES */
@@ -193,21 +193,11 @@ struct property *menu_add_prompt(enum prop_type type, const char *prompt,
struct menu *menu = current_entry;
while ((menu = menu->parent) != NULL) {
- struct expr *dup_expr;
if (!menu->visibility)
continue;
- /*
- * Do not add a reference to the menu's visibility
- * expression but use a copy of it. Otherwise the
- * expression reduction functions will modify
- * expressions that have multiple references which
- * can cause unwanted side effects.
- */
- dup_expr = expr_copy(menu->visibility);
-
prop->visible.expr = expr_alloc_and(prop->visible.expr,
- dup_expr);
+ menu->visibility);
}
}
@@ -323,7 +313,7 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
*/
basedep = rewrite_m(menu->dep);
basedep = expr_transform(basedep);
- basedep = expr_alloc_and(expr_copy(parent->dep), basedep);
+ basedep = expr_alloc_and(parent->dep, basedep);
basedep = expr_eliminate_dups(basedep);
menu->dep = basedep;
@@ -367,7 +357,7 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
*/
dep = rewrite_m(prop->visible.expr);
dep = expr_transform(dep);
- dep = expr_alloc_and(expr_copy(basedep), dep);
+ dep = expr_alloc_and(basedep, dep);
dep = expr_eliminate_dups(dep);
prop->visible.expr = dep;
@@ -378,11 +368,11 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
if (prop->type == P_SELECT) {
struct symbol *es = prop_get_symbol(prop);
es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
- expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
+ expr_alloc_and(expr_alloc_symbol(menu->sym), dep));
} else if (prop->type == P_IMPLY) {
struct symbol *es = prop_get_symbol(prop);
es->implied.expr = expr_alloc_or(es->implied.expr,
- expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
+ expr_alloc_and(expr_alloc_symbol(menu->sym), dep));
}
}
}
@@ -442,22 +432,18 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
*/
dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
dep = expr_eliminate_dups(expr_transform(dep));
- dep2 = expr_copy(basedep);
+ dep2 = basedep;
expr_eliminate_eq(&dep, &dep2);
- expr_free(dep);
if (!expr_is_yes(dep2)) {
/* Not superset, quit */
- expr_free(dep2);
break;
}
/* Superset, put in submenu */
- expr_free(dep2);
next:
_menu_finalize(menu, false);
menu->parent = parent;
last_menu = menu;
}
- expr_free(basedep);
if (last_menu) {
parent->list = parent->next;
parent->next = last_menu->next;
@@ -547,6 +533,7 @@ bool menu_is_empty(struct menu *menu)
bool menu_is_visible(struct menu *menu)
{
+ struct menu *child;
struct symbol *sym;
tristate visible;
@@ -565,7 +552,17 @@ bool menu_is_visible(struct menu *menu)
} else
visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr);
- return visible != no;
+ if (visible != no)
+ return true;
+
+ if (!sym || sym_get_tristate_value(menu->sym) == no)
+ return false;
+
+ for (child = menu->list; child; child = child->next)
+ if (menu_is_visible(child))
+ return true;
+
+ return false;
}
const char *menu_get_prompt(const struct menu *menu)
diff --git a/scripts/kconfig/merge_config.sh b/scripts/kconfig/merge_config.sh
index 902eb429b9db..79c09b378be8 100755
--- a/scripts/kconfig/merge_config.sh
+++ b/scripts/kconfig/merge_config.sh
@@ -112,8 +112,8 @@ INITFILE=$1
shift;
if [ ! -r "$INITFILE" ]; then
- echo "The base file '$INITFILE' does not exist. Exit." >&2
- exit 1
+ echo "The base file '$INITFILE' does not exist. Creating one..." >&2
+ touch "$INITFILE"
fi
MERGE_LIST=$*
@@ -167,6 +167,8 @@ for ORIG_MERGE_FILE in $MERGE_LIST ; do
sed -i "/$CFG[ =]/d" $MERGE_FILE
fi
done
+ # In case the previous file lacks a new line at the end
+ echo >> $TMP_FILE
cat $MERGE_FILE >> $TMP_FILE
done
diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c
index b91ca47e9e9a..c0b2dabf6c89 100644
--- a/scripts/kconfig/nconf.c
+++ b/scripts/kconfig/nconf.c
@@ -12,6 +12,7 @@
#include <stdlib.h>
#include <list.h>
+#include <xalloc.h>
#include "lkc.h"
#include "mnconf-common.h"
#include "nconf.h"
@@ -466,7 +467,7 @@ static void handle_f9(int *key, struct menu *current_item)
return;
}
-/* return != 0 to indicate the key was handles */
+/* return != 0 to indicate the key was handled */
static int process_special_keys(int *key, struct menu *menu)
{
int i;
diff --git a/scripts/kconfig/nconf.gui.c b/scripts/kconfig/nconf.gui.c
index 25a7263ef3c8..4bfdf8ac2a9a 100644
--- a/scripts/kconfig/nconf.gui.c
+++ b/scripts/kconfig/nconf.gui.c
@@ -4,6 +4,7 @@
*
* Derived from menuconfig.
*/
+#include <xalloc.h>
#include "nconf.h"
#include "lkc.h"
@@ -276,6 +277,15 @@ int btn_dialog(WINDOW *main_window, const char *msg, int btn_num, ...)
case KEY_RIGHT:
menu_driver(menu, REQ_RIGHT_ITEM);
break;
+ case 9: /* TAB */
+ if (btn_num > 1) {
+ /* cycle through buttons */
+ if (item_index(current_item(menu)) == btn_num - 1)
+ menu_driver(menu, REQ_FIRST_ITEM);
+ else
+ menu_driver(menu, REQ_NEXT_ITEM);
+ }
+ break;
case 10: /* ENTER */
case 27: /* ESCAPE */
case ' ':
diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y
index 61900feb4254..68372d3ff325 100644
--- a/scripts/kconfig/parser.y
+++ b/scripts/kconfig/parser.y
@@ -11,6 +11,7 @@
#include <string.h>
#include <stdbool.h>
+#include <xalloc.h>
#include "lkc.h"
#include "internal.h"
#include "preprocess.h"
@@ -23,7 +24,6 @@
int cdebug = PRINTD;
static void yyerror(const char *err);
-static void zconfprint(const char *err, ...);
static void zconf_error(const char *err, ...);
static bool zconf_endtoken(const char *tokenname,
const char *expected_tokenname);
@@ -158,8 +158,14 @@ config_stmt: config_entry_start config_option_list
yynerrs++;
}
- list_add_tail(&current_entry->sym->choice_link,
- &current_choice->choice_members);
+ /*
+ * If the same symbol appears twice in a choice block, the list
+ * node would be added twice, leading to a broken linked list.
+ * list_empty() ensures that this symbol has not yet added.
+ */
+ if (list_empty(&current_entry->sym->choice_link))
+ list_add_tail(&current_entry->sym->choice_link,
+ &current_choice->choice_members);
}
printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno);
@@ -176,7 +182,7 @@ menuconfig_stmt: menuconfig_entry_start config_option_list
if (current_entry->prompt)
current_entry->prompt->type = P_MENU;
else
- zconfprint("warning: menuconfig statement without prompt");
+ zconf_error("menuconfig statement without prompt");
printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno);
};
@@ -286,12 +292,6 @@ choice_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL
printd(DEBUG_PARSE, "%s:%d:prompt\n", cur_filename, cur_lineno);
};
-choice_option: T_BOOL T_WORD_QUOTE if_expr T_EOL
-{
- menu_add_prompt(P_PROMPT, $2, $3);
- printd(DEBUG_PARSE, "%s:%d:bool\n", cur_filename, cur_lineno);
-};
-
choice_option: T_DEFAULT nonconst_symbol if_expr T_EOL
{
menu_add_symbol(P_DEFAULT, $2, $3);
@@ -401,14 +401,14 @@ help: help_start T_HELPTEXT
{
if (current_entry->help) {
free(current_entry->help);
- zconfprint("warning: '%s' defined with more than one help text -- only the last one will be used",
- current_entry->sym->name ?: "<choice>");
+ zconf_error("'%s' defined with more than one help text",
+ current_entry->sym->name ?: "<choice>");
}
/* Is the help text empty or all whitespace? */
if ($2[strspn($2, " \f\n\r\t\v")] == '\0')
- zconfprint("warning: '%s' defined with blank help text",
- current_entry->sym->name ?: "<choice>");
+ zconf_error("'%s' defined with blank help text",
+ current_entry->sym->name ?: "<choice>");
current_entry->help = $2;
};
@@ -530,14 +530,6 @@ void conf_parse(const char *name)
yydebug = 1;
yyparse();
- /*
- * FIXME:
- * cur_filename and cur_lineno are used even after yyparse();
- * menu_finalize() calls menu_add_symbol(). This should be fixed.
- */
- cur_filename = "<none>";
- cur_lineno = 0;
-
str_printf(&autoconf_cmd,
"\n"
"$(autoconfig): $(deps_config)\n"
@@ -599,17 +591,6 @@ static bool zconf_endtoken(const char *tokenname,
return true;
}
-static void zconfprint(const char *err, ...)
-{
- va_list ap;
-
- fprintf(stderr, "%s:%d: ", cur_filename, cur_lineno);
- va_start(ap, err);
- vfprintf(stderr, err, ap);
- va_end(ap);
- fprintf(stderr, "\n");
-}
-
static void zconf_error(const char *err, ...)
{
va_list ap;
@@ -715,10 +696,6 @@ static void print_symbol(FILE *out, const struct menu *menu)
print_quoted_string(out, prop->text);
fputc('\n', out);
break;
- case P_SYMBOL:
- fputs( " symbol ", out);
- fprintf(out, "%s\n", prop->menu->sym->name);
- break;
default:
fprintf(out, " unknown prop %d!\n", prop->type);
break;
diff --git a/scripts/kconfig/preprocess.c b/scripts/kconfig/preprocess.c
index 67d1fb95c491..783abcaa5cc5 100644
--- a/scripts/kconfig/preprocess.c
+++ b/scripts/kconfig/preprocess.c
@@ -11,6 +11,7 @@
#include <array_size.h>
#include <list.h>
+#include <xalloc.h>
#include "internal.h"
#include "lkc.h"
#include "preprocess.h"
diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc
index 7d239c032b3d..eaa465b0ccf9 100644
--- a/scripts/kconfig/qconf.cc
+++ b/scripts/kconfig/qconf.cc
@@ -22,6 +22,7 @@
#include <stdlib.h>
+#include <xalloc.h>
#include "lkc.h"
#include "qconf.h"
@@ -109,7 +110,7 @@ void ConfigItem::updateMenu(void)
if (prop) switch (prop->type) {
case P_MENU:
- if (list->mode == singleMode || list->mode == symbolMode) {
+ if (list->mode == singleMode) {
/* a menuconfig entry is displayed differently
* depending whether it's at the view root or a child.
*/
@@ -158,7 +159,7 @@ void ConfigItem::updateMenu(void)
ch = 'M';
break;
default:
- if (sym_is_choice_value(sym) && type == S_BOOLEAN)
+ if (sym_is_choice_value(sym))
setIcon(promptColIdx, choiceNoIcon);
else
setIcon(promptColIdx, symbolNoIcon);
@@ -174,17 +175,16 @@ void ConfigItem::updateMenu(void)
setText(dataColIdx, sym_get_string_value(sym));
break;
}
- if (!sym_has_value(sym) && visible)
+ if (!sym_has_value(sym))
prompt += " (NEW)";
set_prompt:
setText(promptColIdx, prompt);
}
-void ConfigItem::testUpdateMenu(bool v)
+void ConfigItem::testUpdateMenu(void)
{
ConfigItem* i;
- visible = v;
if (!menu)
return;
@@ -306,7 +306,6 @@ ConfigList::ConfigList(QWidget *parent, const char *name)
{
setObjectName(name);
setSortingEnabled(false);
- setRootIsDecorated(true);
setVerticalScrollMode(ScrollPerPixel);
setHorizontalScrollMode(ScrollPerPixel);
@@ -429,27 +428,26 @@ void ConfigList::updateList()
item = (ConfigItem*)(*it);
if (!item->menu)
continue;
- item->testUpdateMenu(menu_is_visible(item->menu));
+ item->testUpdateMenu();
++it;
}
return;
}
- if (rootEntry != &rootmenu && (mode == singleMode ||
- (mode == symbolMode && rootEntry->parent != &rootmenu))) {
+ if (rootEntry != &rootmenu && mode == singleMode) {
item = (ConfigItem *)topLevelItem(0);
if (!item)
- item = new ConfigItem(this, 0, true);
+ item = new ConfigItem(this, 0);
last = item;
}
if ((mode == singleMode || (mode == symbolMode && !(rootEntry->flags & MENU_ROOT))) &&
rootEntry->sym && rootEntry->prompt) {
item = last ? last->nextSibling() : nullptr;
if (!item)
- item = new ConfigItem(this, last, rootEntry, true);
+ item = new ConfigItem(this, last, rootEntry);
else
- item->testUpdateMenu(true);
+ item->testUpdateMenu();
updateMenuList(item, rootEntry);
update();
@@ -598,7 +596,6 @@ void ConfigList::updateMenuList(ConfigItem *parent, struct menu* menu)
struct menu* child;
ConfigItem* item;
ConfigItem* last;
- bool visible;
enum prop_type type;
if (!menu) {
@@ -630,14 +627,13 @@ void ConfigList::updateMenuList(ConfigItem *parent, struct menu* menu)
break;
}
- visible = menu_is_visible(child);
if (!menuSkip(child)) {
if (!child->sym && !child->list && !child->prompt)
continue;
if (!item || item->menu != child)
- item = new ConfigItem(parent, last, child, visible);
+ item = new ConfigItem(parent, last, child);
else
- item->testUpdateMenu(visible);
+ item->testUpdateMenu();
if (mode == fullMode || mode == menuMode || type != P_MENU)
updateMenuList(item, child);
@@ -663,7 +659,6 @@ void ConfigList::updateMenuList(struct menu *menu)
struct menu* child;
ConfigItem* item;
ConfigItem* last;
- bool visible;
enum prop_type type;
if (!menu) {
@@ -695,14 +690,13 @@ void ConfigList::updateMenuList(struct menu *menu)
break;
}
- visible = menu_is_visible(child);
if (!menuSkip(child)) {
if (!child->sym && !child->list && !child->prompt)
continue;
if (!item || item->menu != child)
- item = new ConfigItem(this, last, child, visible);
+ item = new ConfigItem(this, last, child);
else
- item->testUpdateMenu(visible);
+ item->testUpdateMenu();
if (mode == fullMode || mode == menuMode || type != P_MENU)
updateMenuList(item, child);
@@ -730,7 +724,7 @@ void ConfigList::keyPressEvent(QKeyEvent* ev)
struct menu *menu;
enum prop_type type;
- if (ev->key() == Qt::Key_Escape && mode != fullMode && mode != listMode) {
+ if (ev->key() == Qt::Key_Escape && mode == singleMode) {
emit parentSelected();
ev->accept();
return;
@@ -780,13 +774,6 @@ void ConfigList::keyPressEvent(QKeyEvent* ev)
ev->accept();
}
-void ConfigList::mousePressEvent(QMouseEvent* e)
-{
- //QPoint p(contentsToViewport(e->pos()));
- //printf("contentsMousePressEvent: %d,%d\n", p.x(), p.y());
- Parent::mousePressEvent(e);
-}
-
void ConfigList::mouseReleaseEvent(QMouseEvent* e)
{
QPoint p = e->pos();
@@ -833,13 +820,6 @@ skip:
Parent::mouseReleaseEvent(e);
}
-void ConfigList::mouseMoveEvent(QMouseEvent* e)
-{
- //QPoint p(contentsToViewport(e->pos()));
- //printf("contentsMouseMoveEvent: %d,%d\n", p.x(), p.y());
- Parent::mouseMoveEvent(e);
-}
-
void ConfigList::mouseDoubleClickEvent(QMouseEvent* e)
{
QPoint p = e->pos();
@@ -1021,7 +1001,7 @@ void ConfigInfoView::menuInfo(void)
if (sym->name) {
stream << " (";
if (showDebug())
- stream << "<a href=\"s" << sym->name << "\">";
+ stream << "<a href=\"" << sym->name << "\">";
stream << print_filter(sym->name);
if (showDebug())
stream << "</a>";
@@ -1030,7 +1010,7 @@ void ConfigInfoView::menuInfo(void)
} else if (sym->name) {
stream << "<big><b>";
if (showDebug())
- stream << "<a href=\"s" << sym->name << "\">";
+ stream << "<a href=\"" << sym->name << "\">";
stream << print_filter(sym->name);
if (showDebug())
stream << "</a>";
@@ -1085,16 +1065,15 @@ QString ConfigInfoView::debug_info(struct symbol *sym)
switch (prop->type) {
case P_PROMPT:
case P_MENU:
- stream << "prompt: <a href=\"m" << sym->name << "\">";
+ stream << "prompt: ";
stream << print_filter(prop->text);
- stream << "</a><br>";
+ stream << "<br>";
break;
case P_DEFAULT:
case P_SELECT:
case P_RANGE:
case P_COMMENT:
case P_IMPLY:
- case P_SYMBOL:
stream << prop_get_type_name(prop->type);
stream << ": ";
expr_print(prop->expr, expr_print_help,
@@ -1122,28 +1101,19 @@ QString ConfigInfoView::print_filter(const QString &str)
{
QRegularExpression re("[<>&\"\\n]");
QString res = str;
+
+ QHash<QChar, QString> patterns;
+ patterns['<'] = "&lt;";
+ patterns['>'] = "&gt;";
+ patterns['&'] = "&amp;";
+ patterns['"'] = "&quot;";
+ patterns['\n'] = "<br>";
+
for (int i = 0; (i = res.indexOf(re, i)) >= 0;) {
- switch (res[i].toLatin1()) {
- case '<':
- res.replace(i, 1, "&lt;");
- i += 4;
- break;
- case '>':
- res.replace(i, 1, "&gt;");
- i += 4;
- break;
- case '&':
- res.replace(i, 1, "&amp;");
- i += 5;
- break;
- case '"':
- res.replace(i, 1, "&quot;");
- i += 6;
- break;
- case '\n':
- res.replace(i, 1, "<br>");
- i += 4;
- break;
+ const QString n = patterns.value(res[i], QString());
+ if (!n.isEmpty()) {
+ res.replace(i, 1, n);
+ i += n.length();
}
}
return res;
@@ -1154,7 +1124,7 @@ void ConfigInfoView::expr_print_help(void *data, struct symbol *sym, const char
QTextStream *stream = reinterpret_cast<QTextStream *>(data);
if (sym && sym->name && !(sym->flags & SYMBOL_CONST)) {
- *stream << "<a href=\"s" << sym->name << "\">";
+ *stream << "<a href=\"" << sym->name << "\">";
*stream << print_filter(str);
*stream << "</a>";
} else {
@@ -1164,39 +1134,11 @@ void ConfigInfoView::expr_print_help(void *data, struct symbol *sym, const char
void ConfigInfoView::clicked(const QUrl &url)
{
- QByteArray str = url.toEncoded();
- const std::size_t count = str.size();
- char *data = new char[count + 1];
- struct symbol **result;
- struct menu *m = NULL;
-
- if (count < 1) {
- delete[] data;
- return;
- }
+ struct menu *m;
- memcpy(data, str.constData(), count);
- data[count] = '\0';
-
- /* Seek for exact match */
- data[0] = '^';
- strcat(data, "$");
- result = sym_re_search(data);
- if (!result) {
- delete[] data;
- return;
- }
-
- sym = *result;
-
- /* Seek for the menu which holds the symbol */
- for (struct property *prop = sym->prop; prop; prop = prop->next) {
- if (prop->type != P_PROMPT && prop->type != P_MENU)
- continue;
- m = prop->menu;
- break;
- }
+ sym = sym_find(url.toEncoded().constData());
+ m = sym_get_prompt_menu(sym);
if (!m) {
/* Symbol is not visible as a menu */
symbolInfo();
@@ -1204,9 +1146,6 @@ void ConfigInfoView::clicked(const QUrl &url)
} else {
emit menuSelected(m);
}
-
- free(result);
- delete[] data;
}
void ConfigInfoView::contextMenuEvent(QContextMenuEvent *event)
@@ -1240,8 +1179,7 @@ ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow *parent)
layout2->addWidget(searchButton);
layout1->addLayout(layout2);
- split = new QSplitter(this);
- split->setOrientation(Qt::Vertical);
+ split = new QSplitter(Qt::Vertical, this);
list = new ConfigList(split, "search");
list->mode = listMode;
info = new ConfigInfoView(split, "search");
@@ -1300,8 +1238,7 @@ void ConfigSearchWindow::search(void)
return;
for (p = result; *p; p++) {
for_all_prompts((*p), prop)
- lastItem = new ConfigItem(list, lastItem, prop->menu,
- menu_is_visible(prop->menu));
+ lastItem = new ConfigItem(list, lastItem, prop->menu);
}
}
@@ -1341,61 +1278,56 @@ ConfigMainWindow::ConfigMainWindow(void)
ConfigItem::menubackIcon = QIcon(QPixmap(xpm_menuback));
QWidget *widget = new QWidget(this);
- QVBoxLayout *layout = new QVBoxLayout(widget);
setCentralWidget(widget);
- split1 = new QSplitter(widget);
- split1->setOrientation(Qt::Horizontal);
- split1->setChildrenCollapsible(false);
-
- menuList = new ConfigList(widget, "menu");
+ QVBoxLayout *layout = new QVBoxLayout(widget);
- split2 = new QSplitter(widget);
+ split2 = new QSplitter(Qt::Vertical, widget);
+ layout->addWidget(split2);
split2->setChildrenCollapsible(false);
- split2->setOrientation(Qt::Vertical);
- // create config tree
- configList = new ConfigList(widget, "config");
+ split1 = new QSplitter(Qt::Horizontal, split2);
+ split1->setChildrenCollapsible(false);
- helpText = new ConfigInfoView(widget, "help");
+ configList = new ConfigList(split1, "config");
- layout->addWidget(split2);
- split2->addWidget(split1);
- split1->addWidget(configList);
- split1->addWidget(menuList);
- split2->addWidget(helpText);
+ menuList = new ConfigList(split1, "menu");
+ helpText = new ConfigInfoView(split2, "help");
setTabOrder(configList, helpText);
+
configList->setFocus();
backAction = new QAction(QPixmap(xpm_back), "Back", this);
+ backAction->setShortcut(QKeySequence::Back);
connect(backAction, &QAction::triggered,
this, &ConfigMainWindow::goBack);
QAction *quitAction = new QAction("&Quit", this);
- quitAction->setShortcut(Qt::CTRL | Qt::Key_Q);
+ quitAction->setShortcut(QKeySequence::Quit);
connect(quitAction, &QAction::triggered,
this, &ConfigMainWindow::close);
- QAction *loadAction = new QAction(QPixmap(xpm_load), "&Load", this);
- loadAction->setShortcut(Qt::CTRL | Qt::Key_L);
+ QAction *loadAction = new QAction(QPixmap(xpm_load), "&Open", this);
+ loadAction->setShortcut(QKeySequence::Open);
connect(loadAction, &QAction::triggered,
this, &ConfigMainWindow::loadConfig);
saveAction = new QAction(QPixmap(xpm_save), "&Save", this);
- saveAction->setShortcut(Qt::CTRL | Qt::Key_S);
+ saveAction->setShortcut(QKeySequence::Save);
connect(saveAction, &QAction::triggered,
this, &ConfigMainWindow::saveConfig);
conf_set_changed_callback(conf_changed);
- configname = xstrdup(conf_get_configname());
+ configname = conf_get_configname();
QAction *saveAsAction = new QAction("Save &As...", this);
+ saveAsAction->setShortcut(QKeySequence::SaveAs);
connect(saveAsAction, &QAction::triggered,
this, &ConfigMainWindow::saveConfigAs);
QAction *searchAction = new QAction("&Find", this);
- searchAction->setShortcut(Qt::CTRL | Qt::Key_F);
+ searchAction->setShortcut(QKeySequence::Find);
connect(searchAction, &QAction::triggered,
this, &ConfigMainWindow::searchConfig);
singleViewAction = new QAction(QPixmap(xpm_single_view), "Single View", this);
@@ -1505,6 +1437,11 @@ ConfigMainWindow::ConfigMainWindow(void)
connect(helpText, &ConfigInfoView::menuSelected,
this, &ConfigMainWindow::setMenuLink);
+ connect(configApp, &QApplication::aboutToQuit,
+ this, &ConfigMainWindow::saveSettings);
+
+ conf_read(NULL);
+
QString listMode = configSettings->value("/listMode", "symbol").toString();
if (listMode == "single")
showSingleView();
@@ -1526,28 +1463,22 @@ ConfigMainWindow::ConfigMainWindow(void)
void ConfigMainWindow::loadConfig(void)
{
QString str;
- QByteArray ba;
- const char *name;
- str = QFileDialog::getOpenFileName(this, "", configname);
- if (str.isNull())
+ str = QFileDialog::getOpenFileName(this, QString(), configname);
+ if (str.isEmpty())
return;
- ba = str.toLocal8Bit();
- name = ba.data();
-
- if (conf_read(name))
+ if (conf_read(str.toLocal8Bit().constData()))
QMessageBox::information(this, "qconf", "Unable to load configuration!");
- free(configname);
- configname = xstrdup(name);
+ configname = str;
ConfigList::updateListAllForAll();
}
bool ConfigMainWindow::saveConfig(void)
{
- if (conf_write(configname)) {
+ if (conf_write(configname.toLocal8Bit().constData())) {
QMessageBox::information(this, "qconf", "Unable to save configuration!");
return false;
}
@@ -1559,23 +1490,17 @@ bool ConfigMainWindow::saveConfig(void)
void ConfigMainWindow::saveConfigAs(void)
{
QString str;
- QByteArray ba;
- const char *name;
- str = QFileDialog::getSaveFileName(this, "", configname);
- if (str.isNull())
+ str = QFileDialog::getSaveFileName(this, QString(), configname);
+ if (str.isEmpty())
return;
- ba = str.toLocal8Bit();
- name = ba.data();
-
- if (conf_write(name)) {
+ if (conf_write(str.toLocal8Bit().constData())) {
QMessageBox::information(this, "qconf", "Unable to save configuration!");
}
conf_write_autoconf(0);
- free(configname);
- configname = xstrdup(name);
+ configname = str;
}
void ConfigMainWindow::searchConfig(void)
@@ -1660,9 +1585,6 @@ void ConfigMainWindow::listFocusChanged(void)
void ConfigMainWindow::goBack(void)
{
- if (configList->rootEntry == &rootmenu)
- return;
-
configList->setParentMenu();
}
@@ -1903,10 +1825,6 @@ int main(int ac, char** av)
v = new ConfigMainWindow();
//zconfdump(stdout);
- configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit()));
- configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings()));
-
- conf_read(NULL);
v->show();
configApp->exec();
diff --git a/scripts/kconfig/qconf.h b/scripts/kconfig/qconf.h
index 53373064d90a..62ab3286d04f 100644
--- a/scripts/kconfig/qconf.h
+++ b/scripts/kconfig/qconf.h
@@ -55,9 +55,7 @@ public:
protected:
void keyPressEvent(QKeyEvent *e);
- void mousePressEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
- void mouseMoveEvent(QMouseEvent *e);
void mouseDoubleClickEvent(QMouseEvent *e);
void focusInEvent(QFocusEvent *e);
void contextMenuEvent(QContextMenuEvent *e);
@@ -116,25 +114,25 @@ public:
class ConfigItem : public QTreeWidgetItem {
typedef class QTreeWidgetItem Parent;
public:
- ConfigItem(ConfigList *parent, ConfigItem *after, struct menu *m, bool v)
- : Parent(parent, after), nextItem(0), menu(m), visible(v), goParent(false)
+ ConfigItem(ConfigList *parent, ConfigItem *after, struct menu *m)
+ : Parent(parent, after), nextItem(0), menu(m), goParent(false)
{
init();
}
- ConfigItem(ConfigItem *parent, ConfigItem *after, struct menu *m, bool v)
- : Parent(parent, after), nextItem(0), menu(m), visible(v), goParent(false)
+ ConfigItem(ConfigItem *parent, ConfigItem *after, struct menu *m)
+ : Parent(parent, after), nextItem(0), menu(m), goParent(false)
{
init();
}
- ConfigItem(ConfigList *parent, ConfigItem *after, bool v)
- : Parent(parent, after), nextItem(0), menu(0), visible(v), goParent(true)
+ ConfigItem(ConfigList *parent, ConfigItem *after)
+ : Parent(parent, after), nextItem(0), menu(0), goParent(true)
{
init();
}
~ConfigItem(void);
void init(void);
void updateMenu(void);
- void testUpdateMenu(bool v);
+ void testUpdateMenu(void);
ConfigList* listView() const
{
return (ConfigList*)Parent::treeWidget();
@@ -161,7 +159,6 @@ public:
ConfigItem* nextItem;
struct menu *menu;
- bool visible;
bool goParent;
static QIcon symbolYesIcon, symbolModIcon, symbolNoIcon;
@@ -237,7 +234,7 @@ protected:
class ConfigMainWindow : public QMainWindow {
Q_OBJECT
- char *configname;
+ QString configname;
static QAction *saveAction;
static void conf_changed(bool);
public:
diff --git a/scripts/kconfig/streamline_config.pl b/scripts/kconfig/streamline_config.pl
index d51cd7ac15d2..8e23faab5d22 100755
--- a/scripts/kconfig/streamline_config.pl
+++ b/scripts/kconfig/streamline_config.pl
@@ -144,6 +144,7 @@ my %selects;
my %prompts;
my %objects;
my %config2kfile;
+my %defaults;
my $var;
my $iflevel = 0;
my @ifdeps;
@@ -220,8 +221,9 @@ sub read_kconfig {
$depends{$config} = $1;
} elsif ($state eq "DEP" && /^\s*depends\s+on\s+(.*)$/) {
$depends{$config} .= " " . $1;
- } elsif ($state eq "DEP" && /^\s*def(_(bool|tristate)|ault)\s+(\S.*)$/) {
+ } elsif ($state ne "NONE" && /^\s*def(_(bool|tristate)|ault)\s+(\S.*)$/) {
my $dep = $3;
+ $defaults{$config} = 1;
if ($dep !~ /^\s*(y|m|n)\s*$/) {
$dep =~ s/.*\sif\s+//;
$depends{$config} .= " " . $dep;
@@ -503,7 +505,7 @@ sub parse_config_selects
# Check if something other than a module selects this config
if (defined($orig_configs{$conf}) && $orig_configs{$conf} ne "m") {
- dprint "$conf (non module) selects config, we are good\n";
+ dprint "$conf (non module) selects $config, we are good\n";
# we are good with this
return;
}
@@ -523,8 +525,16 @@ sub parse_config_selects
# If no possible config selected this, then something happened.
if (!defined($next_config)) {
- print STDERR "WARNING: $config is required, but nothing in the\n";
- print STDERR " current config selects it.\n";
+
+ # Some config options have no prompt, and nothing selects them, but
+ # they stay turned on once the final checks for the configs
+ # are done. These configs have a default option, so turn off the
+ # warnings for configs with default options.
+ if (!defined($defaults{$config})) {
+ print STDERR "WARNING: $config is required, but nothing in the\n";
+ print STDERR " current config selects it.\n";
+ }
+
return;
}
diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c
index 71502abd3b12..d57f8cbba291 100644
--- a/scripts/kconfig/symbol.c
+++ b/scripts/kconfig/symbol.c
@@ -9,6 +9,8 @@
#include <string.h>
#include <regex.h>
+#include <hash.h>
+#include <xalloc.h>
#include "internal.h"
#include "lkc.h"
@@ -69,6 +71,24 @@ const char *sym_type_name(enum symbol_type type)
}
/**
+ * sym_get_prompt_menu - get the menu entry with a prompt
+ *
+ * @sym: a symbol pointer
+ *
+ * Return: the menu entry with a prompt.
+ */
+struct menu *sym_get_prompt_menu(const struct symbol *sym)
+{
+ struct menu *m;
+
+ list_for_each_entry(m, &sym->menus, link)
+ if (m->prompt)
+ return m;
+
+ return NULL;
+}
+
+/**
* sym_get_choice_menu - get the parent choice menu if present
*
* @sym: a symbol pointer
@@ -78,18 +98,12 @@ const char *sym_type_name(enum symbol_type type)
struct menu *sym_get_choice_menu(const struct symbol *sym)
{
struct menu *menu = NULL;
- struct menu *m;
/*
* Choice members must have a prompt. Find a menu entry with a prompt,
* and assume it resides inside a choice block.
*/
- list_for_each_entry(m, &sym->menus, link)
- if (m->prompt) {
- menu = m;
- break;
- }
-
+ menu = sym_get_prompt_menu(sym);
if (!menu)
return NULL;
@@ -374,6 +388,7 @@ static void sym_warn_unmet_dep(const struct symbol *sym)
" Selected by [m]:\n");
fputs(str_get(&gs), stderr);
+ str_free(&gs);
sym_warnings++;
}
@@ -517,6 +532,7 @@ void sym_clear_all_valid(void)
for_all_symbols(sym)
sym->flags &= ~SYMBOL_VALID;
+ expr_invalidate_all();
conf_set_changed(true);
sym_calc_value(modules_sym);
}
@@ -863,7 +879,7 @@ const char *sym_get_string_value(struct symbol *sym)
default:
;
}
- return (const char *)sym->curr.val;
+ return sym->curr.val;
}
bool sym_is_changeable(const struct symbol *sym)
@@ -892,7 +908,7 @@ struct symbol *sym_lookup(const char *name, int flags)
case 'n': return &symbol_no;
}
}
- hash = strhash(name);
+ hash = hash_str(name);
hash_for_each_possible(sym_hashtable, symbol, node, hash) {
if (symbol->name &&
@@ -935,7 +951,7 @@ struct symbol *sym_find(const char *name)
case 'n': return &symbol_no;
}
}
- hash = strhash(name);
+ hash = hash_str(name);
hash_for_each_possible(sym_hashtable, symbol, node, hash) {
if (symbol->name &&
@@ -1321,8 +1337,6 @@ const char *prop_get_type_name(enum prop_type type)
return "imply";
case P_RANGE:
return "range";
- case P_SYMBOL:
- return "symbol";
case P_UNKNOWN:
break;
}
diff --git a/scripts/kconfig/util.c b/scripts/kconfig/util.c
index 696ff477671e..5cdcee144b58 100644
--- a/scripts/kconfig/util.c
+++ b/scripts/kconfig/util.c
@@ -8,19 +8,11 @@
#include <stdlib.h>
#include <string.h>
+#include <hash.h>
#include <hashtable.h>
+#include <xalloc.h>
#include "lkc.h"
-unsigned int strhash(const char *s)
-{
- /* fnv32 hash */
- unsigned int hash = 2166136261U;
-
- for (; *s; s++)
- hash = (hash ^ *s) * 0x01000193;
- return hash;
-}
-
/* hash table of all parsed Kconfig files */
static HASHTABLE_DEFINE(file_hashtable, 1U << 11);
@@ -34,7 +26,7 @@ const char *file_lookup(const char *name)
{
struct file *file;
size_t len;
- int hash = strhash(name);
+ int hash = hash_str(name);
hash_for_each_possible(file_hashtable, file, node, hash)
if (!strcmp(name, file->name))
@@ -102,52 +94,3 @@ char *str_get(const struct gstr *gs)
{
return gs->s;
}
-
-void *xmalloc(size_t size)
-{
- void *p = malloc(size);
- if (p)
- return p;
- fprintf(stderr, "Out of memory.\n");
- exit(1);
-}
-
-void *xcalloc(size_t nmemb, size_t size)
-{
- void *p = calloc(nmemb, size);
- if (p)
- return p;
- fprintf(stderr, "Out of memory.\n");
- exit(1);
-}
-
-void *xrealloc(void *p, size_t size)
-{
- p = realloc(p, size);
- if (p)
- return p;
- fprintf(stderr, "Out of memory.\n");
- exit(1);
-}
-
-char *xstrdup(const char *s)
-{
- char *p;
-
- p = strdup(s);
- if (p)
- return p;
- fprintf(stderr, "Out of memory.\n");
- exit(1);
-}
-
-char *xstrndup(const char *s, size_t n)
-{
- char *p;
-
- p = strndup(s, n);
- if (p)
- return p;
- fprintf(stderr, "Out of memory.\n");
- exit(1);
-}
diff --git a/scripts/kernel-doc b/scripts/kernel-doc
index 2791f8195203..af6cf408b96d 100755
--- a/scripts/kernel-doc
+++ b/scripts/kernel-doc
@@ -26,7 +26,7 @@ kernel-doc - Print formatted kernel documentation to stdout
kernel-doc [-h] [-v] [-Werror] [-Wall] [-Wreturn] [-Wshort-desc[ription]] [-Wcontents-before-sections]
[ -man |
- -rst [-sphinx-version VERSION] [-enable-lineno] |
+ -rst [-enable-lineno] |
-none
]
[
@@ -130,7 +130,6 @@ if ($#ARGV == -1) {
}
my $kernelversion;
-my ($sphinx_major, $sphinx_minor, $sphinx_patch);
my $dohighlight = "";
@@ -138,7 +137,6 @@ my $verbose = 0;
my $Werror = 0;
my $Wreturn = 0;
my $Wshort_desc = 0;
-my $Wcontents_before_sections = 0;
my $output_mode = "rst";
my $output_preformatted = 0;
my $no_doc_sections = 0;
@@ -160,7 +158,7 @@ my @export_file_list;
my @build_time;
if (defined($ENV{'KBUILD_BUILD_TIMESTAMP'}) &&
- (my $seconds = `date -d"${ENV{'KBUILD_BUILD_TIMESTAMP'}}" +%s`) ne '') {
+ (my $seconds = `date -d "${ENV{'KBUILD_BUILD_TIMESTAMP'}}" +%s`) ne '') {
@build_time = gmtime($seconds);
} else {
@build_time = localtime;
@@ -179,7 +177,7 @@ my ($function, %function_table, %parametertypes, $declaration_purpose);
my %nosymbol_table = ();
my $declaration_start_line;
my ($type, $declaration_name, $return_type);
-my ($newsection, $newcontents, $prototype, $brcount, %source_map);
+my ($newsection, $newcontents, $prototype, $brcount);
if (defined($ENV{'KBUILD_VERBOSE'}) && $ENV{'KBUILD_VERBOSE'} =~ '1') {
$verbose = 1;
@@ -224,7 +222,6 @@ use constant {
STATE_INLINE => 7, # gathering doc outside main block
};
my $state;
-my $in_doc_sect;
my $leading_space;
# Inline documentation state
@@ -267,7 +264,7 @@ my $doc_inline_sect = '\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)';
my $doc_inline_end = '^\s*\*/\s*$';
my $doc_inline_oneline = '^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$';
my $export_symbol = '^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*;';
-my $export_symbol_ns = '^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*\w+\)\s*;';
+my $export_symbol_ns = '^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+"\)\s*;';
my $function_pointer = qr{([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)};
my $attribute = qr{__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)}i;
@@ -333,12 +330,9 @@ while ($ARGV[0] =~ m/^--?(.*)/) {
$Wreturn = 1;
} elsif ($cmd eq "Wshort-desc" or $cmd eq "Wshort-description") {
$Wshort_desc = 1;
- } elsif ($cmd eq "Wcontents-before-sections") {
- $Wcontents_before_sections = 1;
} elsif ($cmd eq "Wall") {
$Wreturn = 1;
$Wshort_desc = 1;
- $Wcontents_before_sections = 1;
} elsif (($cmd eq "h") || ($cmd eq "help")) {
pod2usage(-exitval => 0, -verbose => 2);
} elsif ($cmd eq 'no-doc-sections') {
@@ -347,23 +341,6 @@ while ($ARGV[0] =~ m/^--?(.*)/) {
$enable_lineno = 1;
} elsif ($cmd eq 'show-not-found') {
$show_not_found = 1; # A no-op but don't fail
- } elsif ($cmd eq "sphinx-version") {
- my $ver_string = shift @ARGV;
- if ($ver_string =~ m/^(\d+)(\.\d+)?(\.\d+)?/) {
- $sphinx_major = $1;
- if (defined($2)) {
- $sphinx_minor = substr($2,1);
- } else {
- $sphinx_minor = 0;
- }
- if (defined($3)) {
- $sphinx_patch = substr($3,1)
- } else {
- $sphinx_patch = 0;
- }
- } else {
- die "Sphinx version should either major.minor or major.minor.patch format\n";
- }
} else {
# Unknown argument
pod2usage(
@@ -387,8 +364,6 @@ while ($ARGV[0] =~ m/^--?(.*)/) {
# continue execution near EOF;
-# The C domain dialect changed on Sphinx 3. So, we need to check the
-# version in order to produce the right tags.
sub findprog($)
{
foreach(split(/:/, $ENV{PATH})) {
@@ -396,42 +371,6 @@ sub findprog($)
}
}
-sub get_sphinx_version()
-{
- my $ver;
-
- my $cmd = "sphinx-build";
- if (!findprog($cmd)) {
- my $cmd = "sphinx-build3";
- if (!findprog($cmd)) {
- $sphinx_major = 1;
- $sphinx_minor = 2;
- $sphinx_patch = 0;
- printf STDERR "Warning: Sphinx version not found. Using default (Sphinx version %d.%d.%d)\n",
- $sphinx_major, $sphinx_minor, $sphinx_patch;
- return;
- }
- }
-
- open IN, "$cmd --version 2>&1 |";
- while (<IN>) {
- if (m/^\s*sphinx-build\s+([\d]+)\.([\d\.]+)(\+\/[\da-f]+)?$/) {
- $sphinx_major = $1;
- $sphinx_minor = $2;
- $sphinx_patch = $3;
- last;
- }
- # Sphinx 1.2.x uses a different format
- if (m/^\s*Sphinx.*\s+([\d]+)\.([\d\.]+)$/) {
- $sphinx_major = $1;
- $sphinx_minor = $2;
- $sphinx_patch = $3;
- last;
- }
- }
- close IN;
-}
-
# get kernel version from env
sub get_kernel_version() {
my $version = 'unknown kernel version';
@@ -569,6 +508,8 @@ sub output_function_man(%) {
my %args = %{$_[0]};
my ($parameter, $section);
my $count;
+ my $func_macro = $args{'func_macro'};
+ my $paramcount = $#{$args{'parameterlist'}}; # -1 is empty
print ".TH \"$args{'function'}\" 9 \"$args{'function'}\" \"$man_date\" \"Kernel Hacker's Manual\" LINUX\n";
@@ -600,7 +541,10 @@ sub output_function_man(%) {
$parenth = "";
}
- print ".SH ARGUMENTS\n";
+ $paramcount = $#{$args{'parameterlist'}}; # -1 is empty
+ if ($paramcount >= 0) {
+ print ".SH ARGUMENTS\n";
+ }
foreach $parameter (@{$args{'parameterlist'}}) {
my $parameter_name = $parameter;
$parameter_name =~ s/\[.*//;
@@ -811,6 +755,10 @@ sub output_highlight_rst {
if ($block) {
$output .= highlight_block($block);
}
+
+ $output =~ s/^\n+//g;
+ $output =~ s/\n+$//g;
+
foreach $line (split "\n", $output) {
print $lineprefix . $line . "\n";
}
@@ -822,10 +770,16 @@ sub output_function_rst(%) {
my $oldprefix = $lineprefix;
my $signature = "";
- if ($args{'functiontype'} ne "") {
- $signature = $args{'functiontype'} . " " . $args{'function'} . " (";
- } else {
- $signature = $args{'function'} . " (";
+ my $func_macro = $args{'func_macro'};
+ my $paramcount = $#{$args{'parameterlist'}}; # -1 is empty
+
+ if ($func_macro) {
+ $signature = $args{'function'};
+ } else {
+ if ($args{'functiontype'}) {
+ $signature = $args{'functiontype'} . " ";
+ }
+ $signature .= $args{'function'} . " (";
}
my $count = 0;
@@ -844,11 +798,14 @@ sub output_function_rst(%) {
}
}
- $signature .= ")";
+ if (!$func_macro) {
+ $signature .= ")";
+ }
+
+ if ($args{'typedef'} || $args{'functiontype'} eq "") {
+ print ".. c:macro:: ". $args{'function'} . "\n\n";
- if ($sphinx_major < 3) {
if ($args{'typedef'}) {
- print ".. c:type:: ". $args{'function'} . "\n\n";
print_lineno($declaration_start_line);
print " **Typedef**: ";
$lineprefix = "";
@@ -856,25 +813,10 @@ sub output_function_rst(%) {
print "\n\n**Syntax**\n\n";
print " ``$signature``\n\n";
} else {
- print ".. c:function:: $signature\n\n";
+ print "``$signature``\n\n";
}
} else {
- if ($args{'typedef'} || $args{'functiontype'} eq "") {
- print ".. c:macro:: ". $args{'function'} . "\n\n";
-
- if ($args{'typedef'}) {
- print_lineno($declaration_start_line);
- print " **Typedef**: ";
- $lineprefix = "";
- output_highlight_rst($args{'purpose'});
- print "\n\n**Syntax**\n\n";
- print " ``$signature``\n\n";
- } else {
- print "``$signature``\n\n";
- }
- } else {
- print ".. c:function:: $signature\n\n";
- }
+ print ".. c:function:: $signature\n\n";
}
if (!$args{'typedef'}) {
@@ -888,9 +830,11 @@ sub output_function_rst(%) {
# Put our descriptive text into a container (thus an HTML <div>) to help
# set the function prototypes apart.
#
- print ".. container:: kernelindent\n\n";
$lineprefix = " ";
- print $lineprefix . "**Parameters**\n\n";
+ if ($paramcount >= 0) {
+ print ".. container:: kernelindent\n\n";
+ print $lineprefix . "**Parameters**\n\n";
+ }
foreach $parameter (@{$args{'parameterlist'}}) {
my $parameter_name = $parameter;
$parameter_name =~ s/\[.*//;
@@ -940,13 +884,9 @@ sub output_enum_rst(%) {
my $count;
my $outer;
- if ($sphinx_major < 3) {
- my $name = "enum " . $args{'enum'};
- print "\n\n.. c:type:: " . $name . "\n\n";
- } else {
- my $name = $args{'enum'};
- print "\n\n.. c:enum:: " . $name . "\n\n";
- }
+ my $name = $args{'enum'};
+ print "\n\n.. c:enum:: " . $name . "\n\n";
+
print_lineno($declaration_start_line);
$lineprefix = " ";
output_highlight_rst($args{'purpose'});
@@ -977,11 +917,8 @@ sub output_typedef_rst(%) {
my $oldprefix = $lineprefix;
my $name;
- if ($sphinx_major < 3) {
- $name = "typedef " . $args{'typedef'};
- } else {
- $name = $args{'typedef'};
- }
+ $name = $args{'typedef'};
+
print "\n\n.. c:type:: " . $name . "\n\n";
print_lineno($declaration_start_line);
$lineprefix = " ";
@@ -997,17 +934,13 @@ sub output_struct_rst(%) {
my ($parameter);
my $oldprefix = $lineprefix;
- if ($sphinx_major < 3) {
- my $name = $args{'type'} . " " . $args{'struct'};
- print "\n\n.. c:type:: " . $name . "\n\n";
+ my $name = $args{'struct'};
+ if ($args{'type'} eq 'union') {
+ print "\n\n.. c:union:: " . $name . "\n\n";
} else {
- my $name = $args{'struct'};
- if ($args{'type'} eq 'union') {
- print "\n\n.. c:union:: " . $name . "\n\n";
- } else {
- print "\n\n.. c:struct:: " . $name . "\n\n";
- }
+ print "\n\n.. c:struct:: " . $name . "\n\n";
}
+
print_lineno($declaration_start_line);
$lineprefix = " ";
output_highlight_rst($args{'purpose'});
@@ -1704,7 +1637,7 @@ sub check_return_section {
sub dump_function($$) {
my $prototype = shift;
my $file = shift;
- my $noret = 0;
+ my $func_macro = 0;
print_lineno($new_start_line);
@@ -1769,7 +1702,7 @@ sub dump_function($$) {
# declaration_name and opening parenthesis (notice the \s+).
$return_type = $1;
$declaration_name = $2;
- $noret = 1;
+ $func_macro = 1;
} elsif ($prototype =~ m/^()($name)\s*$prototype_end/ ||
$prototype =~ m/^($type1)\s+($name)\s*$prototype_end/ ||
$prototype =~ m/^($type2+)\s*($name)\s*$prototype_end/) {
@@ -1796,7 +1729,7 @@ sub dump_function($$) {
# of warnings goes sufficiently down, the check is only performed in
# -Wreturn mode.
# TODO: always perform the check.
- if ($Wreturn && !$noret) {
+ if ($Wreturn && !$func_macro) {
check_return_section($file, $declaration_name, $return_type);
}
@@ -1814,7 +1747,8 @@ sub dump_function($$) {
'parametertypes' => \%parametertypes,
'sectionlist' => \@sectionlist,
'sections' => \%sections,
- 'purpose' => $declaration_purpose
+ 'purpose' => $declaration_purpose,
+ 'func_macro' => $func_macro
});
} else {
output_declaration($declaration_name,
@@ -1827,7 +1761,8 @@ sub dump_function($$) {
'parametertypes' => \%parametertypes,
'sectionlist' => \@sectionlist,
'sections' => \%sections,
- 'purpose' => $declaration_purpose
+ 'purpose' => $declaration_purpose,
+ 'func_macro' => $func_macro
});
}
}
@@ -1988,10 +1923,6 @@ sub map_filename($) {
$file = $orig_file;
}
- if (defined($source_map{$file})) {
- $file = $source_map{$file};
- }
-
return $file;
}
@@ -2027,7 +1958,6 @@ sub process_export_file($) {
sub process_normal() {
if (/$doc_start/o) {
$state = STATE_NAME; # next line is always the function name
- $in_doc_sect = 0;
$declaration_start_line = $. + 1;
}
}
@@ -2068,7 +1998,7 @@ sub process_name($$) {
# Look for foo() or static void foo() - description; or misspelt
# identifier
elsif (/^$decl_start$fn_type?(\w+)\s*$parenthesis?\s*$decl_end?$/ ||
- /^$decl_start$fn_type?(\w+.*)$parenthesis?\s*$decl_end$/) {
+ /^$decl_start$fn_type?(\w+[^-:]*)$parenthesis?\s*$decl_end$/) {
$identifier = $1;
$decl_type = 'function';
$identifier =~ s/^define\s+//;
@@ -2132,7 +2062,6 @@ sub process_body($$) {
}
if (/$doc_sect/i) { # case insensitive for supported section names
- $in_doc_sect = 1;
$newsection = $1;
$newcontents = $2;
@@ -2149,14 +2078,10 @@ sub process_body($$) {
}
if (($contents ne "") && ($contents ne "\n")) {
- if (!$in_doc_sect && $Wcontents_before_sections) {
- emit_warning("${file}:$.", "contents before sections\n");
- }
dump_section($file, $section, $contents);
$section = $section_default;
}
- $in_doc_sect = 1;
$state = STATE_BODY;
$contents = $newcontents;
$new_start_line = $.;
@@ -2322,7 +2247,6 @@ sub process_inline($$) {
sub process_file($) {
my $file;
- my $initial_section_counter = $section_counter;
my ($orig_file) = @_;
$file = map_filename($orig_file);
@@ -2360,8 +2284,7 @@ sub process_file($) {
}
# Make sure we got something interesting.
- if ($initial_section_counter == $section_counter && $
- output_mode ne "none") {
+ if (!$section_counter && $output_mode ne "none") {
if ($output_selection == OUTPUT_INCLUDE) {
emit_warning("${file}:1", "'$_' not found\n")
for keys %function_table;
@@ -2372,11 +2295,6 @@ sub process_file($) {
close IN_FILE;
}
-
-if ($output_mode eq "rst") {
- get_sphinx_version() if (!$sphinx_major);
-}
-
$kernelversion = get_kernel_version();
# generate a sequence of code that will splice in highlighting information
@@ -2388,19 +2306,6 @@ for (my $k = 0; $k < @highlights; $k++) {
$dohighlight .= "\$contents =~ s:$pattern:$result:gs;\n";
}
-# Read the file that maps relative names to absolute names for
-# separate source and object directories and for shadow trees.
-if (open(SOURCE_MAP, "<.tmp_filelist.txt")) {
- my ($relname, $absname);
- while(<SOURCE_MAP>) {
- chop();
- ($relname, $absname) = (split())[0..1];
- $relname =~ s:^/+::;
- $source_map{$relname} = $absname;
- }
- close(SOURCE_MAP);
-}
-
if ($output_selection == OUTPUT_EXPORTED ||
$output_selection == OUTPUT_INTERNAL) {
@@ -2456,17 +2361,6 @@ Do not output documentation, only warnings.
=head3 reStructuredText only
-=over 8
-
-=item -sphinx-version VERSION
-
-Use the ReST C domain dialect compatible with a specific Sphinx Version.
-
-If not specified, kernel-doc will auto-detect using the sphinx-build version
-found on PATH.
-
-=back
-
=head2 Output selection (mutually exclusive):
=over 8
diff --git a/scripts/lib/abi/abi_parser.py b/scripts/lib/abi/abi_parser.py
new file mode 100644
index 000000000000..66a738013ce1
--- /dev/null
+++ b/scripts/lib/abi/abi_parser.py
@@ -0,0 +1,628 @@
+#!/usr/bin/env python3
+# pylint: disable=R0902,R0903,R0911,R0912,R0913,R0914,R0915,R0917,C0302
+# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
+# SPDX-License-Identifier: GPL-2.0
+
+"""
+Parse ABI documentation and produce results from it.
+"""
+
+from argparse import Namespace
+import logging
+import os
+import re
+
+from pprint import pformat
+from random import randrange, seed
+
+# Import Python modules
+
+from helpers import AbiDebug, ABI_DIR
+
+
+class AbiParser:
+ """Main class to parse ABI files"""
+
+ TAGS = r"(what|where|date|kernelversion|contact|description|users)"
+ XREF = r"(?:^|\s|\()(\/(?:sys|config|proc|dev|kvd)\/[^,.:;\)\s]+)(?:[,.:;\)\s]|\Z)"
+
+ def __init__(self, directory, logger=None,
+ enable_lineno=False, show_warnings=True, debug=0):
+ """Stores arguments for the class and initialize class vars"""
+
+ self.directory = directory
+ self.enable_lineno = enable_lineno
+ self.show_warnings = show_warnings
+ self.debug = debug
+
+ if not logger:
+ self.log = logging.getLogger("get_abi")
+ else:
+ self.log = logger
+
+ self.data = {}
+ self.what_symbols = {}
+ self.file_refs = {}
+ self.what_refs = {}
+
+ # Ignore files that contain such suffixes
+ self.ignore_suffixes = (".rej", ".org", ".orig", ".bak", "~")
+
+ # Regular expressions used on parser
+ self.re_abi_dir = re.compile(r"(.*)" + ABI_DIR)
+ self.re_tag = re.compile(r"(\S+)(:\s*)(.*)", re.I)
+ self.re_valid = re.compile(self.TAGS)
+ self.re_start_spc = re.compile(r"(\s*)(\S.*)")
+ self.re_whitespace = re.compile(r"^\s+")
+
+ # Regular used on print
+ self.re_what = re.compile(r"(\/?(?:[\w\-]+\/?){1,2})")
+ self.re_escape = re.compile(r"([\.\x01-\x08\x0e-\x1f\x21-\x2f\x3a-\x40\x7b-\xff])")
+ self.re_unprintable = re.compile(r"([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\xff]+)")
+ self.re_title_mark = re.compile(r"\n[\-\*\=\^\~]+\n")
+ self.re_doc = re.compile(r"Documentation/(?!devicetree)(\S+)\.rst")
+ self.re_abi = re.compile(r"(Documentation/ABI/)([\w\/\-]+)")
+ self.re_xref_node = re.compile(self.XREF)
+
+ def warn(self, fdata, msg, extra=None):
+ """Displays a parse error if warning is enabled"""
+
+ if not self.show_warnings:
+ return
+
+ msg = f"{fdata.fname}:{fdata.ln}: {msg}"
+ if extra:
+ msg += "\n\t\t" + extra
+
+ self.log.warning(msg)
+
+ def add_symbol(self, what, fname, ln=None, xref=None):
+ """Create a reference table describing where each 'what' is located"""
+
+ if what not in self.what_symbols:
+ self.what_symbols[what] = {"file": {}}
+
+ if fname not in self.what_symbols[what]["file"]:
+ self.what_symbols[what]["file"][fname] = []
+
+ if ln and ln not in self.what_symbols[what]["file"][fname]:
+ self.what_symbols[what]["file"][fname].append(ln)
+
+ if xref:
+ self.what_symbols[what]["xref"] = xref
+
+ def _parse_line(self, fdata, line):
+ """Parse a single line of an ABI file"""
+
+ new_what = False
+ new_tag = False
+ content = None
+
+ match = self.re_tag.match(line)
+ if match:
+ new = match.group(1).lower()
+ sep = match.group(2)
+ content = match.group(3)
+
+ match = self.re_valid.search(new)
+ if match:
+ new_tag = match.group(1)
+ else:
+ if fdata.tag == "description":
+ # New "tag" is actually part of description.
+ # Don't consider it a tag
+ new_tag = False
+ elif fdata.tag != "":
+ self.warn(fdata, f"tag '{fdata.tag}' is invalid", line)
+
+ if new_tag:
+ # "where" is Invalid, but was a common mistake. Warn if found
+ if new_tag == "where":
+ self.warn(fdata, "tag 'Where' is invalid. Should be 'What:' instead")
+ new_tag = "what"
+
+ if new_tag == "what":
+ fdata.space = None
+
+ if content not in self.what_symbols:
+ self.add_symbol(what=content, fname=fdata.fname, ln=fdata.ln)
+
+ if fdata.tag == "what":
+ fdata.what.append(content.strip("\n"))
+ else:
+ if fdata.key:
+ if "description" not in self.data.get(fdata.key, {}):
+ self.warn(fdata, f"{fdata.key} doesn't have a description")
+
+ for w in fdata.what:
+ self.add_symbol(what=w, fname=fdata.fname,
+ ln=fdata.what_ln, xref=fdata.key)
+
+ fdata.label = content
+ new_what = True
+
+ key = "abi_" + content.lower()
+ fdata.key = self.re_unprintable.sub("_", key).strip("_")
+
+ # Avoid duplicated keys but using a defined seed, to make
+ # the namespace identical if there aren't changes at the
+ # ABI symbols
+ seed(42)
+
+ while fdata.key in self.data:
+ char = randrange(0, 51) + ord("A")
+ if char > ord("Z"):
+ char += ord("a") - ord("Z") - 1
+
+ fdata.key += chr(char)
+
+ if fdata.key and fdata.key not in self.data:
+ self.data[fdata.key] = {
+ "what": [content],
+ "file": [fdata.file_ref],
+ "path": fdata.ftype,
+ "line_no": fdata.ln,
+ }
+
+ fdata.what = self.data[fdata.key]["what"]
+
+ self.what_refs[content] = fdata.key
+ fdata.tag = new_tag
+ fdata.what_ln = fdata.ln
+
+ if fdata.nametag["what"]:
+ t = (content, fdata.key)
+ if t not in fdata.nametag["symbols"]:
+ fdata.nametag["symbols"].append(t)
+
+ return
+
+ if fdata.tag and new_tag:
+ fdata.tag = new_tag
+
+ if new_what:
+ fdata.label = ""
+
+ if "description" in self.data[fdata.key]:
+ self.data[fdata.key]["description"] += "\n\n"
+
+ if fdata.file_ref not in self.data[fdata.key]["file"]:
+ self.data[fdata.key]["file"].append(fdata.file_ref)
+
+ if self.debug == AbiDebug.WHAT_PARSING:
+ self.log.debug("what: %s", fdata.what)
+
+ if not fdata.what:
+ self.warn(fdata, "'What:' should come first:", line)
+ return
+
+ if new_tag == "description":
+ fdata.space = None
+
+ if content:
+ sep = sep.replace(":", " ")
+
+ c = " " * len(new_tag) + sep + content
+ c = c.expandtabs()
+
+ match = self.re_start_spc.match(c)
+ if match:
+ # Preserve initial spaces for the first line
+ fdata.space = match.group(1)
+ content = match.group(2) + "\n"
+
+ self.data[fdata.key][fdata.tag] = content
+
+ return
+
+ # Store any contents before tags at the database
+ if not fdata.tag and "what" in fdata.nametag:
+ fdata.nametag["description"] += line
+ return
+
+ if fdata.tag == "description":
+ content = line.expandtabs()
+
+ if self.re_whitespace.sub("", content) == "":
+ self.data[fdata.key][fdata.tag] += "\n"
+ return
+
+ if fdata.space is None:
+ match = self.re_start_spc.match(content)
+ if match:
+ # Preserve initial spaces for the first line
+ fdata.space = match.group(1)
+
+ content = match.group(2) + "\n"
+ else:
+ if content.startswith(fdata.space):
+ content = content[len(fdata.space):]
+
+ else:
+ fdata.space = ""
+
+ if fdata.tag == "what":
+ w = content.strip("\n")
+ if w:
+ self.data[fdata.key][fdata.tag].append(w)
+ else:
+ self.data[fdata.key][fdata.tag] += content
+ return
+
+ content = line.strip()
+ if fdata.tag:
+ if fdata.tag == "what":
+ w = content.strip("\n")
+ if w:
+ self.data[fdata.key][fdata.tag].append(w)
+ else:
+ self.data[fdata.key][fdata.tag] += "\n" + content.rstrip("\n")
+ return
+
+ # Everything else is error
+ if content:
+ self.warn(fdata, "Unexpected content", line)
+
+ def parse_readme(self, nametag, fname):
+ """Parse ABI README file"""
+
+ nametag["what"] = ["Introduction"]
+ nametag["path"] = "README"
+ with open(fname, "r", encoding="utf8", errors="backslashreplace") as fp:
+ for line in fp:
+ match = self.re_tag.match(line)
+ if match:
+ new = match.group(1).lower()
+
+ match = self.re_valid.search(new)
+ if match:
+ nametag["description"] += "\n:" + line
+ continue
+
+ nametag["description"] += line
+
+ def parse_file(self, fname, path, basename):
+ """Parse a single file"""
+
+ ref = f"abi_file_{path}_{basename}"
+ ref = self.re_unprintable.sub("_", ref).strip("_")
+
+ # Store per-file state into a namespace variable. This will be used
+ # by the per-line parser state machine and by the warning function.
+ fdata = Namespace
+
+ fdata.fname = fname
+ fdata.name = basename
+
+ pos = fname.find(ABI_DIR)
+ if pos > 0:
+ f = fname[pos:]
+ else:
+ f = fname
+
+ fdata.file_ref = (f, ref)
+ self.file_refs[f] = ref
+
+ fdata.ln = 0
+ fdata.what_ln = 0
+ fdata.tag = ""
+ fdata.label = ""
+ fdata.what = []
+ fdata.key = None
+ fdata.xrefs = None
+ fdata.space = None
+ fdata.ftype = path.split("/")[0]
+
+ fdata.nametag = {}
+ fdata.nametag["what"] = [f"ABI file {path}/{basename}"]
+ fdata.nametag["type"] = "File"
+ fdata.nametag["path"] = fdata.ftype
+ fdata.nametag["file"] = [fdata.file_ref]
+ fdata.nametag["line_no"] = 1
+ fdata.nametag["description"] = ""
+ fdata.nametag["symbols"] = []
+
+ self.data[ref] = fdata.nametag
+
+ if self.debug & AbiDebug.WHAT_OPEN:
+ self.log.debug("Opening file %s", fname)
+
+ if basename == "README":
+ self.parse_readme(fdata.nametag, fname)
+ return
+
+ with open(fname, "r", encoding="utf8", errors="backslashreplace") as fp:
+ for line in fp:
+ fdata.ln += 1
+
+ self._parse_line(fdata, line)
+
+ if "description" in fdata.nametag:
+ fdata.nametag["description"] = fdata.nametag["description"].lstrip("\n")
+
+ if fdata.key:
+ if "description" not in self.data.get(fdata.key, {}):
+ self.warn(fdata, f"{fdata.key} doesn't have a description")
+
+ for w in fdata.what:
+ self.add_symbol(what=w, fname=fname, xref=fdata.key)
+
+ def _parse_abi(self, root=None):
+ """Internal function to parse documentation ABI recursively"""
+
+ if not root:
+ root = self.directory
+
+ with os.scandir(root) as obj:
+ for entry in obj:
+ name = os.path.join(root, entry.name)
+
+ if entry.is_dir():
+ self._parse_abi(name)
+ continue
+
+ if not entry.is_file():
+ continue
+
+ basename = os.path.basename(name)
+
+ if basename.startswith("."):
+ continue
+
+ if basename.endswith(self.ignore_suffixes):
+ continue
+
+ path = self.re_abi_dir.sub("", os.path.dirname(name))
+
+ self.parse_file(name, path, basename)
+
+ def parse_abi(self, root=None):
+ """Parse documentation ABI"""
+
+ self._parse_abi(root)
+
+ if self.debug & AbiDebug.DUMP_ABI_STRUCTS:
+ self.log.debug(pformat(self.data))
+
+ def desc_txt(self, desc):
+ """Print description as found inside ABI files"""
+
+ desc = desc.strip(" \t\n")
+
+ return desc + "\n\n"
+
+ def xref(self, fname):
+ """
+ Converts a Documentation/ABI + basename into a ReST cross-reference
+ """
+
+ xref = self.file_refs.get(fname)
+ if not xref:
+ return None
+ else:
+ return xref
+
+ def desc_rst(self, desc):
+ """Enrich ReST output by creating cross-references"""
+
+ # Remove title markups from the description
+ # Having titles inside ABI files will only work if extra
+ # care would be taken in order to strictly follow the same
+ # level order for each markup.
+ desc = self.re_title_mark.sub("\n\n", "\n" + desc)
+ desc = desc.rstrip(" \t\n").lstrip("\n")
+
+ # Python's regex performance for non-compiled expressions is a lot
+ # than Perl, as Perl automatically caches them at their
+ # first usage. Here, we'll need to do the same, as otherwise the
+ # performance penalty is be high
+
+ new_desc = ""
+ for d in desc.split("\n"):
+ if d == "":
+ new_desc += "\n"
+ continue
+
+ # Use cross-references for doc files where needed
+ d = self.re_doc.sub(r":doc:`/\1`", d)
+
+ # Use cross-references for ABI generated docs where needed
+ matches = self.re_abi.findall(d)
+ for m in matches:
+ abi = m[0] + m[1]
+
+ xref = self.file_refs.get(abi)
+ if not xref:
+ # This may happen if ABI is on a separate directory,
+ # like parsing ABI testing and symbol is at stable.
+ # The proper solution is to move this part of the code
+ # for it to be inside sphinx/kernel_abi.py
+ self.log.info("Didn't find ABI reference for '%s'", abi)
+ else:
+ new = self.re_escape.sub(r"\\\1", m[1])
+ d = re.sub(fr"\b{abi}\b", f":ref:`{new} <{xref}>`", d)
+
+ # Seek for cross reference symbols like /sys/...
+ # Need to be careful to avoid doing it on a code block
+ if d[0] not in [" ", "\t"]:
+ matches = self.re_xref_node.findall(d)
+ for m in matches:
+ # Finding ABI here is more complex due to wildcards
+ xref = self.what_refs.get(m)
+ if xref:
+ new = self.re_escape.sub(r"\\\1", m)
+ d = re.sub(fr"\b{m}\b", f":ref:`{new} <{xref}>`", d)
+
+ new_desc += d + "\n"
+
+ return new_desc + "\n\n"
+
+ def doc(self, output_in_txt=False, show_symbols=True, show_file=True,
+ filter_path=None):
+ """Print ABI at stdout"""
+
+ part = None
+ for key, v in sorted(self.data.items(),
+ key=lambda x: (x[1].get("type", ""),
+ x[1].get("what"))):
+
+ wtype = v.get("type", "Symbol")
+ file_ref = v.get("file")
+ names = v.get("what", [""])
+
+ if wtype == "File":
+ if not show_file:
+ continue
+ else:
+ if not show_symbols:
+ continue
+
+ if filter_path:
+ if v.get("path") != filter_path:
+ continue
+
+ msg = ""
+
+ if wtype != "File":
+ cur_part = names[0]
+ if cur_part.find("/") >= 0:
+ match = self.re_what.match(cur_part)
+ if match:
+ symbol = match.group(1).rstrip("/")
+ cur_part = "Symbols under " + symbol
+
+ if cur_part and cur_part != part:
+ part = cur_part
+ msg += part + "\n"+ "-" * len(part) +"\n\n"
+
+ msg += f".. _{key}:\n\n"
+
+ max_len = 0
+ for i in range(0, len(names)): # pylint: disable=C0200
+ names[i] = "**" + self.re_escape.sub(r"\\\1", names[i]) + "**"
+
+ max_len = max(max_len, len(names[i]))
+
+ msg += "+-" + "-" * max_len + "-+\n"
+ for name in names:
+ msg += f"| {name}" + " " * (max_len - len(name)) + " |\n"
+ msg += "+-" + "-" * max_len + "-+\n"
+ msg += "\n"
+
+ for ref in file_ref:
+ if wtype == "File":
+ msg += f".. _{ref[1]}:\n\n"
+ else:
+ base = os.path.basename(ref[0])
+ msg += f"Defined on file :ref:`{base} <{ref[1]}>`\n\n"
+
+ if wtype == "File":
+ msg += names[0] +"\n" + "-" * len(names[0]) +"\n\n"
+
+ desc = v.get("description")
+ if not desc and wtype != "File":
+ msg += f"DESCRIPTION MISSING for {names[0]}\n\n"
+
+ if desc:
+ if output_in_txt:
+ msg += self.desc_txt(desc)
+ else:
+ msg += self.desc_rst(desc)
+
+ symbols = v.get("symbols")
+ if symbols:
+ msg += "Has the following ABI:\n\n"
+
+ for w, label in symbols:
+ # Escape special chars from content
+ content = self.re_escape.sub(r"\\\1", w)
+
+ msg += f"- :ref:`{content} <{label}>`\n\n"
+
+ users = v.get("users")
+ if users and users.strip(" \t\n"):
+ users = users.strip("\n").replace('\n', '\n\t')
+ msg += f"Users:\n\t{users}\n\n"
+
+ ln = v.get("line_no", 1)
+
+ yield (msg, file_ref[0][0], ln)
+
+ def check_issues(self):
+ """Warn about duplicated ABI entries"""
+
+ for what, v in self.what_symbols.items():
+ files = v.get("file")
+ if not files:
+ # Should never happen if the parser works properly
+ self.log.warning("%s doesn't have a file associated", what)
+ continue
+
+ if len(files) == 1:
+ continue
+
+ f = []
+ for fname, lines in sorted(files.items()):
+ if not lines:
+ f.append(f"{fname}")
+ elif len(lines) == 1:
+ f.append(f"{fname}:{lines[0]}")
+ else:
+ m = fname + "lines "
+ m += ", ".join(str(x) for x in lines)
+ f.append(m)
+
+ self.log.warning("%s is defined %d times: %s", what, len(f), "; ".join(f))
+
+ def search_symbols(self, expr):
+ """ Searches for ABI symbols """
+
+ regex = re.compile(expr, re.I)
+
+ found_keys = 0
+ for t in sorted(self.data.items(), key=lambda x: [0]):
+ v = t[1]
+
+ wtype = v.get("type", "")
+ if wtype == "File":
+ continue
+
+ for what in v.get("what", [""]):
+ if regex.search(what):
+ found_keys += 1
+
+ kernelversion = v.get("kernelversion", "").strip(" \t\n")
+ date = v.get("date", "").strip(" \t\n")
+ contact = v.get("contact", "").strip(" \t\n")
+ users = v.get("users", "").strip(" \t\n")
+ desc = v.get("description", "").strip(" \t\n")
+
+ files = []
+ for f in v.get("file", ()):
+ files.append(f[0])
+
+ what = str(found_keys) + ". " + what
+ title_tag = "-" * len(what)
+
+ print(f"\n{what}\n{title_tag}\n")
+
+ if kernelversion:
+ print(f"Kernel version:\t\t{kernelversion}")
+
+ if date:
+ print(f"Date:\t\t\t{date}")
+
+ if contact:
+ print(f"Contact:\t\t{contact}")
+
+ if users:
+ print(f"Users:\t\t\t{users}")
+
+ print("Defined on file(s):\t" + ", ".join(files))
+
+ if desc:
+ desc = desc.strip("\n")
+ print(f"\n{desc}\n")
+
+ if not found_keys:
+ print(f"Regular expression /{expr}/ not found.")
diff --git a/scripts/lib/abi/abi_regex.py b/scripts/lib/abi/abi_regex.py
new file mode 100644
index 000000000000..8a57846cbc69
--- /dev/null
+++ b/scripts/lib/abi/abi_regex.py
@@ -0,0 +1,234 @@
+#!/usr/bin/env python3
+# xxpylint: disable=R0903
+# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
+# SPDX-License-Identifier: GPL-2.0
+
+"""
+Convert ABI what into regular expressions
+"""
+
+import re
+import sys
+
+from pprint import pformat
+
+from abi_parser import AbiParser
+from helpers import AbiDebug
+
+class AbiRegex(AbiParser):
+ """Extends AbiParser to search ABI nodes with regular expressions"""
+
+ # Escape only ASCII visible characters
+ escape_symbols = r"([\x21-\x29\x2b-\x2d\x3a-\x40\x5c\x60\x7b-\x7e])"
+ leave_others = "others"
+
+ # Tuples with regular expressions to be compiled and replacement data
+ re_whats = [
+ # Drop escape characters that might exist
+ (re.compile("\\\\"), ""),
+
+ # Temporarily escape dot characters
+ (re.compile(r"\."), "\xf6"),
+
+ # Temporarily change [0-9]+ type of patterns
+ (re.compile(r"\[0\-9\]\+"), "\xff"),
+
+ # Temporarily change [\d+-\d+] type of patterns
+ (re.compile(r"\[0\-\d+\]"), "\xff"),
+ (re.compile(r"\[0:\d+\]"), "\xff"),
+ (re.compile(r"\[(\d+)\]"), "\xf4\\\\d+\xf5"),
+
+ # Temporarily change [0-9] type of patterns
+ (re.compile(r"\[(\d)\-(\d)\]"), "\xf4\1-\2\xf5"),
+
+ # Handle multiple option patterns
+ (re.compile(r"[\{\<\[]([\w_]+)(?:[,|]+([\w_]+)){1,}[\}\>\]]"), r"(\1|\2)"),
+
+ # Handle wildcards
+ (re.compile(r"([^\/])\*"), "\\1\\\\w\xf7"),
+ (re.compile(r"/\*/"), "/.*/"),
+ (re.compile(r"/\xf6\xf6\xf6"), "/.*"),
+ (re.compile(r"\<[^\>]+\>"), "\\\\w\xf7"),
+ (re.compile(r"\{[^\}]+\}"), "\\\\w\xf7"),
+ (re.compile(r"\[[^\]]+\]"), "\\\\w\xf7"),
+
+ (re.compile(r"XX+"), "\\\\w\xf7"),
+ (re.compile(r"([^A-Z])[XYZ]([^A-Z])"), "\\1\\\\w\xf7\\2"),
+ (re.compile(r"([^A-Z])[XYZ]$"), "\\1\\\\w\xf7"),
+ (re.compile(r"_[AB]_"), "_\\\\w\xf7_"),
+
+ # Recover [0-9] type of patterns
+ (re.compile(r"\xf4"), "["),
+ (re.compile(r"\xf5"), "]"),
+
+ # Remove duplicated spaces
+ (re.compile(r"\s+"), r" "),
+
+ # Special case: drop comparison as in:
+ # What: foo = <something>
+ # (this happens on a few IIO definitions)
+ (re.compile(r"\s*\=.*$"), ""),
+
+ # Escape all other symbols
+ (re.compile(escape_symbols), r"\\\1"),
+ (re.compile(r"\\\\"), r"\\"),
+ (re.compile(r"\\([\[\]\(\)\|])"), r"\1"),
+ (re.compile(r"(\d+)\\(-\d+)"), r"\1\2"),
+
+ (re.compile(r"\xff"), r"\\d+"),
+
+ # Special case: IIO ABI which a parenthesis.
+ (re.compile(r"sqrt(.*)"), r"sqrt(.*)"),
+
+ # Simplify regexes with multiple .*
+ (re.compile(r"(?:\.\*){2,}"), ""),
+
+ # Recover dot characters
+ (re.compile(r"\xf6"), "\\."),
+ # Recover plus characters
+ (re.compile(r"\xf7"), "+"),
+ ]
+ re_has_num = re.compile(r"\\d")
+
+ # Symbol name after escape_chars that are considered a devnode basename
+ re_symbol_name = re.compile(r"(\w|\\[\.\-\:])+$")
+
+ # List of popular group names to be skipped to minimize regex group size
+ # Use AbiDebug.SUBGROUP_SIZE to detect those
+ skip_names = set(["devices", "hwmon"])
+
+ def regex_append(self, what, new):
+ """
+ Get a search group for a subset of regular expressions.
+
+ As ABI may have thousands of symbols, using a for to search all
+ regular expressions is at least O(n^2). When there are wildcards,
+ the complexity increases substantially, eventually becoming exponential.
+
+ To avoid spending too much time on them, use a logic to split
+ them into groups. The smaller the group, the better, as it would
+ mean that searches will be confined to a small number of regular
+ expressions.
+
+ The conversion to a regex subset is tricky, as we need something
+ that can be easily obtained from the sysfs symbol and from the
+ regular expression. So, we need to discard nodes that have
+ wildcards.
+
+ If it can't obtain a subgroup, place the regular expression inside
+ a special group (self.leave_others).
+ """
+
+ search_group = None
+
+ for search_group in reversed(new.split("/")):
+ if not search_group or search_group in self.skip_names:
+ continue
+ if self.re_symbol_name.match(search_group):
+ break
+
+ if not search_group:
+ search_group = self.leave_others
+
+ if self.debug & AbiDebug.SUBGROUP_MAP:
+ self.log.debug("%s: mapped as %s", what, search_group)
+
+ try:
+ if search_group not in self.regex_group:
+ self.regex_group[search_group] = []
+
+ self.regex_group[search_group].append(re.compile(new))
+ if self.search_string:
+ if what.find(self.search_string) >= 0:
+ print(f"What: {what}")
+ except re.PatternError:
+ self.log.warning("Ignoring '%s' as it produced an invalid regex:\n"
+ " '%s'", what, new)
+
+ def get_regexes(self, what):
+ """
+ Given an ABI devnode, return a list of all regular expressions that
+ may match it, based on the sub-groups created by regex_append()
+ """
+
+ re_list = []
+
+ patches = what.split("/")
+ patches.reverse()
+ patches.append(self.leave_others)
+
+ for search_group in patches:
+ if search_group in self.regex_group:
+ re_list += self.regex_group[search_group]
+
+ return re_list
+
+ def __init__(self, *args, **kwargs):
+ """
+ Override init method to get verbose argument
+ """
+
+ self.regex_group = None
+ self.search_string = None
+ self.re_string = None
+
+ if "search_string" in kwargs:
+ self.search_string = kwargs.get("search_string")
+ del kwargs["search_string"]
+
+ if self.search_string:
+
+ try:
+ self.re_string = re.compile(self.search_string)
+ except re.PatternError as e:
+ msg = f"{self.search_string} is not a valid regular expression"
+ raise ValueError(msg) from e
+
+ super().__init__(*args, **kwargs)
+
+ def parse_abi(self, *args, **kwargs):
+
+ super().parse_abi(*args, **kwargs)
+
+ self.regex_group = {}
+
+ print("Converting ABI What fields into regexes...", file=sys.stderr)
+
+ for t in sorted(self.data.items(), key=lambda x: x[0]):
+ v = t[1]
+ if v.get("type") == "File":
+ continue
+
+ v["regex"] = []
+
+ for what in v.get("what", []):
+ if not what.startswith("/sys"):
+ continue
+
+ new = what
+ for r, s in self.re_whats:
+ try:
+ new = r.sub(s, new)
+ except re.PatternError as e:
+ # Help debugging troubles with new regexes
+ raise re.PatternError(f"{e}\nwhile re.sub('{r.pattern}', {s}, str)") from e
+
+ v["regex"].append(new)
+
+ if self.debug & AbiDebug.REGEX:
+ self.log.debug("%-90s <== %s", new, what)
+
+ # Store regex into a subgroup to speedup searches
+ self.regex_append(what, new)
+
+ if self.debug & AbiDebug.SUBGROUP_DICT:
+ self.log.debug("%s", pformat(self.regex_group))
+
+ if self.debug & AbiDebug.SUBGROUP_SIZE:
+ biggestd_keys = sorted(self.regex_group.keys(),
+ key= lambda k: len(self.regex_group[k]),
+ reverse=True)
+
+ print("Top regex subgroups:", file=sys.stderr)
+ for k in biggestd_keys[:10]:
+ print(f"{k} has {len(self.regex_group[k])} elements", file=sys.stderr)
diff --git a/scripts/lib/abi/helpers.py b/scripts/lib/abi/helpers.py
new file mode 100644
index 000000000000..639b23e4ca33
--- /dev/null
+++ b/scripts/lib/abi/helpers.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
+# pylint: disable=R0903
+# SPDX-License-Identifier: GPL-2.0
+
+"""
+Helper classes for ABI parser
+"""
+
+ABI_DIR = "Documentation/ABI/"
+
+
+class AbiDebug:
+ """Debug levels"""
+
+ WHAT_PARSING = 1
+ WHAT_OPEN = 2
+ DUMP_ABI_STRUCTS = 4
+ UNDEFINED = 8
+ REGEX = 16
+ SUBGROUP_MAP = 32
+ SUBGROUP_DICT = 64
+ SUBGROUP_SIZE = 128
+ GRAPH = 256
+
+
+DEBUG_HELP = """
+1 - enable debug parsing logic
+2 - enable debug messages on file open
+4 - enable debug for ABI parse data
+8 - enable extra debug information to identify troubles
+ with ABI symbols found at the local machine that
+ weren't found on ABI documentation (used only for
+ undefined subcommand)
+16 - enable debug for what to regex conversion
+32 - enable debug for symbol regex subgroups
+64 - enable debug for sysfs graph tree variable
+"""
diff --git a/scripts/lib/abi/system_symbols.py b/scripts/lib/abi/system_symbols.py
new file mode 100644
index 000000000000..f15c94a6e33c
--- /dev/null
+++ b/scripts/lib/abi/system_symbols.py
@@ -0,0 +1,378 @@
+#!/usr/bin/env python3
+# pylint: disable=R0902,R0912,R0914,R0915,R1702
+# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
+# SPDX-License-Identifier: GPL-2.0
+
+"""
+Parse ABI documentation and produce results from it.
+"""
+
+import os
+import re
+import sys
+
+from concurrent import futures
+from datetime import datetime
+from random import shuffle
+
+from helpers import AbiDebug
+
+class SystemSymbols:
+ """Stores arguments for the class and initialize class vars"""
+
+ def graph_add_file(self, path, link=None):
+ """
+ add a file path to the sysfs graph stored at self.root
+ """
+
+ if path in self.files:
+ return
+
+ name = ""
+ ref = self.root
+ for edge in path.split("/"):
+ name += edge + "/"
+ if edge not in ref:
+ ref[edge] = {"__name": [name.rstrip("/")]}
+
+ ref = ref[edge]
+
+ if link and link not in ref["__name"]:
+ ref["__name"].append(link.rstrip("/"))
+
+ self.files.add(path)
+
+ def print_graph(self, root_prefix="", root=None, level=0):
+ """Prints a reference tree graph using UTF-8 characters"""
+
+ if not root:
+ root = self.root
+ level = 0
+
+ # Prevent endless traverse
+ if level > 5:
+ return
+
+ if level > 0:
+ prefix = "├──"
+ last_prefix = "└──"
+ else:
+ prefix = ""
+ last_prefix = ""
+
+ items = list(root.items())
+
+ names = root.get("__name", [])
+ for k, edge in items:
+ if k == "__name":
+ continue
+
+ if not k:
+ k = "/"
+
+ if len(names) > 1:
+ k += " links: " + ",".join(names[1:])
+
+ if edge == items[-1][1]:
+ print(root_prefix + last_prefix + k)
+ p = root_prefix
+ if level > 0:
+ p += " "
+ self.print_graph(p, edge, level + 1)
+ else:
+ print(root_prefix + prefix + k)
+ p = root_prefix + "│ "
+ self.print_graph(p, edge, level + 1)
+
+ def _walk(self, root):
+ """
+ Walk through sysfs to get all devnodes that aren't ignored.
+
+ By default, uses /sys as sysfs mounting point. If another
+ directory is used, it replaces them to /sys at the patches.
+ """
+
+ with os.scandir(root) as obj:
+ for entry in obj:
+ path = os.path.join(root, entry.name)
+ if self.sysfs:
+ p = path.replace(self.sysfs, "/sys", count=1)
+ else:
+ p = path
+
+ if self.re_ignore.search(p):
+ return
+
+ # Handle link first to avoid directory recursion
+ if entry.is_symlink():
+ real = os.path.realpath(path)
+ if not self.sysfs:
+ self.aliases[path] = real
+ else:
+ real = real.replace(self.sysfs, "/sys", count=1)
+
+ # Add absfile location to graph if it doesn't exist
+ if not self.re_ignore.search(real):
+ # Add link to the graph
+ self.graph_add_file(real, p)
+
+ elif entry.is_file():
+ self.graph_add_file(p)
+
+ elif entry.is_dir():
+ self._walk(path)
+
+ def __init__(self, abi, sysfs="/sys", hints=False):
+ """
+ Initialize internal variables and get a list of all files inside
+ sysfs that can currently be parsed.
+
+ Please notice that there are several entries on sysfs that aren't
+ documented as ABI. Ignore those.
+
+ The real paths will be stored under self.files. Aliases will be
+ stored in separate, as self.aliases.
+ """
+
+ self.abi = abi
+ self.log = abi.log
+
+ if sysfs != "/sys":
+ self.sysfs = sysfs.rstrip("/")
+ else:
+ self.sysfs = None
+
+ self.hints = hints
+
+ self.root = {}
+ self.aliases = {}
+ self.files = set()
+
+ dont_walk = [
+ # Those require root access and aren't documented at ABI
+ f"^{sysfs}/kernel/debug",
+ f"^{sysfs}/kernel/tracing",
+ f"^{sysfs}/fs/pstore",
+ f"^{sysfs}/fs/bpf",
+ f"^{sysfs}/fs/fuse",
+
+ # This is not documented at ABI
+ f"^{sysfs}/module",
+
+ f"^{sysfs}/fs/cgroup", # this is big and has zero docs under ABI
+ f"^{sysfs}/firmware", # documented elsewhere: ACPI, DT bindings
+ "sections|notes", # aren't actually part of ABI
+
+ # kernel-parameters.txt - not easy to parse
+ "parameters",
+ ]
+
+ self.re_ignore = re.compile("|".join(dont_walk))
+
+ print(f"Reading {sysfs} directory contents...", file=sys.stderr)
+ self._walk(sysfs)
+
+ def check_file(self, refs, found):
+ """Check missing ABI symbols for a given sysfs file"""
+
+ res_list = []
+
+ try:
+ for names in refs:
+ fname = names[0]
+
+ res = {
+ "found": False,
+ "fname": fname,
+ "msg": "",
+ }
+ res_list.append(res)
+
+ re_what = self.abi.get_regexes(fname)
+ if not re_what:
+ self.abi.log.warning(f"missing rules for {fname}")
+ continue
+
+ for name in names:
+ for r in re_what:
+ if self.abi.debug & AbiDebug.UNDEFINED:
+ self.log.debug("check if %s matches '%s'", name, r.pattern)
+ if r.match(name):
+ res["found"] = True
+ if found:
+ res["msg"] += f" {fname}: regex:\n\t"
+ continue
+
+ if self.hints and not res["found"]:
+ res["msg"] += f" {fname} not found. Tested regexes:\n"
+ for r in re_what:
+ res["msg"] += " " + r.pattern + "\n"
+
+ except KeyboardInterrupt:
+ pass
+
+ return res_list
+
+ def _ref_interactor(self, root):
+ """Recursive function to interact over the sysfs tree"""
+
+ for k, v in root.items():
+ if isinstance(v, dict):
+ yield from self._ref_interactor(v)
+
+ if root == self.root or k == "__name":
+ continue
+
+ if self.abi.re_string:
+ fname = v["__name"][0]
+ if self.abi.re_string.search(fname):
+ yield v
+ else:
+ yield v
+
+
+ def get_fileref(self, all_refs, chunk_size):
+ """Interactor to group refs into chunks"""
+
+ n = 0
+ refs = []
+
+ for ref in all_refs:
+ refs.append(ref)
+
+ n += 1
+ if n >= chunk_size:
+ yield refs
+ n = 0
+ refs = []
+
+ yield refs
+
+ def check_undefined_symbols(self, max_workers=None, chunk_size=50,
+ found=None, dry_run=None):
+ """Seach ABI for sysfs symbols missing documentation"""
+
+ self.abi.parse_abi()
+
+ if self.abi.debug & AbiDebug.GRAPH:
+ self.print_graph()
+
+ all_refs = []
+ for ref in self._ref_interactor(self.root):
+ all_refs.append(ref["__name"])
+
+ if dry_run:
+ print("Would check", file=sys.stderr)
+ for ref in all_refs:
+ print(", ".join(ref))
+
+ return
+
+ print("Starting to search symbols (it may take several minutes):",
+ file=sys.stderr)
+ start = datetime.now()
+ old_elapsed = None
+
+ # Python doesn't support multithreading due to limitations on its
+ # global lock (GIL). While Python 3.13 finally made GIL optional,
+ # there are still issues related to it. Also, we want to have
+ # backward compatibility with older versions of Python.
+ #
+ # So, use instead multiprocess. However, Python is very slow passing
+ # data from/to multiple processes. Also, it may consume lots of memory
+ # if the data to be shared is not small. So, we need to group workload
+ # in chunks that are big enough to generate performance gains while
+ # not being so big that would cause out-of-memory.
+
+ num_refs = len(all_refs)
+ print(f"Number of references to parse: {num_refs}", file=sys.stderr)
+
+ if not max_workers:
+ max_workers = os.cpu_count()
+ elif max_workers > os.cpu_count():
+ max_workers = os.cpu_count()
+
+ max_workers = max(max_workers, 1)
+
+ max_chunk_size = int((num_refs + max_workers - 1) / max_workers)
+ chunk_size = min(chunk_size, max_chunk_size)
+ chunk_size = max(1, chunk_size)
+
+ if max_workers > 1:
+ executor = futures.ProcessPoolExecutor
+
+ # Place references in a random order. This may help improving
+ # performance, by mixing complex/simple expressions when creating
+ # chunks
+ shuffle(all_refs)
+ else:
+ # Python has a high overhead with processes. When there's just
+ # one worker, it is faster to not create a new process.
+ # Yet, User still deserves to have a progress print. So, use
+ # python's "thread", which is actually a single process, using
+ # an internal schedule to switch between tasks. No performance
+ # gains for non-IO tasks, but still it can be quickly interrupted
+ # from time to time to display progress.
+ executor = futures.ThreadPoolExecutor
+
+ not_found = []
+ f_list = []
+ with executor(max_workers=max_workers) as exe:
+ for refs in self.get_fileref(all_refs, chunk_size):
+ if refs:
+ try:
+ f_list.append(exe.submit(self.check_file, refs, found))
+
+ except KeyboardInterrupt:
+ return
+
+ total = len(f_list)
+
+ if not total:
+ if self.abi.re_string:
+ print(f"No ABI symbol matches {self.abi.search_string}")
+ else:
+ self.abi.log.warning("No ABI symbols found")
+ return
+
+ print(f"{len(f_list):6d} jobs queued on {max_workers} workers",
+ file=sys.stderr)
+
+ while f_list:
+ try:
+ t = futures.wait(f_list, timeout=1,
+ return_when=futures.FIRST_COMPLETED)
+
+ done = t[0]
+
+ for fut in done:
+ res_list = fut.result()
+
+ for res in res_list:
+ if not res["found"]:
+ not_found.append(res["fname"])
+ if res["msg"]:
+ print(res["msg"])
+
+ f_list.remove(fut)
+ except KeyboardInterrupt:
+ return
+
+ except RuntimeError as e:
+ self.abi.log.warning(f"Future: {e}")
+ break
+
+ if sys.stderr.isatty():
+ elapsed = str(datetime.now() - start).split(".", maxsplit=1)[0]
+ if len(f_list) < total:
+ elapsed += f" ({total - len(f_list)}/{total} jobs completed). "
+ if elapsed != old_elapsed:
+ print(elapsed + "\r", end="", flush=True,
+ file=sys.stderr)
+ old_elapsed = elapsed
+
+ elapsed = str(datetime.now() - start).split(".", maxsplit=1)[0]
+ print(elapsed, file=sys.stderr)
+
+ for f in sorted(not_found):
+ print(f"{f} not found.")
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index f7b2503cdba9..51367c2bfc21 100755
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -31,6 +31,7 @@ set -e
LD="$1"
KBUILD_LDFLAGS="$2"
LDFLAGS_vmlinux="$3"
+VMLINUX="$4"
is_enabled() {
grep -q "^$1=y" include/config/auto.conf
@@ -68,6 +69,10 @@ vmlinux_link()
libs="${KBUILD_VMLINUX_LIBS}"
fi
+ if is_enabled CONFIG_GENERIC_BUILTIN_DTB; then
+ objs="${objs} .builtin-dtbs.o"
+ fi
+
if is_enabled CONFIG_MODULES; then
objs="${objs} .vmlinux.export.o"
fi
@@ -93,34 +98,22 @@ vmlinux_link()
ldflags="${ldflags} ${wl}--strip-debug"
fi
- if is_enabled CONFIG_VMLINUX_MAP; then
- ldflags="${ldflags} ${wl}-Map=${output}.map"
+ if [ -n "${generate_map}" ]; then
+ ldflags="${ldflags} ${wl}-Map=vmlinux.map"
fi
${ld} ${ldflags} -o ${output} \
${wl}--whole-archive ${objs} ${wl}--no-whole-archive \
${wl}--start-group ${libs} ${wl}--end-group \
- ${kallsymso} ${btf_vmlinux_bin_o} ${ldlibs}
+ ${kallsymso} ${btf_vmlinux_bin_o} ${arch_vmlinux_o} ${ldlibs}
}
# generate .BTF typeinfo from DWARF debuginfo
# ${1} - vmlinux image
gen_btf()
{
- local pahole_ver
local btf_data=${1}.btf.o
- if ! [ -x "$(command -v ${PAHOLE})" ]; then
- echo >&2 "BTF: ${1}: pahole (${PAHOLE}) is not available"
- return 1
- fi
-
- pahole_ver=$(${PAHOLE} --version | sed -E 's/v([0-9]+)\.([0-9]+)/\1\2/')
- if [ "${pahole_ver}" -lt "116" ]; then
- echo >&2 "BTF: ${1}: pahole version $(${PAHOLE} --version) is too old, need at least v1.16"
- return 1
- fi
-
info BTF "${btf_data}"
LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${PAHOLE_FLAGS} ${1}
@@ -152,14 +145,6 @@ kallsyms()
kallsymopt="${kallsymopt} --all-symbols"
fi
- if is_enabled CONFIG_KALLSYMS_ABSOLUTE_PERCPU; then
- kallsymopt="${kallsymopt} --absolute-percpu"
- fi
-
- if is_enabled CONFIG_LTO_CLANG; then
- kallsymopt="${kallsymopt} --lto-clang"
- fi
-
info KSYMS "${2}.S"
scripts/kallsyms ${kallsymopt} "${1}" > "${2}.S"
@@ -189,12 +174,14 @@ mksysmap()
sorttable()
{
- ${objtree}/scripts/sorttable ${1}
+ ${NM} -S ${1} > .tmp_vmlinux.nm-sort
+ ${objtree}/scripts/sorttable -s .tmp_vmlinux.nm-sort ${1}
}
cleanup()
{
rm -f .btf.*
+ rm -f .tmp_vmlinux.nm-sort
rm -f System.map
rm -f vmlinux
rm -f vmlinux.map
@@ -214,12 +201,19 @@ fi
${MAKE} -f "${srctree}/scripts/Makefile.build" obj=init init/version-timestamp.o
+arch_vmlinux_o=
+if is_enabled CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX; then
+ arch_vmlinux_o=arch/${SRCARCH}/tools/vmlinux.arch.o
+fi
+
btf_vmlinux_bin_o=
kallsymso=
strip_debug=
+generate_map=
if is_enabled CONFIG_KALLSYMS; then
- kallsyms /dev/null .tmp_vmlinux0.kallsyms
+ true > .tmp_vmlinux0.syms
+ kallsyms .tmp_vmlinux0.syms .tmp_vmlinux0.kallsyms
fi
if is_enabled CONFIG_KALLSYMS || is_enabled CONFIG_DEBUG_INFO_BTF; then
@@ -246,14 +240,14 @@ if is_enabled CONFIG_KALLSYMS; then
# Generate section listing all symbols and add it into vmlinux
# It's a four step process:
# 0) Generate a dummy __kallsyms with empty symbol list.
- # 1) Link .tmp_vmlinux.kallsyms1 so it has all symbols and sections,
+ # 1) Link .tmp_vmlinux1.kallsyms so it has all symbols and sections,
# with a dummy __kallsyms.
- # Running kallsyms on that gives us .tmp_kallsyms1.o with
+ # Running kallsyms on that gives us .tmp_vmlinux1.kallsyms.o with
# the right size
- # 2) Link .tmp_vmlinux.kallsyms2 so it now has a __kallsyms section of
+ # 2) Link .tmp_vmlinux2.kallsyms so it now has a __kallsyms section of
# the right size, but due to the added section, some
# addresses have shifted.
- # From here, we generate a correct .tmp_vmlinux.kallsyms2.o
+ # From here, we generate a correct .tmp_vmlinux2.kallsyms.o
# 3) That link may have expanded the kernel image enough that
# more linker branch stubs / trampolines had to be added, which
# introduces new names, which further expands kallsyms. Do another
@@ -284,19 +278,27 @@ fi
strip_debug=
-vmlinux_link vmlinux
+if is_enabled CONFIG_VMLINUX_MAP; then
+ generate_map=1
+fi
+
+vmlinux_link "${VMLINUX}"
# fill in BTF IDs
-if is_enabled CONFIG_DEBUG_INFO_BTF && is_enabled CONFIG_BPF; then
- info BTFIDS vmlinux
- ${RESOLVE_BTFIDS} vmlinux
+if is_enabled CONFIG_DEBUG_INFO_BTF; then
+ info BTFIDS "${VMLINUX}"
+ RESOLVE_BTFIDS_ARGS=""
+ if is_enabled CONFIG_WERROR; then
+ RESOLVE_BTFIDS_ARGS=" --fatal_warnings "
+ fi
+ ${RESOLVE_BTFIDS} ${RESOLVE_BTFIDS_ARGS} "${VMLINUX}"
fi
-mksysmap vmlinux System.map
+mksysmap "${VMLINUX}" System.map
if is_enabled CONFIG_BUILDTIME_TABLE_SORT; then
- info SORTTAB vmlinux
- if ! sorttable vmlinux; then
+ info SORTTAB "${VMLINUX}"
+ if ! sorttable "${VMLINUX}"; then
echo >&2 Failed to sort kernel tables
exit 1
fi
@@ -312,4 +314,4 @@ if is_enabled CONFIG_KALLSYMS; then
fi
# For fixdep
-echo "vmlinux: $0" > .vmlinux.d
+echo "${VMLINUX}: $0" > ".${VMLINUX}.d"
diff --git a/scripts/macro_checker.py b/scripts/macro_checker.py
new file mode 100755
index 000000000000..ba550982e98f
--- /dev/null
+++ b/scripts/macro_checker.py
@@ -0,0 +1,131 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: GPL-2.0
+# Author: Julian Sun <sunjunchao2870@gmail.com>
+
+""" Find macro definitions with unused parameters. """
+
+import argparse
+import os
+import re
+
+parser = argparse.ArgumentParser()
+
+parser.add_argument("path", type=str, help="The file or dir path that needs check")
+parser.add_argument("-v", "--verbose", action="store_true",
+ help="Check conditional macros, but may lead to more false positives")
+args = parser.parse_args()
+
+macro_pattern = r"#define\s+(\w+)\(([^)]*)\)"
+# below vars were used to reduce false positives
+fp_patterns = [r"\s*do\s*\{\s*\}\s*while\s*\(\s*0\s*\)",
+ r"\(?0\)?", r"\(?1\)?"]
+correct_macros = []
+cond_compile_mark = "#if"
+cond_compile_end = "#endif"
+
+def check_macro(macro_line, report):
+ match = re.match(macro_pattern, macro_line)
+ if match:
+ macro_def = re.sub(macro_pattern, '', macro_line)
+ identifier = match.group(1)
+ content = match.group(2)
+ arguments = [item.strip() for item in content.split(',') if item.strip()]
+
+ macro_def = macro_def.strip()
+ if not macro_def:
+ return
+ # used to reduce false positives, like #define endfor_nexthops(rt) }
+ if len(macro_def) == 1:
+ return
+
+ for fp_pattern in fp_patterns:
+ if (re.match(fp_pattern, macro_def)):
+ return
+
+ for arg in arguments:
+ # used to reduce false positives
+ if "..." in arg:
+ return
+ for arg in arguments:
+ if not arg in macro_def and report == False:
+ return
+ # if there is a correct macro with the same name, do not report it.
+ if not arg in macro_def and identifier not in correct_macros:
+ print(f"Argument {arg} is not used in function-line macro {identifier}")
+ return
+
+ correct_macros.append(identifier)
+
+
+# remove comment and whitespace
+def macro_strip(macro):
+ comment_pattern1 = r"\/\/*"
+ comment_pattern2 = r"\/\**\*\/"
+
+ macro = macro.strip()
+ macro = re.sub(comment_pattern1, '', macro)
+ macro = re.sub(comment_pattern2, '', macro)
+
+ return macro
+
+def file_check_macro(file_path, report):
+ # number of conditional compiling
+ cond_compile = 0
+ # only check .c and .h file
+ if not file_path.endswith(".c") and not file_path.endswith(".h"):
+ return
+
+ with open(file_path, "r") as f:
+ while True:
+ line = f.readline()
+ if not line:
+ break
+ line = line.strip()
+ if line.startswith(cond_compile_mark):
+ cond_compile += 1
+ continue
+ if line.startswith(cond_compile_end):
+ cond_compile -= 1
+ continue
+
+ macro = re.match(macro_pattern, line)
+ if macro:
+ macro = macro_strip(macro.string)
+ while macro[-1] == '\\':
+ macro = macro[0:-1]
+ macro = macro.strip()
+ macro += f.readline()
+ macro = macro_strip(macro)
+ if not args.verbose:
+ if file_path.endswith(".c") and cond_compile != 0:
+ continue
+ # 1 is for #ifdef xxx at the beginning of the header file
+ if file_path.endswith(".h") and cond_compile != 1:
+ continue
+ check_macro(macro, report)
+
+def get_correct_macros(path):
+ file_check_macro(path, False)
+
+def dir_check_macro(dir_path):
+
+ for dentry in os.listdir(dir_path):
+ path = os.path.join(dir_path, dentry)
+ if os.path.isdir(path):
+ dir_check_macro(path)
+ elif os.path.isfile(path):
+ get_correct_macros(path)
+ file_check_macro(path, True)
+
+
+def main():
+ if os.path.isfile(args.path):
+ get_correct_macros(args.path)
+ file_check_macro(args.path, True)
+ elif os.path.isdir(args.path):
+ dir_check_macro(args.path)
+ else:
+ print(f"{args.path} doesn't exit or is neither a file nor a dir")
+
+if __name__ == "__main__":
+ main() \ No newline at end of file
diff --git a/scripts/make_fit.py b/scripts/make_fit.py
index 4a1bb2f55861..1683e5ec6e67 100755
--- a/scripts/make_fit.py
+++ b/scripts/make_fit.py
@@ -279,7 +279,11 @@ def build_fit(args):
if os.path.splitext(fname)[1] != '.dtb':
continue
- (model, compat, files) = process_dtb(fname, args)
+ try:
+ (model, compat, files) = process_dtb(fname, args)
+ except Exception as e:
+ sys.stderr.write(f"Error processing {fname}:\n")
+ raise e
for fn in files:
if fn not in fdts:
diff --git a/scripts/min-tool-version.sh b/scripts/min-tool-version.sh
index 91c91201212c..787868183b84 100755
--- a/scripts/min-tool-version.sh
+++ b/scripts/min-tool-version.sh
@@ -19,12 +19,14 @@ binutils)
gcc)
if [ "$ARCH" = parisc64 ]; then
echo 12.0.0
+ elif [ "$SRCARCH" = x86 ]; then
+ echo 8.1.0
else
echo 5.1.0
fi
;;
llvm)
- if [ "$SRCARCH" = s390 ]; then
+ if [ "$SRCARCH" = s390 -o "$SRCARCH" = x86 ]; then
echo 15.0.0
elif [ "$SRCARCH" = loongarch ]; then
echo 18.0.0
diff --git a/scripts/mksysmap b/scripts/mksysmap
index c12723a04655..3accbdb269ac 100755
--- a/scripts/mksysmap
+++ b/scripts/mksysmap
@@ -26,7 +26,7 @@
# (do not forget a space before each pattern)
# local symbols for ARM, MIPS, etc.
-/ \\$/d
+/ \$/d
# local labels, .LBB, .Ltmpxxx, .L__unnamed_xx, .LASANPC, etc.
/ \.L/d
@@ -39,7 +39,7 @@
/ __pi_\.L/d
# arm64 local symbols in non-VHE KVM namespace
-/ __kvm_nvhe_\\$/d
+/ __kvm_nvhe_\$/d
/ __kvm_nvhe_\.L/d
# lld arm/aarch64/mips thunks
diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c
index 518200813d4e..d3d00e85edf7 100644
--- a/scripts/mod/devicetable-offsets.c
+++ b/scripts/mod/devicetable-offsets.c
@@ -153,6 +153,10 @@ int main(void)
DEVID_FIELD(i3c_device_id, part_id);
DEVID_FIELD(i3c_device_id, extra_info);
+ DEVID(slim_device_id);
+ DEVID_FIELD(slim_device_id, manf_id);
+ DEVID_FIELD(slim_device_id, prod_code);
+
DEVID(spi_device_id);
DEVID_FIELD(spi_device_id, name);
@@ -233,7 +237,6 @@ int main(void)
DEVID(typec_device_id);
DEVID_FIELD(typec_device_id, svid);
- DEVID_FIELD(typec_device_id, mode);
DEVID(tee_client_device_id);
DEVID_FIELD(tee_client_device_id, uuid);
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index 5d1c61fa5a55..00586119a25b 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -10,6 +10,12 @@
* of the GNU General Public License, incorporated herein by reference.
*/
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "list.h"
+#include "xalloc.h"
+
#include "modpost.h"
#include "devicetable-offsets.h"
@@ -31,6 +37,66 @@ typedef Elf64_Addr kernel_ulong_t;
#include <ctype.h>
#include <stdbool.h>
+/**
+ * module_alias_printf - add auto-generated MODULE_ALIAS()
+ *
+ * @mod: module
+ * @append_wildcard: append '*' for future extension if not exist yet
+ * @fmt: printf(3)-like format
+ */
+static void __attribute__((format (printf, 3, 4)))
+module_alias_printf(struct module *mod, bool append_wildcard,
+ const char *fmt, ...)
+{
+ struct module_alias *new, *als;
+ size_t len;
+ int n;
+ va_list ap;
+
+ /* Determine required size. */
+ va_start(ap, fmt);
+ n = vsnprintf(NULL, 0, fmt, ap);
+ va_end(ap);
+
+ if (n < 0) {
+ error("vsnprintf failed\n");
+ return;
+ }
+
+ len = n + 1; /* extra byte for '\0' */
+
+ if (append_wildcard)
+ len++; /* extra byte for '*' */
+
+ new = xmalloc(sizeof(*new) + len);
+
+ /* Now, really print it to the allocated buffer */
+ va_start(ap, fmt);
+ n = vsnprintf(new->str, len, fmt, ap);
+ va_end(ap);
+
+ if (n < 0) {
+ error("vsnprintf failed\n");
+ free(new);
+ return;
+ }
+
+ if (append_wildcard && (n == 0 || new->str[n - 1] != '*')) {
+ new->str[n] = '*';
+ new->str[n + 1] = '\0';
+ }
+
+ /* avoid duplication */
+ list_for_each_entry(als, &mod->aliases, node) {
+ if (!strcmp(als->str, new->str)) {
+ free(new);
+ return;
+ }
+ }
+
+ list_add_tail(&new->node, &mod->aliases);
+}
+
typedef uint32_t __u32;
typedef uint16_t __u16;
typedef unsigned char __u8;
@@ -56,35 +122,25 @@ typedef struct {
* we handle those differences explicitly below */
#include "../../include/linux/mod_devicetable.h"
-/* This array collects all instances that use the generic do_table */
struct devtable {
- const char *device_id; /* name of table, __mod_<name>__*_device_table. */
+ const char *device_id;
unsigned long id_size;
- int (*do_entry)(const char *filename, void *symval, char *alias);
+ void (*do_entry)(struct module *mod, void *symval);
};
-/* Size of alias provided to do_entry functions */
-#define ALIAS_SIZE 500
-
/* Define a variable f that holds the value of field f of struct devid
* based at address m.
*/
#define DEF_FIELD(m, devid, f) \
- typeof(((struct devid *)0)->f) f = TO_NATIVE(*(typeof(f) *)((m) + OFF_##devid##_##f))
-
-/* Define a variable v that holds the address of field f of struct devid
- * based at address m. Due to the way typeof works, for a field of type
- * T[N] the variable has type T(*)[N], _not_ T*.
- */
-#define DEF_FIELD_ADDR_VAR(m, devid, f, v) \
- typeof(((struct devid *)0)->f) *v = ((m) + OFF_##devid##_##f)
+ typeof(((struct devid *)0)->f) f = \
+ get_unaligned_native((typeof(f) *)((m) + OFF_##devid##_##f))
/* Define a variable f that holds the address of field f of struct devid
* based at address m. Due to the way typeof works, for a field of type
* T[N] the variable has type T(*)[N], _not_ T*.
*/
#define DEF_FIELD_ADDR(m, devid, f) \
- DEF_FIELD_ADDR_VAR(m, devid, f, f)
+ typeof(((struct devid *)0)->f) *f = ((m) + OFF_##devid##_##f)
#define ADD(str, sep, cond, field) \
do { \
@@ -99,15 +155,6 @@ do { \
sprintf(str + strlen(str), "*"); \
} while(0)
-/* End in a wildcard, for future extension */
-static inline void add_wildcard(char *str)
-{
- int len = strlen(str);
-
- if (str[len - 1] != '*')
- strcat(str + len, "*");
-}
-
static inline void add_uuid(char *str, uuid_le uuid)
{
int len = strlen(str);
@@ -130,40 +177,6 @@ static inline void add_guid(char *str, guid_t guid)
guid.b[12], guid.b[13], guid.b[14], guid.b[15]);
}
-/**
- * Check that sizeof(device_id type) are consistent with size of section
- * in .o file. If in-consistent then userspace and kernel does not agree
- * on actual size which is a bug.
- * Also verify that the final entry in the table is all zeros.
- * Ignore both checks if build host differ from target host and size differs.
- **/
-static void device_id_check(const char *modname, const char *device_id,
- unsigned long size, unsigned long id_size,
- void *symval)
-{
- int i;
-
- if (size % id_size || size < id_size) {
- fatal("%s: sizeof(struct %s_device_id)=%lu is not a modulo of the size of section __mod_%s__<identifier>_device_table=%lu.\n"
- "Fix definition of struct %s_device_id in mod_devicetable.h\n",
- modname, device_id, id_size, device_id, size, device_id);
- }
- /* Verify last one is a terminator */
- for (i = 0; i < id_size; i++ ) {
- if (*(uint8_t*)(symval+size-id_size+i)) {
- fprintf(stderr,
- "%s: struct %s_device_id is %lu bytes. The last of %lu is:\n",
- modname, device_id, id_size, size / id_size);
- for (i = 0; i < id_size; i++ )
- fprintf(stderr,"0x%02x ",
- *(uint8_t*)(symval+size-id_size+i) );
- fprintf(stderr,"\n");
- fatal("%s: struct %s_device_id is not terminated with a NULL entry!\n",
- modname, device_id);
- }
- }
-}
-
/* USB is special because the bcdDevice can be matched against a numeric range */
/* Looks like "usb:vNpNdNdcNdscNdpNicNiscNipNinN" */
static void do_usb_entry(void *symval,
@@ -229,9 +242,7 @@ static void do_usb_entry(void *symval,
ADD(alias, "in", match_flags&USB_DEVICE_ID_MATCH_INT_NUMBER,
bInterfaceNumber);
- add_wildcard(alias);
- buf_printf(&mod->dev_table_buf,
- "MODULE_ALIAS(\"%s\");\n", alias);
+ module_alias_printf(mod, true, "%s", alias);
}
/* Handles increment/decrement of BCD formatted integers */
@@ -273,7 +284,7 @@ static unsigned int incbcd(unsigned int *bcd,
return init;
}
-static void do_usb_entry_multi(void *symval, struct module *mod)
+static void do_usb_entry_multi(struct module *mod, void *symval)
{
unsigned int devlo, devhi;
unsigned char chi, clo, max;
@@ -338,22 +349,7 @@ static void do_usb_entry_multi(void *symval, struct module *mod)
}
}
-static void do_usb_table(void *symval, unsigned long size,
- struct module *mod)
-{
- unsigned int i;
- const unsigned long id_size = SIZE_usb_device_id;
-
- device_id_check(mod->name, "usb", size, id_size, symval);
-
- /* Leave last one: it's the terminator. */
- size -= id_size;
-
- for (i = 0; i < size; i += id_size)
- do_usb_entry_multi(symval + i, mod);
-}
-
-static void do_of_entry_multi(void *symval, struct module *mod)
+static void do_of_entry(struct module *mod, void *symval)
{
char alias[500];
int len;
@@ -375,56 +371,39 @@ static void do_of_entry_multi(void *symval, struct module *mod)
if (isspace(*tmp))
*tmp = '_';
- buf_printf(&mod->dev_table_buf, "MODULE_ALIAS(\"%s\");\n", alias);
- strcat(alias, "C");
- add_wildcard(alias);
- buf_printf(&mod->dev_table_buf, "MODULE_ALIAS(\"%s\");\n", alias);
-}
-
-static void do_of_table(void *symval, unsigned long size,
- struct module *mod)
-{
- unsigned int i;
- const unsigned long id_size = SIZE_of_device_id;
-
- device_id_check(mod->name, "of", size, id_size, symval);
-
- /* Leave last one: it's the terminator. */
- size -= id_size;
-
- for (i = 0; i < size; i += id_size)
- do_of_entry_multi(symval + i, mod);
+ module_alias_printf(mod, false, "%s", alias);
+ module_alias_printf(mod, false, "%sC*", alias);
}
/* Looks like: hid:bNvNpN */
-static int do_hid_entry(const char *filename,
- void *symval, char *alias)
+static void do_hid_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
+
DEF_FIELD(symval, hid_device_id, bus);
DEF_FIELD(symval, hid_device_id, group);
DEF_FIELD(symval, hid_device_id, vendor);
DEF_FIELD(symval, hid_device_id, product);
- sprintf(alias, "hid:");
ADD(alias, "b", bus != HID_BUS_ANY, bus);
ADD(alias, "g", group != HID_GROUP_ANY, group);
ADD(alias, "v", vendor != HID_ANY_ID, vendor);
ADD(alias, "p", product != HID_ANY_ID, product);
- return 1;
+ module_alias_printf(mod, false, "hid:%s", alias);
}
/* Looks like: ieee1394:venNmoNspNverN */
-static int do_ieee1394_entry(const char *filename,
- void *symval, char *alias)
+static void do_ieee1394_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
+
DEF_FIELD(symval, ieee1394_device_id, match_flags);
DEF_FIELD(symval, ieee1394_device_id, vendor_id);
DEF_FIELD(symval, ieee1394_device_id, model_id);
DEF_FIELD(symval, ieee1394_device_id, specifier_id);
DEF_FIELD(symval, ieee1394_device_id, version);
- strcpy(alias, "ieee1394:");
ADD(alias, "ven", match_flags & IEEE1394_MATCH_VENDOR_ID,
vendor_id);
ADD(alias, "mo", match_flags & IEEE1394_MATCH_MODEL_ID,
@@ -434,14 +413,13 @@ static int do_ieee1394_entry(const char *filename,
ADD(alias, "ver", match_flags & IEEE1394_MATCH_VERSION,
version);
- add_wildcard(alias);
- return 1;
+ module_alias_printf(mod, true, "ieee1394:%s", alias);
}
/* Looks like: pci:vNdNsvNsdNbcNscNiN or <prefix>_pci:vNdNsvNsdNbcNscNiN. */
-static int do_pci_entry(const char *filename,
- void *symval, char *alias)
+static void do_pci_entry(struct module *mod, void *symval)
{
+ char alias[256];
/* Class field can be divided into these three. */
unsigned char baseclass, subclass, interface,
baseclass_mask, subclass_mask, interface_mask;
@@ -464,7 +442,6 @@ static int do_pci_entry(const char *filename,
default:
warn("Unknown PCI driver_override alias %08X\n",
override_only);
- return 0;
}
ADD(alias, "v", vendor != PCI_ANY_ID, vendor);
@@ -483,28 +460,28 @@ static int do_pci_entry(const char *filename,
|| (subclass_mask != 0 && subclass_mask != 0xFF)
|| (interface_mask != 0 && interface_mask != 0xFF)) {
warn("Can't handle masks in %s:%04X\n",
- filename, class_mask);
- return 0;
+ mod->name, class_mask);
+ return;
}
ADD(alias, "bc", baseclass_mask == 0xFF, baseclass);
ADD(alias, "sc", subclass_mask == 0xFF, subclass);
ADD(alias, "i", interface_mask == 0xFF, interface);
- add_wildcard(alias);
- return 1;
+
+ module_alias_printf(mod, true, "%s", alias);
}
/* looks like: "ccw:tNmNdtNdmN" */
-static int do_ccw_entry(const char *filename,
- void *symval, char *alias)
+static void do_ccw_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
+
DEF_FIELD(symval, ccw_device_id, match_flags);
DEF_FIELD(symval, ccw_device_id, cu_type);
DEF_FIELD(symval, ccw_device_id, cu_model);
DEF_FIELD(symval, ccw_device_id, dev_type);
DEF_FIELD(symval, ccw_device_id, dev_model);
- strcpy(alias, "ccw:");
ADD(alias, "t", match_flags&CCW_DEVICE_ID_MATCH_CU_TYPE,
cu_type);
ADD(alias, "m", match_flags&CCW_DEVICE_ID_MATCH_CU_MODEL,
@@ -513,47 +490,42 @@ static int do_ccw_entry(const char *filename,
dev_type);
ADD(alias, "dm", match_flags&CCW_DEVICE_ID_MATCH_DEVICE_MODEL,
dev_model);
- add_wildcard(alias);
- return 1;
+
+ module_alias_printf(mod, true, "ccw:%s", alias);
}
/* looks like: "ap:tN" */
-static int do_ap_entry(const char *filename,
- void *symval, char *alias)
+static void do_ap_entry(struct module *mod, void *symval)
{
DEF_FIELD(symval, ap_device_id, dev_type);
- sprintf(alias, "ap:t%02X*", dev_type);
- return 1;
+ module_alias_printf(mod, false, "ap:t%02X*", dev_type);
}
/* looks like: "css:tN" */
-static int do_css_entry(const char *filename,
- void *symval, char *alias)
+static void do_css_entry(struct module *mod, void *symval)
{
DEF_FIELD(symval, css_device_id, type);
- sprintf(alias, "css:t%01X", type);
- return 1;
+ module_alias_printf(mod, false, "css:t%01X", type);
}
/* Looks like: "serio:tyNprNidNexN" */
-static int do_serio_entry(const char *filename,
- void *symval, char *alias)
+static void do_serio_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
+
DEF_FIELD(symval, serio_device_id, type);
DEF_FIELD(symval, serio_device_id, proto);
DEF_FIELD(symval, serio_device_id, id);
DEF_FIELD(symval, serio_device_id, extra);
- strcpy(alias, "serio:");
ADD(alias, "ty", type != SERIO_ANY, type);
ADD(alias, "pr", proto != SERIO_ANY, proto);
ADD(alias, "id", id != SERIO_ANY, id);
ADD(alias, "ex", extra != SERIO_ANY, extra);
- add_wildcard(alias);
- return 1;
+ module_alias_printf(mod, true, "serio:%s", alias);
}
/* looks like: "acpi:ACPI0003" or "acpi:PNP0C0B" or "acpi:LNXVIDEO" or
@@ -563,127 +535,73 @@ static int do_serio_entry(const char *filename,
* or _CLS. Also, bb, ss, and pp can be substituted with ??
* as don't care byte.
*/
-static int do_acpi_entry(const char *filename,
- void *symval, char *alias)
+static void do_acpi_entry(struct module *mod, void *symval)
{
DEF_FIELD_ADDR(symval, acpi_device_id, id);
- DEF_FIELD_ADDR(symval, acpi_device_id, cls);
- DEF_FIELD_ADDR(symval, acpi_device_id, cls_msk);
+ DEF_FIELD(symval, acpi_device_id, cls);
+ DEF_FIELD(symval, acpi_device_id, cls_msk);
- if (id && strlen((const char *)*id))
- sprintf(alias, "acpi*:%s:*", *id);
- else if (cls) {
+ if ((*id)[0])
+ module_alias_printf(mod, false, "acpi*:%s:*", *id);
+ else {
+ char alias[256];
int i, byte_shift, cnt = 0;
unsigned int msk;
- sprintf(&alias[cnt], "acpi*:");
- cnt = 6;
for (i = 1; i <= 3; i++) {
byte_shift = 8 * (3-i);
- msk = (*cls_msk >> byte_shift) & 0xFF;
+ msk = (cls_msk >> byte_shift) & 0xFF;
if (msk)
sprintf(&alias[cnt], "%02x",
- (*cls >> byte_shift) & 0xFF);
+ (cls >> byte_shift) & 0xFF);
else
sprintf(&alias[cnt], "??");
cnt += 2;
}
- sprintf(&alias[cnt], ":*");
+ module_alias_printf(mod, false, "acpi*:%s:*", alias);
}
- return 1;
}
/* looks like: "pnp:dD" */
-static void do_pnp_device_entry(void *symval, unsigned long size,
- struct module *mod)
+static void do_pnp_device_entry(struct module *mod, void *symval)
{
- const unsigned long id_size = SIZE_pnp_device_id;
- const unsigned int count = (size / id_size)-1;
- unsigned int i;
-
- device_id_check(mod->name, "pnp", size, id_size, symval);
-
- for (i = 0; i < count; i++) {
- DEF_FIELD_ADDR(symval + i*id_size, pnp_device_id, id);
- char acpi_id[sizeof(*id)];
- int j;
+ DEF_FIELD_ADDR(symval, pnp_device_id, id);
+ char acpi_id[sizeof(*id)];
- buf_printf(&mod->dev_table_buf,
- "MODULE_ALIAS(\"pnp:d%s*\");\n", *id);
-
- /* fix broken pnp bus lowercasing */
- for (j = 0; j < sizeof(acpi_id); j++)
- acpi_id[j] = toupper((*id)[j]);
- buf_printf(&mod->dev_table_buf,
- "MODULE_ALIAS(\"acpi*:%s:*\");\n", acpi_id);
- }
+ /* fix broken pnp bus lowercasing */
+ for (unsigned int i = 0; i < sizeof(acpi_id); i++)
+ acpi_id[i] = toupper((*id)[i]);
+ module_alias_printf(mod, false, "pnp:d%s*", *id);
+ module_alias_printf(mod, false, "acpi*:%s:*", acpi_id);
}
/* looks like: "pnp:dD" for every device of the card */
-static void do_pnp_card_entries(void *symval, unsigned long size,
- struct module *mod)
+static void do_pnp_card_entry(struct module *mod, void *symval)
{
- const unsigned long id_size = SIZE_pnp_card_device_id;
- const unsigned int count = (size / id_size)-1;
- unsigned int i;
-
- device_id_check(mod->name, "pnp", size, id_size, symval);
-
- for (i = 0; i < count; i++) {
- unsigned int j;
- DEF_FIELD_ADDR(symval + i * id_size, pnp_card_device_id, devs);
-
- for (j = 0; j < PNP_MAX_DEVICES; j++) {
- const char *id = (char *)(*devs)[j].id;
- int i2, j2;
- int dup = 0;
+ DEF_FIELD_ADDR(symval, pnp_card_device_id, devs);
- if (!id[0])
- break;
+ for (unsigned int i = 0; i < PNP_MAX_DEVICES; i++) {
+ const char *id = (char *)(*devs)[i].id;
+ char acpi_id[PNP_ID_LEN];
- /* find duplicate, already added value */
- for (i2 = 0; i2 < i && !dup; i2++) {
- DEF_FIELD_ADDR_VAR(symval + i2 * id_size,
- pnp_card_device_id,
- devs, devs_dup);
-
- for (j2 = 0; j2 < PNP_MAX_DEVICES; j2++) {
- const char *id2 =
- (char *)(*devs_dup)[j2].id;
-
- if (!id2[0])
- break;
-
- if (!strcmp(id, id2)) {
- dup = 1;
- break;
- }
- }
- }
-
- /* add an individual alias for every device entry */
- if (!dup) {
- char acpi_id[PNP_ID_LEN];
- int k;
+ if (!id[0])
+ break;
- buf_printf(&mod->dev_table_buf,
- "MODULE_ALIAS(\"pnp:d%s*\");\n", id);
+ /* fix broken pnp bus lowercasing */
+ for (unsigned int j = 0; j < sizeof(acpi_id); j++)
+ acpi_id[j] = toupper(id[j]);
- /* fix broken pnp bus lowercasing */
- for (k = 0; k < sizeof(acpi_id); k++)
- acpi_id[k] = toupper(id[k]);
- buf_printf(&mod->dev_table_buf,
- "MODULE_ALIAS(\"acpi*:%s:*\");\n", acpi_id);
- }
- }
+ /* add an individual alias for every device entry */
+ module_alias_printf(mod, false, "pnp:d%s*", id);
+ module_alias_printf(mod, false, "acpi*:%s:*", acpi_id);
}
}
/* Looks like: pcmcia:mNcNfNfnNpfnNvaNvbNvcNvdN. */
-static int do_pcmcia_entry(const char *filename,
- void *symval, char *alias)
+static void do_pcmcia_entry(struct module *mod, void *symval)
{
- unsigned int i;
+ char alias[256] = {};
+
DEF_FIELD(symval, pcmcia_device_id, match_flags);
DEF_FIELD(symval, pcmcia_device_id, manf_id);
DEF_FIELD(symval, pcmcia_device_id, card_id);
@@ -692,11 +610,6 @@ static int do_pcmcia_entry(const char *filename,
DEF_FIELD(symval, pcmcia_device_id, device_no);
DEF_FIELD_ADDR(symval, pcmcia_device_id, prod_id_hash);
- for (i=0; i<4; i++) {
- (*prod_id_hash)[i] = TO_NATIVE((*prod_id_hash)[i]);
- }
-
- strcpy(alias, "pcmcia:");
ADD(alias, "m", match_flags & PCMCIA_DEV_ID_MATCH_MANF_ID,
manf_id);
ADD(alias, "c", match_flags & PCMCIA_DEV_ID_MATCH_CARD_ID,
@@ -707,18 +620,21 @@ static int do_pcmcia_entry(const char *filename,
function);
ADD(alias, "pfn", match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO,
device_no);
- ADD(alias, "pa", match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID1, (*prod_id_hash)[0]);
- ADD(alias, "pb", match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID2, (*prod_id_hash)[1]);
- ADD(alias, "pc", match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID3, (*prod_id_hash)[2]);
- ADD(alias, "pd", match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID4, (*prod_id_hash)[3]);
+ ADD(alias, "pa", match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID1,
+ get_unaligned_native(*prod_id_hash + 0));
+ ADD(alias, "pb", match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID2,
+ get_unaligned_native(*prod_id_hash + 1));
+ ADD(alias, "pc", match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID3,
+ get_unaligned_native(*prod_id_hash + 2));
+ ADD(alias, "pd", match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID4,
+ get_unaligned_native(*prod_id_hash + 3));
- add_wildcard(alias);
- return 1;
+ module_alias_printf(mod, true, "pcmcia:%s", alias);
}
-static int do_vio_entry(const char *filename, void *symval,
- char *alias)
+static void do_vio_entry(struct module *mod, void *symval)
{
+ char alias[256];
char *tmp;
DEF_FIELD_ADDR(symval, vio_device_id, type);
DEF_FIELD_ADDR(symval, vio_device_id, compat);
@@ -731,8 +647,7 @@ static int do_vio_entry(const char *filename, void *symval,
if (isspace (*tmp))
*tmp = '_';
- add_wildcard(alias);
- return 1;
+ module_alias_printf(mod, true, "%s", alias);
}
static void do_input(char *alias,
@@ -740,17 +655,17 @@ static void do_input(char *alias,
{
unsigned int i;
- for (i = min / BITS_PER_LONG; i < max / BITS_PER_LONG + 1; i++)
- arr[i] = TO_NATIVE(arr[i]);
- for (i = min; i < max; i++)
- if (arr[i / BITS_PER_LONG] & (1L << (i%BITS_PER_LONG)))
+ for (i = min; i <= max; i++)
+ if (get_unaligned_native(arr + i / BITS_PER_LONG) &
+ (1ULL << (i % BITS_PER_LONG)))
sprintf(alias + strlen(alias), "%X,*", i);
}
/* input:b0v0p0e0-eXkXrXaXmXlXsXfXwX where X is comma-separated %02X. */
-static int do_input_entry(const char *filename, void *symval,
- char *alias)
+static void do_input_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
+
DEF_FIELD(symval, input_device_id, flags);
DEF_FIELD(symval, input_device_id, bustype);
DEF_FIELD(symval, input_device_id, vendor);
@@ -766,8 +681,6 @@ static int do_input_entry(const char *filename, void *symval,
DEF_FIELD_ADDR(symval, input_device_id, ffbit);
DEF_FIELD_ADDR(symval, input_device_id, swbit);
- sprintf(alias, "input:");
-
ADD(alias, "b", flags & INPUT_DEVICE_ID_MATCH_BUS, bustype);
ADD(alias, "v", flags & INPUT_DEVICE_ID_MATCH_VENDOR, vendor);
ADD(alias, "p", flags & INPUT_DEVICE_ID_MATCH_PRODUCT, product);
@@ -802,102 +715,96 @@ static int do_input_entry(const char *filename, void *symval,
sprintf(alias + strlen(alias), "w*");
if (flags & INPUT_DEVICE_ID_MATCH_SWBIT)
do_input(alias, *swbit, 0, INPUT_DEVICE_ID_SW_MAX);
- return 1;
+
+ module_alias_printf(mod, false, "input:%s", alias);
}
-static int do_eisa_entry(const char *filename, void *symval,
- char *alias)
+static void do_eisa_entry(struct module *mod, void *symval)
{
DEF_FIELD_ADDR(symval, eisa_device_id, sig);
- if (sig[0])
- sprintf(alias, EISA_DEVICE_MODALIAS_FMT "*", *sig);
- else
- strcat(alias, "*");
- return 1;
+ module_alias_printf(mod, false, EISA_DEVICE_MODALIAS_FMT "*", *sig);
}
/* Looks like: parisc:tNhvNrevNsvN */
-static int do_parisc_entry(const char *filename, void *symval,
- char *alias)
+static void do_parisc_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
+
DEF_FIELD(symval, parisc_device_id, hw_type);
DEF_FIELD(symval, parisc_device_id, hversion);
DEF_FIELD(symval, parisc_device_id, hversion_rev);
DEF_FIELD(symval, parisc_device_id, sversion);
- strcpy(alias, "parisc:");
ADD(alias, "t", hw_type != PA_HWTYPE_ANY_ID, hw_type);
ADD(alias, "hv", hversion != PA_HVERSION_ANY_ID, hversion);
ADD(alias, "rev", hversion_rev != PA_HVERSION_REV_ANY_ID, hversion_rev);
ADD(alias, "sv", sversion != PA_SVERSION_ANY_ID, sversion);
- add_wildcard(alias);
- return 1;
+ module_alias_printf(mod, true, "parisc:%s", alias);
}
/* Looks like: sdio:cNvNdN. */
-static int do_sdio_entry(const char *filename,
- void *symval, char *alias)
+static void do_sdio_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
+
DEF_FIELD(symval, sdio_device_id, class);
DEF_FIELD(symval, sdio_device_id, vendor);
DEF_FIELD(symval, sdio_device_id, device);
- strcpy(alias, "sdio:");
ADD(alias, "c", class != (__u8)SDIO_ANY_ID, class);
ADD(alias, "v", vendor != (__u16)SDIO_ANY_ID, vendor);
ADD(alias, "d", device != (__u16)SDIO_ANY_ID, device);
- add_wildcard(alias);
- return 1;
+
+ module_alias_printf(mod, true, "sdio:%s", alias);
}
/* Looks like: ssb:vNidNrevN. */
-static int do_ssb_entry(const char *filename,
- void *symval, char *alias)
+static void do_ssb_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
+
DEF_FIELD(symval, ssb_device_id, vendor);
DEF_FIELD(symval, ssb_device_id, coreid);
DEF_FIELD(symval, ssb_device_id, revision);
- strcpy(alias, "ssb:");
ADD(alias, "v", vendor != SSB_ANY_VENDOR, vendor);
ADD(alias, "id", coreid != SSB_ANY_ID, coreid);
ADD(alias, "rev", revision != SSB_ANY_REV, revision);
- add_wildcard(alias);
- return 1;
+
+ module_alias_printf(mod, true, "ssb:%s", alias);
}
/* Looks like: bcma:mNidNrevNclN. */
-static int do_bcma_entry(const char *filename,
- void *symval, char *alias)
+static void do_bcma_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
+
DEF_FIELD(symval, bcma_device_id, manuf);
DEF_FIELD(symval, bcma_device_id, id);
DEF_FIELD(symval, bcma_device_id, rev);
DEF_FIELD(symval, bcma_device_id, class);
- strcpy(alias, "bcma:");
ADD(alias, "m", manuf != BCMA_ANY_MANUF, manuf);
ADD(alias, "id", id != BCMA_ANY_ID, id);
ADD(alias, "rev", rev != BCMA_ANY_REV, rev);
ADD(alias, "cl", class != BCMA_ANY_CLASS, class);
- add_wildcard(alias);
- return 1;
+
+ module_alias_printf(mod, true, "bcma:%s", alias);
}
/* Looks like: virtio:dNvN */
-static int do_virtio_entry(const char *filename, void *symval,
- char *alias)
+static void do_virtio_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
+
DEF_FIELD(symval, virtio_device_id, device);
DEF_FIELD(symval, virtio_device_id, vendor);
- strcpy(alias, "virtio:");
ADD(alias, "d", device != VIRTIO_DEV_ANY_ID, device);
ADD(alias, "v", vendor != VIRTIO_DEV_ANY_ID, vendor);
- add_wildcard(alias);
- return 1;
+ module_alias_printf(mod, true, "virtio:%s", alias);
}
/*
@@ -905,69 +812,65 @@ static int do_virtio_entry(const char *filename, void *symval,
* Each byte of the guid will be represented by two hex characters
* in the name.
*/
-
-static int do_vmbus_entry(const char *filename, void *symval,
- char *alias)
+static void do_vmbus_entry(struct module *mod, void *symval)
{
- int i;
DEF_FIELD_ADDR(symval, hv_vmbus_device_id, guid);
- char guid_name[(sizeof(*guid) + 1) * 2];
+ char guid_name[sizeof(*guid) * 2 + 1];
- for (i = 0; i < (sizeof(*guid) * 2); i += 2)
- sprintf(&guid_name[i], "%02x", TO_NATIVE((guid->b)[i/2]));
+ for (int i = 0; i < sizeof(*guid); i++)
+ sprintf(&guid_name[i * 2], "%02x", guid->b[i]);
- strcpy(alias, "vmbus:");
- strcat(alias, guid_name);
-
- return 1;
+ module_alias_printf(mod, false, "vmbus:%s", guid_name);
}
/* Looks like: rpmsg:S */
-static int do_rpmsg_entry(const char *filename, void *symval,
- char *alias)
+static void do_rpmsg_entry(struct module *mod, void *symval)
{
DEF_FIELD_ADDR(symval, rpmsg_device_id, name);
- sprintf(alias, RPMSG_DEVICE_MODALIAS_FMT, *name);
- return 1;
+ module_alias_printf(mod, false, RPMSG_DEVICE_MODALIAS_FMT, *name);
}
/* Looks like: i2c:S */
-static int do_i2c_entry(const char *filename, void *symval,
- char *alias)
+static void do_i2c_entry(struct module *mod, void *symval)
{
DEF_FIELD_ADDR(symval, i2c_device_id, name);
- sprintf(alias, I2C_MODULE_PREFIX "%s", *name);
- return 1;
+ module_alias_printf(mod, false, I2C_MODULE_PREFIX "%s", *name);
}
-static int do_i3c_entry(const char *filename, void *symval,
- char *alias)
+static void do_i3c_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
+
DEF_FIELD(symval, i3c_device_id, match_flags);
DEF_FIELD(symval, i3c_device_id, dcr);
DEF_FIELD(symval, i3c_device_id, manuf_id);
DEF_FIELD(symval, i3c_device_id, part_id);
DEF_FIELD(symval, i3c_device_id, extra_info);
- strcpy(alias, "i3c:");
ADD(alias, "dcr", match_flags & I3C_MATCH_DCR, dcr);
ADD(alias, "manuf", match_flags & I3C_MATCH_MANUF, manuf_id);
ADD(alias, "part", match_flags & I3C_MATCH_PART, part_id);
ADD(alias, "ext", match_flags & I3C_MATCH_EXTRA_INFO, extra_info);
- return 1;
+ module_alias_printf(mod, false, "i3c:%s", alias);
+}
+
+static void do_slim_entry(struct module *mod, void *symval)
+{
+ DEF_FIELD(symval, slim_device_id, manf_id);
+ DEF_FIELD(symval, slim_device_id, prod_code);
+
+ module_alias_printf(mod, false, "slim:%x:%x:*", manf_id, prod_code);
}
/* Looks like: spi:S */
-static int do_spi_entry(const char *filename, void *symval,
- char *alias)
+static void do_spi_entry(struct module *mod, void *symval)
{
DEF_FIELD_ADDR(symval, spi_device_id, name);
- sprintf(alias, SPI_MODULE_PREFIX "%s", *name);
- return 1;
+ module_alias_printf(mod, false, SPI_MODULE_PREFIX "%s", *name);
}
static const struct dmifield {
@@ -1002,12 +905,11 @@ static void dmi_ascii_filter(char *d, const char *s)
}
-static int do_dmi_entry(const char *filename, void *symval,
- char *alias)
+static void do_dmi_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
int i, j;
DEF_FIELD_ADDR(symval, dmi_system_id, matches);
- sprintf(alias, "dmi*");
for (i = 0; i < ARRAY_SIZE(dmi_fields); i++) {
for (j = 0; j < 4; j++) {
@@ -1022,80 +924,75 @@ static int do_dmi_entry(const char *filename, void *symval,
}
}
- strcat(alias, ":");
- return 1;
+ module_alias_printf(mod, false, "dmi*%s:", alias);
}
-static int do_platform_entry(const char *filename,
- void *symval, char *alias)
+static void do_platform_entry(struct module *mod, void *symval)
{
DEF_FIELD_ADDR(symval, platform_device_id, name);
- sprintf(alias, PLATFORM_MODULE_PREFIX "%s", *name);
- return 1;
+
+ module_alias_printf(mod, false, PLATFORM_MODULE_PREFIX "%s", *name);
}
-static int do_mdio_entry(const char *filename,
- void *symval, char *alias)
+static void do_mdio_entry(struct module *mod, void *symval)
{
+ char id[33];
int i;
DEF_FIELD(symval, mdio_device_id, phy_id);
DEF_FIELD(symval, mdio_device_id, phy_id_mask);
- alias += sprintf(alias, MDIO_MODULE_PREFIX);
-
for (i = 0; i < 32; i++) {
if (!((phy_id_mask >> (31-i)) & 1))
- *(alias++) = '?';
+ id[i] = '?';
else if ((phy_id >> (31-i)) & 1)
- *(alias++) = '1';
+ id[i] = '1';
else
- *(alias++) = '0';
+ id[i] = '0';
}
/* Terminate the string */
- *alias = 0;
+ id[32] = '\0';
- return 1;
+ module_alias_printf(mod, false, MDIO_MODULE_PREFIX "%s", id);
}
/* Looks like: zorro:iN. */
-static int do_zorro_entry(const char *filename, void *symval,
- char *alias)
+static void do_zorro_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
DEF_FIELD(symval, zorro_device_id, id);
- strcpy(alias, "zorro:");
+
ADD(alias, "i", id != ZORRO_WILDCARD, id);
- return 1;
+
+ module_alias_printf(mod, false, "zorro:%s", alias);
}
/* looks like: "pnp:dD" */
-static int do_isapnp_entry(const char *filename,
- void *symval, char *alias)
+static void do_isapnp_entry(struct module *mod, void *symval)
{
DEF_FIELD(symval, isapnp_device_id, vendor);
DEF_FIELD(symval, isapnp_device_id, function);
- sprintf(alias, "pnp:d%c%c%c%x%x%x%x*",
+ module_alias_printf(mod, false, "pnp:d%c%c%c%x%x%x%x*",
'A' + ((vendor >> 2) & 0x3f) - 1,
'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1,
'A' + ((vendor >> 8) & 0x1f) - 1,
(function >> 4) & 0x0f, function & 0x0f,
(function >> 12) & 0x0f, (function >> 8) & 0x0f);
- return 1;
}
/* Looks like: "ipack:fNvNdN". */
-static int do_ipack_entry(const char *filename,
- void *symval, char *alias)
+static void do_ipack_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
DEF_FIELD(symval, ipack_device_id, format);
DEF_FIELD(symval, ipack_device_id, vendor);
DEF_FIELD(symval, ipack_device_id, device);
- strcpy(alias, "ipack:");
+
ADD(alias, "f", format != IPACK_ANY_FORMAT, format);
ADD(alias, "v", vendor != IPACK_ANY_ID, vendor);
ADD(alias, "d", device != IPACK_ANY_ID, device);
- add_wildcard(alias);
- return 1;
+
+ module_alias_printf(mod, true, "ipack:%s", alias);
}
/*
@@ -1146,9 +1043,9 @@ static void append_nibble_mask(char **outp,
* N is exactly 8 digits, where each is an upper-case hex digit, or
* a ? or [] pattern matching exactly one digit.
*/
-static int do_amba_entry(const char *filename,
- void *symval, char *alias)
+static void do_amba_entry(struct module *mod, void *symval)
{
+ char alias[256];
unsigned int digit;
char *p = alias;
DEF_FIELD(symval, amba_id, id);
@@ -1156,15 +1053,14 @@ static int do_amba_entry(const char *filename,
if ((id & mask) != id)
fatal("%s: Masked-off bit(s) of AMBA device ID are non-zero: id=0x%08X, mask=0x%08X. Please fix this driver.\n",
- filename, id, mask);
+ mod->name, id, mask);
- p += sprintf(alias, "amba:d");
for (digit = 0; digit < 8; digit++)
append_nibble_mask(&p,
(id >> (4 * (7 - digit))) & 0xf,
(mask >> (4 * (7 - digit))) & 0xf);
- return 1;
+ module_alias_printf(mod, false, "amba:d%s", alias);
}
/*
@@ -1173,13 +1069,11 @@ static int do_amba_entry(const char *filename,
* N is exactly 2 digits, where each is an upper-case hex digit, or
* a ? or [] pattern matching exactly one digit.
*/
-static int do_mips_cdmm_entry(const char *filename,
- void *symval, char *alias)
+static void do_mips_cdmm_entry(struct module *mod, void *symval)
{
DEF_FIELD(symval, mips_cdmm_device_id, type);
- sprintf(alias, "mipscdmm:t%02X*", type);
- return 1;
+ module_alias_printf(mod, false, "mipscdmm:t%02X*", type);
}
/* LOOKS like cpu:type:x86,venVVVVfamFFFFmodMMMM:feature:*,FEAT,*
@@ -1188,137 +1082,130 @@ static int do_mips_cdmm_entry(const char *filename,
* complicated.
*/
-static int do_x86cpu_entry(const char *filename, void *symval,
- char *alias)
+static void do_x86cpu_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
+
DEF_FIELD(symval, x86_cpu_id, feature);
DEF_FIELD(symval, x86_cpu_id, family);
DEF_FIELD(symval, x86_cpu_id, model);
DEF_FIELD(symval, x86_cpu_id, vendor);
- strcpy(alias, "cpu:type:x86,");
ADD(alias, "ven", vendor != X86_VENDOR_ANY, vendor);
ADD(alias, "fam", family != X86_FAMILY_ANY, family);
ADD(alias, "mod", model != X86_MODEL_ANY, model);
strcat(alias, ":feature:*");
if (feature != X86_FEATURE_ANY)
sprintf(alias + strlen(alias), "%04X*", feature);
- return 1;
+
+ module_alias_printf(mod, false, "cpu:type:x86,%s", alias);
}
/* LOOKS like cpu:type:*:feature:*FEAT* */
-static int do_cpu_entry(const char *filename, void *symval, char *alias)
+static void do_cpu_entry(struct module *mod, void *symval)
{
DEF_FIELD(symval, cpu_feature, feature);
- sprintf(alias, "cpu:type:*:feature:*%04X*", feature);
- return 1;
+ module_alias_printf(mod, false, "cpu:type:*:feature:*%04X*", feature);
}
/* Looks like: mei:S:uuid:N:* */
-static int do_mei_entry(const char *filename, void *symval,
- char *alias)
+static void do_mei_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
+
DEF_FIELD_ADDR(symval, mei_cl_device_id, name);
DEF_FIELD_ADDR(symval, mei_cl_device_id, uuid);
DEF_FIELD(symval, mei_cl_device_id, version);
- sprintf(alias, MEI_CL_MODULE_PREFIX);
- sprintf(alias + strlen(alias), "%s:", (*name)[0] ? *name : "*");
add_uuid(alias, *uuid);
ADD(alias, ":", version != MEI_CL_VERSION_ANY, version);
- strcat(alias, ":*");
-
- return 1;
+ module_alias_printf(mod, false, MEI_CL_MODULE_PREFIX "%s:%s:*",
+ (*name)[0] ? *name : "*", alias);
}
/* Looks like: rapidio:vNdNavNadN */
-static int do_rio_entry(const char *filename,
- void *symval, char *alias)
+static void do_rio_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
+
DEF_FIELD(symval, rio_device_id, did);
DEF_FIELD(symval, rio_device_id, vid);
DEF_FIELD(symval, rio_device_id, asm_did);
DEF_FIELD(symval, rio_device_id, asm_vid);
- strcpy(alias, "rapidio:");
ADD(alias, "v", vid != RIO_ANY_ID, vid);
ADD(alias, "d", did != RIO_ANY_ID, did);
ADD(alias, "av", asm_vid != RIO_ANY_ID, asm_vid);
ADD(alias, "ad", asm_did != RIO_ANY_ID, asm_did);
- add_wildcard(alias);
- return 1;
+ module_alias_printf(mod, true, "rapidio:%s", alias);
}
/* Looks like: ulpi:vNpN */
-static int do_ulpi_entry(const char *filename, void *symval,
- char *alias)
+static void do_ulpi_entry(struct module *mod, void *symval)
{
DEF_FIELD(symval, ulpi_device_id, vendor);
DEF_FIELD(symval, ulpi_device_id, product);
- sprintf(alias, "ulpi:v%04xp%04x", vendor, product);
-
- return 1;
+ module_alias_printf(mod, false, "ulpi:v%04xp%04x", vendor, product);
}
/* Looks like: hdaudio:vNrNaN */
-static int do_hda_entry(const char *filename, void *symval, char *alias)
+static void do_hda_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
+
DEF_FIELD(symval, hda_device_id, vendor_id);
DEF_FIELD(symval, hda_device_id, rev_id);
DEF_FIELD(symval, hda_device_id, api_version);
- strcpy(alias, "hdaudio:");
ADD(alias, "v", vendor_id != 0, vendor_id);
ADD(alias, "r", rev_id != 0, rev_id);
ADD(alias, "a", api_version != 0, api_version);
- add_wildcard(alias);
- return 1;
+ module_alias_printf(mod, true, "hdaudio:%s", alias);
}
/* Looks like: sdw:mNpNvNcN */
-static int do_sdw_entry(const char *filename, void *symval, char *alias)
+static void do_sdw_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
+
DEF_FIELD(symval, sdw_device_id, mfg_id);
DEF_FIELD(symval, sdw_device_id, part_id);
DEF_FIELD(symval, sdw_device_id, sdw_version);
DEF_FIELD(symval, sdw_device_id, class_id);
- strcpy(alias, "sdw:");
ADD(alias, "m", mfg_id != 0, mfg_id);
ADD(alias, "p", part_id != 0, part_id);
ADD(alias, "v", sdw_version != 0, sdw_version);
ADD(alias, "c", class_id != 0, class_id);
- add_wildcard(alias);
- return 1;
+ module_alias_printf(mod, true, "sdw:%s", alias);
}
/* Looks like: fsl-mc:vNdN */
-static int do_fsl_mc_entry(const char *filename, void *symval,
- char *alias)
+static void do_fsl_mc_entry(struct module *mod, void *symval)
{
DEF_FIELD(symval, fsl_mc_device_id, vendor);
DEF_FIELD_ADDR(symval, fsl_mc_device_id, obj_type);
- sprintf(alias, "fsl-mc:v%08Xd%s", vendor, *obj_type);
- return 1;
+ module_alias_printf(mod, false, "fsl-mc:v%08Xd%s", vendor, *obj_type);
}
/* Looks like: tbsvc:kSpNvNrN */
-static int do_tbsvc_entry(const char *filename, void *symval, char *alias)
+static void do_tbsvc_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
+
DEF_FIELD(symval, tb_service_id, match_flags);
DEF_FIELD_ADDR(symval, tb_service_id, protocol_key);
DEF_FIELD(symval, tb_service_id, protocol_id);
DEF_FIELD(symval, tb_service_id, protocol_version);
DEF_FIELD(symval, tb_service_id, protocol_revision);
- strcpy(alias, "tbsvc:");
if (match_flags & TBSVC_MATCH_PROTOCOL_KEY)
sprintf(alias + strlen(alias), "k%s", *protocol_key);
else
@@ -1329,93 +1216,75 @@ static int do_tbsvc_entry(const char *filename, void *symval, char *alias)
ADD(alias, "r", match_flags & TBSVC_MATCH_PROTOCOL_REVISION,
protocol_revision);
- add_wildcard(alias);
- return 1;
+ module_alias_printf(mod, true, "tbsvc:%s", alias);
}
-/* Looks like: typec:idNmN */
-static int do_typec_entry(const char *filename, void *symval, char *alias)
+/* Looks like: typec:idN */
+static void do_typec_entry(struct module *mod, void *symval)
{
DEF_FIELD(symval, typec_device_id, svid);
- DEF_FIELD(symval, typec_device_id, mode);
-
- sprintf(alias, "typec:id%04X", svid);
- ADD(alias, "m", mode != TYPEC_ANY_MODE, mode);
- return 1;
+ module_alias_printf(mod, false, "typec:id%04X", svid);
}
/* Looks like: tee:uuid */
-static int do_tee_entry(const char *filename, void *symval, char *alias)
+static void do_tee_entry(struct module *mod, void *symval)
{
DEF_FIELD_ADDR(symval, tee_client_device_id, uuid);
- sprintf(alias, "tee:%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ module_alias_printf(mod, true,
+ "tee:%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
uuid->b[0], uuid->b[1], uuid->b[2], uuid->b[3], uuid->b[4],
uuid->b[5], uuid->b[6], uuid->b[7], uuid->b[8], uuid->b[9],
uuid->b[10], uuid->b[11], uuid->b[12], uuid->b[13], uuid->b[14],
uuid->b[15]);
-
- add_wildcard(alias);
- return 1;
}
/* Looks like: wmi:guid */
-static int do_wmi_entry(const char *filename, void *symval, char *alias)
+static void do_wmi_entry(struct module *mod, void *symval)
{
- int len;
DEF_FIELD_ADDR(symval, wmi_device_id, guid_string);
if (strlen(*guid_string) != UUID_STRING_LEN) {
warn("Invalid WMI device id 'wmi:%s' in '%s'\n",
- *guid_string, filename);
- return 0;
+ *guid_string, mod->name);
+ return;
}
- len = snprintf(alias, ALIAS_SIZE, WMI_MODULE_PREFIX "%s", *guid_string);
- if (len < 0 || len >= ALIAS_SIZE) {
- warn("Could not generate all MODULE_ALIAS's in '%s'\n",
- filename);
- return 0;
- }
- return 1;
+ module_alias_printf(mod, false, WMI_MODULE_PREFIX "%s", *guid_string);
}
/* Looks like: mhi:S */
-static int do_mhi_entry(const char *filename, void *symval, char *alias)
+static void do_mhi_entry(struct module *mod, void *symval)
{
DEF_FIELD_ADDR(symval, mhi_device_id, chan);
- sprintf(alias, MHI_DEVICE_MODALIAS_FMT, *chan);
- return 1;
+ module_alias_printf(mod, false, MHI_DEVICE_MODALIAS_FMT, *chan);
}
/* Looks like: mhi_ep:S */
-static int do_mhi_ep_entry(const char *filename, void *symval, char *alias)
+static void do_mhi_ep_entry(struct module *mod, void *symval)
{
DEF_FIELD_ADDR(symval, mhi_device_id, chan);
- sprintf(alias, MHI_EP_DEVICE_MODALIAS_FMT, *chan);
- return 1;
+ module_alias_printf(mod, false, MHI_EP_DEVICE_MODALIAS_FMT, *chan);
}
/* Looks like: ishtp:{guid} */
-static int do_ishtp_entry(const char *filename, void *symval, char *alias)
+static void do_ishtp_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
DEF_FIELD_ADDR(symval, ishtp_device_id, guid);
- strcpy(alias, ISHTP_MODULE_PREFIX "{");
add_guid(alias, *guid);
- strcat(alias, "}");
- return 1;
+ module_alias_printf(mod, false, ISHTP_MODULE_PREFIX "{%s}", alias);
}
-static int do_auxiliary_entry(const char *filename, void *symval, char *alias)
+static void do_auxiliary_entry(struct module *mod, void *symval)
{
DEF_FIELD_ADDR(symval, auxiliary_device_id, name);
- sprintf(alias, AUXILIARY_MODULE_PREFIX "%s", *name);
- return 1;
+ module_alias_printf(mod, false, AUXILIARY_MODULE_PREFIX "%s", *name);
}
/*
@@ -1423,8 +1292,10 @@ static int do_auxiliary_entry(const char *filename, void *symval, char *alias)
*
* N is exactly 2 digits, where each is an upper-case hex digit.
*/
-static int do_ssam_entry(const char *filename, void *symval, char *alias)
+static void do_ssam_entry(struct module *mod, void *symval)
{
+ char alias[256] = {};
+
DEF_FIELD(symval, ssam_device_id, match_flags);
DEF_FIELD(symval, ssam_device_id, domain);
DEF_FIELD(symval, ssam_device_id, category);
@@ -1432,30 +1303,28 @@ static int do_ssam_entry(const char *filename, void *symval, char *alias)
DEF_FIELD(symval, ssam_device_id, instance);
DEF_FIELD(symval, ssam_device_id, function);
- sprintf(alias, "ssam:d%02Xc%02X", domain, category);
ADD(alias, "t", match_flags & SSAM_MATCH_TARGET, target);
ADD(alias, "i", match_flags & SSAM_MATCH_INSTANCE, instance);
ADD(alias, "f", match_flags & SSAM_MATCH_FUNCTION, function);
- return 1;
+ module_alias_printf(mod, false, "ssam:d%02Xc%02X%s",
+ domain, category, alias);
}
/* Looks like: dfl:tNfN */
-static int do_dfl_entry(const char *filename, void *symval, char *alias)
+static void do_dfl_entry(struct module *mod, void *symval)
{
DEF_FIELD(symval, dfl_device_id, type);
DEF_FIELD(symval, dfl_device_id, feature_id);
- sprintf(alias, "dfl:t%04Xf%04X", type, feature_id);
-
- add_wildcard(alias);
- return 1;
+ module_alias_printf(mod, true, "dfl:t%04Xf%04X", type, feature_id);
}
/* Looks like: cdx:vNdN */
-static int do_cdx_entry(const char *filename, void *symval,
- char *alias)
+static void do_cdx_entry(struct module *mod, void *symval)
{
+ char alias[256];
+
DEF_FIELD(symval, cdx_device_id, vendor);
DEF_FIELD(symval, cdx_device_id, device);
DEF_FIELD(symval, cdx_device_id, subvendor);
@@ -1474,7 +1343,7 @@ static int do_cdx_entry(const char *filename, void *symval,
default:
warn("Unknown CDX driver_override alias %08X\n",
override_only);
- return 0;
+ return;
}
ADD(alias, "v", vendor != CDX_ANY_ID, vendor);
@@ -1483,24 +1352,22 @@ static int do_cdx_entry(const char *filename, void *symval,
ADD(alias, "sd", subdevice != CDX_ANY_ID, subdevice);
ADD(alias, "c", class_mask == 0xFFFFFF, class);
- return 1;
+ module_alias_printf(mod, false, "%s", alias);
}
-static int do_vchiq_entry(const char *filename, void *symval, char *alias)
+static void do_vchiq_entry(struct module *mod, void *symval)
{
DEF_FIELD_ADDR(symval, vchiq_device_id, name);
- sprintf(alias, "vchiq:%s", *name);
- return 1;
+ module_alias_printf(mod, false, "vchiq:%s", *name);
}
/* Looks like: coreboot:tN */
-static int do_coreboot_entry(const char *filename, void *symval, char *alias)
+static void do_coreboot_entry(struct module *mod, void *symval)
{
DEF_FIELD(symval, coreboot_device_id, tag);
- sprintf(alias, "coreboot:t%08X", tag);
- return 1;
+ module_alias_printf(mod, false, "coreboot:t%08X", tag);
}
/* Does namelen bytes of name exactly match the symbol? */
@@ -1512,25 +1379,34 @@ static bool sym_is(const char *name, unsigned namelen, const char *symbol)
return memcmp(name, symbol, namelen) == 0;
}
-static void do_table(void *symval, unsigned long size,
+static void do_table(const char *name, void *symval, unsigned long size,
unsigned long id_size,
const char *device_id,
- int (*do_entry)(const char *filename, void *symval, char *alias),
+ void (*do_entry)(struct module *mod, void *symval),
struct module *mod)
{
unsigned int i;
- char alias[ALIAS_SIZE];
- device_id_check(mod->name, device_id, size, id_size, symval);
- /* Leave last one: it's the terminator. */
- size -= id_size;
+ if (size % id_size || size < id_size) {
+ error("%s: type mismatch between %s[] and MODULE_DEVICE_TABLE(%s, ...)\n",
+ mod->name, name, device_id);
+ return;
+ }
- for (i = 0; i < size; i += id_size) {
- if (do_entry(mod->name, symval+i, alias)) {
- buf_printf(&mod->dev_table_buf,
- "MODULE_ALIAS(\"%s\");\n", alias);
+ /* Verify the last entry is a terminator */
+ for (i = size - id_size; i < size; i++) {
+ if (*(uint8_t *)(symval + i)) {
+ error("%s: %s[] is not terminated with a NULL entry\n",
+ mod->name, name);
+ return;
}
}
+
+ /* Leave last one: it's the terminator. */
+ size -= id_size;
+
+ for (i = 0; i < size; i += id_size)
+ do_entry(mod, symval + i);
}
static const struct devtable devtable[] = {
@@ -1555,6 +1431,7 @@ static const struct devtable devtable[] = {
{"rpmsg", SIZE_rpmsg_device_id, do_rpmsg_entry},
{"i2c", SIZE_i2c_device_id, do_i2c_entry},
{"i3c", SIZE_i3c_device_id, do_i3c_entry},
+ {"slim", SIZE_slim_device_id, do_slim_entry},
{"spi", SIZE_spi_device_id, do_spi_entry},
{"dmi", SIZE_dmi_system_id, do_dmi_entry},
{"platform", SIZE_platform_device_id, do_platform_entry},
@@ -1585,6 +1462,10 @@ static const struct devtable devtable[] = {
{"cdx", SIZE_cdx_device_id, do_cdx_entry},
{"vchiq", SIZE_vchiq_device_id, do_vchiq_entry},
{"coreboot", SIZE_coreboot_device_id, do_coreboot_entry},
+ {"of", SIZE_of_device_id, do_of_entry},
+ {"usb", SIZE_usb_device_id, do_usb_entry_multi},
+ {"pnp", SIZE_pnp_device_id, do_pnp_device_entry},
+ {"pnp_card", SIZE_pnp_card_device_id, do_pnp_card_entry},
};
/* Create MODULE_ALIAS() statements.
@@ -1595,8 +1476,9 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
{
void *symval;
char *zeros = NULL;
- const char *name, *identifier;
- unsigned int namelen;
+ const char *type, *name;
+ size_t typelen;
+ static const char *prefix = "__mod_device_table__";
/* We're looking for a section relative symbol */
if (!sym->st_shndx || get_secindex(info, sym) >= info->num_sections)
@@ -1606,19 +1488,16 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT)
return;
- /* All our symbols are of form __mod_<name>__<identifier>_device_table. */
- if (strncmp(symname, "__mod_", strlen("__mod_")))
- return;
- name = symname + strlen("__mod_");
- namelen = strlen(name);
- if (namelen < strlen("_device_table"))
- return;
- if (strcmp(name + namelen - strlen("_device_table"), "_device_table"))
+ /* All our symbols are of form __mod_device_table__<type>__<name>. */
+ if (!strstarts(symname, prefix))
return;
- identifier = strstr(name, "__");
- if (!identifier)
+ type = symname + strlen(prefix);
+
+ name = strstr(type, "__");
+ if (!name)
return;
- namelen = identifier - name;
+ typelen = name - type;
+ name += strlen("__");
/* Handle all-NULL symbols allocated into .bss */
if (info->sechdrs[get_secindex(info, sym)].sh_type & SHT_NOBITS) {
@@ -1628,35 +1507,15 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
symval = sym_get_data(info, sym);
}
- /* First handle the "special" cases */
- if (sym_is(name, namelen, "usb"))
- do_usb_table(symval, sym->st_size, mod);
- else if (sym_is(name, namelen, "of"))
- do_of_table(symval, sym->st_size, mod);
- else if (sym_is(name, namelen, "pnp"))
- do_pnp_device_entry(symval, sym->st_size, mod);
- else if (sym_is(name, namelen, "pnp_card"))
- do_pnp_card_entries(symval, sym->st_size, mod);
- else {
- int i;
-
- for (i = 0; i < ARRAY_SIZE(devtable); i++) {
- const struct devtable *p = &devtable[i];
+ for (int i = 0; i < ARRAY_SIZE(devtable); i++) {
+ const struct devtable *p = &devtable[i];
- if (sym_is(name, namelen, p->device_id)) {
- do_table(symval, sym->st_size, p->id_size,
- p->device_id, p->do_entry, mod);
- break;
- }
+ if (sym_is(type, typelen, p->device_id)) {
+ do_table(name, symval, sym->st_size, p->id_size,
+ p->device_id, p->do_entry, mod);
+ break;
}
}
- free(zeros);
-}
-/* Now add out buffered information to the generated C source */
-void add_moddevtable(struct buffer *buf, struct module *mod)
-{
- buf_printf(buf, "\n");
- buf_write(buf, mod->dev_table_buf.p, mod->dev_table_buf.pos);
- free(mod->dev_table_buf.p);
+ free(zeros);
}
diff --git a/scripts/mod/mk_elfconfig.c b/scripts/mod/mk_elfconfig.c
index 680eade89be1..e8cee4e4bc73 100644
--- a/scripts/mod/mk_elfconfig.c
+++ b/scripts/mod/mk_elfconfig.c
@@ -8,7 +8,6 @@ int
main(int argc, char **argv)
{
unsigned char ei[EI_NIDENT];
- union { short s; char c[2]; } endian_test;
if (fread(ei, 1, EI_NIDENT, stdin) != EI_NIDENT) {
fprintf(stderr, "Error: input truncated\n");
@@ -28,30 +27,6 @@ main(int argc, char **argv)
default:
exit(1);
}
- switch (ei[EI_DATA]) {
- case ELFDATA2LSB:
- printf("#define KERNEL_ELFDATA ELFDATA2LSB\n");
- break;
- case ELFDATA2MSB:
- printf("#define KERNEL_ELFDATA ELFDATA2MSB\n");
- break;
- default:
- exit(1);
- }
-
- if (sizeof(unsigned long) == 4) {
- printf("#define HOST_ELFCLASS ELFCLASS32\n");
- } else if (sizeof(unsigned long) == 8) {
- printf("#define HOST_ELFCLASS ELFCLASS64\n");
- }
-
- endian_test.s = 0x0102;
- if (memcmp(endian_test.c, "\x01\x02", 2) == 0)
- printf("#define HOST_ELFDATA ELFDATA2MSB\n");
- else if (memcmp(endian_test.c, "\x02\x01", 2) == 0)
- printf("#define HOST_ELFDATA ELFDATA2LSB\n");
- else
- exit(1);
return 0;
}
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index d16d0ace2775..be89921d60b6 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -21,8 +21,10 @@
#include <stdbool.h>
#include <errno.h>
+#include <hash.h>
#include <hashtable.h>
#include <list.h>
+#include <xalloc.h>
#include "modpost.h"
#include "../../include/linux/license.h"
@@ -31,6 +33,10 @@ static bool module_enabled;
static bool modversions;
/* Is CONFIG_MODULE_SRCVERSION_ALL set? */
static bool all_versions;
+/* Is CONFIG_BASIC_MODVERSIONS set? */
+static bool basic_modversions;
+/* Is CONFIG_EXTENDED_MODVERSIONS set? */
+static bool extended_modversions;
/* If we are modposting external module set to 1 */
static bool external_module;
/* Only warn about unresolved symbols */
@@ -50,6 +56,9 @@ static bool error_occurred;
static bool extra_warn;
+bool target_is_big_endian;
+bool host_is_big_endian;
+
/*
* Cut off the warnings when there are too many. This typically occurs when
* vmlinux is missing. ('make modules' without building vmlinux.)
@@ -63,20 +72,15 @@ static unsigned int nr_unresolved;
#define MODULE_NAME_LEN (64 - sizeof(Elf_Addr))
-void modpost_log(enum loglevel loglevel, const char *fmt, ...)
+void modpost_log(bool is_error, const char *fmt, ...)
{
va_list arglist;
- switch (loglevel) {
- case LOG_WARN:
- fprintf(stderr, "WARNING: ");
- break;
- case LOG_ERROR:
+ if (is_error) {
fprintf(stderr, "ERROR: ");
error_occurred = true;
- break;
- default: /* invalid loglevel, ignore */
- break;
+ } else {
+ fprintf(stderr, "WARNING: ");
}
fprintf(stderr, "modpost: ");
@@ -94,12 +98,16 @@ static inline bool strends(const char *str, const char *postfix)
return strcmp(str + strlen(str) - strlen(postfix), postfix) == 0;
}
-void *do_nofail(void *ptr, const char *expr)
+/**
+ * get_basename - return the last part of a pathname.
+ *
+ * @path: path to extract the filename from.
+ */
+const char *get_basename(const char *path)
{
- if (!ptr)
- fatal("Memory allocation failure: %s.\n", expr);
+ const char *tail = strrchr(path, '/');
- return ptr;
+ return tail ? tail + 1 : path;
}
char *read_text_file(const char *filename)
@@ -120,7 +128,7 @@ char *read_text_file(const char *filename)
exit(1);
}
- buf = NOFAIL(malloc(st.st_size + 1));
+ buf = xmalloc(st.st_size + 1);
nbytes = st.st_size;
@@ -163,12 +171,13 @@ char *get_line(char **stringp)
/* A list of all modules we processed */
LIST_HEAD(modules);
-static struct module *find_module(const char *modname)
+static struct module *find_module(const char *filename, const char *modname)
{
struct module *mod;
list_for_each_entry(mod, &modules, list) {
- if (strcmp(mod->name, modname) == 0)
+ if (!strcmp(mod->dump_file, filename) &&
+ !strcmp(mod->name, modname))
return mod;
}
return NULL;
@@ -178,13 +187,14 @@ static struct module *new_module(const char *name, size_t namelen)
{
struct module *mod;
- mod = NOFAIL(malloc(sizeof(*mod) + namelen + 1));
+ mod = xmalloc(sizeof(*mod) + namelen + 1);
memset(mod, 0, sizeof(*mod));
INIT_LIST_HEAD(&mod->exported_symbols);
INIT_LIST_HEAD(&mod->unresolved_symbols);
INIT_LIST_HEAD(&mod->missing_namespaces);
INIT_LIST_HEAD(&mod->imported_namespaces);
+ INIT_LIST_HEAD(&mod->aliases);
memcpy(mod->name, name, namelen);
mod->name[namelen] = '\0';
@@ -192,8 +202,8 @@ static struct module *new_module(const char *name, size_t namelen)
/*
* Set mod->is_gpl_compatible to true by default. If MODULE_LICENSE()
- * is missing, do not check the use for EXPORT_SYMBOL_GPL() becasue
- * modpost will exit wiht error anyway.
+ * is missing, do not check the use for EXPORT_SYMBOL_GPL() because
+ * modpost will exit with an error anyway.
*/
mod->is_gpl_compatible = true;
@@ -218,26 +228,13 @@ struct symbol {
static HASHTABLE_DEFINE(symbol_hashtable, 1U << 10);
-/* This is based on the hash algorithm from gdbm, via tdb */
-static inline unsigned int tdb_hash(const char *name)
-{
- unsigned value; /* Used to compute the hash value. */
- unsigned i; /* Used to cycle through random values. */
-
- /* Set the initial value from the key size. */
- for (value = 0x238F13AF * strlen(name), i = 0; name[i]; i++)
- value = (value + (((unsigned char *)name)[i] << (i*5 % 24)));
-
- return (1103515243 * value + 12345);
-}
-
/**
* Allocate a new symbols for use in the hash of exported symbols or
* the list of unresolved symbols per module
**/
static struct symbol *alloc_symbol(const char *name)
{
- struct symbol *s = NOFAIL(malloc(sizeof(*s) + strlen(name) + 1));
+ struct symbol *s = xmalloc(sizeof(*s) + strlen(name) + 1);
memset(s, 0, sizeof(*s));
strcpy(s->name, name);
@@ -248,7 +245,7 @@ static struct symbol *alloc_symbol(const char *name)
/* For the hash of exported symbols */
static void hash_add_symbol(struct symbol *sym)
{
- hash_add(symbol_hashtable, &sym->hnode, tdb_hash(sym->name));
+ hash_add(symbol_hashtable, &sym->hnode, hash_str(sym->name));
}
static void sym_add_unresolved(const char *name, struct module *mod, bool weak)
@@ -269,7 +266,7 @@ static struct symbol *sym_find_with_module(const char *name, struct module *mod)
if (name[0] == '.')
name++;
- hash_for_each_possible(symbol_hashtable, s, hnode, tdb_hash(name)) {
+ hash_for_each_possible(symbol_hashtable, s, hnode, hash_str(name)) {
if (strcmp(s->name, name) == 0 && (!mod || s->module == mod))
return s;
}
@@ -310,8 +307,7 @@ static void add_namespace(struct list_head *head, const char *namespace)
struct namespace_list *ns_entry;
if (!contains_namespace(head, namespace)) {
- ns_entry = NOFAIL(malloc(sizeof(*ns_entry) +
- strlen(namespace) + 1));
+ ns_entry = xmalloc(sizeof(*ns_entry) + strlen(namespace) + 1);
strcpy(ns_entry->namespace, namespace);
list_add_tail(&ns_entry->list, head);
}
@@ -350,8 +346,6 @@ static const char *sec_name(const struct elf_info *info, unsigned int secindex)
return sech_name(info, &info->sechdrs[secindex]);
}
-#define strstarts(str, prefix) (strncmp(str, prefix, strlen(prefix)) == 0)
-
static struct symbol *sym_add_exported(const char *name, struct module *mod,
bool gpl_only, const char *namespace)
{
@@ -366,7 +360,7 @@ static struct symbol *sym_add_exported(const char *name, struct module *mod,
s = alloc_symbol(name);
s->module = mod;
s->is_gpl_only = gpl_only;
- s->namespace = NOFAIL(strdup(namespace));
+ s->namespace = xstrdup(namespace);
list_add_tail(&s->list, &mod->exported_symbols);
hash_add_symbol(s);
@@ -438,6 +432,18 @@ static int parse_elf(struct elf_info *info, const char *filename)
/* Not an ELF file - silently ignore it */
return 0;
}
+
+ switch (hdr->e_ident[EI_DATA]) {
+ case ELFDATA2LSB:
+ target_is_big_endian = false;
+ break;
+ case ELFDATA2MSB:
+ target_is_big_endian = true;
+ break;
+ default:
+ fatal("target endian is unknown\n");
+ }
+
/* Fix endianness in ELF header */
hdr->e_type = TO_NATIVE(hdr->e_type);
hdr->e_machine = TO_NATIVE(hdr->e_machine);
@@ -513,6 +519,9 @@ static int parse_elf(struct elf_info *info, const char *filename)
info->modinfo_len = sechdrs[i].sh_size;
} else if (!strcmp(secname, ".export_symbol")) {
info->export_symbol_secndx = i;
+ } else if (!strcmp(secname, ".no_trim_symbol")) {
+ info->no_trim_symbol = (void *)hdr + sechdrs[i].sh_offset;
+ info->no_trim_symbol_len = sechdrs[i].sh_size;
}
if (sechdrs[i].sh_type == SHT_SYMTAB) {
@@ -622,7 +631,7 @@ static void handle_symbol(struct module *mod, struct elf_info *info,
if (ELF_ST_TYPE(sym->st_info) == STT_SPARC_REGISTER)
break;
if (symname[0] == '.') {
- char *munged = NOFAIL(strdup(symname));
+ char *munged = xstrdup(symname);
munged[0] = '_';
munged[1] = toupper(munged[1]);
symname = munged;
@@ -690,10 +699,7 @@ static char *get_modinfo(struct elf_info *info, const char *tag)
static const char *sym_name(struct elf_info *elf, Elf_Sym *sym)
{
- if (sym)
- return elf->strtab + sym->st_name;
- else
- return "(unknown)";
+ return sym ? elf->strtab + sym->st_name : "";
}
/*
@@ -786,7 +792,7 @@ static void check_section(const char *modname, struct elf_info *elf,
".ltext", ".ltext.*"
#define OTHER_TEXT_SECTIONS ".ref.text", ".head.text", ".spinlock.text", \
".fixup", ".entry.text", ".exception.text", \
- ".coldtext", ".softirqentry.text"
+ ".coldtext", ".softirqentry.text", ".irqentry.text"
#define ALL_TEXT_SECTIONS ".init.text", ".exit.text", \
TEXT_SECTIONS, OTHER_TEXT_SECTIONS
@@ -1006,6 +1012,7 @@ static void default_mismatch_handler(const char *modname, struct elf_info *elf,
Elf_Sym *from;
const char *tosym;
const char *fromsym;
+ char taddr_str[16];
from = find_fromsym(elf, faddr, fsecndx);
fromsym = sym_name(elf, from);
@@ -1019,10 +1026,17 @@ static void default_mismatch_handler(const char *modname, struct elf_info *elf,
sec_mismatch_count++;
- warn("%s: section mismatch in reference: %s+0x%x (section: %s) -> %s (section: %s)\n",
- modname, fromsym,
- (unsigned int)(faddr - (from ? from->st_value : 0)),
- fromsec, tosym, tosec);
+ if (!tosym[0])
+ snprintf(taddr_str, sizeof(taddr_str), "0x%x", (unsigned int)taddr);
+
+ /*
+ * The format for the reference source: <symbol_name>+<offset> or <address>
+ * The format for the reference destination: <symbol_name> or <address>
+ */
+ warn("%s: section mismatch in reference: %s%s0x%x (section: %s) -> %s (section: %s)\n",
+ modname, fromsym, fromsym[0] ? "+" : "",
+ (unsigned int)(faddr - (fromsym[0] ? from->st_value : 0)),
+ fromsec, tosym[0] ? tosym : taddr_str, tosec);
if (mismatch->mismatch == EXTABLE_TO_NON_TEXT) {
if (match(tosec, mismatch->bad_tosec))
@@ -1143,9 +1157,9 @@ static Elf_Addr addend_386_rel(uint32_t *location, unsigned int r_type)
{
switch (r_type) {
case R_386_32:
- return TO_NATIVE(*location);
+ return get_unaligned_native(location);
case R_386_PC32:
- return TO_NATIVE(*location) + 4;
+ return get_unaligned_native(location) + 4;
}
return (Elf_Addr)(-1);
@@ -1166,24 +1180,24 @@ static Elf_Addr addend_arm_rel(void *loc, Elf_Sym *sym, unsigned int r_type)
switch (r_type) {
case R_ARM_ABS32:
case R_ARM_REL32:
- inst = TO_NATIVE(*(uint32_t *)loc);
+ inst = get_unaligned_native((uint32_t *)loc);
return inst + sym->st_value;
case R_ARM_MOVW_ABS_NC:
case R_ARM_MOVT_ABS:
- inst = TO_NATIVE(*(uint32_t *)loc);
+ inst = get_unaligned_native((uint32_t *)loc);
offset = sign_extend32(((inst & 0xf0000) >> 4) | (inst & 0xfff),
15);
return offset + sym->st_value;
case R_ARM_PC24:
case R_ARM_CALL:
case R_ARM_JUMP24:
- inst = TO_NATIVE(*(uint32_t *)loc);
+ inst = get_unaligned_native((uint32_t *)loc);
offset = sign_extend32((inst & 0x00ffffff) << 2, 25);
return offset + sym->st_value + 8;
case R_ARM_THM_MOVW_ABS_NC:
case R_ARM_THM_MOVT_ABS:
- upper = TO_NATIVE(*(uint16_t *)loc);
- lower = TO_NATIVE(*((uint16_t *)loc + 1));
+ upper = get_unaligned_native((uint16_t *)loc);
+ lower = get_unaligned_native((uint16_t *)loc + 1);
offset = sign_extend32(((upper & 0x000f) << 12) |
((upper & 0x0400) << 1) |
((lower & 0x7000) >> 4) |
@@ -1200,8 +1214,8 @@ static Elf_Addr addend_arm_rel(void *loc, Elf_Sym *sym, unsigned int r_type)
* imm11 = lower[10:0]
* imm32 = SignExtend(S:J2:J1:imm6:imm11:'0')
*/
- upper = TO_NATIVE(*(uint16_t *)loc);
- lower = TO_NATIVE(*((uint16_t *)loc + 1));
+ upper = get_unaligned_native((uint16_t *)loc);
+ lower = get_unaligned_native((uint16_t *)loc + 1);
sign = (upper >> 10) & 1;
j1 = (lower >> 13) & 1;
@@ -1224,8 +1238,8 @@ static Elf_Addr addend_arm_rel(void *loc, Elf_Sym *sym, unsigned int r_type)
* I2 = NOT(J2 XOR S)
* imm32 = SignExtend(S:I1:I2:imm10:imm11:'0')
*/
- upper = TO_NATIVE(*(uint16_t *)loc);
- lower = TO_NATIVE(*((uint16_t *)loc + 1));
+ upper = get_unaligned_native((uint16_t *)loc);
+ lower = get_unaligned_native((uint16_t *)loc + 1);
sign = (upper >> 10) & 1;
j1 = (lower >> 13) & 1;
@@ -1246,7 +1260,7 @@ static Elf_Addr addend_mips_rel(uint32_t *location, unsigned int r_type)
{
uint32_t inst;
- inst = TO_NATIVE(*location);
+ inst = get_unaligned_native(location);
switch (r_type) {
case R_MIPS_LO16:
return inst & 0xffff;
@@ -1459,14 +1473,8 @@ static void extract_crcs_for_object(const char *object, struct module *mod)
const char *base;
int dirlen, ret;
- base = strrchr(object, '/');
- if (base) {
- base++;
- dirlen = base - object;
- } else {
- dirlen = 0;
- base = object;
- }
+ base = get_basename(object);
+ dirlen = base - object;
ret = snprintf(cmd_file, sizeof(cmd_file), "%.*s.%s.cmd",
dirlen, object, base);
@@ -1567,6 +1575,14 @@ static void read_symbols(const char *modname)
/* strip trailing .o */
mod = new_module(modname, strlen(modname) - strlen(".o"));
+ /* save .no_trim_symbol section for later use */
+ if (info.no_trim_symbol_len) {
+ mod->no_trim_symbol = xmalloc(info.no_trim_symbol_len);
+ memcpy(mod->no_trim_symbol, info.no_trim_symbol,
+ info.no_trim_symbol_len);
+ mod->no_trim_symbol_len = info.no_trim_symbol_len;
+ }
+
if (!mod->is_vmlinux) {
license = get_modinfo(&info, "license");
if (!license)
@@ -1586,7 +1602,7 @@ static void read_symbols(const char *modname)
namespace);
}
- if (extra_warn && !get_modinfo(&info, "description"))
+ if (!get_modinfo(&info, "description"))
warn("missing MODULE_DESCRIPTION() in %s\n", modname);
}
@@ -1662,7 +1678,7 @@ void buf_write(struct buffer *buf, const char *s, int len)
{
if (buf->size - buf->pos < len) {
buf->size += len + SZ;
- buf->p = NOFAIL(realloc(buf->p, buf->size));
+ buf->p = xrealloc(buf->p, buf->size);
}
strncpy(buf->p + buf->pos, s, len);
buf->pos += len;
@@ -1677,7 +1693,7 @@ static void check_exports(struct module *mod)
exp = find_symbol(s->name);
if (!exp) {
if (!s->weak && nr_unresolved++ < MAX_UNRESOLVED_REPORTS)
- modpost_log(warn_unresolved ? LOG_WARN : LOG_ERROR,
+ modpost_log(!warn_unresolved,
"\"%s\" [%s.ko] undefined!\n",
s->name, mod->name);
continue;
@@ -1693,14 +1709,10 @@ static void check_exports(struct module *mod)
s->crc_valid = exp->crc_valid;
s->crc = exp->crc;
- basename = strrchr(mod->name, '/');
- if (basename)
- basename++;
- else
- basename = mod->name;
+ basename = get_basename(mod->name);
if (!contains_namespace(&mod->imported_namespaces, exp->namespace)) {
- modpost_log(allow_missing_ns_imports ? LOG_WARN : LOG_ERROR,
+ modpost_log(!allow_missing_ns_imports,
"module %s uses symbol %s from namespace %s, but does not import it.\n",
basename, exp->name, exp->namespace);
add_namespace(&mod->missing_namespaces, exp->namespace);
@@ -1729,15 +1741,34 @@ static void handle_white_list_exports(const char *white_list)
free(buf);
}
+/*
+ * Keep symbols recorded in the .no_trim_symbol section. This is necessary to
+ * prevent CONFIG_TRIM_UNUSED_KSYMS from dropping EXPORT_SYMBOL because
+ * symbol_get() relies on the symbol being present in the ksymtab for lookups.
+ */
+static void keep_no_trim_symbols(struct module *mod)
+{
+ unsigned long size = mod->no_trim_symbol_len;
+
+ for (char *s = mod->no_trim_symbol; s; s = next_string(s , &size)) {
+ struct symbol *sym;
+
+ /*
+ * If find_symbol() returns NULL, this symbol is not provided
+ * by any module, and symbol_get() will fail.
+ */
+ sym = find_symbol(s);
+ if (sym)
+ sym->used = true;
+ }
+}
+
static void check_modname_len(struct module *mod)
{
const char *mod_name;
- mod_name = strrchr(mod->name, '/');
- if (mod_name == NULL)
- mod_name = mod->name;
- else
- mod_name++;
+ mod_name = get_basename(mod->name);
+
if (strlen(mod_name) >= MODULE_NAME_LEN)
error("module name is too long [%s.ko]\n", mod->name);
}
@@ -1748,26 +1779,9 @@ static void check_modname_len(struct module *mod)
static void add_header(struct buffer *b, struct module *mod)
{
buf_printf(b, "#include <linux/module.h>\n");
- /*
- * Include build-salt.h after module.h in order to
- * inherit the definitions.
- */
- buf_printf(b, "#define INCLUDE_VERMAGIC\n");
- buf_printf(b, "#include <linux/build-salt.h>\n");
- buf_printf(b, "#include <linux/elfnote-lto.h>\n");
buf_printf(b, "#include <linux/export-internal.h>\n");
- buf_printf(b, "#include <linux/vermagic.h>\n");
buf_printf(b, "#include <linux/compiler.h>\n");
buf_printf(b, "\n");
- buf_printf(b, "#ifdef CONFIG_UNWINDER_ORC\n");
- buf_printf(b, "#include <asm/orc_header.h>\n");
- buf_printf(b, "ORC_HEADER;\n");
- buf_printf(b, "#endif\n");
- buf_printf(b, "\n");
- buf_printf(b, "BUILD_SALT;\n");
- buf_printf(b, "BUILD_LTO_INFO;\n");
- buf_printf(b, "\n");
- buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n");
buf_printf(b, "MODULE_INFO(name, KBUILD_MODNAME);\n");
buf_printf(b, "\n");
buf_printf(b, "__visible struct module __this_module\n");
@@ -1785,12 +1799,6 @@ static void add_header(struct buffer *b, struct module *mod)
if (!external_module)
buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n");
- buf_printf(b,
- "\n"
- "#ifdef CONFIG_MITIGATION_RETPOLINE\n"
- "MODULE_INFO(retpoline, \"Y\");\n"
- "#endif\n");
-
if (strstarts(mod->name, "drivers/staging"))
buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n");
@@ -1834,13 +1842,56 @@ static void add_exported_symbols(struct buffer *buf, struct module *mod)
}
/**
+ * Record CRCs for unresolved symbols, supporting long names
+ */
+static void add_extended_versions(struct buffer *b, struct module *mod)
+{
+ struct symbol *s;
+
+ if (!extended_modversions)
+ return;
+
+ buf_printf(b, "\n");
+ buf_printf(b, "static const u32 ____version_ext_crcs[]\n");
+ buf_printf(b, "__used __section(\"__version_ext_crcs\") = {\n");
+ list_for_each_entry(s, &mod->unresolved_symbols, list) {
+ if (!s->module)
+ continue;
+ if (!s->crc_valid) {
+ warn("\"%s\" [%s.ko] has no CRC!\n",
+ s->name, mod->name);
+ continue;
+ }
+ buf_printf(b, "\t0x%08x,\n", s->crc);
+ }
+ buf_printf(b, "};\n");
+
+ buf_printf(b, "static const char ____version_ext_names[]\n");
+ buf_printf(b, "__used __section(\"__version_ext_names\") =\n");
+ list_for_each_entry(s, &mod->unresolved_symbols, list) {
+ if (!s->module)
+ continue;
+ if (!s->crc_valid)
+ /*
+ * We already warned on this when producing the crc
+ * table.
+ * We need to skip its name too, as the indexes in
+ * both tables need to align.
+ */
+ continue;
+ buf_printf(b, "\t\"%s\\0\"\n", s->name);
+ }
+ buf_printf(b, ";\n");
+}
+
+/**
* Record CRCs for unresolved symbols
**/
static void add_versions(struct buffer *b, struct module *mod)
{
struct symbol *s;
- if (!modversions)
+ if (!basic_modversions)
return;
buf_printf(b, "\n");
@@ -1856,11 +1907,16 @@ static void add_versions(struct buffer *b, struct module *mod)
continue;
}
if (strlen(s->name) >= MODULE_NAME_LEN) {
- error("too long symbol \"%s\" [%s.ko]\n",
- s->name, mod->name);
- break;
+ if (extended_modversions) {
+ /* this symbol will only be in the extended info */
+ continue;
+ } else {
+ error("too long symbol \"%s\" [%s.ko]\n",
+ s->name, mod->name);
+ break;
+ }
}
- buf_printf(b, "\t{ %#8x, \"%s\" },\n",
+ buf_printf(b, "\t{ 0x%08x, \"%s\" },\n",
s->crc, s->name);
}
@@ -1889,11 +1945,7 @@ static void add_depends(struct buffer *b, struct module *mod)
continue;
s->module->seen = true;
- p = strrchr(s->module->name, '/');
- if (p)
- p++;
- else
- p = s->module->name;
+ p = get_basename(s->module->name);
buf_printf(b, "%s%s", first ? "" : ",", p);
first = 0;
}
@@ -1947,7 +1999,7 @@ static void write_if_changed(struct buffer *b, const char *fname)
if (st.st_size != b->pos)
goto close_write;
- tmp = NOFAIL(malloc(b->pos));
+ tmp = xmalloc(b->pos);
if (fread(tmp, 1, b->pos, file) != b->pos)
goto free_write;
@@ -1982,14 +2034,23 @@ static void write_vmlinux_export_c_file(struct module *mod)
static void write_mod_c_file(struct module *mod)
{
struct buffer buf = { };
+ struct module_alias *alias, *next;
char fname[PATH_MAX];
int ret;
add_header(&buf, mod);
add_exported_symbols(&buf, mod);
add_versions(&buf, mod);
+ add_extended_versions(&buf, mod);
add_depends(&buf, mod);
- add_moddevtable(&buf, mod);
+
+ buf_printf(&buf, "\n");
+ list_for_each_entry_safe(alias, next, &mod->aliases, node) {
+ buf_printf(&buf, "MODULE_ALIAS(\"%s\");\n", alias->str);
+ list_del(&alias->node);
+ free(alias);
+ }
+
add_srcversion(&buf, mod);
ret = snprintf(fname, sizeof(fname), "%s.mod.c", mod->name);
@@ -2051,10 +2112,10 @@ static void read_dump(const char *fname)
continue;
}
- mod = find_module(modname);
+ mod = find_module(fname, modname);
if (!mod) {
mod = new_module(modname, strlen(modname));
- mod->from_dump = true;
+ mod->dump_file = fname;
}
s = sym_add_exported(symname, mod, gpl_only, namespace);
sym_set_crc(s, crc);
@@ -2073,7 +2134,7 @@ static void write_dump(const char *fname)
struct symbol *sym;
list_for_each_entry(mod, &modules, list) {
- if (mod->from_dump)
+ if (mod->dump_file)
continue;
list_for_each_entry(sym, &mod->exported_symbols, list) {
if (trim_unused_exports && !sym->used)
@@ -2097,7 +2158,7 @@ static void write_namespace_deps_files(const char *fname)
list_for_each_entry(mod, &modules, list) {
- if (mod->from_dump || list_empty(&mod->missing_namespaces))
+ if (mod->dump_file || list_empty(&mod->missing_namespaces))
continue;
buf_printf(&ns_deps_buf, "%s.ko:", mod->name);
@@ -2117,6 +2178,25 @@ struct dump_list {
const char *file;
};
+static void check_host_endian(void)
+{
+ static const union {
+ short s;
+ char c[2];
+ } endian_test = { .c = {0x01, 0x02} };
+
+ switch (endian_test.s) {
+ case 0x0102:
+ host_is_big_endian = true;
+ break;
+ case 0x0201:
+ host_is_big_endian = false;
+ break;
+ default:
+ fatal("Unknown host endian\n");
+ }
+}
+
int main(int argc, char **argv)
{
struct module *mod;
@@ -2127,13 +2207,13 @@ int main(int argc, char **argv)
LIST_HEAD(dump_lists);
struct dump_list *dl, *dl2;
- while ((opt = getopt(argc, argv, "ei:MmnT:to:au:WwENd:")) != -1) {
+ while ((opt = getopt(argc, argv, "ei:MmnT:to:au:WwENd:xb")) != -1) {
switch (opt) {
case 'e':
external_module = true;
break;
case 'i':
- dl = NOFAIL(malloc(sizeof(*dl)));
+ dl = xmalloc(sizeof(*dl));
dl->file = optarg;
list_add_tail(&dl->list, &dump_lists);
break;
@@ -2176,11 +2256,19 @@ int main(int argc, char **argv)
case 'd':
missing_namespace_deps = optarg;
break;
+ case 'b':
+ basic_modversions = true;
+ break;
+ case 'x':
+ extended_modversions = true;
+ break;
default:
exit(1);
}
}
+ check_host_endian();
+
list_for_each_entry_safe(dl, dl2, &dump_lists, list) {
read_dump(dl->file);
list_del(&dl->list);
@@ -2194,7 +2282,9 @@ int main(int argc, char **argv)
read_symbols_from_files(files_source);
list_for_each_entry(mod, &modules, list) {
- if (mod->from_dump || mod->is_vmlinux)
+ keep_no_trim_symbols(mod);
+
+ if (mod->dump_file || mod->is_vmlinux)
continue;
check_modname_len(mod);
@@ -2205,7 +2295,7 @@ int main(int argc, char **argv)
handle_white_list_exports(unused_exports_white_list);
list_for_each_entry(mod, &modules, list) {
- if (mod->from_dump)
+ if (mod->dump_file)
continue;
if (mod->is_vmlinux)
diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h
index 58197b34a3c8..9133e4c3803f 100644
--- a/scripts/mod/modpost.h
+++ b/scripts/mod/modpost.h
@@ -62,21 +62,26 @@
x); \
})
-#if KERNEL_ELFDATA != HOST_ELFDATA
+#define TO_NATIVE(x) \
+ (target_is_big_endian == host_is_big_endian ? x : bswap(x))
-#define TO_NATIVE(x) (bswap(x))
-
-#else /* endianness matches */
-
-#define TO_NATIVE(x) (x)
+#define __get_unaligned_t(type, ptr) ({ \
+ const struct { type x; } __attribute__((__packed__)) *__pptr = \
+ (typeof(__pptr))(ptr); \
+ __pptr->x; \
+})
-#endif
+#define get_unaligned(ptr) __get_unaligned_t(typeof(*(ptr)), (ptr))
-#define NOFAIL(ptr) do_nofail((ptr), #ptr)
+#define get_unaligned_native(ptr) \
+({ \
+ typeof(*(ptr)) _val = get_unaligned(ptr); \
+ TO_NATIVE(_val); \
+})
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
-void *do_nofail(void *ptr, const char *expr);
+#define strstarts(str, prefix) (strncmp(str, prefix, strlen(prefix)) == 0)
struct buffer {
char *p;
@@ -90,22 +95,43 @@ buf_printf(struct buffer *buf, const char *fmt, ...);
void
buf_write(struct buffer *buf, const char *s, int len);
+/**
+ * struct module_alias - auto-generated MODULE_ALIAS()
+ *
+ * @node: linked to module::aliases
+ * @str: a string for MODULE_ALIAS()
+ */
+struct module_alias {
+ struct list_head node;
+ char str[];
+};
+
+/**
+ * struct module - represent a module (vmlinux or *.ko)
+ *
+ * @dump_file: path to the .symvers file if loaded from a file
+ * @aliases: list head for module_aliases
+ * @no_trim_symbol: .no_trim_symbol section data
+ * @no_trim_symbol_len: length of the .no_trim_symbol section
+ */
struct module {
struct list_head list;
struct list_head exported_symbols;
struct list_head unresolved_symbols;
+ const char *dump_file;
bool is_gpl_compatible;
- bool from_dump; /* true if module was loaded from *.symvers */
bool is_vmlinux;
bool seen;
bool has_init;
bool has_cleanup;
- struct buffer dev_table_buf;
char srcversion[25];
// Missing namespace dependencies
struct list_head missing_namespaces;
// Actual imported namespaces
struct list_head imported_namespaces;
+ struct list_head aliases;
+ char *no_trim_symbol;
+ unsigned int no_trim_symbol_len;
char name[];
};
@@ -119,6 +145,8 @@ struct elf_info {
char *strtab;
char *modinfo;
unsigned int modinfo_len;
+ char *no_trim_symbol;
+ unsigned int no_trim_symbol_len;
/* support for 32bit section numbers */
@@ -181,23 +209,20 @@ Elf_Sym *symsearch_find_nearest(struct elf_info *elf, Elf_Addr addr,
/* file2alias.c */
void handle_moddevtable(struct module *mod, struct elf_info *info,
Elf_Sym *sym, const char *symname);
-void add_moddevtable(struct buffer *buf, struct module *mod);
/* sumversion.c */
void get_src_version(const char *modname, char sum[], unsigned sumlen);
/* from modpost.c */
+extern bool target_is_big_endian;
+extern bool host_is_big_endian;
+const char *get_basename(const char *path);
char *read_text_file(const char *filename);
char *get_line(char **stringp);
void *sym_get_data(const struct elf_info *info, const Elf_Sym *sym);
-enum loglevel {
- LOG_WARN,
- LOG_ERROR,
-};
-
void __attribute__((format(printf, 2, 3)))
-modpost_log(enum loglevel loglevel, const char *fmt, ...);
+modpost_log(bool is_error, const char *fmt, ...);
/*
* warn - show the given message, then let modpost continue running, still
@@ -212,6 +237,6 @@ modpost_log(enum loglevel loglevel, const char *fmt, ...);
* fatal - show the given message, and bail out immediately. This should be
* used when there is no point to continue running modpost.
*/
-#define warn(fmt, args...) modpost_log(LOG_WARN, fmt, ##args)
-#define error(fmt, args...) modpost_log(LOG_ERROR, fmt, ##args)
+#define warn(fmt, args...) modpost_log(false, fmt, ##args)
+#define error(fmt, args...) modpost_log(true, fmt, ##args)
#define fatal(fmt, args...) do { error(fmt, ##args); exit(1); } while (1)
diff --git a/scripts/mod/sumversion.c b/scripts/mod/sumversion.c
index dc4878502276..3dd28b4d0099 100644
--- a/scripts/mod/sumversion.c
+++ b/scripts/mod/sumversion.c
@@ -8,6 +8,8 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
+
+#include <xalloc.h>
#include "modpost.h"
/*
@@ -305,18 +307,13 @@ static int parse_source_files(const char *objfile, struct md4_ctx *md)
const char *base;
int dirlen, ret = 0, check_files = 0;
- cmd = NOFAIL(malloc(strlen(objfile) + sizeof("..cmd")));
+ cmd = xmalloc(strlen(objfile) + sizeof("..cmd"));
- base = strrchr(objfile, '/');
- if (base) {
- base++;
- dirlen = base - objfile;
- sprintf(cmd, "%.*s.%s.cmd", dirlen, objfile, base);
- } else {
- dirlen = 0;
- sprintf(cmd, ".%s.cmd", objfile);
- }
- dir = NOFAIL(malloc(dirlen + 1));
+ base = get_basename(objfile);
+ dirlen = base - objfile;
+ sprintf(cmd, "%.*s.%s.cmd", dirlen, objfile, base);
+
+ dir = xmalloc(dirlen + 1);
strncpy(dir, objfile, dirlen);
dir[dirlen] = '\0';
@@ -333,7 +330,7 @@ static int parse_source_files(const char *objfile, struct md4_ctx *md)
line++;
p = line;
- if (strncmp(line, "source_", sizeof("source_")-1) == 0) {
+ if (strstarts(line, "source_")) {
p = strrchr(line, ' ');
if (!p) {
warn("malformed line: %s\n", line);
@@ -347,7 +344,7 @@ static int parse_source_files(const char *objfile, struct md4_ctx *md)
}
continue;
}
- if (strncmp(line, "deps_", sizeof("deps_")-1) == 0) {
+ if (strstarts(line, "deps_")) {
check_files = 1;
continue;
}
@@ -390,7 +387,7 @@ out_file:
/* Calc and record src checksum. */
void get_src_version(const char *modname, char sum[], unsigned sumlen)
{
- char *buf;
+ char *buf, *pos;
struct md4_ctx md;
char *fname;
char filelist[PATH_MAX + 1];
@@ -399,9 +396,10 @@ void get_src_version(const char *modname, char sum[], unsigned sumlen)
snprintf(filelist, sizeof(filelist), "%s.mod", modname);
buf = read_text_file(filelist);
+ pos = buf;
md4_init(&md);
- while ((fname = strsep(&buf, "\n"))) {
+ while ((fname = strsep(&pos, "\n"))) {
if (!*fname)
continue;
if (!(is_static_library(fname)) &&
diff --git a/scripts/mod/symsearch.c b/scripts/mod/symsearch.c
index aa4ed51f9960..b9737b92f7f8 100644
--- a/scripts/mod/symsearch.c
+++ b/scripts/mod/symsearch.c
@@ -4,7 +4,7 @@
* Helper functions for finding the symbol in an ELF which is "nearest"
* to a given address.
*/
-
+#include <xalloc.h>
#include "modpost.h"
struct syminfo {
@@ -125,8 +125,8 @@ void symsearch_init(struct elf_info *elf)
{
unsigned int table_size = symbol_count(elf);
- elf->symsearch = NOFAIL(malloc(sizeof(struct symsearch) +
- sizeof(struct syminfo) * table_size));
+ elf->symsearch = xmalloc(sizeof(struct symsearch) +
+ sizeof(struct syminfo) * table_size);
elf->symsearch->table_size = table_size;
symsearch_populate(elf, elf->symsearch->table, table_size);
diff --git a/scripts/module-common.c b/scripts/module-common.c
new file mode 100644
index 000000000000..12fbc6d3aae8
--- /dev/null
+++ b/scripts/module-common.c
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/module.h>
+/*
+ * Include build-salt.h after module.h in order to
+ * inherit the definitions.
+ */
+#define INCLUDE_VERMAGIC
+#include <linux/build-salt.h>
+#include <linux/elfnote-lto.h>
+#include <linux/vermagic.h>
+
+#ifdef CONFIG_UNWINDER_ORC
+#include <asm/orc_header.h>
+ORC_HEADER;
+#endif
+
+BUILD_SALT;
+BUILD_LTO_INFO;
+
+MODULE_INFO(vermagic, VERMAGIC_STRING);
+
+#ifdef CONFIG_MITIGATION_RETPOLINE
+MODULE_INFO(retpoline, "Y");
+#endif
diff --git a/scripts/module.lds.S b/scripts/module.lds.S
index 3f43edef813c..450f1088d5fd 100644
--- a/scripts/module.lds.S
+++ b/scripts/module.lds.S
@@ -16,12 +16,13 @@ SECTIONS {
*(.discard)
*(.discard.*)
*(.export_symbol)
+ *(.no_trim_symbol)
}
- __ksymtab 0 : { *(SORT(___ksymtab+*)) }
- __ksymtab_gpl 0 : { *(SORT(___ksymtab_gpl+*)) }
- __kcrctab 0 : { *(SORT(___kcrctab+*)) }
- __kcrctab_gpl 0 : { *(SORT(___kcrctab_gpl+*)) }
+ __ksymtab 0 : ALIGN(8) { *(SORT(___ksymtab+*)) }
+ __ksymtab_gpl 0 : ALIGN(8) { *(SORT(___ksymtab_gpl+*)) }
+ __kcrctab 0 : ALIGN(4) { *(SORT(___kcrctab+*)) }
+ __kcrctab_gpl 0 : ALIGN(4) { *(SORT(___kcrctab_gpl+*)) }
.ctors 0 : ALIGN(8) { *(SORT(.ctors.*)) *(.ctors) }
.init_array 0 : ALIGN(8) { *(SORT(.init_array.*)) *(.init_array) }
@@ -29,6 +30,7 @@ SECTIONS {
.altinstructions 0 : ALIGN(8) { KEEP(*(.altinstructions)) }
__bug_table 0 : ALIGN(8) { KEEP(*(__bug_table)) }
__jump_table 0 : ALIGN(8) { KEEP(*(__jump_table)) }
+ __ex_table 0 : ALIGN(4) { KEEP(*(__ex_table)) }
__patchable_function_entries : { *(__patchable_function_entries) }
@@ -50,7 +52,7 @@ SECTIONS {
.data : {
*(.data .data.[0-9a-zA-Z_]*)
*(.data..L*)
- CODETAG_SECTIONS()
+ MOD_CODETAG_SECTIONS()
}
.rodata : {
@@ -59,9 +61,10 @@ SECTIONS {
}
#else
.data : {
- CODETAG_SECTIONS()
+ MOD_CODETAG_SECTIONS()
}
#endif
+ MOD_SEPARATE_CODETAG_SECTIONS()
}
/* bring in arch-specific sections */
diff --git a/scripts/nsdeps b/scripts/nsdeps
index f1718cc0d700..a3372166ac01 100644
--- a/scripts/nsdeps
+++ b/scripts/nsdeps
@@ -19,22 +19,16 @@ if ! { echo "$SPATCH_REQ_VERSION"; echo "$SPATCH_VERSION"; } | sort -CV ; then
exit 1
fi
-if [ "$KBUILD_EXTMOD" ]; then
- src_prefix=
-else
- src_prefix=$srctree/
-fi
-
generate_deps_for_ns() {
$SPATCH --very-quiet --in-place --sp-file \
- $srctree/scripts/coccinelle/misc/add_namespace.cocci -D nsdeps -D ns=$1 $2
+ $srctree/scripts/coccinelle/misc/add_namespace.cocci -D nsdeps -D ns=\"$1\" $2
}
generate_deps() {
local mod=${1%.ko:}
shift
local namespaces="$*"
- local mod_source_files=$(sed "s|^\(.*\)\.o$|${src_prefix}\1.c|" $mod.mod)
+ local mod_source_files=$(sed "s|^\(.*\)\.o$|${srcroot}/\1.c|" $mod.mod)
for ns in $namespaces; do
echo "Adding namespace $ns to module $mod.ko."
@@ -57,4 +51,4 @@ generate_deps() {
while read line
do
generate_deps $line
-done < $MODULES_NSDEPS
+done < modules.nsdeps
diff --git a/scripts/package/PKGBUILD b/scripts/package/PKGBUILD
index 663ce300dd06..452374d63c24 100644
--- a/scripts/package/PKGBUILD
+++ b/scripts/package/PKGBUILD
@@ -3,10 +3,13 @@
# Contributor: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
pkgbase=${PACMAN_PKGBASE:-linux-upstream}
-pkgname=("${pkgbase}" "${pkgbase}-api-headers")
-if grep -q CONFIG_MODULES=y include/config/auto.conf; then
- pkgname+=("${pkgbase}-headers")
-fi
+pkgname=("${pkgbase}")
+
+_extrapackages=${PACMAN_EXTRAPACKAGES-headers api-headers debug}
+for pkg in $_extrapackages; do
+ pkgname+=("${pkgbase}-${pkg}")
+done
+
pkgver="${KERNELRELEASE//-/_}"
# The PKGBUILD is evaluated multiple times.
# Running scripts/build-version from here would introduce inconsistencies.
@@ -19,7 +22,6 @@ license=(GPL-2.0-only)
makedepends=(
bc
bison
- cpio
flex
gettext
kmod
@@ -33,11 +35,17 @@ makedepends=(
)
options=(!debug !strip !buildflags !makeflags)
-build() {
+_prologue() {
# MAKEFLAGS from makepkg.conf override the ones inherited from kbuild.
# Bypass this override with a custom variable.
export MAKEFLAGS="${KBUILD_MAKEFLAGS}"
- cd "${objtree}"
+
+ # Kbuild works in the output directory, where this PKGBUILD is located.
+ cd "$(dirname "${BASH_SOURCE[0]}")"
+}
+
+build() {
+ _prologue
${MAKE} KERNELRELEASE="${KERNELRELEASE}" KBUILD_BUILD_VERSION="${pkgrel}"
}
@@ -45,9 +53,9 @@ build() {
_package() {
pkgdesc="The ${pkgdesc} kernel and modules"
- export MAKEFLAGS="${KBUILD_MAKEFLAGS}"
- cd "${objtree}"
- local modulesdir="${pkgdir}/usr/${MODLIB}"
+ local modulesdir="${pkgdir}/usr/lib/modules/${KERNELRELEASE}"
+
+ _prologue
echo "Installing boot image..."
# systemd expects to find the kernel here to allow hibernation
@@ -73,14 +81,17 @@ _package() {
_package-headers() {
pkgdesc="Headers and scripts for building modules for the ${pkgdesc} kernel"
- export MAKEFLAGS="${KBUILD_MAKEFLAGS}"
- cd "${objtree}"
- local builddir="${pkgdir}/usr/${MODLIB}/build"
+ local builddir="${pkgdir}/usr/lib/modules/${KERNELRELEASE}/build"
+
+ _prologue
- echo "Installing build files..."
- "${srctree}/scripts/package/install-extmod-build" "${builddir}"
+ if grep -q CONFIG_MODULES=y include/config/auto.conf; then
+ echo "Installing build files..."
+ "${srctree}/scripts/package/install-extmod-build" "${builddir}"
+ fi
echo "Installing System.map and config..."
+ mkdir -p "${builddir}"
cp System.map "${builddir}/System.map"
cp .config "${builddir}/.config"
@@ -91,15 +102,27 @@ _package-headers() {
_package-api-headers() {
pkgdesc="Kernel headers sanitized for use in userspace"
- provides=(linux-api-headers)
+ provides=(linux-api-headers="${pkgver}")
conflicts=(linux-api-headers)
- export MAKEFLAGS="${KBUILD_MAKEFLAGS}"
- cd "${objtree}"
+ _prologue
${MAKE} headers_install INSTALL_HDR_PATH="${pkgdir}/usr"
}
+_package-debug(){
+ pkgdesc="Non-stripped vmlinux file for the ${pkgdesc} kernel"
+
+ local debugdir="${pkgdir}/usr/src/debug/${pkgbase}"
+ local builddir="${pkgdir}/usr/lib/modules/${KERNELRELEASE}/build"
+
+ _prologue
+
+ install -Dt "${debugdir}" -m644 vmlinux
+ mkdir -p "${builddir}"
+ ln -sr "${debugdir}/vmlinux" "${builddir}/vmlinux"
+}
+
for _p in "${pkgname[@]}"; do
eval "package_$_p() {
$(declare -f "_package${_p#$pkgbase}")
diff --git a/scripts/package/builddeb b/scripts/package/builddeb
index c1757db6aa8a..3627ca227e5a 100755
--- a/scripts/package/builddeb
+++ b/scripts/package/builddeb
@@ -5,10 +5,12 @@
#
# Simple script to generate a deb package for a Linux kernel. All the
# complexity of what to do with a kernel after it is installed or removed
-# is left to other scripts and packages: they can install scripts in the
-# /etc/kernel/{pre,post}{inst,rm}.d/ directories (or an alternative location
-# specified in KDEB_HOOKDIR) that will be called on package install and
-# removal.
+# is left to other scripts and packages. Scripts can be placed into the
+# preinst, postinst, prerm and postrm directories in /etc/kernel or
+# /usr/share/kernel. A different list of search directories can be given
+# via KDEB_HOOKDIR. Scripts in directories earlier in the list will
+# override scripts of the same name in later directories. The script will
+# be called on package installation and removal.
set -eu
@@ -63,18 +65,21 @@ install_linux_image () {
esac
cp "$(${MAKE} -s -f ${srctree}/Makefile image_name)" "${pdir}/${installed_image_path}"
+ if [ "${ARCH}" != um ]; then
+ install_maint_scripts "${pdir}"
+ fi
+}
+
+install_maint_scripts () {
# Install the maintainer scripts
# Note: hook scripts under /etc/kernel are also executed by official Debian
# kernel packages, as well as kernel packages built using make-kpkg.
# make-kpkg sets $INITRD to indicate whether an initramfs is wanted, and
# so do we; recent versions of dracut and initramfs-tools will obey this.
- debhookdir=${KDEB_HOOKDIR:-/etc/kernel}
+ debhookdir=${KDEB_HOOKDIR:-/etc/kernel /usr/share/kernel}
for script in postinst postrm preinst prerm; do
- mkdir -p "${pdir}${debhookdir}/${script}.d"
-
mkdir -p "${pdir}/DEBIAN"
cat <<-EOF > "${pdir}/DEBIAN/${script}"
-
#!/bin/sh
set -e
@@ -85,7 +90,15 @@ install_linux_image () {
# Tell initramfs builder whether it's wanted
export INITRD=$(if_enabled_echo CONFIG_BLK_DEV_INITRD Yes No)
- test -d ${debhookdir}/${script}.d && run-parts --arg="${KERNELRELEASE}" --arg="/${installed_image_path}" ${debhookdir}/${script}.d
+ # run-parts will error out if one of its directory arguments does not
+ # exist, so filter the list of hook directories accordingly.
+ hookdirs=
+ for dir in ${debhookdir}; do
+ test -d "\$dir/${script}.d" || continue
+ hookdirs="\$hookdirs \$dir/${script}.d"
+ done
+ hookdirs="\${hookdirs# }"
+ test -n "\$hookdirs" && run-parts --arg="${KERNELRELEASE}" --arg="/${installed_image_path}" \$hookdirs
exit 0
EOF
chmod 755 "${pdir}/DEBIAN/${script}"
@@ -97,16 +110,18 @@ install_linux_image_dbg () {
# Parse modules.order directly because 'make modules_install' may sign,
# compress modules, and then run unneeded depmod.
- while read -r mod; do
- mod="${mod%.o}.ko"
- dbg="${pdir}/usr/lib/debug/lib/modules/${KERNELRELEASE}/kernel/${mod}"
- buildid=$("${READELF}" -n "${mod}" | sed -n 's@^.*Build ID: \(..\)\(.*\)@\1/\2@p')
- link="${pdir}/usr/lib/debug/.build-id/${buildid}.debug"
-
- mkdir -p "${dbg%/*}" "${link%/*}"
- "${OBJCOPY}" --only-keep-debug "${mod}" "${dbg}"
- ln -sf --relative "${dbg}" "${link}"
- done < modules.order
+ if is_enabled CONFIG_MODULES; then
+ while read -r mod; do
+ mod="${mod%.o}.ko"
+ dbg="${pdir}/usr/lib/debug/lib/modules/${KERNELRELEASE}/kernel/${mod}"
+ buildid=$("${READELF}" -n "${mod}" | sed -n 's@^.*Build ID: \(..\)\(.*\)@\1/\2@p')
+ link="${pdir}/usr/lib/debug/.build-id/${buildid}.debug"
+
+ mkdir -p "${dbg%/*}" "${link%/*}"
+ "${OBJCOPY}" --only-keep-debug "${mod}" "${dbg}"
+ ln -sf --relative "${dbg}" "${link}"
+ done < modules.order
+ fi
# Build debug package
# Different tools want the image in different locations
@@ -124,7 +139,7 @@ install_kernel_headers () {
pdir=debian/$1
version=${1#linux-headers-}
- "${srctree}/scripts/package/install-extmod-build" "${pdir}/usr/src/linux-headers-${version}"
+ CC="${DEB_HOST_GNU_TYPE}-gcc" "${srctree}/scripts/package/install-extmod-build" "${pdir}/usr/src/linux-headers-${version}"
mkdir -p $pdir/lib/modules/$version/
ln -s /usr/src/linux-headers-$version $pdir/lib/modules/$version/build
diff --git a/scripts/package/debian/rules b/scripts/package/debian/rules
index ca07243bd5cd..a417a7f8bbc1 100755
--- a/scripts/package/debian/rules
+++ b/scripts/package/debian/rules
@@ -21,9 +21,11 @@ ifeq ($(origin KBUILD_VERBOSE),undefined)
endif
endif
-revision = $(lastword $(subst -, ,$(shell dpkg-parsechangelog -S Version)))
+revision = $(shell dpkg-parsechangelog -S Version | sed -n 's/.*-//p')
CROSS_COMPILE ?= $(filter-out $(DEB_BUILD_GNU_TYPE)-, $(DEB_HOST_GNU_TYPE)-)
-make-opts = ARCH=$(ARCH) KERNELRELEASE=$(KERNELRELEASE) KBUILD_BUILD_VERSION=$(revision) $(addprefix CROSS_COMPILE=,$(CROSS_COMPILE))
+make-opts = ARCH=$(ARCH) KERNELRELEASE=$(KERNELRELEASE) \
+ $(addprefix KBUILD_BUILD_VERSION=,$(revision)) \
+ $(addprefix CROSS_COMPILE=,$(CROSS_COMPILE))
binary-targets := $(addprefix binary-, image image-dbg headers libc-dev)
@@ -41,6 +43,10 @@ package = $($(@:binary-%=%-package))
# which package is being processed in the build log.
DH_OPTIONS = -p$(package)
+# Note: future removal of KDEB_COMPRESS
+# dpkg-deb >= 1.21.10 supports the DPKG_DEB_COMPRESSOR_TYPE environment
+# variable, which provides the same functionality as KDEB_COMPRESS. The
+# KDEB_COMPRESS variable will be removed in the future.
define binary
$(Q)dh_testdir $(DH_OPTIONS)
$(Q)dh_testroot $(DH_OPTIONS)
diff --git a/scripts/package/install-extmod-build b/scripts/package/install-extmod-build
index 8cc9e13403ae..b96538787f3d 100755
--- a/scripts/package/install-extmod-build
+++ b/scripts/package/install-extmod-build
@@ -9,15 +9,22 @@ is_enabled() {
grep -q "^$1=y" include/config/auto.conf
}
+find_in_scripts() {
+ find scripts \
+ \( -name atomic -o -name dtc -o -name kconfig -o -name package \) -prune -o \
+ ! -name unifdef -a ! -name mk_elfconfig -a \( -type f -o -type l \) -print
+}
+
mkdir -p "${destdir}"
(
cd "${srctree}"
echo Makefile
find "arch/${SRCARCH}" -maxdepth 1 -name 'Makefile*'
- find include scripts -type f -o -type l
+ find "arch/${SRCARCH}" -name generated -prune -o -name include -type d -print
find "arch/${SRCARCH}" -name Kbuild.platforms -o -name Platform
- find "arch/${SRCARCH}" -name include -type d
+ find include \( -name config -o -name generated \) -prune -o \( -type f -o -type l \) -print
+ find_in_scripts
) | tar -c -f - -C "${srctree}" -T - | tar -xf - -C "${destdir}"
{
@@ -25,12 +32,40 @@ mkdir -p "${destdir}"
echo tools/objtool/objtool
fi
- find "arch/${SRCARCH}/include" Module.symvers include scripts -type f
+ echo Module.symvers
+ echo "arch/${SRCARCH}/include/generated"
+ echo include/config/auto.conf
+ echo include/config/kernel.release
+ echo include/generated
+ find_in_scripts
if is_enabled CONFIG_GCC_PLUGINS; then
find scripts/gcc-plugins -name '*.so'
fi
} | tar -c -f - -T - | tar -xf - -C "${destdir}"
-# copy .config manually to be where it's expected to be
-cp "${KCONFIG_CONFIG}" "${destdir}/.config"
+# When ${CC} and ${HOSTCC} differ, rebuild host programs using ${CC}.
+#
+# This caters to host programs that participate in Kbuild. objtool and
+# resolve_btfids are out of scope.
+if [ "${CC}" != "${HOSTCC}" ]; then
+ cat "${destdir}/scripts/Makefile" - <<-'EOF' > "${destdir}/scripts/Kbuild"
+ subdir-y += basic
+ hostprogs-always-y += mod/modpost
+ mod/modpost-objs := $(addprefix mod/, modpost.o file2alias.o sumversion.o symsearch.o)
+ EOF
+
+ # HOSTCXX is not overridden. The C++ compiler is used to build:
+ # - scripts/kconfig/qconf, which is unneeded for external module builds
+ # - GCC plugins, which will not work on the installed system even after
+ # being rebuilt.
+ #
+ # Clear VPATH and srcroot because the source files reside in the output
+ # directory.
+ # shellcheck disable=SC2016 # $(MAKE) and $(build) will be expanded by Make
+ "${MAKE}" run-command KBUILD_RUN_COMMAND='+$(MAKE) HOSTCC='"${CC}"' VPATH= srcroot=. $(build)='"$(realpath --relative-base=. "${destdir}")"/scripts
+
+ rm -f "${destdir}/scripts/Kbuild"
+fi
+
+find "${destdir}" \( -name '.*.cmd' -o -name '*.o' \) -delete
diff --git a/scripts/package/kernel.spec b/scripts/package/kernel.spec
index ac3e5ac01d8a..726f34e11960 100644
--- a/scripts/package/kernel.spec
+++ b/scripts/package/kernel.spec
@@ -2,8 +2,6 @@
%{!?_arch: %define _arch dummy}
%{!?make: %define make make}
%define makeflags %{?_smp_mflags} ARCH=%{ARCH}
-%define __spec_install_post /usr/lib/rpm/brp-compress || :
-%define debug_package %{nil}
Name: kernel
Summary: The Linux Kernel
@@ -46,6 +44,36 @@ This package provides kernel headers and makefiles sufficient to build modules
against the %{version} kernel package.
%endif
+%if %{with_debuginfo}
+# list of debuginfo-related options taken from distribution kernel.spec
+# files
+%undefine _include_minidebuginfo
+%undefine _find_debuginfo_dwz_opts
+%undefine _unique_build_ids
+%undefine _unique_debug_names
+%undefine _unique_debug_srcs
+%undefine _debugsource_packages
+%undefine _debuginfo_subpackages
+%global _find_debuginfo_opts -r
+%global _missing_build_ids_terminate_build 1
+%global _no_recompute_build_ids 1
+%{debug_package}
+%endif
+# some (but not all) versions of rpmbuild emit %%debug_package with
+# %%install. since we've already emitted it manually, that would cause
+# a package redefinition error. ensure that doesn't happen
+%define debug_package %{nil}
+
+# later, we make all modules executable so that find-debuginfo.sh strips
+# them up. but they don't actually need to be executable, so remove the
+# executable bit, taking care to do it _after_ find-debuginfo.sh has run
+%define __spec_install_post \
+ %{?__debug_package:%{__debug_install_post}} \
+ %{__arch_install_post} \
+ %{__os_install_post} \
+ find %{buildroot}/lib/modules/%{KERNELRELEASE} -name "*.ko" -type f \\\
+ | xargs --no-run-if-empty chmod u-x
+
%prep
%setup -q -n linux
cp %{SOURCE1} .config
@@ -89,8 +117,22 @@ ln -fns /usr/src/kernels/%{KERNELRELEASE} %{buildroot}/lib/modules/%{KERNELRELEA
echo "%exclude /lib/modules/%{KERNELRELEASE}/build"
} > %{buildroot}/kernel.list
+# make modules executable so that find-debuginfo.sh strips them. this
+# will be undone later in %%__spec_install_post
+find %{buildroot}/lib/modules/%{KERNELRELEASE} -name "*.ko" -type f \
+ | xargs --no-run-if-empty chmod u+x
+
+%if %{with_debuginfo}
+# copying vmlinux directly to the debug directory means it will not get
+# stripped (but its source paths will still be collected + fixed up)
+mkdir -p %{buildroot}/usr/lib/debug/lib/modules/%{KERNELRELEASE}
+cp vmlinux %{buildroot}/usr/lib/debug/lib/modules/%{KERNELRELEASE}
+%endif
+
%clean
rm -rf %{buildroot}
+rm -f debugfiles.list debuglinks.list debugsourcefiles.list debugsources.list \
+ elfbins.list
%post
if [ -x /usr/bin/kernel-install ]; then
diff --git a/scripts/package/mkdebian b/scripts/package/mkdebian
index 10637d403777..744ddba01d93 100755
--- a/scripts/package/mkdebian
+++ b/scripts/package/mkdebian
@@ -70,6 +70,15 @@ set_debarch() {
debarch=sh4$(if_enabled_echo CONFIG_CPU_BIG_ENDIAN eb)
fi
;;
+ um)
+ if is_enabled CONFIG_64BIT; then
+ debarch=amd64
+ else
+ debarch=i386
+ fi
+ ;;
+ loongarch64)
+ debarch=loong64 ;;
esac
if [ -z "$debarch" ]; then
debarch=$(dpkg-architecture -qDEB_HOST_ARCH)
@@ -148,11 +157,12 @@ while [ $# -gt 0 ]; do
done
# Some variables and settings used throughout the script
-version=$KERNELRELEASE
if [ "${KDEB_PKGVERSION:+set}" ]; then
packageversion=$KDEB_PKGVERSION
else
- packageversion=$(${srctree}/scripts/setlocalversion --no-local ${srctree})-$($srctree/scripts/build-version)
+ upstream_version=$("${srctree}/scripts/setlocalversion" --no-local "${srctree}" | sed 's/-\(rc[1-9]\)/~\1/')
+ debian_revision=$("${srctree}/scripts/build-version")
+ packageversion=${upstream_version}-${debian_revision}
fi
sourcename=${KDEB_SOURCENAME:-linux-upstream}
@@ -179,6 +189,8 @@ fi
echo $debarch > debian/arch
+host_gnu=$(dpkg-architecture -a "${debarch}" -q DEB_HOST_GNU_TYPE | sed 's/_/-/g')
+
# Generate a simple changelog template
cat <<EOF > debian/changelog
$sourcename ($packageversion) $distribution; urgency=low
@@ -196,14 +208,18 @@ Priority: optional
Maintainer: $maintainer
Rules-Requires-Root: no
Build-Depends: debhelper-compat (= 12)
-Build-Depends-Arch: bc, bison, cpio, flex, kmod, libelf-dev:native, libssl-dev:native, rsync
+Build-Depends-Arch: bc, bison, flex,
+ gcc-${host_gnu} <!pkg.${sourcename}.nokernelheaders>,
+ kmod, libelf-dev:native,
+ libssl-dev:native, libssl-dev <!pkg.${sourcename}.nokernelheaders>,
+ python3:native, rsync
Homepage: https://www.kernel.org/
-Package: $packagename-$version
+Package: $packagename-${KERNELRELEASE}
Architecture: $debarch
-Description: Linux kernel, version $version
+Description: Linux kernel, version ${KERNELRELEASE}
This package contains the Linux kernel, modules and corresponding other
- files, version: $version.
+ files, version: ${KERNELRELEASE}.
EOF
if [ "${SRCARCH}" != um ]; then
@@ -222,10 +238,11 @@ EOF
if is_enabled CONFIG_MODULES; then
cat <<EOF >> debian/control
-Package: linux-headers-$version
+Package: linux-headers-${KERNELRELEASE}
Architecture: $debarch
-Description: Linux kernel headers for $version on $debarch
- This package provides kernel header files for $version on $debarch
+Build-Profiles: <!pkg.${sourcename}.nokernelheaders>
+Description: Linux kernel headers for ${KERNELRELEASE} on $debarch
+ This package provides kernel header files for ${KERNELRELEASE} on $debarch
.
This is useful for people who need to build external modules
EOF
@@ -235,10 +252,11 @@ fi
if is_enabled CONFIG_DEBUG_INFO; then
cat <<EOF >> debian/control
-Package: linux-image-$version-dbg
+Package: linux-image-${KERNELRELEASE}-dbg
Section: debug
Architecture: $debarch
-Description: Linux kernel debugging symbols for $version
+Build-Profiles: <!pkg.${sourcename}.nokerneldbg>
+Description: Linux kernel debugging symbols for ${KERNELRELEASE}
This package will come in handy if you need to debug the kernel. It provides
all the necessary debug symbols for the kernel and its modules.
EOF
diff --git a/scripts/package/mkspec b/scripts/package/mkspec
index 4dc1466dfc81..c7375bfc25a9 100755
--- a/scripts/package/mkspec
+++ b/scripts/package/mkspec
@@ -23,6 +23,16 @@ else
echo '%define with_devel 0'
fi
+# debuginfo package generation uses find-debuginfo.sh under the hood,
+# which only works on uncompressed modules that contain debuginfo
+if grep -q CONFIG_DEBUG_INFO=y include/config/auto.conf &&
+ (! grep -q CONFIG_MODULE_COMPRESS=y include/config/auto.conf) &&
+ (! grep -q CONFIG_DEBUG_INFO_SPLIT=y include/config/auto.conf); then
+echo '%define with_debuginfo %{?_without_debuginfo: 0} %{?!_without_debuginfo: 1}'
+else
+echo '%define with_debuginfo 0'
+fi
+
cat<<EOF
%define ARCH ${ARCH}
%define KERNELRELEASE ${KERNELRELEASE}
diff --git a/scripts/remove-stale-files b/scripts/remove-stale-files
index f38d26b78c2a..6e39fa8540df 100755
--- a/scripts/remove-stale-files
+++ b/scripts/remove-stale-files
@@ -20,4 +20,9 @@ set -e
# yard. Stale files stay in this file for a while (for some release cycles?),
# then will be really dead and removed from the code base entirely.
+# moved to security/selinux/genheaders
+rm -f scripts/selinux/genheaders/genheaders
+
rm -f *.spec
+
+rm -f lib/test_fortify.log
diff --git a/scripts/rust_is_available.sh b/scripts/rust_is_available.sh
index 5262c56dd674..d2323de0692c 100755
--- a/scripts/rust_is_available.sh
+++ b/scripts/rust_is_available.sh
@@ -123,8 +123,10 @@ fi
# Non-stable and distributions' versions may have a version suffix, e.g. `-dev`.
#
# The dummy parameter `workaround-for-0.69.0` is required to support 0.69.0
-# (https://github.com/rust-lang/rust-bindgen/pull/2678). It can be removed when
-# the minimum version is upgraded past that (0.69.1 already fixed the issue).
+# (https://github.com/rust-lang/rust-bindgen/pull/2678) and 0.71.0
+# (https://github.com/rust-lang/rust-bindgen/pull/3040). It can be removed when
+# the minimum version is upgraded past the latter (0.69.1 and 0.71.1 both fixed
+# the issue).
rust_bindings_generator_output=$( \
LC_ALL=C "$BINDGEN" --version workaround-for-0.69.0 2>/dev/null
) || rust_bindings_generator_code=$?
@@ -225,6 +227,21 @@ if [ "$bindgen_libclang_cversion" -lt "$bindgen_libclang_min_cversion" ]; then
exit 1
fi
+if [ "$bindgen_libclang_cversion" -ge 1900100 ] &&
+ [ "$rust_bindings_generator_cversion" -lt 6905 ]; then
+ # Distributions may have patched the issue (e.g. Debian did).
+ if ! "$BINDGEN" $(dirname $0)/rust_is_available_bindgen_libclang_concat.h | grep -q foofoo; then
+ echo >&2 "***"
+ echo >&2 "*** Rust bindings generator '$BINDGEN' < 0.69.5 together with libclang >= 19.1"
+ echo >&2 "*** may not work due to a bug (https://github.com/rust-lang/rust-bindgen/pull/2824),"
+ echo >&2 "*** unless patched (like Debian's)."
+ echo >&2 "*** Your bindgen version: $rust_bindings_generator_version"
+ echo >&2 "*** Your libclang version: $bindgen_libclang_version"
+ echo >&2 "***"
+ warning=1
+ fi
+fi
+
# If the C compiler is Clang, then we can also check whether its version
# matches the `libclang` version used by the Rust bindings generator.
#
diff --git a/scripts/rust_is_available_bindgen_libclang_concat.h b/scripts/rust_is_available_bindgen_libclang_concat.h
new file mode 100644
index 000000000000..efc6e98d0f1d
--- /dev/null
+++ b/scripts/rust_is_available_bindgen_libclang_concat.h
@@ -0,0 +1,3 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#define F(x) int x##x
+F(foo);
diff --git a/scripts/rust_is_available_test.py b/scripts/rust_is_available_test.py
index 413741037fb3..4fcc319dea84 100755
--- a/scripts/rust_is_available_test.py
+++ b/scripts/rust_is_available_test.py
@@ -54,7 +54,7 @@ else:
""")
@classmethod
- def generate_bindgen(cls, version_stdout, libclang_stderr, version_0_66_patched=False):
+ def generate_bindgen(cls, version_stdout, libclang_stderr, version_0_66_patched=False, libclang_concat_patched=False):
if libclang_stderr is None:
libclang_case = f"raise SystemExit({cls.bindgen_default_bindgen_libclang_failure_exit_code})"
else:
@@ -65,12 +65,19 @@ else:
else:
version_0_66_case = "raise SystemExit(1)"
+ if libclang_concat_patched:
+ libclang_concat_case = "print('pub static mut foofoo: ::std::os::raw::c_int;')"
+ else:
+ libclang_concat_case = "pass"
+
return cls.generate_executable(f"""#!/usr/bin/env python3
import sys
if "rust_is_available_bindgen_libclang.h" in " ".join(sys.argv):
{libclang_case}
elif "rust_is_available_bindgen_0_66.h" in " ".join(sys.argv):
{version_0_66_case}
+elif "rust_is_available_bindgen_libclang_concat.h" in " ".join(sys.argv):
+ {libclang_concat_case}
else:
print({repr(version_stdout)})
""")
@@ -268,6 +275,31 @@ else:
result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
self.assertIn(f"libclang (used by the Rust bindings generator '{bindgen}') is too old.", result.stderr)
+ def test_bindgen_bad_libclang_concat(self):
+ for (bindgen_version, libclang_version, expected_not_patched) in (
+ ("0.69.4", "18.0.0", self.Expected.SUCCESS),
+ ("0.69.4", "19.1.0", self.Expected.SUCCESS_WITH_WARNINGS),
+ ("0.69.4", "19.2.0", self.Expected.SUCCESS_WITH_WARNINGS),
+
+ ("0.69.5", "18.0.0", self.Expected.SUCCESS),
+ ("0.69.5", "19.1.0", self.Expected.SUCCESS),
+ ("0.69.5", "19.2.0", self.Expected.SUCCESS),
+
+ ("0.70.0", "18.0.0", self.Expected.SUCCESS),
+ ("0.70.0", "19.1.0", self.Expected.SUCCESS),
+ ("0.70.0", "19.2.0", self.Expected.SUCCESS),
+ ):
+ with self.subTest(bindgen_version=bindgen_version, libclang_version=libclang_version):
+ cc = self.generate_clang(f"clang version {libclang_version}")
+ libclang_stderr = f"scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {libclang_version} [-W#pragma-messages], err: false"
+ bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr)
+ result = self.run_script(expected_not_patched, { "BINDGEN": bindgen, "CC": cc })
+ if expected_not_patched == self.Expected.SUCCESS_WITH_WARNINGS:
+ self.assertIn(f"Rust bindings generator '{bindgen}' < 0.69.5 together with libclang >= 19.1", result.stderr)
+
+ bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr, libclang_concat_patched=True)
+ result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen, "CC": cc })
+
def test_clang_matches_bindgen_libclang_different_bindgen(self):
bindgen = self.generate_bindgen_libclang("scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version 999.0.0 [-W#pragma-messages], err: false")
result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "BINDGEN": bindgen })
diff --git a/scripts/rustc-llvm-version.sh b/scripts/rustc-llvm-version.sh
new file mode 100755
index 000000000000..a500d1ae3101
--- /dev/null
+++ b/scripts/rustc-llvm-version.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+#
+# Usage: $ ./rustc-llvm-version.sh rustc
+#
+# Print the LLVM version that the Rust compiler uses in a 6 digit form.
+
+# Convert the version string x.y.z to a canonical up-to-6-digits form.
+get_canonical_version()
+{
+ IFS=.
+ set -- $1
+ echo $((10000 * $1 + 100 * $2 + $3))
+}
+
+if output=$("$@" --version --verbose 2>/dev/null | grep -E 'LLVM.*[0-9]+\.[0-9]+\.[0-9]+'); then
+ set -- $output
+ get_canonical_version $3
+else
+ echo 0
+ exit 1
+fi
diff --git a/scripts/rustc-version.sh b/scripts/rustc-version.sh
new file mode 100755
index 000000000000..4e22593e2eab
--- /dev/null
+++ b/scripts/rustc-version.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+#
+# Usage: $ ./rustc-version.sh rustc
+#
+# Print the Rust compiler version in a 6 or 7-digit form.
+
+# Convert the version string x.y.z to a canonical up-to-7-digits form.
+#
+# Note that this function uses one more digit (compared to other
+# instances in other version scripts) to give a bit more space to
+# `rustc` since it will reach 1.100.0 in late 2026.
+get_canonical_version()
+{
+ IFS=.
+ set -- $1
+ echo $((100000 * $1 + 100 * $2 + $3))
+}
+
+if output=$("$@" --version 2>/dev/null); then
+ set -- $output
+ get_canonical_version $2
+else
+ echo 0
+ exit 1
+fi
diff --git a/scripts/rustdoc_test_gen.rs b/scripts/rustdoc_test_gen.rs
index 5ebd42ae4a3f..ec8d70ac888b 100644
--- a/scripts/rustdoc_test_gen.rs
+++ b/scripts/rustdoc_test_gen.rs
@@ -15,8 +15,8 @@
//! - Test code should be able to define functions and call them, without having to carry
//! the context.
//!
-//! - Later on, we may want to be able to test non-kernel code (e.g. `core`, `alloc` or
-//! third-party crates) which likely use the standard library `assert*!` macros.
+//! - Later on, we may want to be able to test non-kernel code (e.g. `core` or third-party
+//! crates) which likely use the standard library `assert*!` macros.
//!
//! For this reason, instead of the passed context, `kunit_get_current_test()` is used instead
//! (i.e. `current->kunit_test`).
@@ -87,8 +87,8 @@ fn find_real_path<'a>(srctree: &Path, valid_paths: &'a mut Vec<PathBuf>, file: &
assert!(
valid_paths.len() > 0,
- "No path candidates found. This is likely a bug in the build system, or some files went \
- away while compiling."
+ "No path candidates found for `{file}`. This is likely a bug in the build system, or some \
+ files went away while compiling."
);
if valid_paths.len() > 1 {
@@ -97,8 +97,8 @@ fn find_real_path<'a>(srctree: &Path, valid_paths: &'a mut Vec<PathBuf>, file: &
eprintln!(" {path:?}");
}
panic!(
- "Several path candidates found, please resolve the ambiguity by renaming a file or \
- folder."
+ "Several path candidates found for `{file}`, please resolve the ambiguity by renaming \
+ a file or folder."
);
}
diff --git a/scripts/selinux/Makefile b/scripts/selinux/Makefile
index 59494e14989b..4b1308fa5732 100644
--- a/scripts/selinux/Makefile
+++ b/scripts/selinux/Makefile
@@ -1,2 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
-subdir-y := mdp genheaders
+subdir-y := mdp
diff --git a/scripts/selinux/genheaders/Makefile b/scripts/selinux/genheaders/Makefile
deleted file mode 100644
index 1faf7f07e8db..000000000000
--- a/scripts/selinux/genheaders/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-hostprogs-always-y += genheaders
-HOST_EXTRACFLAGS += \
- -I$(srctree)/include/uapi -I$(srctree)/include \
- -I$(srctree)/security/selinux/include
diff --git a/scripts/selinux/genheaders/genheaders.c b/scripts/selinux/genheaders/genheaders.c
deleted file mode 100644
index 15520806889e..000000000000
--- a/scripts/selinux/genheaders/genheaders.c
+++ /dev/null
@@ -1,157 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/* NOTE: we really do want to use the kernel headers here */
-#define __EXPORTED_HEADERS__
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <ctype.h>
-
-struct security_class_mapping {
- const char *name;
- const char *perms[sizeof(unsigned) * 8 + 1];
-};
-
-#include "classmap.h"
-#include "initial_sid_to_string.h"
-
-const char *progname;
-
-static void usage(void)
-{
- printf("usage: %s flask.h av_permissions.h\n", progname);
- exit(1);
-}
-
-static char *stoupperx(const char *s)
-{
- char *s2 = strdup(s);
- char *p;
-
- if (!s2) {
- fprintf(stderr, "%s: out of memory\n", progname);
- exit(3);
- }
-
- for (p = s2; *p; p++)
- *p = toupper(*p);
- return s2;
-}
-
-int main(int argc, char *argv[])
-{
- int i, j;
- int isids_len;
- FILE *fout;
-
- progname = argv[0];
-
- if (argc < 3)
- usage();
-
- fout = fopen(argv[1], "w");
- if (!fout) {
- fprintf(stderr, "Could not open %s for writing: %s\n",
- argv[1], strerror(errno));
- exit(2);
- }
-
- fprintf(fout, "/* This file is automatically generated. Do not edit. */\n");
- fprintf(fout, "#ifndef _SELINUX_FLASK_H_\n#define _SELINUX_FLASK_H_\n\n");
-
- for (i = 0; secclass_map[i].name; i++) {
- char *name = stoupperx(secclass_map[i].name);
-
- fprintf(fout, "#define SECCLASS_%-39s %2d\n", name, i+1);
- free(name);
- }
-
- fprintf(fout, "\n");
-
- isids_len = sizeof(initial_sid_to_string) / sizeof(char *);
- for (i = 1; i < isids_len; i++) {
- const char *s = initial_sid_to_string[i];
- if (s) {
- char *sidname = stoupperx(s);
-
- fprintf(fout, "#define SECINITSID_%-39s %2d\n", sidname, i);
- free(sidname);
- }
- }
- fprintf(fout, "\n#define SECINITSID_NUM %d\n", i-1);
- fprintf(fout, "\nstatic inline bool security_is_socket_class(u16 kern_tclass)\n");
- fprintf(fout, "{\n");
- fprintf(fout, "\tbool sock = false;\n\n");
- fprintf(fout, "\tswitch (kern_tclass) {\n");
- for (i = 0; secclass_map[i].name; i++) {
- static char s[] = "SOCKET";
- int len, l;
- char *name = stoupperx(secclass_map[i].name);
-
- len = strlen(name);
- l = sizeof(s) - 1;
- if (len >= l && memcmp(name + len - l, s, l) == 0)
- fprintf(fout, "\tcase SECCLASS_%s:\n", name);
- free(name);
- }
- fprintf(fout, "\t\tsock = true;\n");
- fprintf(fout, "\t\tbreak;\n");
- fprintf(fout, "\tdefault:\n");
- fprintf(fout, "\t\tbreak;\n");
- fprintf(fout, "\t}\n\n");
- fprintf(fout, "\treturn sock;\n");
- fprintf(fout, "}\n");
-
- fprintf(fout, "\n#endif\n");
-
- if (fclose(fout) != 0) {
- fprintf(stderr, "Could not successfully close %s: %s\n",
- argv[1], strerror(errno));
- exit(4);
- }
-
- fout = fopen(argv[2], "w");
- if (!fout) {
- fprintf(stderr, "Could not open %s for writing: %s\n",
- argv[2], strerror(errno));
- exit(5);
- }
-
- fprintf(fout, "/* This file is automatically generated. Do not edit. */\n");
- fprintf(fout, "#ifndef _SELINUX_AV_PERMISSIONS_H_\n#define _SELINUX_AV_PERMISSIONS_H_\n\n");
-
- for (i = 0; secclass_map[i].name; i++) {
- const struct security_class_mapping *map = &secclass_map[i];
- int len;
- char *name = stoupperx(map->name);
-
- len = strlen(name);
- for (j = 0; map->perms[j]; j++) {
- char *permname;
-
- if (j >= 32) {
- fprintf(stderr, "Too many permissions to fit into an access vector at (%s, %s).\n",
- map->name, map->perms[j]);
- exit(5);
- }
- permname = stoupperx(map->perms[j]);
- fprintf(fout, "#define %s__%-*s 0x%08xU\n", name,
- 39-len, permname, 1U<<j);
- free(permname);
- }
- free(name);
- }
-
- fprintf(fout, "\n#endif\n");
-
- if (fclose(fout) != 0) {
- fprintf(stderr, "Could not successfully close %s: %s\n",
- argv[2], strerror(errno));
- exit(6);
- }
-
- exit(0);
-}
diff --git a/scripts/selinux/install_policy.sh b/scripts/selinux/install_policy.sh
index 24086793b0d8..db40237e60ce 100755
--- a/scripts/selinux/install_policy.sh
+++ b/scripts/selinux/install_policy.sh
@@ -6,27 +6,24 @@ if [ `id -u` -ne 0 ]; then
exit 1
fi
-SF=`which setfiles`
-if [ $? -eq 1 ]; then
+SF=`which setfiles` || {
echo "Could not find setfiles"
echo "Do you have policycoreutils installed?"
exit 1
-fi
+}
-CP=`which checkpolicy`
-if [ $? -eq 1 ]; then
+CP=`which checkpolicy` || {
echo "Could not find checkpolicy"
echo "Do you have checkpolicy installed?"
exit 1
-fi
+}
VERS=`$CP -V | awk '{print $1}'`
-ENABLED=`which selinuxenabled`
-if [ $? -eq 1 ]; then
+ENABLED=`which selinuxenabled` || {
echo "Could not find selinuxenabled"
echo "Do you have libselinux-utils installed?"
exit 1
-fi
+}
if selinuxenabled; then
echo "SELinux is already enabled"
diff --git a/scripts/selinux/mdp/Makefile b/scripts/selinux/mdp/Makefile
index d61058ddd15c..673782e3212f 100644
--- a/scripts/selinux/mdp/Makefile
+++ b/scripts/selinux/mdp/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
hostprogs-always-y += mdp
HOST_EXTRACFLAGS += \
- -I$(srctree)/include/uapi -I$(srctree)/include \
+ -I$(srctree)/include \
-I$(srctree)/security/selinux/include -I$(objtree)/include
clean-files := policy.* file_contexts
diff --git a/scripts/selinux/mdp/mdp.c b/scripts/selinux/mdp/mdp.c
index 1415604c3d24..ea7fbe595971 100644
--- a/scripts/selinux/mdp/mdp.c
+++ b/scripts/selinux/mdp/mdp.c
@@ -11,10 +11,6 @@
* Authors: Serge E. Hallyn <serue@us.ibm.com>
*/
-
-/* NOTE: we really do want to use the kernel headers here */
-#define __EXPORTED_HEADERS__
-
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@@ -171,9 +167,6 @@ int main(int argc, char *argv[])
#ifdef CONFIG_JFS_SECURITY
FS_USE("xattr", "jfs");
#endif
-#ifdef CONFIG_REISERFS_FS_SECURITY
- FS_USE("xattr", "reiserfs");
-#endif
#ifdef CONFIG_JFFS2_FS_SECURITY
FS_USE("xattr", "jffs2");
#endif
diff --git a/scripts/setlocalversion b/scripts/setlocalversion
index 38b96c6797f4..28169d7e143b 100755
--- a/scripts/setlocalversion
+++ b/scripts/setlocalversion
@@ -10,6 +10,8 @@
#
#
+set -e
+
usage() {
echo "Usage: $0 [--no-local] [srctree]" >&2
exit 1
@@ -30,6 +32,29 @@ if test $# -gt 0 -o ! -d "$srctree"; then
usage
fi
+try_tag() {
+ tag="$1"
+
+ # Is $tag an annotated tag?
+ if [ "$(git cat-file -t "$tag" 2> /dev/null)" != tag ]; then
+ return
+ fi
+
+ # Is it an ancestor of HEAD, and if so, how many commits are in $tag..HEAD?
+ # shellcheck disable=SC2046 # word splitting is the point here
+ set -- $(git rev-list --count --left-right "$tag"...HEAD 2> /dev/null)
+
+ # $1 is 0 if and only if $tag is an ancestor of HEAD. Use
+ # string comparison, because $1 is empty if the 'git rev-list'
+ # command somehow failed.
+ if [ "$1" != 0 ]; then
+ return
+ fi
+
+ # $2 is the number of commits in the range $tag..HEAD, possibly 0.
+ count="$2"
+}
+
scm_version()
{
local short=false
@@ -61,33 +86,33 @@ scm_version()
# stable kernel: 6.1.7 -> v6.1.7
version_tag=v$(echo "${KERNELVERSION}" | sed -E 's/^([0-9]+\.[0-9]+)\.0(.*)$/\1\2/')
+ # try_tag initializes count if the tag is usable.
+ count=
+
# If a localversion* file exists, and the corresponding
# annotated tag exists and is an ancestor of HEAD, use
# it. This is the case in linux-next.
- tag=${file_localversion#-}
- desc=
- if [ -n "${tag}" ]; then
- desc=$(git describe --match=$tag 2>/dev/null)
+ if [ -n "${file_localversion#-}" ] ; then
+ try_tag "${file_localversion#-}"
fi
# Otherwise, if a localversion* file exists, and the tag
# obtained by appending it to the tag derived from
# KERNELVERSION exists and is an ancestor of HEAD, use
# it. This is e.g. the case in linux-rt.
- if [ -z "${desc}" ] && [ -n "${file_localversion}" ]; then
- tag="${version_tag}${file_localversion}"
- desc=$(git describe --match=$tag 2>/dev/null)
+ if [ -z "${count}" ] && [ -n "${file_localversion}" ]; then
+ try_tag "${version_tag}${file_localversion}"
fi
# Otherwise, default to the annotated tag derived from KERNELVERSION.
- if [ -z "${desc}" ]; then
- tag="${version_tag}"
- desc=$(git describe --match=$tag 2>/dev/null)
+ if [ -z "${count}" ]; then
+ try_tag "${version_tag}"
fi
- # If we are at the tagged commit, we ignore it because the version is
- # well-defined.
- if [ "${tag}" != "${desc}" ]; then
+ # If we are at the tagged commit, we ignore it because the
+ # version is well-defined. If none of the attempted tags exist
+ # or were usable, $count is still empty.
+ if [ -z "${count}" ] || [ "${count}" -gt 0 ]; then
# If only the short version is requested, don't bother
# running further git commands
@@ -95,14 +120,15 @@ scm_version()
echo "+"
return
fi
+
# If we are past the tagged commit, we pretty print it.
# (like 6.1.0-14595-g292a089d78d3)
- if [ -n "${desc}" ]; then
- echo "${desc}" | awk -F- '{printf("-%05d", $(NF-1))}'
+ if [ -n "${count}" ]; then
+ printf "%s%05d" "-" "${count}"
fi
# Add -g and exactly 12 hex chars.
- printf '%s%s' -g "$(echo $head | cut -c1-12)"
+ printf '%s%.12s' -g "$head"
fi
if ${no_dirty}; then
diff --git a/scripts/show_delta b/scripts/show_delta
index 291ad65e3089..3755b6c6e557 100755
--- a/scripts/show_delta
+++ b/scripts/show_delta
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-only
#
# show_deltas: Read list of printk messages instrumented with
diff --git a/scripts/sign-file.c b/scripts/sign-file.c
index 3edb156ae52c..7070245edfc1 100644
--- a/scripts/sign-file.c
+++ b/scripts/sign-file.c
@@ -27,14 +27,17 @@
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
-#include <openssl/engine.h>
-
-/*
- * OpenSSL 3.0 deprecates the OpenSSL's ENGINE API.
- *
- * Remove this if/when that API is no longer used
- */
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#if OPENSSL_VERSION_MAJOR >= 3
+# define USE_PKCS11_PROVIDER
+# include <openssl/provider.h>
+# include <openssl/store.h>
+#else
+# if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0)
+# define USE_PKCS11_ENGINE
+# include <openssl/engine.h>
+# endif
+#endif
+#include "ssl-common.h"
/*
* Use CMS if we have openssl-1.0.0 or newer available - otherwise we have to
@@ -83,41 +86,6 @@ void format(void)
exit(2);
}
-static void display_openssl_errors(int l)
-{
- const char *file;
- char buf[120];
- int e, line;
-
- if (ERR_peek_error() == 0)
- return;
- fprintf(stderr, "At main.c:%d:\n", l);
-
- while ((e = ERR_get_error_line(&file, &line))) {
- ERR_error_string(e, buf);
- fprintf(stderr, "- SSL %s: %s:%d\n", buf, file, line);
- }
-}
-
-static void drain_openssl_errors(void)
-{
- const char *file;
- int line;
-
- if (ERR_peek_error() == 0)
- return;
- while (ERR_get_error_line(&file, &line)) {}
-}
-
-#define ERR(cond, fmt, ...) \
- do { \
- bool __cond = (cond); \
- display_openssl_errors(__LINE__); \
- if (__cond) { \
- errx(1, fmt, ## __VA_ARGS__); \
- } \
- } while(0)
-
static const char *key_pass;
static int pem_pw_cb(char *buf, int len, int w, void *v)
@@ -139,28 +107,64 @@ static int pem_pw_cb(char *buf, int len, int w, void *v)
return pwlen;
}
-static EVP_PKEY *read_private_key(const char *private_key_name)
+static EVP_PKEY *read_private_key_pkcs11(const char *private_key_name)
{
- EVP_PKEY *private_key;
+ EVP_PKEY *private_key = NULL;
+#ifdef USE_PKCS11_PROVIDER
+ OSSL_STORE_CTX *store;
+ if (!OSSL_PROVIDER_try_load(NULL, "pkcs11", true))
+ ERR(1, "OSSL_PROVIDER_try_load(pkcs11)");
+ if (!OSSL_PROVIDER_try_load(NULL, "default", true))
+ ERR(1, "OSSL_PROVIDER_try_load(default)");
+
+ store = OSSL_STORE_open(private_key_name, NULL, NULL, NULL, NULL);
+ ERR(!store, "OSSL_STORE_open");
+
+ while (!OSSL_STORE_eof(store)) {
+ OSSL_STORE_INFO *info = OSSL_STORE_load(store);
+
+ if (!info) {
+ drain_openssl_errors(__LINE__, 0);
+ continue;
+ }
+ if (OSSL_STORE_INFO_get_type(info) == OSSL_STORE_INFO_PKEY) {
+ private_key = OSSL_STORE_INFO_get1_PKEY(info);
+ ERR(!private_key, "OSSL_STORE_INFO_get1_PKEY");
+ }
+ OSSL_STORE_INFO_free(info);
+ if (private_key)
+ break;
+ }
+ OSSL_STORE_close(store);
+#elif defined(USE_PKCS11_ENGINE)
+ ENGINE *e;
+
+ ENGINE_load_builtin_engines();
+ drain_openssl_errors(__LINE__, 1);
+ e = ENGINE_by_id("pkcs11");
+ ERR(!e, "Load PKCS#11 ENGINE");
+ if (ENGINE_init(e))
+ drain_openssl_errors(__LINE__, 1);
+ else
+ ERR(1, "ENGINE_init");
+ if (key_pass)
+ ERR(!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0), "Set PKCS#11 PIN");
+ private_key = ENGINE_load_private_key(e, private_key_name, NULL, NULL);
+ ERR(!private_key, "%s", private_key_name);
+#else
+ fprintf(stderr, "no pkcs11 engine/provider available\n");
+ exit(1);
+#endif
+ return private_key;
+}
+
+static EVP_PKEY *read_private_key(const char *private_key_name)
+{
if (!strncmp(private_key_name, "pkcs11:", 7)) {
- ENGINE *e;
-
- ENGINE_load_builtin_engines();
- drain_openssl_errors();
- e = ENGINE_by_id("pkcs11");
- ERR(!e, "Load PKCS#11 ENGINE");
- if (ENGINE_init(e))
- drain_openssl_errors();
- else
- ERR(1, "ENGINE_init");
- if (key_pass)
- ERR(!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0),
- "Set PKCS#11 PIN");
- private_key = ENGINE_load_private_key(e, private_key_name,
- NULL, NULL);
- ERR(!private_key, "%s", private_key_name);
+ return read_private_key_pkcs11(private_key_name);
} else {
+ EVP_PKEY *private_key;
BIO *b;
b = BIO_new_file(private_key_name, "rb");
@@ -169,9 +173,9 @@ static EVP_PKEY *read_private_key(const char *private_key_name)
NULL);
ERR(!private_key, "%s", private_key_name);
BIO_free(b);
- }
- return private_key;
+ return private_key;
+ }
}
static X509 *read_x509(const char *x509_name)
@@ -306,7 +310,7 @@ int main(int argc, char **argv)
/* Digest the module data. */
OpenSSL_add_all_digests();
- display_openssl_errors(__LINE__);
+ drain_openssl_errors(__LINE__, 0);
digest_algo = EVP_get_digestbyname(hash_algo);
ERR(!digest_algo, "EVP_get_digestbyname");
diff --git a/scripts/sorttable.c b/scripts/sorttable.c
index 83cdb843d92f..deed676bfe38 100644
--- a/scripts/sorttable.c
+++ b/scripts/sorttable.c
@@ -28,6 +28,7 @@
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
+#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
@@ -64,14 +65,246 @@
#define EM_LOONGARCH 258
#endif
+typedef union {
+ Elf32_Ehdr e32;
+ Elf64_Ehdr e64;
+} Elf_Ehdr;
+
+typedef union {
+ Elf32_Shdr e32;
+ Elf64_Shdr e64;
+} Elf_Shdr;
+
+typedef union {
+ Elf32_Sym e32;
+ Elf64_Sym e64;
+} Elf_Sym;
+
+typedef union {
+ Elf32_Rela e32;
+ Elf64_Rela e64;
+} Elf_Rela;
+
static uint32_t (*r)(const uint32_t *);
static uint16_t (*r2)(const uint16_t *);
static uint64_t (*r8)(const uint64_t *);
static void (*w)(uint32_t, uint32_t *);
-static void (*w2)(uint16_t, uint16_t *);
static void (*w8)(uint64_t, uint64_t *);
typedef void (*table_sort_t)(char *, int);
+static struct elf_funcs {
+ int (*compare_extable)(const void *a, const void *b);
+ uint64_t (*ehdr_shoff)(Elf_Ehdr *ehdr);
+ uint16_t (*ehdr_shstrndx)(Elf_Ehdr *ehdr);
+ uint16_t (*ehdr_shentsize)(Elf_Ehdr *ehdr);
+ uint16_t (*ehdr_shnum)(Elf_Ehdr *ehdr);
+ uint64_t (*shdr_addr)(Elf_Shdr *shdr);
+ uint64_t (*shdr_offset)(Elf_Shdr *shdr);
+ uint64_t (*shdr_size)(Elf_Shdr *shdr);
+ uint64_t (*shdr_entsize)(Elf_Shdr *shdr);
+ uint32_t (*shdr_link)(Elf_Shdr *shdr);
+ uint32_t (*shdr_name)(Elf_Shdr *shdr);
+ uint32_t (*shdr_type)(Elf_Shdr *shdr);
+ uint8_t (*sym_type)(Elf_Sym *sym);
+ uint32_t (*sym_name)(Elf_Sym *sym);
+ uint64_t (*sym_value)(Elf_Sym *sym);
+ uint16_t (*sym_shndx)(Elf_Sym *sym);
+ uint64_t (*rela_offset)(Elf_Rela *rela);
+ uint64_t (*rela_info)(Elf_Rela *rela);
+ uint64_t (*rela_addend)(Elf_Rela *rela);
+ void (*rela_write_addend)(Elf_Rela *rela, uint64_t val);
+} e;
+
+static uint64_t ehdr64_shoff(Elf_Ehdr *ehdr)
+{
+ return r8(&ehdr->e64.e_shoff);
+}
+
+static uint64_t ehdr32_shoff(Elf_Ehdr *ehdr)
+{
+ return r(&ehdr->e32.e_shoff);
+}
+
+static uint64_t ehdr_shoff(Elf_Ehdr *ehdr)
+{
+ return e.ehdr_shoff(ehdr);
+}
+
+#define EHDR_HALF(fn_name) \
+static uint16_t ehdr64_##fn_name(Elf_Ehdr *ehdr) \
+{ \
+ return r2(&ehdr->e64.e_##fn_name); \
+} \
+ \
+static uint16_t ehdr32_##fn_name(Elf_Ehdr *ehdr) \
+{ \
+ return r2(&ehdr->e32.e_##fn_name); \
+} \
+ \
+static uint16_t ehdr_##fn_name(Elf_Ehdr *ehdr) \
+{ \
+ return e.ehdr_##fn_name(ehdr); \
+}
+
+EHDR_HALF(shentsize)
+EHDR_HALF(shstrndx)
+EHDR_HALF(shnum)
+
+#define SHDR_WORD(fn_name) \
+static uint32_t shdr64_##fn_name(Elf_Shdr *shdr) \
+{ \
+ return r(&shdr->e64.sh_##fn_name); \
+} \
+ \
+static uint32_t shdr32_##fn_name(Elf_Shdr *shdr) \
+{ \
+ return r(&shdr->e32.sh_##fn_name); \
+} \
+ \
+static uint32_t shdr_##fn_name(Elf_Shdr *shdr) \
+{ \
+ return e.shdr_##fn_name(shdr); \
+}
+
+#define SHDR_ADDR(fn_name) \
+static uint64_t shdr64_##fn_name(Elf_Shdr *shdr) \
+{ \
+ return r8(&shdr->e64.sh_##fn_name); \
+} \
+ \
+static uint64_t shdr32_##fn_name(Elf_Shdr *shdr) \
+{ \
+ return r(&shdr->e32.sh_##fn_name); \
+} \
+ \
+static uint64_t shdr_##fn_name(Elf_Shdr *shdr) \
+{ \
+ return e.shdr_##fn_name(shdr); \
+}
+
+#define SHDR_WORD(fn_name) \
+static uint32_t shdr64_##fn_name(Elf_Shdr *shdr) \
+{ \
+ return r(&shdr->e64.sh_##fn_name); \
+} \
+ \
+static uint32_t shdr32_##fn_name(Elf_Shdr *shdr) \
+{ \
+ return r(&shdr->e32.sh_##fn_name); \
+} \
+static uint32_t shdr_##fn_name(Elf_Shdr *shdr) \
+{ \
+ return e.shdr_##fn_name(shdr); \
+}
+
+SHDR_ADDR(addr)
+SHDR_ADDR(offset)
+SHDR_ADDR(size)
+SHDR_ADDR(entsize)
+
+SHDR_WORD(link)
+SHDR_WORD(name)
+SHDR_WORD(type)
+
+#define SYM_ADDR(fn_name) \
+static uint64_t sym64_##fn_name(Elf_Sym *sym) \
+{ \
+ return r8(&sym->e64.st_##fn_name); \
+} \
+ \
+static uint64_t sym32_##fn_name(Elf_Sym *sym) \
+{ \
+ return r(&sym->e32.st_##fn_name); \
+} \
+ \
+static uint64_t sym_##fn_name(Elf_Sym *sym) \
+{ \
+ return e.sym_##fn_name(sym); \
+}
+
+#define SYM_WORD(fn_name) \
+static uint32_t sym64_##fn_name(Elf_Sym *sym) \
+{ \
+ return r(&sym->e64.st_##fn_name); \
+} \
+ \
+static uint32_t sym32_##fn_name(Elf_Sym *sym) \
+{ \
+ return r(&sym->e32.st_##fn_name); \
+} \
+ \
+static uint32_t sym_##fn_name(Elf_Sym *sym) \
+{ \
+ return e.sym_##fn_name(sym); \
+}
+
+#define SYM_HALF(fn_name) \
+static uint16_t sym64_##fn_name(Elf_Sym *sym) \
+{ \
+ return r2(&sym->e64.st_##fn_name); \
+} \
+ \
+static uint16_t sym32_##fn_name(Elf_Sym *sym) \
+{ \
+ return r2(&sym->e32.st_##fn_name); \
+} \
+ \
+static uint16_t sym_##fn_name(Elf_Sym *sym) \
+{ \
+ return e.sym_##fn_name(sym); \
+}
+
+static uint8_t sym64_type(Elf_Sym *sym)
+{
+ return ELF64_ST_TYPE(sym->e64.st_info);
+}
+
+static uint8_t sym32_type(Elf_Sym *sym)
+{
+ return ELF32_ST_TYPE(sym->e32.st_info);
+}
+
+static uint8_t sym_type(Elf_Sym *sym)
+{
+ return e.sym_type(sym);
+}
+
+SYM_ADDR(value)
+SYM_WORD(name)
+SYM_HALF(shndx)
+
+#define __maybe_unused __attribute__((__unused__))
+
+#define RELA_ADDR(fn_name) \
+static uint64_t rela64_##fn_name(Elf_Rela *rela) \
+{ \
+ return r8((uint64_t *)&rela->e64.r_##fn_name); \
+} \
+ \
+static uint64_t rela32_##fn_name(Elf_Rela *rela) \
+{ \
+ return r((uint32_t *)&rela->e32.r_##fn_name); \
+} \
+ \
+static uint64_t __maybe_unused rela_##fn_name(Elf_Rela *rela) \
+{ \
+ return e.rela_##fn_name(rela); \
+}
+
+RELA_ADDR(offset)
+RELA_ADDR(info)
+RELA_ADDR(addend)
+
+static void rela64_write_addend(Elf_Rela *rela, uint64_t val)
+{
+ w8(val, (uint64_t *)&rela->e64.r_addend);
+}
+
+static void rela32_write_addend(Elf_Rela *rela, uint64_t val)
+{
+ w(val, (uint32_t *)&rela->e32.r_addend);
+}
+
/*
* Get the whole file as a programming convenience in order to avoid
* malloc+lseek+read+free of many pieces. If successful, then mmap
@@ -146,24 +379,14 @@ static void wbe(uint32_t val, uint32_t *x)
put_unaligned_be32(val, x);
}
-static void w2be(uint16_t val, uint16_t *x)
-{
- put_unaligned_be16(val, x);
-}
-
-static void w8be(uint64_t val, uint64_t *x)
-{
- put_unaligned_be64(val, x);
-}
-
static void wle(uint32_t val, uint32_t *x)
{
put_unaligned_le32(val, x);
}
-static void w2le(uint16_t val, uint16_t *x)
+static void w8be(uint64_t val, uint64_t *x)
{
- put_unaligned_le16(val, x);
+ put_unaligned_be64(val, x);
}
static void w8le(uint64_t val, uint64_t *x)
@@ -195,10 +418,740 @@ static inline unsigned int get_secindex(unsigned int shndx,
return r(&symtab_shndx_start[sym_offs]);
}
-/* 32 bit and 64 bit are very similar */
-#include "sorttable.h"
-#define SORTTABLE_64
-#include "sorttable.h"
+static int compare_extable_32(const void *a, const void *b)
+{
+ Elf32_Addr av = r(a);
+ Elf32_Addr bv = r(b);
+
+ if (av < bv)
+ return -1;
+ return av > bv;
+}
+
+static int compare_extable_64(const void *a, const void *b)
+{
+ Elf64_Addr av = r8(a);
+ Elf64_Addr bv = r8(b);
+
+ if (av < bv)
+ return -1;
+ return av > bv;
+}
+
+static int compare_extable(const void *a, const void *b)
+{
+ return e.compare_extable(a, b);
+}
+
+static inline void *get_index(void *start, int entsize, int index)
+{
+ return start + (entsize * index);
+}
+
+static int extable_ent_size;
+static int long_size;
+
+#define ERRSTR_MAXSZ 256
+
+#ifdef UNWINDER_ORC_ENABLED
+/* ORC unwinder only support X86_64 */
+#include <asm/orc_types.h>
+
+static char g_err[ERRSTR_MAXSZ];
+static int *g_orc_ip_table;
+static struct orc_entry *g_orc_table;
+
+static pthread_t orc_sort_thread;
+
+static inline unsigned long orc_ip(const int *ip)
+{
+ return (unsigned long)ip + *ip;
+}
+
+static int orc_sort_cmp(const void *_a, const void *_b)
+{
+ struct orc_entry *orc_a, *orc_b;
+ const int *a = g_orc_ip_table + *(int *)_a;
+ const int *b = g_orc_ip_table + *(int *)_b;
+ unsigned long a_val = orc_ip(a);
+ unsigned long b_val = orc_ip(b);
+
+ if (a_val > b_val)
+ return 1;
+ if (a_val < b_val)
+ return -1;
+
+ /*
+ * The "weak" section terminator entries need to always be on the left
+ * to ensure the lookup code skips them in favor of real entries.
+ * These terminator entries exist to handle any gaps created by
+ * whitelisted .o files which didn't get objtool generation.
+ */
+ orc_a = g_orc_table + (a - g_orc_ip_table);
+ orc_b = g_orc_table + (b - g_orc_ip_table);
+ if (orc_a->type == ORC_TYPE_UNDEFINED && orc_b->type == ORC_TYPE_UNDEFINED)
+ return 0;
+ return orc_a->type == ORC_TYPE_UNDEFINED ? -1 : 1;
+}
+
+static void *sort_orctable(void *arg)
+{
+ int i;
+ int *idxs = NULL;
+ int *tmp_orc_ip_table = NULL;
+ struct orc_entry *tmp_orc_table = NULL;
+ unsigned int *orc_ip_size = (unsigned int *)arg;
+ unsigned int num_entries = *orc_ip_size / sizeof(int);
+ unsigned int orc_size = num_entries * sizeof(struct orc_entry);
+
+ idxs = (int *)malloc(*orc_ip_size);
+ if (!idxs) {
+ snprintf(g_err, ERRSTR_MAXSZ, "malloc idxs: %s",
+ strerror(errno));
+ pthread_exit(g_err);
+ }
+
+ tmp_orc_ip_table = (int *)malloc(*orc_ip_size);
+ if (!tmp_orc_ip_table) {
+ snprintf(g_err, ERRSTR_MAXSZ, "malloc tmp_orc_ip_table: %s",
+ strerror(errno));
+ pthread_exit(g_err);
+ }
+
+ tmp_orc_table = (struct orc_entry *)malloc(orc_size);
+ if (!tmp_orc_table) {
+ snprintf(g_err, ERRSTR_MAXSZ, "malloc tmp_orc_table: %s",
+ strerror(errno));
+ pthread_exit(g_err);
+ }
+
+ /* initialize indices array, convert ip_table to absolute address */
+ for (i = 0; i < num_entries; i++) {
+ idxs[i] = i;
+ tmp_orc_ip_table[i] = g_orc_ip_table[i] + i * sizeof(int);
+ }
+ memcpy(tmp_orc_table, g_orc_table, orc_size);
+
+ qsort(idxs, num_entries, sizeof(int), orc_sort_cmp);
+
+ for (i = 0; i < num_entries; i++) {
+ if (idxs[i] == i)
+ continue;
+
+ /* convert back to relative address */
+ g_orc_ip_table[i] = tmp_orc_ip_table[idxs[i]] - i * sizeof(int);
+ g_orc_table[i] = tmp_orc_table[idxs[i]];
+ }
+
+ free(idxs);
+ free(tmp_orc_ip_table);
+ free(tmp_orc_table);
+ pthread_exit(NULL);
+}
+#endif
+
+#ifdef MCOUNT_SORT_ENABLED
+
+static int compare_values_64(const void *a, const void *b)
+{
+ uint64_t av = *(uint64_t *)a;
+ uint64_t bv = *(uint64_t *)b;
+
+ if (av < bv)
+ return -1;
+ return av > bv;
+}
+
+static int compare_values_32(const void *a, const void *b)
+{
+ uint32_t av = *(uint32_t *)a;
+ uint32_t bv = *(uint32_t *)b;
+
+ if (av < bv)
+ return -1;
+ return av > bv;
+}
+
+static int (*compare_values)(const void *a, const void *b);
+
+/* Only used for sorting mcount table */
+static void rela_write_addend(Elf_Rela *rela, uint64_t val)
+{
+ e.rela_write_addend(rela, val);
+}
+
+struct func_info {
+ uint64_t addr;
+ uint64_t size;
+};
+
+/* List of functions created by: nm -S vmlinux */
+static struct func_info *function_list;
+static int function_list_size;
+
+/* Allocate functions in 1k blocks */
+#define FUNC_BLK_SIZE 1024
+#define FUNC_BLK_MASK (FUNC_BLK_SIZE - 1)
+
+static int add_field(uint64_t addr, uint64_t size)
+{
+ struct func_info *fi;
+ int fsize = function_list_size;
+
+ if (!(fsize & FUNC_BLK_MASK)) {
+ fsize += FUNC_BLK_SIZE;
+ fi = realloc(function_list, fsize * sizeof(struct func_info));
+ if (!fi)
+ return -1;
+ function_list = fi;
+ }
+ fi = &function_list[function_list_size++];
+ fi->addr = addr;
+ fi->size = size;
+ return 0;
+}
+
+/* Used for when mcount/fentry is before the function entry */
+static int before_func;
+
+/* Only return match if the address lies inside the function size */
+static int cmp_func_addr(const void *K, const void *A)
+{
+ uint64_t key = *(const uint64_t *)K;
+ const struct func_info *a = A;
+
+ if (key + before_func < a->addr)
+ return -1;
+ return key >= a->addr + a->size;
+}
+
+/* Find the function in function list that is bounded by the function size */
+static int find_func(uint64_t key)
+{
+ return bsearch(&key, function_list, function_list_size,
+ sizeof(struct func_info), cmp_func_addr) != NULL;
+}
+
+static int cmp_funcs(const void *A, const void *B)
+{
+ const struct func_info *a = A;
+ const struct func_info *b = B;
+
+ if (a->addr < b->addr)
+ return -1;
+ return a->addr > b->addr;
+}
+
+static int parse_symbols(const char *fname)
+{
+ FILE *fp;
+ char addr_str[20]; /* Only need 17, but round up to next int size */
+ char size_str[20];
+ char type;
+
+ fp = fopen(fname, "r");
+ if (!fp) {
+ perror(fname);
+ return -1;
+ }
+
+ while (fscanf(fp, "%16s %16s %c %*s\n", addr_str, size_str, &type) == 3) {
+ uint64_t addr;
+ uint64_t size;
+
+ /* Only care about functions */
+ if (type != 't' && type != 'T' && type != 'W')
+ continue;
+
+ addr = strtoull(addr_str, NULL, 16);
+ size = strtoull(size_str, NULL, 16);
+ if (add_field(addr, size) < 0)
+ return -1;
+ }
+ fclose(fp);
+
+ qsort(function_list, function_list_size, sizeof(struct func_info), cmp_funcs);
+
+ return 0;
+}
+
+static pthread_t mcount_sort_thread;
+static bool sort_reloc;
+
+static long rela_type;
+
+static char m_err[ERRSTR_MAXSZ];
+
+struct elf_mcount_loc {
+ Elf_Ehdr *ehdr;
+ Elf_Shdr *init_data_sec;
+ uint64_t start_mcount_loc;
+ uint64_t stop_mcount_loc;
+};
+
+/* Fill the array with the content of the relocs */
+static int fill_relocs(void *ptr, uint64_t size, Elf_Ehdr *ehdr, uint64_t start_loc)
+{
+ Elf_Shdr *shdr_start;
+ Elf_Rela *rel;
+ unsigned int shnum;
+ unsigned int count = 0;
+ int shentsize;
+ void *array_end = ptr + size;
+
+ shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr));
+ shentsize = ehdr_shentsize(ehdr);
+
+ shnum = ehdr_shnum(ehdr);
+ if (shnum == SHN_UNDEF)
+ shnum = shdr_size(shdr_start);
+
+ for (int i = 0; i < shnum; i++) {
+ Elf_Shdr *shdr = get_index(shdr_start, shentsize, i);
+ void *end;
+
+ if (shdr_type(shdr) != SHT_RELA)
+ continue;
+
+ rel = (void *)ehdr + shdr_offset(shdr);
+ end = (void *)rel + shdr_size(shdr);
+
+ for (; (void *)rel < end; rel = (void *)rel + shdr_entsize(shdr)) {
+ uint64_t offset = rela_offset(rel);
+
+ if (offset >= start_loc && offset < start_loc + size) {
+ if (ptr + long_size > array_end) {
+ snprintf(m_err, ERRSTR_MAXSZ,
+ "Too many relocations");
+ return -1;
+ }
+
+ /* Make sure this has the correct type */
+ if (rela_info(rel) != rela_type) {
+ snprintf(m_err, ERRSTR_MAXSZ,
+ "rela has type %lx but expected %lx\n",
+ (long)rela_info(rel), rela_type);
+ return -1;
+ }
+
+ if (long_size == 4)
+ *(uint32_t *)ptr = rela_addend(rel);
+ else
+ *(uint64_t *)ptr = rela_addend(rel);
+ ptr += long_size;
+ count++;
+ }
+ }
+ }
+ return count;
+}
+
+/* Put the sorted vals back into the relocation elements */
+static void replace_relocs(void *ptr, uint64_t size, Elf_Ehdr *ehdr, uint64_t start_loc)
+{
+ Elf_Shdr *shdr_start;
+ Elf_Rela *rel;
+ unsigned int shnum;
+ int shentsize;
+
+ shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr));
+ shentsize = ehdr_shentsize(ehdr);
+
+ shnum = ehdr_shnum(ehdr);
+ if (shnum == SHN_UNDEF)
+ shnum = shdr_size(shdr_start);
+
+ for (int i = 0; i < shnum; i++) {
+ Elf_Shdr *shdr = get_index(shdr_start, shentsize, i);
+ void *end;
+
+ if (shdr_type(shdr) != SHT_RELA)
+ continue;
+
+ rel = (void *)ehdr + shdr_offset(shdr);
+ end = (void *)rel + shdr_size(shdr);
+
+ for (; (void *)rel < end; rel = (void *)rel + shdr_entsize(shdr)) {
+ uint64_t offset = rela_offset(rel);
+
+ if (offset >= start_loc && offset < start_loc + size) {
+ if (long_size == 4)
+ rela_write_addend(rel, *(uint32_t *)ptr);
+ else
+ rela_write_addend(rel, *(uint64_t *)ptr);
+ ptr += long_size;
+ }
+ }
+ }
+}
+
+static int fill_addrs(void *ptr, uint64_t size, void *addrs)
+{
+ void *end = ptr + size;
+ int count = 0;
+
+ for (; ptr < end; ptr += long_size, addrs += long_size, count++) {
+ if (long_size == 4)
+ *(uint32_t *)ptr = r(addrs);
+ else
+ *(uint64_t *)ptr = r8(addrs);
+ }
+ return count;
+}
+
+static void replace_addrs(void *ptr, uint64_t size, void *addrs)
+{
+ void *end = ptr + size;
+
+ for (; ptr < end; ptr += long_size, addrs += long_size) {
+ if (long_size == 4)
+ w(*(uint32_t *)ptr, addrs);
+ else
+ w8(*(uint64_t *)ptr, addrs);
+ }
+}
+
+/* Sort the addresses stored between __start_mcount_loc to __stop_mcount_loc in vmlinux */
+static void *sort_mcount_loc(void *arg)
+{
+ struct elf_mcount_loc *emloc = (struct elf_mcount_loc *)arg;
+ uint64_t offset = emloc->start_mcount_loc - shdr_addr(emloc->init_data_sec)
+ + shdr_offset(emloc->init_data_sec);
+ uint64_t size = emloc->stop_mcount_loc - emloc->start_mcount_loc;
+ unsigned char *start_loc = (void *)emloc->ehdr + offset;
+ Elf_Ehdr *ehdr = emloc->ehdr;
+ void *e_msg = NULL;
+ void *vals;
+ int count;
+
+ vals = malloc(long_size * size);
+ if (!vals) {
+ snprintf(m_err, ERRSTR_MAXSZ, "Failed to allocate sort array");
+ pthread_exit(m_err);
+ }
+
+ if (sort_reloc) {
+ count = fill_relocs(vals, size, ehdr, emloc->start_mcount_loc);
+ /* gcc may use relocs to save the addresses, but clang does not. */
+ if (!count) {
+ count = fill_addrs(vals, size, start_loc);
+ sort_reloc = 0;
+ }
+ } else
+ count = fill_addrs(vals, size, start_loc);
+
+ if (count < 0) {
+ e_msg = m_err;
+ goto out;
+ }
+
+ if (count != size / long_size) {
+ snprintf(m_err, ERRSTR_MAXSZ, "Expected %u mcount elements but found %u\n",
+ (int)(size / long_size), count);
+ e_msg = m_err;
+ goto out;
+ }
+
+ /* zero out any locations not found by function list */
+ if (function_list_size) {
+ for (void *ptr = vals; ptr < vals + size; ptr += long_size) {
+ uint64_t key;
+
+ key = long_size == 4 ? *(uint32_t *)ptr : *(uint64_t *)ptr;
+ if (!find_func(key)) {
+ if (long_size == 4)
+ *(uint32_t *)ptr = 0;
+ else
+ *(uint64_t *)ptr = 0;
+ }
+ }
+ }
+
+ compare_values = long_size == 4 ? compare_values_32 : compare_values_64;
+
+ qsort(vals, count, long_size, compare_values);
+
+ if (sort_reloc)
+ replace_relocs(vals, size, ehdr, emloc->start_mcount_loc);
+ else
+ replace_addrs(vals, size, start_loc);
+
+out:
+ free(vals);
+
+ pthread_exit(e_msg);
+}
+
+/* Get the address of __start_mcount_loc and __stop_mcount_loc in System.map */
+static void get_mcount_loc(struct elf_mcount_loc *emloc, Elf_Shdr *symtab_sec,
+ const char *strtab)
+{
+ Elf_Sym *sym, *end_sym;
+ int symentsize = shdr_entsize(symtab_sec);
+ int found = 0;
+
+ sym = (void *)emloc->ehdr + shdr_offset(symtab_sec);
+ end_sym = (void *)sym + shdr_size(symtab_sec);
+
+ while (sym < end_sym) {
+ if (!strcmp(strtab + sym_name(sym), "__start_mcount_loc")) {
+ emloc->start_mcount_loc = sym_value(sym);
+ if (++found == 2)
+ break;
+ } else if (!strcmp(strtab + sym_name(sym), "__stop_mcount_loc")) {
+ emloc->stop_mcount_loc = sym_value(sym);
+ if (++found == 2)
+ break;
+ }
+ sym = (void *)sym + symentsize;
+ }
+
+ if (!emloc->start_mcount_loc) {
+ fprintf(stderr, "get start_mcount_loc error!");
+ return;
+ }
+
+ if (!emloc->stop_mcount_loc) {
+ fprintf(stderr, "get stop_mcount_loc error!");
+ return;
+ }
+}
+#else /* MCOUNT_SORT_ENABLED */
+static inline int parse_symbols(const char *fname) { return 0; }
+#endif
+
+static int do_sort(Elf_Ehdr *ehdr,
+ char const *const fname,
+ table_sort_t custom_sort)
+{
+ int rc = -1;
+ Elf_Shdr *shdr_start;
+ Elf_Shdr *strtab_sec = NULL;
+ Elf_Shdr *symtab_sec = NULL;
+ Elf_Shdr *extab_sec = NULL;
+ Elf_Shdr *string_sec;
+ Elf_Sym *sym;
+ const Elf_Sym *symtab;
+ Elf32_Word *symtab_shndx = NULL;
+ Elf_Sym *sort_needed_sym = NULL;
+ Elf_Shdr *sort_needed_sec;
+ uint32_t *sort_needed_loc;
+ void *sym_start;
+ void *sym_end;
+ const char *secstrings;
+ const char *strtab;
+ char *extab_image;
+ int sort_need_index;
+ int symentsize;
+ int shentsize;
+ int idx;
+ int i;
+ unsigned int shnum;
+ unsigned int shstrndx;
+#ifdef MCOUNT_SORT_ENABLED
+ struct elf_mcount_loc mstruct = {0};
+#endif
+#ifdef UNWINDER_ORC_ENABLED
+ unsigned int orc_ip_size = 0;
+ unsigned int orc_size = 0;
+ unsigned int orc_num_entries = 0;
+#endif
+
+ shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr));
+ shentsize = ehdr_shentsize(ehdr);
+
+ shstrndx = ehdr_shstrndx(ehdr);
+ if (shstrndx == SHN_XINDEX)
+ shstrndx = shdr_link(shdr_start);
+ string_sec = get_index(shdr_start, shentsize, shstrndx);
+ secstrings = (const char *)ehdr + shdr_offset(string_sec);
+
+ shnum = ehdr_shnum(ehdr);
+ if (shnum == SHN_UNDEF)
+ shnum = shdr_size(shdr_start);
+
+ for (i = 0; i < shnum; i++) {
+ Elf_Shdr *shdr = get_index(shdr_start, shentsize, i);
+
+ idx = shdr_name(shdr);
+ if (!strcmp(secstrings + idx, "__ex_table"))
+ extab_sec = shdr;
+ if (!strcmp(secstrings + idx, ".symtab"))
+ symtab_sec = shdr;
+ if (!strcmp(secstrings + idx, ".strtab"))
+ strtab_sec = shdr;
+
+ if (shdr_type(shdr) == SHT_SYMTAB_SHNDX)
+ symtab_shndx = (Elf32_Word *)((const char *)ehdr +
+ shdr_offset(shdr));
+
+#ifdef MCOUNT_SORT_ENABLED
+ /* locate the .init.data section in vmlinux */
+ if (!strcmp(secstrings + idx, ".init.data"))
+ mstruct.init_data_sec = shdr;
+#endif
+
+#ifdef UNWINDER_ORC_ENABLED
+ /* locate the ORC unwind tables */
+ if (!strcmp(secstrings + idx, ".orc_unwind_ip")) {
+ orc_ip_size = shdr_size(shdr);
+ g_orc_ip_table = (int *)((void *)ehdr +
+ shdr_offset(shdr));
+ }
+ if (!strcmp(secstrings + idx, ".orc_unwind")) {
+ orc_size = shdr_size(shdr);
+ g_orc_table = (struct orc_entry *)((void *)ehdr +
+ shdr_offset(shdr));
+ }
+#endif
+ } /* for loop */
+
+#ifdef UNWINDER_ORC_ENABLED
+ if (!g_orc_ip_table || !g_orc_table) {
+ fprintf(stderr,
+ "incomplete ORC unwind tables in file: %s\n", fname);
+ goto out;
+ }
+
+ orc_num_entries = orc_ip_size / sizeof(int);
+ if (orc_ip_size % sizeof(int) != 0 ||
+ orc_size % sizeof(struct orc_entry) != 0 ||
+ orc_num_entries != orc_size / sizeof(struct orc_entry)) {
+ fprintf(stderr,
+ "inconsistent ORC unwind table entries in file: %s\n",
+ fname);
+ goto out;
+ }
+
+ /* create thread to sort ORC unwind tables concurrently */
+ if (pthread_create(&orc_sort_thread, NULL,
+ sort_orctable, &orc_ip_size)) {
+ fprintf(stderr,
+ "pthread_create orc_sort_thread failed '%s': %s\n",
+ strerror(errno), fname);
+ goto out;
+ }
+#endif
+ if (!extab_sec) {
+ fprintf(stderr, "no __ex_table in file: %s\n", fname);
+ goto out;
+ }
+
+ if (!symtab_sec) {
+ fprintf(stderr, "no .symtab in file: %s\n", fname);
+ goto out;
+ }
+
+ if (!strtab_sec) {
+ fprintf(stderr, "no .strtab in file: %s\n", fname);
+ goto out;
+ }
+
+ extab_image = (void *)ehdr + shdr_offset(extab_sec);
+ strtab = (const char *)ehdr + shdr_offset(strtab_sec);
+ symtab = (const Elf_Sym *)((const char *)ehdr + shdr_offset(symtab_sec));
+
+#ifdef MCOUNT_SORT_ENABLED
+ mstruct.ehdr = ehdr;
+ get_mcount_loc(&mstruct, symtab_sec, strtab);
+
+ if (!mstruct.init_data_sec || !mstruct.start_mcount_loc || !mstruct.stop_mcount_loc) {
+ fprintf(stderr,
+ "incomplete mcount's sort in file: %s\n",
+ fname);
+ goto out;
+ }
+
+ /* create thread to sort mcount_loc concurrently */
+ if (pthread_create(&mcount_sort_thread, NULL, &sort_mcount_loc, &mstruct)) {
+ fprintf(stderr,
+ "pthread_create mcount_sort_thread failed '%s': %s\n",
+ strerror(errno), fname);
+ goto out;
+ }
+#endif
+
+ if (custom_sort) {
+ custom_sort(extab_image, shdr_size(extab_sec));
+ } else {
+ int num_entries = shdr_size(extab_sec) / extable_ent_size;
+ qsort(extab_image, num_entries,
+ extable_ent_size, compare_extable);
+ }
+
+ /* find the flag main_extable_sort_needed */
+ sym_start = (void *)ehdr + shdr_offset(symtab_sec);
+ sym_end = sym_start + shdr_size(symtab_sec);
+ symentsize = shdr_entsize(symtab_sec);
+
+ for (sym = sym_start; (void *)sym + symentsize < sym_end;
+ sym = (void *)sym + symentsize) {
+ if (sym_type(sym) != STT_OBJECT)
+ continue;
+ if (!strcmp(strtab + sym_name(sym),
+ "main_extable_sort_needed")) {
+ sort_needed_sym = sym;
+ break;
+ }
+ }
+
+ if (!sort_needed_sym) {
+ fprintf(stderr,
+ "no main_extable_sort_needed symbol in file: %s\n",
+ fname);
+ goto out;
+ }
+
+ sort_need_index = get_secindex(sym_shndx(sym),
+ ((void *)sort_needed_sym - (void *)symtab) / symentsize,
+ symtab_shndx);
+ sort_needed_sec = get_index(shdr_start, shentsize, sort_need_index);
+ sort_needed_loc = (void *)ehdr +
+ shdr_offset(sort_needed_sec) +
+ sym_value(sort_needed_sym) - shdr_addr(sort_needed_sec);
+
+ /* extable has been sorted, clear the flag */
+ w(0, sort_needed_loc);
+ rc = 0;
+
+out:
+#ifdef UNWINDER_ORC_ENABLED
+ if (orc_sort_thread) {
+ void *retval = NULL;
+ /* wait for ORC tables sort done */
+ rc = pthread_join(orc_sort_thread, &retval);
+ if (rc) {
+ fprintf(stderr,
+ "pthread_join failed '%s': %s\n",
+ strerror(errno), fname);
+ } else if (retval) {
+ rc = -1;
+ fprintf(stderr,
+ "failed to sort ORC tables '%s': %s\n",
+ (char *)retval, fname);
+ }
+ }
+#endif
+
+#ifdef MCOUNT_SORT_ENABLED
+ if (mcount_sort_thread) {
+ void *retval = NULL;
+ /* wait for mcount sort done */
+ rc = pthread_join(mcount_sort_thread, &retval);
+ if (rc) {
+ fprintf(stderr,
+ "pthread_join failed '%s': %s\n",
+ strerror(errno), fname);
+ } else if (retval) {
+ rc = -1;
+ fprintf(stderr,
+ "failed to sort mcount '%s': %s\n",
+ (char *)retval, fname);
+ }
+ }
+#endif
+ return rc;
+}
static int compare_relative_table(const void *a, const void *b)
{
@@ -267,17 +1220,15 @@ static void sort_relative_table_with_data(char *extab_image, int image_size)
static int do_file(char const *const fname, void *addr)
{
- int rc = -1;
- Elf32_Ehdr *ehdr = addr;
+ Elf_Ehdr *ehdr = addr;
table_sort_t custom_sort = NULL;
- switch (ehdr->e_ident[EI_DATA]) {
+ switch (ehdr->e32.e_ident[EI_DATA]) {
case ELFDATA2LSB:
r = rle;
r2 = r2le;
r8 = r8le;
w = wle;
- w2 = w2le;
w8 = w8le;
break;
case ELFDATA2MSB:
@@ -285,25 +1236,31 @@ static int do_file(char const *const fname, void *addr)
r2 = r2be;
r8 = r8be;
w = wbe;
- w2 = w2be;
w8 = w8be;
break;
default:
fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
- ehdr->e_ident[EI_DATA], fname);
+ ehdr->e32.e_ident[EI_DATA], fname);
return -1;
}
- if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0 ||
- (r2(&ehdr->e_type) != ET_EXEC && r2(&ehdr->e_type) != ET_DYN) ||
- ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
+ if (memcmp(ELFMAG, ehdr->e32.e_ident, SELFMAG) != 0 ||
+ (r2(&ehdr->e32.e_type) != ET_EXEC && r2(&ehdr->e32.e_type) != ET_DYN) ||
+ ehdr->e32.e_ident[EI_VERSION] != EV_CURRENT) {
fprintf(stderr, "unrecognized ET_EXEC/ET_DYN file %s\n", fname);
return -1;
}
- switch (r2(&ehdr->e_machine)) {
- case EM_386:
+ switch (r2(&ehdr->e32.e_machine)) {
case EM_AARCH64:
+#ifdef MCOUNT_SORT_ENABLED
+ sort_reloc = true;
+ rela_type = 0x403;
+ /* arm64 uses patchable function entry placing before function */
+ before_func = 8;
+#endif
+ /* fallthrough */
+ case EM_386:
case EM_LOONGARCH:
case EM_RISCV:
case EM_S390:
@@ -324,40 +1281,93 @@ static int do_file(char const *const fname, void *addr)
break;
default:
fprintf(stderr, "unrecognized e_machine %d %s\n",
- r2(&ehdr->e_machine), fname);
+ r2(&ehdr->e32.e_machine), fname);
return -1;
}
- switch (ehdr->e_ident[EI_CLASS]) {
- case ELFCLASS32:
- if (r2(&ehdr->e_ehsize) != sizeof(Elf32_Ehdr) ||
- r2(&ehdr->e_shentsize) != sizeof(Elf32_Shdr)) {
+ switch (ehdr->e32.e_ident[EI_CLASS]) {
+ case ELFCLASS32: {
+ struct elf_funcs efuncs = {
+ .compare_extable = compare_extable_32,
+ .ehdr_shoff = ehdr32_shoff,
+ .ehdr_shentsize = ehdr32_shentsize,
+ .ehdr_shstrndx = ehdr32_shstrndx,
+ .ehdr_shnum = ehdr32_shnum,
+ .shdr_addr = shdr32_addr,
+ .shdr_offset = shdr32_offset,
+ .shdr_link = shdr32_link,
+ .shdr_size = shdr32_size,
+ .shdr_name = shdr32_name,
+ .shdr_type = shdr32_type,
+ .shdr_entsize = shdr32_entsize,
+ .sym_type = sym32_type,
+ .sym_name = sym32_name,
+ .sym_value = sym32_value,
+ .sym_shndx = sym32_shndx,
+ .rela_offset = rela32_offset,
+ .rela_info = rela32_info,
+ .rela_addend = rela32_addend,
+ .rela_write_addend = rela32_write_addend,
+ };
+
+ e = efuncs;
+ long_size = 4;
+ extable_ent_size = 8;
+
+ if (r2(&ehdr->e32.e_ehsize) != sizeof(Elf32_Ehdr) ||
+ r2(&ehdr->e32.e_shentsize) != sizeof(Elf32_Shdr)) {
fprintf(stderr,
"unrecognized ET_EXEC/ET_DYN file: %s\n", fname);
- break;
+ return -1;
+ }
+
}
- rc = do_sort_32(ehdr, fname, custom_sort);
break;
- case ELFCLASS64:
- {
- Elf64_Ehdr *const ghdr = (Elf64_Ehdr *)ehdr;
- if (r2(&ghdr->e_ehsize) != sizeof(Elf64_Ehdr) ||
- r2(&ghdr->e_shentsize) != sizeof(Elf64_Shdr)) {
+ case ELFCLASS64: {
+ struct elf_funcs efuncs = {
+ .compare_extable = compare_extable_64,
+ .ehdr_shoff = ehdr64_shoff,
+ .ehdr_shentsize = ehdr64_shentsize,
+ .ehdr_shstrndx = ehdr64_shstrndx,
+ .ehdr_shnum = ehdr64_shnum,
+ .shdr_addr = shdr64_addr,
+ .shdr_offset = shdr64_offset,
+ .shdr_link = shdr64_link,
+ .shdr_size = shdr64_size,
+ .shdr_name = shdr64_name,
+ .shdr_type = shdr64_type,
+ .shdr_entsize = shdr64_entsize,
+ .sym_type = sym64_type,
+ .sym_name = sym64_name,
+ .sym_value = sym64_value,
+ .sym_shndx = sym64_shndx,
+ .rela_offset = rela64_offset,
+ .rela_info = rela64_info,
+ .rela_addend = rela64_addend,
+ .rela_write_addend = rela64_write_addend,
+ };
+
+ e = efuncs;
+ long_size = 8;
+ extable_ent_size = 16;
+
+ if (r2(&ehdr->e64.e_ehsize) != sizeof(Elf64_Ehdr) ||
+ r2(&ehdr->e64.e_shentsize) != sizeof(Elf64_Shdr)) {
fprintf(stderr,
"unrecognized ET_EXEC/ET_DYN file: %s\n",
fname);
- break;
+ return -1;
}
- rc = do_sort_64(ghdr, fname, custom_sort);
+
}
break;
default:
fprintf(stderr, "unrecognized ELF class %d %s\n",
- ehdr->e_ident[EI_CLASS], fname);
- break;
+ ehdr->e32.e_ident[EI_CLASS], fname);
+ return -1;
}
- return rc;
+ return do_sort(ehdr, fname, custom_sort);
}
int main(int argc, char *argv[])
@@ -365,14 +1375,29 @@ int main(int argc, char *argv[])
int i, n_error = 0; /* gcc-4.3.0 false positive complaint */
size_t size = 0;
void *addr = NULL;
+ int c;
+
+ while ((c = getopt(argc, argv, "s:")) >= 0) {
+ switch (c) {
+ case 's':
+ if (parse_symbols(optarg) < 0) {
+ fprintf(stderr, "Could not parse %s\n", optarg);
+ return -1;
+ }
+ break;
+ default:
+ fprintf(stderr, "usage: sorttable [-s nm-file] vmlinux...\n");
+ return 0;
+ }
+ }
- if (argc < 2) {
+ if ((argc - optind) < 1) {
fprintf(stderr, "usage: sorttable vmlinux...\n");
return 0;
}
/* Process each file in turn, allowing deep failure. */
- for (i = 1; i < argc; i++) {
+ for (i = optind; i < argc; i++) {
addr = mmap_file(argv[i], &size);
if (!addr) {
++n_error;
diff --git a/scripts/sorttable.h b/scripts/sorttable.h
deleted file mode 100644
index 7bd0184380d3..000000000000
--- a/scripts/sorttable.h
+++ /dev/null
@@ -1,497 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * sorttable.h
- *
- * Added ORC unwind tables sort support and other updates:
- * Copyright (C) 1999-2019 Alibaba Group Holding Limited. by:
- * Shile Zhang <shile.zhang@linux.alibaba.com>
- *
- * Copyright 2011 - 2012 Cavium, Inc.
- *
- * Some of code was taken out of arch/x86/kernel/unwind_orc.c, written by:
- * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
- *
- * Some of this code was taken out of recordmcount.h written by:
- *
- * Copyright 2009 John F. Reiser <jreiser@BitWagon.com>. All rights reserved.
- * Copyright 2010 Steven Rostedt <srostedt@redhat.com>, Red Hat Inc.
- */
-
-#undef extable_ent_size
-#undef compare_extable
-#undef get_mcount_loc
-#undef sort_mcount_loc
-#undef elf_mcount_loc
-#undef do_sort
-#undef Elf_Addr
-#undef Elf_Ehdr
-#undef Elf_Shdr
-#undef Elf_Rel
-#undef Elf_Rela
-#undef Elf_Sym
-#undef ELF_R_SYM
-#undef Elf_r_sym
-#undef ELF_R_INFO
-#undef Elf_r_info
-#undef ELF_ST_BIND
-#undef ELF_ST_TYPE
-#undef fn_ELF_R_SYM
-#undef fn_ELF_R_INFO
-#undef uint_t
-#undef _r
-#undef _w
-
-#ifdef SORTTABLE_64
-# define extable_ent_size 16
-# define compare_extable compare_extable_64
-# define get_mcount_loc get_mcount_loc_64
-# define sort_mcount_loc sort_mcount_loc_64
-# define elf_mcount_loc elf_mcount_loc_64
-# define do_sort do_sort_64
-# define Elf_Addr Elf64_Addr
-# define Elf_Ehdr Elf64_Ehdr
-# define Elf_Shdr Elf64_Shdr
-# define Elf_Rel Elf64_Rel
-# define Elf_Rela Elf64_Rela
-# define Elf_Sym Elf64_Sym
-# define ELF_R_SYM ELF64_R_SYM
-# define Elf_r_sym Elf64_r_sym
-# define ELF_R_INFO ELF64_R_INFO
-# define Elf_r_info Elf64_r_info
-# define ELF_ST_BIND ELF64_ST_BIND
-# define ELF_ST_TYPE ELF64_ST_TYPE
-# define fn_ELF_R_SYM fn_ELF64_R_SYM
-# define fn_ELF_R_INFO fn_ELF64_R_INFO
-# define uint_t uint64_t
-# define _r r8
-# define _w w8
-#else
-# define extable_ent_size 8
-# define compare_extable compare_extable_32
-# define get_mcount_loc get_mcount_loc_32
-# define sort_mcount_loc sort_mcount_loc_32
-# define elf_mcount_loc elf_mcount_loc_32
-# define do_sort do_sort_32
-# define Elf_Addr Elf32_Addr
-# define Elf_Ehdr Elf32_Ehdr
-# define Elf_Shdr Elf32_Shdr
-# define Elf_Rel Elf32_Rel
-# define Elf_Rela Elf32_Rela
-# define Elf_Sym Elf32_Sym
-# define ELF_R_SYM ELF32_R_SYM
-# define Elf_r_sym Elf32_r_sym
-# define ELF_R_INFO ELF32_R_INFO
-# define Elf_r_info Elf32_r_info
-# define ELF_ST_BIND ELF32_ST_BIND
-# define ELF_ST_TYPE ELF32_ST_TYPE
-# define fn_ELF_R_SYM fn_ELF32_R_SYM
-# define fn_ELF_R_INFO fn_ELF32_R_INFO
-# define uint_t uint32_t
-# define _r r
-# define _w w
-#endif
-
-#if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED)
-/* ORC unwinder only support X86_64 */
-#include <asm/orc_types.h>
-
-#define ERRSTR_MAXSZ 256
-
-char g_err[ERRSTR_MAXSZ];
-int *g_orc_ip_table;
-struct orc_entry *g_orc_table;
-
-pthread_t orc_sort_thread;
-
-static inline unsigned long orc_ip(const int *ip)
-{
- return (unsigned long)ip + *ip;
-}
-
-static int orc_sort_cmp(const void *_a, const void *_b)
-{
- struct orc_entry *orc_a;
- const int *a = g_orc_ip_table + *(int *)_a;
- const int *b = g_orc_ip_table + *(int *)_b;
- unsigned long a_val = orc_ip(a);
- unsigned long b_val = orc_ip(b);
-
- if (a_val > b_val)
- return 1;
- if (a_val < b_val)
- return -1;
-
- /*
- * The "weak" section terminator entries need to always be on the left
- * to ensure the lookup code skips them in favor of real entries.
- * These terminator entries exist to handle any gaps created by
- * whitelisted .o files which didn't get objtool generation.
- */
- orc_a = g_orc_table + (a - g_orc_ip_table);
- return orc_a->type == ORC_TYPE_UNDEFINED ? -1 : 1;
-}
-
-static void *sort_orctable(void *arg)
-{
- int i;
- int *idxs = NULL;
- int *tmp_orc_ip_table = NULL;
- struct orc_entry *tmp_orc_table = NULL;
- unsigned int *orc_ip_size = (unsigned int *)arg;
- unsigned int num_entries = *orc_ip_size / sizeof(int);
- unsigned int orc_size = num_entries * sizeof(struct orc_entry);
-
- idxs = (int *)malloc(*orc_ip_size);
- if (!idxs) {
- snprintf(g_err, ERRSTR_MAXSZ, "malloc idxs: %s",
- strerror(errno));
- pthread_exit(g_err);
- }
-
- tmp_orc_ip_table = (int *)malloc(*orc_ip_size);
- if (!tmp_orc_ip_table) {
- snprintf(g_err, ERRSTR_MAXSZ, "malloc tmp_orc_ip_table: %s",
- strerror(errno));
- pthread_exit(g_err);
- }
-
- tmp_orc_table = (struct orc_entry *)malloc(orc_size);
- if (!tmp_orc_table) {
- snprintf(g_err, ERRSTR_MAXSZ, "malloc tmp_orc_table: %s",
- strerror(errno));
- pthread_exit(g_err);
- }
-
- /* initialize indices array, convert ip_table to absolute address */
- for (i = 0; i < num_entries; i++) {
- idxs[i] = i;
- tmp_orc_ip_table[i] = g_orc_ip_table[i] + i * sizeof(int);
- }
- memcpy(tmp_orc_table, g_orc_table, orc_size);
-
- qsort(idxs, num_entries, sizeof(int), orc_sort_cmp);
-
- for (i = 0; i < num_entries; i++) {
- if (idxs[i] == i)
- continue;
-
- /* convert back to relative address */
- g_orc_ip_table[i] = tmp_orc_ip_table[idxs[i]] - i * sizeof(int);
- g_orc_table[i] = tmp_orc_table[idxs[i]];
- }
-
- free(idxs);
- free(tmp_orc_ip_table);
- free(tmp_orc_table);
- pthread_exit(NULL);
-}
-#endif
-
-static int compare_extable(const void *a, const void *b)
-{
- Elf_Addr av = _r(a);
- Elf_Addr bv = _r(b);
-
- if (av < bv)
- return -1;
- if (av > bv)
- return 1;
- return 0;
-}
-#ifdef MCOUNT_SORT_ENABLED
-pthread_t mcount_sort_thread;
-
-struct elf_mcount_loc {
- Elf_Ehdr *ehdr;
- Elf_Shdr *init_data_sec;
- uint_t start_mcount_loc;
- uint_t stop_mcount_loc;
-};
-
-/* Sort the addresses stored between __start_mcount_loc to __stop_mcount_loc in vmlinux */
-static void *sort_mcount_loc(void *arg)
-{
- struct elf_mcount_loc *emloc = (struct elf_mcount_loc *)arg;
- uint_t offset = emloc->start_mcount_loc - _r(&(emloc->init_data_sec)->sh_addr)
- + _r(&(emloc->init_data_sec)->sh_offset);
- uint_t count = emloc->stop_mcount_loc - emloc->start_mcount_loc;
- unsigned char *start_loc = (void *)emloc->ehdr + offset;
-
- qsort(start_loc, count/sizeof(uint_t), sizeof(uint_t), compare_extable);
- return NULL;
-}
-
-/* Get the address of __start_mcount_loc and __stop_mcount_loc in System.map */
-static void get_mcount_loc(uint_t *_start, uint_t *_stop)
-{
- FILE *file_start, *file_stop;
- char start_buff[20];
- char stop_buff[20];
- int len = 0;
-
- file_start = popen(" grep start_mcount System.map | awk '{print $1}' ", "r");
- if (!file_start) {
- fprintf(stderr, "get start_mcount_loc error!");
- return;
- }
-
- file_stop = popen(" grep stop_mcount System.map | awk '{print $1}' ", "r");
- if (!file_stop) {
- fprintf(stderr, "get stop_mcount_loc error!");
- pclose(file_start);
- return;
- }
-
- while (fgets(start_buff, sizeof(start_buff), file_start) != NULL) {
- len = strlen(start_buff);
- start_buff[len - 1] = '\0';
- }
- *_start = strtoul(start_buff, NULL, 16);
-
- while (fgets(stop_buff, sizeof(stop_buff), file_stop) != NULL) {
- len = strlen(stop_buff);
- stop_buff[len - 1] = '\0';
- }
- *_stop = strtoul(stop_buff, NULL, 16);
-
- pclose(file_start);
- pclose(file_stop);
-}
-#endif
-static int do_sort(Elf_Ehdr *ehdr,
- char const *const fname,
- table_sort_t custom_sort)
-{
- int rc = -1;
- Elf_Shdr *s, *shdr = (Elf_Shdr *)((char *)ehdr + _r(&ehdr->e_shoff));
- Elf_Shdr *strtab_sec = NULL;
- Elf_Shdr *symtab_sec = NULL;
- Elf_Shdr *extab_sec = NULL;
- Elf_Sym *sym;
- const Elf_Sym *symtab;
- Elf32_Word *symtab_shndx = NULL;
- Elf_Sym *sort_needed_sym = NULL;
- Elf_Shdr *sort_needed_sec;
- Elf_Rel *relocs = NULL;
- int relocs_size = 0;
- uint32_t *sort_needed_loc;
- const char *secstrings;
- const char *strtab;
- char *extab_image;
- int extab_index = 0;
- int i;
- int idx;
- unsigned int shnum;
- unsigned int shstrndx;
-#ifdef MCOUNT_SORT_ENABLED
- struct elf_mcount_loc mstruct = {0};
- uint_t _start_mcount_loc = 0;
- uint_t _stop_mcount_loc = 0;
-#endif
-#if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED)
- unsigned int orc_ip_size = 0;
- unsigned int orc_size = 0;
- unsigned int orc_num_entries = 0;
-#endif
-
- shstrndx = r2(&ehdr->e_shstrndx);
- if (shstrndx == SHN_XINDEX)
- shstrndx = r(&shdr[0].sh_link);
- secstrings = (const char *)ehdr + _r(&shdr[shstrndx].sh_offset);
-
- shnum = r2(&ehdr->e_shnum);
- if (shnum == SHN_UNDEF)
- shnum = _r(&shdr[0].sh_size);
-
- for (i = 0, s = shdr; s < shdr + shnum; i++, s++) {
- idx = r(&s->sh_name);
- if (!strcmp(secstrings + idx, "__ex_table")) {
- extab_sec = s;
- extab_index = i;
- }
- if (!strcmp(secstrings + idx, ".symtab"))
- symtab_sec = s;
- if (!strcmp(secstrings + idx, ".strtab"))
- strtab_sec = s;
-
- if ((r(&s->sh_type) == SHT_REL ||
- r(&s->sh_type) == SHT_RELA) &&
- r(&s->sh_info) == extab_index) {
- relocs = (void *)ehdr + _r(&s->sh_offset);
- relocs_size = _r(&s->sh_size);
- }
- if (r(&s->sh_type) == SHT_SYMTAB_SHNDX)
- symtab_shndx = (Elf32_Word *)((const char *)ehdr +
- _r(&s->sh_offset));
-
-#ifdef MCOUNT_SORT_ENABLED
- /* locate the .init.data section in vmlinux */
- if (!strcmp(secstrings + idx, ".init.data")) {
- get_mcount_loc(&_start_mcount_loc, &_stop_mcount_loc);
- mstruct.ehdr = ehdr;
- mstruct.init_data_sec = s;
- mstruct.start_mcount_loc = _start_mcount_loc;
- mstruct.stop_mcount_loc = _stop_mcount_loc;
- }
-#endif
-
-#if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED)
- /* locate the ORC unwind tables */
- if (!strcmp(secstrings + idx, ".orc_unwind_ip")) {
- orc_ip_size = s->sh_size;
- g_orc_ip_table = (int *)((void *)ehdr +
- s->sh_offset);
- }
- if (!strcmp(secstrings + idx, ".orc_unwind")) {
- orc_size = s->sh_size;
- g_orc_table = (struct orc_entry *)((void *)ehdr +
- s->sh_offset);
- }
-#endif
- } /* for loop */
-
-#if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED)
- if (!g_orc_ip_table || !g_orc_table) {
- fprintf(stderr,
- "incomplete ORC unwind tables in file: %s\n", fname);
- goto out;
- }
-
- orc_num_entries = orc_ip_size / sizeof(int);
- if (orc_ip_size % sizeof(int) != 0 ||
- orc_size % sizeof(struct orc_entry) != 0 ||
- orc_num_entries != orc_size / sizeof(struct orc_entry)) {
- fprintf(stderr,
- "inconsistent ORC unwind table entries in file: %s\n",
- fname);
- goto out;
- }
-
- /* create thread to sort ORC unwind tables concurrently */
- if (pthread_create(&orc_sort_thread, NULL,
- sort_orctable, &orc_ip_size)) {
- fprintf(stderr,
- "pthread_create orc_sort_thread failed '%s': %s\n",
- strerror(errno), fname);
- goto out;
- }
-#endif
-
-#ifdef MCOUNT_SORT_ENABLED
- if (!mstruct.init_data_sec || !_start_mcount_loc || !_stop_mcount_loc) {
- fprintf(stderr,
- "incomplete mcount's sort in file: %s\n",
- fname);
- goto out;
- }
-
- /* create thread to sort mcount_loc concurrently */
- if (pthread_create(&mcount_sort_thread, NULL, &sort_mcount_loc, &mstruct)) {
- fprintf(stderr,
- "pthread_create mcount_sort_thread failed '%s': %s\n",
- strerror(errno), fname);
- goto out;
- }
-#endif
- if (!extab_sec) {
- fprintf(stderr, "no __ex_table in file: %s\n", fname);
- goto out;
- }
-
- if (!symtab_sec) {
- fprintf(stderr, "no .symtab in file: %s\n", fname);
- goto out;
- }
-
- if (!strtab_sec) {
- fprintf(stderr, "no .strtab in file: %s\n", fname);
- goto out;
- }
-
- extab_image = (void *)ehdr + _r(&extab_sec->sh_offset);
- strtab = (const char *)ehdr + _r(&strtab_sec->sh_offset);
- symtab = (const Elf_Sym *)((const char *)ehdr +
- _r(&symtab_sec->sh_offset));
-
- if (custom_sort) {
- custom_sort(extab_image, _r(&extab_sec->sh_size));
- } else {
- int num_entries = _r(&extab_sec->sh_size) / extable_ent_size;
- qsort(extab_image, num_entries,
- extable_ent_size, compare_extable);
- }
-
- /* If there were relocations, we no longer need them. */
- if (relocs)
- memset(relocs, 0, relocs_size);
-
- /* find the flag main_extable_sort_needed */
- for (sym = (void *)ehdr + _r(&symtab_sec->sh_offset);
- sym < sym + _r(&symtab_sec->sh_size) / sizeof(Elf_Sym);
- sym++) {
- if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT)
- continue;
- if (!strcmp(strtab + r(&sym->st_name),
- "main_extable_sort_needed")) {
- sort_needed_sym = sym;
- break;
- }
- }
-
- if (!sort_needed_sym) {
- fprintf(stderr,
- "no main_extable_sort_needed symbol in file: %s\n",
- fname);
- goto out;
- }
-
- sort_needed_sec = &shdr[get_secindex(r2(&sym->st_shndx),
- sort_needed_sym - symtab,
- symtab_shndx)];
- sort_needed_loc = (void *)ehdr +
- _r(&sort_needed_sec->sh_offset) +
- _r(&sort_needed_sym->st_value) -
- _r(&sort_needed_sec->sh_addr);
-
- /* extable has been sorted, clear the flag */
- w(0, sort_needed_loc);
- rc = 0;
-
-out:
-#if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED)
- if (orc_sort_thread) {
- void *retval = NULL;
- /* wait for ORC tables sort done */
- rc = pthread_join(orc_sort_thread, &retval);
- if (rc) {
- fprintf(stderr,
- "pthread_join failed '%s': %s\n",
- strerror(errno), fname);
- } else if (retval) {
- rc = -1;
- fprintf(stderr,
- "failed to sort ORC tables '%s': %s\n",
- (char *)retval, fname);
- }
- }
-#endif
-
-#ifdef MCOUNT_SORT_ENABLED
- if (mcount_sort_thread) {
- void *retval = NULL;
- /* wait for mcount sort done */
- rc = pthread_join(mcount_sort_thread, &retval);
- if (rc) {
- fprintf(stderr,
- "pthread_join failed '%s': %s\n",
- strerror(errno), fname);
- } else if (retval) {
- rc = -1;
- fprintf(stderr,
- "failed to sort mcount '%s': %s\n",
- (char *)retval, fname);
- }
- }
-#endif
- return rc;
-}
diff --git a/scripts/spdxcheck.py b/scripts/spdxcheck.py
index 8b8fb115fc81..8d608f61bf37 100755
--- a/scripts/spdxcheck.py
+++ b/scripts/spdxcheck.py
@@ -214,9 +214,15 @@ class id_parser(object):
# Remove trailing xml comment closure
if line.strip().endswith('-->'):
expr = expr.rstrip('-->').strip()
+ # Remove trailing Jinja2 comment closure
+ if line.strip().endswith('#}'):
+ expr = expr.rstrip('#}').strip()
# Special case for SH magic boot code files
if line.startswith('LIST \"'):
expr = expr.rstrip('\"').strip()
+ # Remove j2 comment closure
+ if line.startswith('{#'):
+ expr = expr.rstrip('#}').strip()
self.parse(expr)
self.spdx_valid += 1
#
diff --git a/scripts/spelling.txt b/scripts/spelling.txt
index 554329a074ce..a290db720b0f 100644
--- a/scripts/spelling.txt
+++ b/scripts/spelling.txt
@@ -141,6 +141,7 @@ anomoly||anomaly
anonynous||anonymous
anway||anyway
aplication||application
+apeared||appeared
appearence||appearance
applicaion||application
appliction||application
@@ -155,6 +156,7 @@ apropriate||appropriate
aquainted||acquainted
aquired||acquired
aquisition||acquisition
+aquires||acquires
arbitary||arbitrary
architechture||architecture
archtecture||architecture
@@ -185,10 +187,12 @@ assotiated||associated
asssert||assert
assum||assume
assumtpion||assumption
+asume||assume
asuming||assuming
asycronous||asynchronous
asychronous||asynchronous
asynchnous||asynchronous
+asynchrnous||asynchronous
asynchronus||asynchronous
asynchromous||asynchronous
asymetric||asymmetric
@@ -218,6 +222,7 @@ autonymous||autonomous
auxillary||auxiliary
auxilliary||auxiliary
avaiable||available
+avaialable||available
avaible||available
availabe||available
availabled||available
@@ -263,12 +268,14 @@ broadcase||broadcast
broadcat||broadcast
bufer||buffer
bufferred||buffered
+bufferur||buffer
bufufer||buffer
cacluated||calculated
caculate||calculate
caculation||calculation
cadidate||candidate
cahces||caches
+calcluate||calculate
calender||calendar
calescing||coalescing
calibraiton||calibration
@@ -331,6 +338,7 @@ chouse||chose
circumvernt||circumvent
claread||cleared
clared||cleared
+clearify||clarify
closeing||closing
clustred||clustered
cnfiguration||configuration
@@ -379,12 +387,14 @@ comsumed||consumed
comunicate||communicate
comunication||communication
conbination||combination
+concurent||concurrent
conditionaly||conditionally
conditon||condition
condtion||condition
condtional||conditional
conected||connected
conector||connector
+configed||configured
configration||configuration
configred||configured
configuartion||configuration
@@ -394,8 +404,10 @@ configuratoin||configuration
configuraton||configuration
configuretion||configuration
configutation||configuration
+congiuration||configuration
conider||consider
conjuction||conjunction
+connction||connection
connecetd||connected
connectinos||connections
connetor||connector
@@ -403,6 +415,8 @@ connnection||connection
connnections||connections
consistancy||consistency
consistant||consistent
+consits||consists
+constructred||constructed
containes||contains
containts||contains
contaisn||contains
@@ -440,6 +454,7 @@ creationg||creating
cryptocraphic||cryptographic
cummulative||cumulative
cunter||counter
+curent||current
curently||currently
cylic||cyclic
dafault||default
@@ -451,7 +466,9 @@ decendant||descendant
decendants||descendants
decompres||decompress
decsribed||described
+decrese||decrease
decription||description
+detault||default
dectected||detected
defailt||default
deferal||deferral
@@ -474,6 +491,7 @@ delare||declare
delares||declares
delaring||declaring
delemiter||delimiter
+deley||delay
delibrately||deliberately
delievered||delivered
demodualtor||demodulator
@@ -487,6 +505,7 @@ depreacte||deprecate
desactivate||deactivate
desciptor||descriptor
desciptors||descriptors
+descritpor||descriptor
descripto||descriptor
descripton||description
descrition||description
@@ -539,6 +558,7 @@ disgest||digest
disired||desired
dispalying||displaying
dissable||disable
+dissapeared||disappeared
diplay||display
directon||direction
direcly||directly
@@ -594,6 +614,7 @@ eigth||eight
elementry||elementary
eletronic||electronic
embeded||embedded
+emtpy||empty
enabledi||enabled
enbale||enable
enble||enable
@@ -601,6 +622,7 @@ enchanced||enhanced
encorporating||incorporating
encrupted||encrypted
encrypiton||encryption
+encryped||encrypted
encryptio||encryption
endianess||endianness
enpoint||endpoint
@@ -630,6 +652,7 @@ etsbalishment||establishment
evalute||evaluate
evalutes||evaluates
evalution||evaluation
+evaulated||evaluated
excecutable||executable
excceed||exceed
exceded||exceeded
@@ -650,15 +673,18 @@ exlcude||exclude
exlcuding||excluding
exlcusive||exclusive
exlusive||exclusive
+exlicitly||explicitly
exmaple||example
expecially||especially
experies||expires
explicite||explicit
+explicity||explicitly
explicitely||explicitly
explict||explicit
explictely||explicitly
explictly||explicitly
expresion||expression
+exprienced||experienced
exprimental||experimental
extened||extended
exteneded||extended
@@ -707,10 +733,12 @@ followign||following
followings||following
follwing||following
fonud||found
+forcebly||forcibly
forseeable||foreseeable
forse||force
fortan||fortran
forwardig||forwarding
+forwared||forwarded
frambuffer||framebuffer
framming||framing
framwork||framework
@@ -751,6 +779,7 @@ grahpical||graphical
granularty||granularity
grapic||graphic
grranted||granted
+grups||groups
guage||gauge
guarenteed||guaranteed
guarentee||guarantee
@@ -764,6 +793,7 @@ hardare||hardware
harware||hardware
hardward||hardware
havind||having
+heigth||height
heirarchically||hierarchically
heirarchy||hierarchy
heirachy||hierarchy
@@ -772,9 +802,11 @@ hearbeat||heartbeat
heterogenous||heterogeneous
hexdecimal||hexadecimal
hybernate||hibernate
+hiearchy||hierarchy
hierachy||hierarchy
hierarchie||hierarchy
homogenous||homogeneous
+horizental||horizontal
howver||however
hsould||should
hypervior||hypervisor
@@ -826,6 +858,7 @@ independed||independent
indiate||indicate
indicat||indicate
inexpect||inexpected
+infalte||inflate
inferface||interface
infinit||infinite
infomation||information
@@ -834,6 +867,7 @@ informations||information
informtion||information
infromation||information
ingore||ignore
+inheritence||inheritance
inital||initial
initalized||initialized
initalised||initialized
@@ -844,6 +878,7 @@ initators||initiators
initialiazation||initialization
initializationg||initialization
initializiation||initialization
+initializtion||initialization
initialze||initialize
initialzed||initialized
initialzing||initializing
@@ -860,6 +895,7 @@ instanciate||instantiate
instanciated||instantiated
instuments||instruments
insufficent||insufficient
+intead||instead
inteface||interface
integreated||integrated
integrety||integrity
@@ -878,6 +914,7 @@ interoprability||interoperability
interuupt||interrupt
interupt||interrupt
interupts||interrupts
+interurpt||interrupt
interrface||interface
interrrupt||interrupt
interrup||interrupt
@@ -925,6 +962,7 @@ jumpimng||jumping
juse||just
jus||just
kown||known
+lable||label
langage||language
langauage||language
langauge||language
@@ -995,6 +1033,7 @@ metdata||metadata
micropone||microphone
microprocesspr||microprocessor
migrateable||migratable
+miliseconds||milliseconds
millenium||millennium
milliseonds||milliseconds
minimim||minimum
@@ -1061,6 +1100,7 @@ notications||notifications
notifcations||notifications
notifed||notified
notity||notify
+notfify||notify
nubmer||number
numebr||number
numer||number
@@ -1102,6 +1142,7 @@ orientatied||orientated
orientied||oriented
orignal||original
originial||original
+orphanded||orphaned
otherise||otherwise
ouput||output
oustanding||outstanding
@@ -1132,6 +1173,7 @@ palne||plane
paramameters||parameters
paramaters||parameters
paramater||parameter
+paramenters||parameters
parametes||parameters
parametised||parametrised
paramter||parameter
@@ -1163,9 +1205,11 @@ peroid||period
persistance||persistence
persistant||persistent
phoneticly||phonetically
+pipline||pipeline
plaform||platform
plalform||platform
platfoem||platform
+platfomr||platform
platfrom||platform
plattform||platform
pleaes||please
@@ -1177,9 +1221,11 @@ poiter||pointer
posible||possible
positon||position
possibilites||possibilities
+postion||position
potocol||protocol
powerfull||powerful
pramater||parameter
+preambule||preamble
preamle||preamble
preample||preamble
preapre||prepare
@@ -1188,6 +1234,7 @@ preceeding||preceding
preceed||precede
precendence||precedence
precission||precision
+predicition||prediction
preemptable||preemptible
prefered||preferred
prefferably||preferably
@@ -1266,9 +1313,11 @@ querrying||querying
queus||queues
randomally||randomly
raoming||roaming
+readyness||readiness
reasearcher||researcher
reasearchers||researchers
reasearch||research
+recalcualte||recalculate
receieve||receive
recepient||recipient
recevied||received
@@ -1281,8 +1330,10 @@ recieves||receives
recieving||receiving
recogniced||recognised
recognizeable||recognizable
+recompte||recompute
recommanded||recommended
recyle||recycle
+redect||reject
redircet||redirect
redirectrion||redirection
redundacy||redundancy
@@ -1290,7 +1341,9 @@ reename||rename
refcounf||refcount
refence||reference
refered||referred
+referencce||reference
referenace||reference
+refererence||reference
refering||referring
refernces||references
refernnce||reference
@@ -1315,17 +1368,21 @@ reloade||reload
remoote||remote
remore||remote
removeable||removable
+repective||respective
repectively||respectively
replacable||replaceable
replacments||replacements
replys||replies
reponse||response
representaion||representation
+repsonse||response
+reqested||requested
reqeust||request
reqister||register
requed||requeued
requestied||requested
requiere||require
+requieres||requires
requirment||requirement
requred||required
requried||required
@@ -1362,6 +1419,7 @@ reuest||request
reuqest||request
reutnred||returned
revsion||revision
+rewritting||rewriting
rmeoved||removed
rmeove||remove
rmeoves||removes
@@ -1412,6 +1470,7 @@ sequencial||sequential
serivce||service
serveral||several
servive||service
+sesion||session
setts||sets
settting||setting
shapshot||snapshot
@@ -1444,6 +1503,7 @@ soluation||solution
souce||source
speach||speech
specfic||specific
+specfication||specification
specfield||specified
speciefied||specified
specifc||specific
@@ -1544,6 +1604,7 @@ syncronus||synchronous
syste||system
sytem||system
sythesis||synthesis
+tagert||target
taht||that
tained||tainted
tarffic||traffic
@@ -1572,10 +1633,13 @@ trys||tries
thses||these
tiggers||triggers
tiggered||triggered
+tiggerring||triggering
tipically||typically
timeing||timing
+timming||timing
timout||timeout
tmis||this
+tolarance||tolerance
toogle||toggle
torerable||tolerable
torlence||tolerance
@@ -1597,9 +1661,12 @@ transision||transition
transistioned||transitioned
transmittd||transmitted
transormed||transformed
+trasaction||transaction
trasfer||transfer
trasmission||transmission
+trasmitter||transmitter
treshold||threshold
+trigged||triggered
triggerd||triggered
trigerred||triggered
trigerring||triggering
@@ -1615,6 +1682,7 @@ uknown||unknown
usccess||success
uncommited||uncommitted
uncompatible||incompatible
+uncomressed||uncompressed
unconditionaly||unconditionally
undeflow||underflow
undelying||underlying
@@ -1682,6 +1750,7 @@ utitity||utility
utitlty||utility
vaid||valid
vaild||valid
+validationg||validating
valide||valid
variantions||variations
varible||variable
@@ -1691,6 +1760,7 @@ verbse||verbose
veify||verify
verfication||verification
veriosn||version
+versoin||version
verisons||versions
verison||version
veritical||vertical
diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install
index c1121f098542..ad9945ccb0cf 100755
--- a/scripts/sphinx-pre-install
+++ b/scripts/sphinx-pre-install
@@ -300,8 +300,6 @@ sub check_sphinx()
}
$cur_version = get_sphinx_version($sphinx);
- die ("$sphinx returned an error") if (!$cur_version);
-
die "$sphinx didn't return its version" if (!$cur_version);
if ($cur_version lt $min_version) {
diff --git a/scripts/ssl-common.h b/scripts/ssl-common.h
new file mode 100644
index 000000000000..2db0e181143c
--- /dev/null
+++ b/scripts/ssl-common.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/*
+ * SSL helper functions shared by sign-file and extract-cert.
+ */
+
+static void drain_openssl_errors(int l, int silent)
+{
+ const char *file;
+ char buf[120];
+ int e, line;
+
+ if (ERR_peek_error() == 0)
+ return;
+ if (!silent)
+ fprintf(stderr, "At main.c:%d:\n", l);
+
+ while ((e = ERR_peek_error_line(&file, &line))) {
+ ERR_error_string(e, buf);
+ if (!silent)
+ fprintf(stderr, "- SSL %s: %s:%d\n", buf, file, line);
+ ERR_get_error();
+ }
+}
+
+#define ERR(cond, fmt, ...) \
+ do { \
+ bool __cond = (cond); \
+ drain_openssl_errors(__LINE__, 0); \
+ if (__cond) { \
+ errx(1, fmt, ## __VA_ARGS__); \
+ } \
+ } while (0)
diff --git a/scripts/subarch.include b/scripts/subarch.include
index 4bd327d0ae42..c4592d59d69b 100644
--- a/scripts/subarch.include
+++ b/scripts/subarch.include
@@ -6,7 +6,7 @@
SUBARCH := $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ \
-e s/sun4u/sparc64/ \
- -e s/arm.*/arm/ -e s/sa110/arm/ \
+ -e /^arm64$$/!s/arm.*/arm/ -e s/sa110/arm/ \
-e s/s390x/s390/ \
-e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
-e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ \
diff --git a/scripts/syscall.tbl b/scripts/syscall.tbl
index 591d85e8ca7e..580b4e246aec 100644
--- a/scripts/syscall.tbl
+++ b/scripts/syscall.tbl
@@ -53,6 +53,7 @@
39 common umount2 sys_umount
40 common mount sys_mount
41 common pivot_root sys_pivot_root
+42 common nfsservctl sys_ni_syscall
43 32 statfs64 sys_statfs64 compat_sys_statfs64
43 64 statfs sys_statfs
44 32 fstatfs64 sys_fstatfs64 compat_sys_fstatfs64
@@ -98,9 +99,9 @@
77 common tee sys_tee
78 common readlinkat sys_readlinkat
79 stat64 fstatat64 sys_fstatat64
-79 newstat fstatat sys_newfstatat
+79 64 newfstatat sys_newfstatat
80 stat64 fstat64 sys_fstat64
-80 newstat fstat sys_newfstat
+80 64 fstat sys_newfstat
81 common sync sys_sync
82 common fsync sys_fsync
83 common fdatasync sys_fdatasync
@@ -402,4 +403,8 @@
460 common lsm_set_self_attr sys_lsm_set_self_attr
461 common lsm_list_modules sys_lsm_list_modules
462 common mseal sys_mseal
-467 common uretprobe sys_uretprobe
+463 common setxattrat sys_setxattrat
+464 common getxattrat sys_getxattrat
+465 common listxattrat sys_listxattrat
+466 common removexattrat sys_removexattrat
+467 common open_tree_attr sys_open_tree_attr
diff --git a/scripts/tags.sh b/scripts/tags.sh
index 191e0461d6d5..98680e9cd7be 100755
--- a/scripts/tags.sh
+++ b/scripts/tags.sh
@@ -146,15 +146,14 @@ dogtags()
# a ^[^#] is prepended by setup_regex unless an anchor is already present
regex_asm=(
'/^\(ENTRY\|_GLOBAL\)([[:space:]]*\([[:alnum:]_\\]*\)).*/\2/'
+ '/^SYM_[[:alnum:]_]*START[[:alnum:]_]*([[:space:]]*\([[:alnum:]_\\]*\)).*/\1/'
)
regex_c=(
'/^SYSCALL_DEFINE[0-9]([[:space:]]*\([[:alnum:]_]*\).*/sys_\1/'
'/^BPF_CALL_[0-9]([[:space:]]*\([[:alnum:]_]*\).*/\1/'
'/^COMPAT_SYSCALL_DEFINE[0-9]([[:space:]]*\([[:alnum:]_]*\).*/compat_sys_\1/'
'/^TRACE_EVENT([[:space:]]*\([[:alnum:]_]*\).*/trace_\1/'
- '/^TRACE_EVENT([[:space:]]*\([[:alnum:]_]*\).*/trace_\1_rcuidle/'
'/^DEFINE_EVENT([^,)]*,[[:space:]]*\([[:alnum:]_]*\).*/trace_\1/'
- '/^DEFINE_EVENT([^,)]*,[[:space:]]*\([[:alnum:]_]*\).*/trace_\1_rcuidle/'
'/^DEFINE_INSN_CACHE_OPS([[:space:]]*\([[:alnum:]_]*\).*/get_\1_slot/'
'/^DEFINE_INSN_CACHE_OPS([[:space:]]*\([[:alnum:]_]*\).*/free_\1_slot/'
'/^PAGEFLAG([[:space:]]*\([[:alnum:]_]*\).*/Page\1/'
@@ -190,8 +189,9 @@ regex_c=(
'/^PCI_OP_WRITE([[:space:]]*\(\w*\).*[1-4])/pci_bus_write_config_\1/'
'/\<DEFINE_\(RT_MUTEX\|MUTEX\|SEMAPHORE\|SPINLOCK\)([[:space:]]*\([[:alnum:]_]*\)/\2/v/'
'/\<DEFINE_\(RAW_SPINLOCK\|RWLOCK\|SEQLOCK\)([[:space:]]*\([[:alnum:]_]*\)/\2/v/'
+ '/\<DEFINE_TIMER(\([^,)]*\),/\1/'
'/\<DECLARE_\(RWSEM\|COMPLETION\)([[:space:]]*\([[:alnum:]_]\+\)/\2/v/'
- '/\<DECLARE_BITMAP([[:space:]]*\([[:alnum:]_]*\)/\1/v/'
+ '/\<DECLARE_BITMAP([[:space:]]*\([[:alnum:]_]\+\)/\1/v/'
'/\(^\|\s\)\(\|L\|H\)LIST_HEAD([[:space:]]*\([[:alnum:]_]*\)/\3/v/'
'/\(^\|\s\)RADIX_TREE([[:space:]]*\([[:alnum:]_]*\)/\2/v/'
'/\<DEFINE_PER_CPU([^,]*,[[:space:]]*\([[:alnum:]_]*\)/\1/v/'
@@ -212,6 +212,15 @@ regex_c=(
'/\<\(DEFINE\|DECLARE\)_STATIC_KEY_\(TRUE\|FALSE\)\(\|_RO\)([[:space:]]*\([[:alnum:]_]\+\)/\4/'
'/^SEQCOUNT_LOCKTYPE(\([^,]*\),[[:space:]]*\([^,]*\),[^)]*)/seqcount_\2_t/'
'/^SEQCOUNT_LOCKTYPE(\([^,]*\),[[:space:]]*\([^,]*\),[^)]*)/seqcount_\2_init/'
+ '/^\<DECLARE_IDTENTRY[[:alnum:]_]*([^,)]*,[[:space:]]*\([[:alnum:]_]\+\)/\1/'
+ '/^\<DEFINE_IDTENTRY[[:alnum:]_]*([[:space:]]*\([[:alnum:]_]\+\)/\1/'
+ '/^\<DEFINE_FREE(\([[:alnum:]_]\+\)/cleanup_\1/'
+ '/^\<DEFINE_CLASS(\([[:alnum:]_]\+\)/class_\1/'
+ '/^\<EXTEND_CLASS(\([[:alnum:]_]\+\),[[:space:]]*\([[:alnum:]_]\+\)/class_\1\2/'
+ '/^\<DEFINE_GUARD(\([[:alnum:]_]\+\)/class_\1/'
+ '/^\<DEFINE_GUARD_COND(\([[:alnum:]_]\+\),[[:space:]]*\([[:alnum:]_]\+\)/class_\1\2/'
+ '/^\<DEFINE_LOCK_GUARD_[[:digit:]](\([[:alnum:]_]\+\)/class_\1/'
+ '/^\<DEFINE_LOCK_GUARD_[[:digit:]]_COND(\([[:alnum:]_]\+\),[[:space:]]*\([[:alnum:]_]\+\)/class_\1\2/'
)
regex_kconfig=(
'/^[[:blank:]]*\(menu\|\)config[[:blank:]]\+\([[:alnum:]_]\+\)/\2/'
@@ -257,19 +266,30 @@ exuberant()
CTAGS_EXTRA="extras"
fi
setup_regex exuberant asm c
- all_target_sources | xargs $1 -a \
- -I __initdata,__exitdata,__initconst,__ro_after_init \
- -I __initdata_memblock \
- -I __refdata,__attribute,__maybe_unused,__always_unused \
- -I __acquires,__releases,__deprecated,__always_inline \
- -I __read_mostly,__aligned,____cacheline_aligned \
- -I ____cacheline_aligned_in_smp \
- -I __cacheline_aligned,__cacheline_aligned_in_smp \
- -I ____cacheline_internodealigned_in_smp \
- -I __used,__packed,__packed2__,__must_check,__must_hold \
- -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL,ACPI_EXPORT_SYMBOL \
- -I DEFINE_TRACE,EXPORT_TRACEPOINT_SYMBOL,EXPORT_TRACEPOINT_SYMBOL_GPL \
- -I static,const \
+ # identifiers to ignore by ctags
+ local ign=(
+ ACPI_EXPORT_SYMBOL
+ DECLARE_BITMAP
+ DEFINE_{TRACE,MUTEX,TIMER}
+ EXPORT_SYMBOL EXPORT_SYMBOL_GPL
+ EXPORT_TRACEPOINT_SYMBOL EXPORT_TRACEPOINT_SYMBOL_GPL
+ ____cacheline_aligned ____cacheline_aligned_in_smp
+ ____cacheline_internodealigned_in_smp
+ __acquires __aligned __always_inline __always_unused
+ __attribute
+ __cacheline_aligned __cacheline_aligned_in_smp
+ __deprecated
+ __exitdata
+ __initconst __initdata __initdata_memblock
+ __maybe_unused __must_check __must_hold
+ __packed __packed2__
+ __read_mostly __refdata __releases __ro_after_init
+ __used
+ const
+ static
+ )
+ all_target_sources | \
+ xargs $1 -a -I "$(IFS=','; echo "${ign[*]}")" \
--$CTAGS_EXTRA=+fq --c-kinds=+px --fields=+iaS --langmap=c:+.h \
"${regex[@]}"
diff --git a/scripts/test_fortify.sh b/scripts/test_fortify.sh
deleted file mode 100644
index c2688ab8281d..000000000000
--- a/scripts/test_fortify.sh
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/bin/sh
-# SPDX-License-Identifier: GPL-2.0-only
-set -e
-
-# Argument 1: Source file to build.
-IN="$1"
-shift
-# Extract just the filename for error messages below.
-FILE="${IN##*/}"
-# Extract the function name for error messages below.
-FUNC="${FILE#*-}"
-FUNC="${FUNC%%-*}"
-FUNC="${FUNC%%.*}"
-# Extract the symbol to test for in build/symbol test below.
-WANT="__${FILE%%-*}"
-
-# Argument 2: Where to write the build log.
-OUT="$1"
-shift
-TMP="${OUT}.tmp"
-
-# Argument 3: Path to "nm" tool.
-NM="$1"
-shift
-
-# Remaining arguments are: $(CC) $(c_flags)
-
-# Clean up temporary file at exit.
-__cleanup() {
- rm -f "$TMP"
-}
-trap __cleanup EXIT
-
-# Function names in warnings are wrapped in backticks under UTF-8 locales.
-# Run the commands with LANG=C so that grep output will not change.
-export LANG=C
-
-status=
-# Attempt to build a source that is expected to fail with a specific warning.
-if "$@" -Werror -c "$IN" -o "$OUT".o 2> "$TMP" ; then
- # If the build succeeds, either the test has failed or the
- # warning may only happen at link time (Clang). In that case,
- # make sure the expected symbol is unresolved in the symbol list.
- # If so, FORTIFY is working for this case.
- if ! $NM -A "$OUT".o | grep -m1 "\bU ${WANT}$" >>"$TMP" ; then
- status="warning: unsafe ${FUNC}() usage lacked '$WANT' symbol in $IN"
- fi
-else
- # If the build failed, check for the warning in the stderr.
- # GCC:
- # ./include/linux/fortify-string.h:316:25: error: call to '__write_overflow_field' declared with attribute warning: detected write beyond size of field (1st parameter); maybe use struct_group()? [-Werror=attribute-warning]
- # Clang 14:
- # ./include/linux/fortify-string.h:316:4: error: call to __write_overflow_field declared with 'warning' attribute: detected write beyond size of field (1st parameter); maybe use struct_group()? [-Werror,-Wattribute-warning]
- if ! grep -Eq -m1 "error: call to .?\b${WANT}\b.?" "$TMP" ; then
- status="warning: unsafe ${FUNC}() usage lacked '$WANT' warning in $IN"
- fi
-fi
-
-if [ -n "$status" ]; then
- # Report on failure results, including compilation warnings.
- echo "$status" | tee "$OUT" >&2
-else
- # Report on good results, and save any compilation output to log.
- echo "ok: unsafe ${FUNC}() usage correctly detected with '$WANT' in $IN" >"$OUT"
-fi
-cat "$TMP" >>"$OUT"
diff --git a/scripts/tracing/draw_functrace.py b/scripts/tracing/draw_functrace.py
deleted file mode 100755
index 42fa87300941..000000000000
--- a/scripts/tracing/draw_functrace.py
+++ /dev/null
@@ -1,129 +0,0 @@
-#!/usr/bin/env python
-# SPDX-License-Identifier: GPL-2.0-only
-
-"""
-Copyright 2008 (c) Frederic Weisbecker <fweisbec@gmail.com>
-
-This script parses a trace provided by the function tracer in
-kernel/trace/trace_functions.c
-The resulted trace is processed into a tree to produce a more human
-view of the call stack by drawing textual but hierarchical tree of
-calls. Only the functions's names and the call time are provided.
-
-Usage:
- Be sure that you have CONFIG_FUNCTION_TRACER
- # mount -t tracefs nodev /sys/kernel/tracing
- # echo function > /sys/kernel/tracing/current_tracer
- $ cat /sys/kernel/tracing/trace_pipe > ~/raw_trace_func
- Wait some times but not too much, the script is a bit slow.
- Break the pipe (Ctrl + Z)
- $ scripts/tracing/draw_functrace.py < ~/raw_trace_func > draw_functrace
- Then you have your drawn trace in draw_functrace
-"""
-
-
-import sys, re
-
-class CallTree:
- """ This class provides a tree representation of the functions
- call stack. If a function has no parent in the kernel (interrupt,
- syscall, kernel thread...) then it is attached to a virtual parent
- called ROOT.
- """
- ROOT = None
-
- def __init__(self, func, time = None, parent = None):
- self._func = func
- self._time = time
- if parent is None:
- self._parent = CallTree.ROOT
- else:
- self._parent = parent
- self._children = []
-
- def calls(self, func, calltime):
- """ If a function calls another one, call this method to insert it
- into the tree at the appropriate place.
- @return: A reference to the newly created child node.
- """
- child = CallTree(func, calltime, self)
- self._children.append(child)
- return child
-
- def getParent(self, func):
- """ Retrieve the last parent of the current node that
- has the name given by func. If this function is not
- on a parent, then create it as new child of root
- @return: A reference to the parent.
- """
- tree = self
- while tree != CallTree.ROOT and tree._func != func:
- tree = tree._parent
- if tree == CallTree.ROOT:
- child = CallTree.ROOT.calls(func, None)
- return child
- return tree
-
- def __repr__(self):
- return self.__toString("", True)
-
- def __toString(self, branch, lastChild):
- if self._time is not None:
- s = "%s----%s (%s)\n" % (branch, self._func, self._time)
- else:
- s = "%s----%s\n" % (branch, self._func)
-
- i = 0
- if lastChild:
- branch = branch[:-1] + " "
- while i < len(self._children):
- if i != len(self._children) - 1:
- s += "%s" % self._children[i].__toString(branch +\
- " |", False)
- else:
- s += "%s" % self._children[i].__toString(branch +\
- " |", True)
- i += 1
- return s
-
-class BrokenLineException(Exception):
- """If the last line is not complete because of the pipe breakage,
- we want to stop the processing and ignore this line.
- """
- pass
-
-class CommentLineException(Exception):
- """ If the line is a comment (as in the beginning of the trace file),
- just ignore it.
- """
- pass
-
-
-def parseLine(line):
- line = line.strip()
- if line.startswith("#"):
- raise CommentLineException
- m = re.match("[^]]+?\\] +([a-z.]+) +([0-9.]+): (\\w+) <-(\\w+)", line)
- if m is None:
- raise BrokenLineException
- return (m.group(2), m.group(3), m.group(4))
-
-
-def main():
- CallTree.ROOT = CallTree("Root (Nowhere)", None, None)
- tree = CallTree.ROOT
-
- for line in sys.stdin:
- try:
- calltime, callee, caller = parseLine(line)
- except BrokenLineException:
- break
- except CommentLineException:
- continue
- tree = tree.getParent(caller)
- tree = tree.calls(callee, calltime)
-
- print(CallTree.ROOT)
-
-if __name__ == "__main__":
- main()
diff --git a/scripts/verify_builtin_ranges.awk b/scripts/verify_builtin_ranges.awk
new file mode 100755
index 000000000000..0de7ed521601
--- /dev/null
+++ b/scripts/verify_builtin_ranges.awk
@@ -0,0 +1,370 @@
+#!/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 <kris.van.hees@oracle.com>
+#
+# Usage: verify_builtin_ranges.awk modules.builtin.ranges System.map \
+# modules.builtin vmlinux.map vmlinux.o.map
+#
+
+# 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 = 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" \
+ " For kernels built with O=<objdir>, cd to <objdir>\n" \
+ " and execute this script as ./source/scripts/..." \
+ >"/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) {
+ print "Syntax: verify_builtin_ranges.awk <ranges-file> <system-map>\n" \
+ " <builtin-file> <vmlinux-map> <vmlinux-o-map>\n" \
+ >"/dev/stderr";
+ total = 0;
+ exit(1);
+ }
+}
+
+# (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 ~ /:\(/ {
+ if (/\.a\(/ && !/ vmlinux\.a\(/)
+ next;
+
+ 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 && /^\./ {
+ # 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 (in vmlinux.map).
+ if (ARGIND == 4 && $2 ~ /^0x0+$/)
+ 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;
+}
+
+# Objects linked in from static libraries are ignored.
+# If the object is vmlinux.o, we need to consult vmlinux.o.map for per-object
+# symbol information
+#
+ARGIND == 4 && /^ [^ ]/ && NF == 4 {
+ if ($4 ~ /\.a\(/)
+ next;
+
+ 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 " mod " (should NOT be)";
+ mismatches++;
+ } else {
+ print $2 " in " mod " (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;
+
+ if (mismatches || missing)
+ exit(1);
+ }
+}
diff --git a/scripts/xz_wrap.sh b/scripts/xz_wrap.sh
index d06baf626abe..f19369687030 100755
--- a/scripts/xz_wrap.sh
+++ b/scripts/xz_wrap.sh
@@ -1,22 +1,162 @@
#!/bin/sh
+# SPDX-License-Identifier: 0BSD
#
# This is a wrapper for xz to compress the kernel image using appropriate
# compression options depending on the architecture.
#
# Author: Lasse Collin <lasse.collin@tukaani.org>
+
+# This has specialized settings for the following archs. However,
+# XZ-compressed kernel isn't currently supported on every listed arch.
#
-# This file has been put into the public domain.
-# You can do whatever you want with this file.
-#
+# Arch Align Notes
+# arm 2/4 ARM and ARM-Thumb2
+# arm64 4
+# csky 2
+# loongarch 4
+# mips 2/4 MicroMIPS is 2-byte aligned
+# parisc 4
+# powerpc 4 Uses its own wrapper for compressors instead of this.
+# riscv 2/4
+# s390 2
+# sh 2
+# sparc 4
+# x86 1
+
+# A few archs use 2-byte or 4-byte aligned instructions depending on
+# the kernel config. This function is used to check if the relevant
+# config option is set to "y".
+is_enabled()
+{
+ grep -q "^$1=y$" include/config/auto.conf
+}
+
+# XZ_VERSION is needed to disable features that aren't available in
+# old XZ Utils versions.
+XZ_VERSION=$($XZ --robot --version) || exit
+XZ_VERSION=$(printf '%s\n' "$XZ_VERSION" | sed -n 's/^XZ_VERSION=//p')
+# Assume that no BCJ filter is available.
BCJ=
-LZMA2OPTS=
+# Set the instruction alignment to 1, 2, or 4 bytes.
+#
+# Set the BCJ filter if one is available.
+# It must match the #ifdef usage in lib/decompress_unxz.c.
case $SRCARCH in
- x86) BCJ=--x86 ;;
- powerpc) BCJ=--powerpc ;;
- arm) BCJ=--arm ;;
- sparc) BCJ=--sparc ;;
+ arm)
+ if is_enabled CONFIG_THUMB2_KERNEL; then
+ ALIGN=2
+ BCJ=--armthumb
+ else
+ ALIGN=4
+ BCJ=--arm
+ fi
+ ;;
+
+ arm64)
+ ALIGN=4
+
+ # ARM64 filter was added in XZ Utils 5.4.0.
+ if [ "$XZ_VERSION" -ge 50040002 ]; then
+ BCJ=--arm64
+ else
+ echo "$0: Upgrading to xz >= 5.4.0" \
+ "would enable the ARM64 filter" \
+ "for better compression" >&2
+ fi
+ ;;
+
+ csky)
+ ALIGN=2
+ ;;
+
+ loongarch)
+ ALIGN=4
+ ;;
+
+ mips)
+ if is_enabled CONFIG_CPU_MICROMIPS; then
+ ALIGN=2
+ else
+ ALIGN=4
+ fi
+ ;;
+
+ parisc)
+ ALIGN=4
+ ;;
+
+ powerpc)
+ ALIGN=4
+
+ # The filter is only for big endian instruction encoding.
+ if is_enabled CONFIG_CPU_BIG_ENDIAN; then
+ BCJ=--powerpc
+ fi
+ ;;
+
+ riscv)
+ if is_enabled CONFIG_RISCV_ISA_C; then
+ ALIGN=2
+ else
+ ALIGN=4
+ fi
+
+ # RISC-V filter was added in XZ Utils 5.6.0.
+ if [ "$XZ_VERSION" -ge 50060002 ]; then
+ BCJ=--riscv
+ else
+ echo "$0: Upgrading to xz >= 5.6.0" \
+ "would enable the RISC-V filter" \
+ "for better compression" >&2
+ fi
+ ;;
+
+ s390)
+ ALIGN=2
+ ;;
+
+ sh)
+ ALIGN=2
+ ;;
+
+ sparc)
+ ALIGN=4
+ BCJ=--sparc
+ ;;
+
+ x86)
+ ALIGN=1
+ BCJ=--x86
+ ;;
+
+ *)
+ echo "$0: Arch-specific tuning is missing for '$SRCARCH'" >&2
+
+ # Guess 2-byte-aligned instructions. Guessing too low
+ # should hurt less than guessing too high.
+ ALIGN=2
+ ;;
+esac
+
+# Select the LZMA2 options matching the instruction alignment.
+case $ALIGN in
+ 1) LZMA2OPTS= ;;
+ 2) LZMA2OPTS=lp=1 ;;
+ 4) LZMA2OPTS=lp=2,lc=2 ;;
+ *) echo "$0: ALIGN wrong or missing" >&2; exit 1 ;;
esac
-exec $XZ --check=crc32 $BCJ --lzma2=$LZMA2OPTS,dict=32MiB
+# Use single-threaded mode because it compresses a little better
+# (and uses less RAM) than multithreaded mode.
+#
+# For the best compression, the dictionary size shouldn't be
+# smaller than the uncompressed kernel. 128 MiB dictionary
+# needs less than 1400 MiB of RAM in single-threaded mode.
+#
+# On the archs that use this script to compress the kernel,
+# decompression in the preboot code is done in single-call mode.
+# Thus the dictionary size doesn't affect the memory requirements
+# of the preboot decompressor at all.
+exec $XZ --check=crc32 --threads=1 $BCJ --lzma2=$LZMA2OPTS,dict=128MiB