summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-10-14 13:32:15 +0100
committerMark Brown <broonie@kernel.org>2026-10-14 13:32:15 +0100
commit2e292f8568f310eb9f8d19c0ba547633da9871f4 (patch)
tree3128553d4293bc5f5fb77bc7e5893b564e35019d /scripts
parent629ce8372527a66aaa92d11fd4c304032b0b48c8 (diff)
parent56a62bca21c960b1222e39e184e550bbf1c9683a (diff)
downloadlinux-next-2e292f8568f310eb9f8d19c0ba547633da9871f4.tar.gz
linux-next-2e292f8568f310eb9f8d19c0ba547633da9871f4.zip
Merge branch 'kbuild-for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/kbuild/linux.git
# Conflicts: # init/Kconfig # scripts/remove-stale-files
Diffstat (limited to 'scripts')
-rw-r--r--scripts/.gitignore1
-rw-r--r--scripts/Kconfig.include29
-rw-r--r--scripts/Kconfig.toolchain277
-rw-r--r--scripts/Makefile8
-rw-r--r--scripts/Makefile.build21
-rw-r--r--scripts/Makefile.gcc-plugins4
-rw-r--r--scripts/Makefile.lib4
-rw-r--r--scripts/Makefile.package2
-rw-r--r--scripts/Makefile.vmlinux20
-rw-r--r--scripts/Makefile.vmlinux_o2
-rw-r--r--scripts/Makefile.warn28
-rwxr-xr-xscripts/check-function-names.sh3
-rwxr-xr-xscripts/checkkconfigsymbols.py2
-rwxr-xr-xscripts/clang-tools/run-clang-tools.py1
-rwxr-xr-xscripts/container86
-rw-r--r--scripts/elf-parse.c103
-rw-r--r--scripts/elf-parse.h54
-rw-r--r--scripts/gen_init_cpio.c782
-rwxr-xr-xscripts/gen_initramfs.sh250
-rwxr-xr-xscripts/jobserver-exec3
-rw-r--r--scripts/kallsyms-sysmap.c248
-rw-r--r--scripts/kallsyms.c379
-rw-r--r--scripts/kallsyms.h44
-rw-r--r--scripts/kconfig/conf.c19
-rw-r--r--scripts/kconfig/confdata.c6
-rwxr-xr-xscripts/kconfig/kconfig-sym-check.pl2
-rw-r--r--scripts/kconfig/lexer.l3
-rw-r--r--scripts/kconfig/lkc.h2
-rw-r--r--scripts/kconfig/lkc_proto.h1
-rw-r--r--scripts/kconfig/lxdialog/textbox.c2
-rw-r--r--scripts/kconfig/menu.c66
-rw-r--r--scripts/kconfig/parser.y8
-rwxr-xr-xscripts/kconfig/streamline_config.pl20
-rw-r--r--scripts/kconfig/symbol.c106
-rw-r--r--scripts/kconfig/tests/conftest.py27
-rw-r--r--scripts/kconfig/tests/def_type/Kconfig28
-rw-r--r--scripts/kconfig/tests/def_type/__init__.py18
-rw-r--r--scripts/kconfig/tests/def_type/expected_guard_n9
-rw-r--r--scripts/kconfig/tests/def_type/expected_guard_y11
-rw-r--r--scripts/kconfig/tests/def_type/guard_n.config1
-rw-r--r--scripts/kconfig/tests/def_type/guard_y.config1
-rw-r--r--scripts/kconfig/tests/err_num_bounds/Kconfig79
-rw-r--r--scripts/kconfig/tests/err_num_bounds/__init__.py12
-rw-r--r--scripts/kconfig/tests/err_num_bounds/expected_stderr10
-rw-r--r--scripts/kconfig/tests/err_num_mismatch/Kconfig38
-rw-r--r--scripts/kconfig/tests/err_num_mismatch/__init__.py9
-rw-r--r--scripts/kconfig/tests/err_num_mismatch/expected_stderr4
-rw-r--r--scripts/kconfig/tests/err_num_non_numeric_ref/Kconfig75
-rw-r--r--scripts/kconfig/tests/err_num_non_numeric_ref/__init__.py9
-rw-r--r--scripts/kconfig/tests/err_num_non_numeric_ref/expected_stderr16
-rw-r--r--scripts/kconfig/tests/err_recursive_dep/Kconfig24
-rw-r--r--scripts/kconfig/tests/err_recursive_dep/expected_stderr13
-rw-r--r--scripts/kconfig/tests/randconfig_probability/Kconfig12
-rw-r--r--scripts/kconfig/tests/randconfig_probability/__init__.py130
-rw-r--r--scripts/kconfig/tests/savedefconfig_range/Kconfig67
-rw-r--r--scripts/kconfig/tests/savedefconfig_range/__init__.py8
-rw-r--r--scripts/kconfig/tests/savedefconfig_range/config8
-rw-r--r--scripts/kconfig/tests/savedefconfig_range/expected_defconfig0
-rw-r--r--scripts/kconfig/tests/warn_changed_input/Kconfig5
-rw-r--r--scripts/kconfig/tests/warn_changed_input/__init__.py21
-rw-r--r--scripts/kconfig/tests/warn_changed_input/expected_config1
-rw-r--r--scripts/kconfig/tests/warn_num_bounds/Kconfig24
-rw-r--r--scripts/kconfig/tests/warn_num_bounds/__init__.py25
-rw-r--r--scripts/kconfig/tests/warn_num_bounds/config7
-rw-r--r--scripts/kconfig/tests/warn_num_bounds/expected_config11
-rw-r--r--scripts/kconfig/tests/warn_num_bounds/expected_config_stderr3
-rw-r--r--scripts/kconfig/tests/warn_num_bounds/expected_frontend_config11
-rw-r--r--scripts/kconfig/tests/warn_num_bounds/expected_frontend_stderr0
-rwxr-xr-xscripts/link-vmlinux.sh24
-rwxr-xr-xscripts/make_fit.py3
-rwxr-xr-xscripts/mkcompile_h2
-rwxr-xr-xscripts/mksysmap94
-rwxr-xr-xscripts/remove-stale-files2
-rw-r--r--scripts/sbom/tests/cmd_graph/test_savedcmd_parser.py2
-rwxr-xr-xscripts/setlocalversion3
-rw-r--r--scripts/sorttable.c15
-rwxr-xr-xscripts/ver_linux1
77 files changed, 3099 insertions, 350 deletions
diff --git a/scripts/.gitignore b/scripts/.gitignore
index 4215c2208f7e..700456543910 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
/asn1_compiler
+/gen_init_cpio
/gen_packed_field_checks
/generate_rust_target
/insert-sys-cert
diff --git a/scripts/Kconfig.include b/scripts/Kconfig.include
index fc10671c297c..a7d9201acfcc 100644
--- a/scripts/Kconfig.include
+++ b/scripts/Kconfig.include
@@ -23,13 +23,33 @@ success = $(if-success,$(1),y,n)
# Return n if <command> exits with 0, y otherwise
failure = $(if-success,$(1),n,y)
+# $(cc-probe,<flag>)
+# The command $(cc-option) and $(cc-option-str) test <flag> with. Not meant
+# to be called directly; use one of those two instead.
+cc-probe = trap "rm -rf .tmp_$$" EXIT; mkdir .tmp_$$; $(CC) -Werror $(CLANG_FLAGS) $(shell,echo $(1) | sed "s;^-Wno-;-W;") -c -x c /dev/null -o .tmp_$$/tmp.o
+
# $(cc-option,<flag>)
# Return y if the compiler supports <flag>, n otherwise
-cc-option = $(success,trap "rm -rf .tmp_$$" EXIT; mkdir .tmp_$$; $(CC) -Werror $(CLANG_FLAGS) $(1) -c -x c /dev/null -o .tmp_$$/tmp.o)
+cc-option = $(success,$(cc-probe,$(1)))
+
+# $(cc-option-str,<flag>[,<fallback>])
+# Return <flag> if the compiler supports it, <fallback> otherwise.
+# <fallback> defaults to an empty string and is returned untested.
+cc-option-str = $(if-success,$(cc-probe,$(1)),$(1),$(2))
+
+# $(ld-probe,<flag>)
+# The command $(ld-option) and $(ld-option-str) test <flag> with. Not meant
+# to be called directly; use one of those two instead.
+ld-probe = $(LD) -v $(1)
# $(ld-option,<flag>)
# Return y if the linker supports <flag>, n otherwise
-ld-option = $(success,$(LD) -v $(1))
+ld-option = $(success,$(ld-probe,$(1)))
+
+# $(ld-option-str,<flag>[,<fallback>])
+# Return <flag> if the linker supports it, <fallback> otherwise.
+# <fallback> defaults to an empty string and is returned untested.
+ld-option-str = $(if-success,$(ld-probe,$(1)),$(1),$(2))
# $(as-instr,<instr>)
# Return y if the assembler supports <instr>, n otherwise
@@ -61,9 +81,8 @@ ld-version := $(shell,set -- $(ld-info) && echo $2)
# machine bit flags
# $(m32-flag): -m32 if the compiler supports it, or an empty string otherwise.
# $(m64-flag): -m64 if the compiler supports it, or an empty string otherwise.
-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)
+m32-flag := $(cc-option-str,-m32)
+m64-flag := $(cc-option-str,-m64)
# Test whether the compiler can link userspace applications
cc_can_link_user = $(success,$(srctree)/scripts/cc-can-link.sh $(CC) $(CLANG_FLAGS) $(USERCFLAGS) $(USERLDFLAGS) $(1))
diff --git a/scripts/Kconfig.toolchain b/scripts/Kconfig.toolchain
new file mode 100644
index 000000000000..b0b9a663ddbe
--- /dev/null
+++ b/scripts/Kconfig.toolchain
@@ -0,0 +1,277 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config CC_VERSION_TEXT
+ string
+ default "$(CC_VERSION_TEXT)"
+ help
+ This is used in unclear ways:
+
+ - Re-run Kconfig when the compiler is updated
+ The 'default' property references the environment variable,
+ CC_VERSION_TEXT so it is recorded in include/config/auto.conf.cmd.
+ When the compiler is updated, Kconfig will be invoked.
+
+ - Ensure full rebuild when the compiler is updated
+ include/linux/compiler-version.h contains this option in the comment
+ line so fixdep adds include/config/CC_VERSION_TEXT into the
+ auto-generated dependency. When the compiler is updated, syncconfig
+ will touch it and then every file will be rebuilt.
+
+config CC_IS_GCC
+ def_bool $(success,test "$(cc-name)" = GCC)
+
+config GCC_VERSION
+ int
+ default $(cc-version) if CC_IS_GCC
+ default 0
+
+config CC_IS_CLANG
+ def_bool $(success,test "$(cc-name)" = Clang)
+
+config CLANG_VERSION
+ int
+ default $(cc-version) if CC_IS_CLANG
+ default 0
+
+config AS_IS_GNU
+ def_bool $(success,test "$(as-name)" = GNU)
+
+config AS_IS_LLVM
+ def_bool $(success,test "$(as-name)" = LLVM)
+
+config AS_VERSION
+ int
+ # Use clang version if this is the integrated assembler
+ default CLANG_VERSION if AS_IS_LLVM
+ default $(as-version)
+
+config LD_IS_BFD
+ def_bool $(success,test "$(ld-name)" = BFD)
+
+config LD_VERSION
+ int
+ default $(ld-version) if LD_IS_BFD
+ default 0
+
+config LD_IS_LLD
+ def_bool $(success,test "$(ld-name)" = LLD)
+
+config LLD_VERSION
+ int
+ default $(ld-version) if LD_IS_LLD
+ default 0
+
+config RUSTC_VERSION
+ int
+ default $(rustc-version)
+ help
+ It does not depend on `RUST` since that one may need to use the version
+ in a `depends on`.
+
+config RUST_IS_AVAILABLE
+ def_bool $(success,$(srctree)/scripts/rust_is_available.sh)
+ help
+ This shows whether a suitable Rust toolchain is available (found).
+
+ Please see Documentation/rust/quick-start.rst for instructions on how
+ to satisfy the build requirements of Rust support.
+
+ In particular, the Makefile target 'rustavailable' is useful to check
+ why the Rust toolchain is not being detected.
+
+config RUSTC_LLVM_VERSION
+ int
+ default $(rustc-llvm-version)
+
+config RUSTC_LLVM_MAJOR_VERSION
+ int
+ default $(shell,expr $(rustc-llvm-version) / 10000)
+
+config RUSTC_CLANG_LLVM_COMPATIBLE
+ bool
+ default y if CC_IS_CLANG && RUSTC_LLVM_MAJOR_VERSION = $(shell,expr $(cc-version) / 10000)
+ help
+ This indicates whether Rust and Clang use LLVM of the same major
+ version.
+
+ Operations involving handling LLVM IR or bitcode (e.g. cross-language
+ LTO) require the same LLVM major version to work properly. For best
+ compatibility it is recommended that the exact same LLVM is used.
+
+config ARCH_HAS_CC_CAN_LINK
+ bool
+
+config CC_CAN_LINK
+ bool
+ default ARCH_CC_CAN_LINK if ARCH_HAS_CC_CAN_LINK
+ default $(cc_can_link_user,$(m64-flag)) if 64BIT
+ default $(cc_can_link_user,$(m32-flag))
+
+# Fixed in GCC 14, 13.3, 12.4 and 11.5
+# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113921
+config GCC_ASM_GOTO_OUTPUT_BROKEN
+ bool
+ depends on CC_IS_GCC
+ default y if GCC_VERSION < 110500
+ default y if GCC_VERSION >= 120000 && GCC_VERSION < 120400
+ default y if GCC_VERSION >= 130000 && GCC_VERSION < 130300
+
+config CC_HAS_ASM_GOTO_OUTPUT
+ def_bool y
+ depends on !GCC_ASM_GOTO_OUTPUT_BROKEN
+ depends on $(success,echo 'int foo(int x) { asm goto ("": "=r"(x) ::: bar); return x; bar: return 0; }' | $(CC) -x c - -c -o /dev/null)
+
+config CC_HAS_ASM_GOTO_TIED_OUTPUT
+ depends on CC_HAS_ASM_GOTO_OUTPUT
+ # Detect buggy gcc and clang, fixed in gcc-11 clang-14.
+ def_bool $(success,echo 'int foo(int *x) { asm goto (".long (%l[bar]) - .": "+m"(*x) ::: bar); return *x; bar: return 0; }' | $CC -x c - -c -o /dev/null)
+
+config TOOLS_SUPPORT_RELR
+ def_bool $(success,env "CC=$(CC)" "LD=$(LD)" "NM=$(NM)" "OBJCOPY=$(OBJCOPY)" $(srctree)/scripts/tools-support-relr.sh)
+
+config CC_HAS_ASM_INLINE
+ def_bool $(success,echo 'void foo(void) { asm inline (""); }' | $(CC) -x c - -c -o /dev/null)
+
+config CC_HAS_ASSUME
+ bool
+ # clang needs to be at least 19.1.0 since the meaning of the assume
+ # attribute changed:
+ # https://github.com/llvm/llvm-project/commit/c44fa3e8a9a44c2e9a575768a3c185354b9f6c17
+ default y if CC_IS_CLANG && CLANG_VERSION >= 190100
+ # supported since gcc 13.1.0
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106654
+ default y if CC_IS_GCC && GCC_VERSION >= 130100
+
+config CC_HAS_NO_PROFILE_FN_ATTR
+ def_bool $(success,echo '__attribute__((no_profile_instrument_function)) int x();' | $(CC) -x c - -c -o /dev/null -Werror)
+
+config CC_OPT_ZERO_INIT_PADDING_BITS
+ def_string "$(cc-option-str,-fzero-init-padding-bits=all)"
+
+config CC_OPT_NO_ADDRSIG
+ def_string "$(cc-option-str,-fno-addrsig)"
+
+config CC_OPT_DIAGNOSTICS_SHOW_CONTEXT
+ def_string "$(cc-option-str,-fdiagnostics-show-context=2)"
+
+config CC_OPT_DIAGNOSTICS_SHOW_INLINING_CHAIN
+ def_string "$(cc-option-str,-fdiagnostics-show-inlining-chain)"
+
+config CC_OPT_STRICT_FLEX_ARRAYS
+ def_string "$(cc-option-str,-fstrict-flex-arrays=3)"
+
+# gcc-10 renamed --param=allow-store-data-races=0 to -fno-allow-store-data-races
+config CC_OPT_ALLOW_STORE_DATA_RACES_PARAM
+ def_string "$(cc-option-str,--param=allow-store-data-races=0)"
+
+config CC_OPT_NO_ALLOW_STORE_DATA_RACES
+ def_string "$(cc-option-str,-fno-allow-store-data-races)"
+
+config CC_HAS_MNOP_MCOUNT
+ def_bool $(cc-option,-mnop-mcount)
+
+# s390-linux-gnu-gcc did not support -mfentry until gcc-9.
+config CC_HAS_MFENTRY
+ def_bool $(cc-option,-mfentry)
+
+config LD_OPT_FORCE_GROUP_ALLOCATION
+ def_string "$(ld-option-str,--force-group-allocation)"
+
+config LD_OPT_NO_WARN_RWX_SEGMENTS
+ def_string "$(ld-option-str,--no-warn-rwx-segments)"
+
+config CC_OPT_WNO_ADDRESS_OF_PACKED_MEMBER
+ def_string "$(cc-option-str,-Wno-address-of-packed-member)"
+
+config CC_OPT_WNO_FORMAT_OVERFLOW_NON_KPRINTF
+ def_string "$(cc-option-str,-Wno-format-overflow-non-kprintf)"
+
+config CC_OPT_WNO_FORMAT_TRUNCATION_NON_KPRINTF
+ def_string "$(cc-option-str,-Wno-format-truncation-non-kprintf)"
+
+config CC_OPT_WNO_DEFAULT_CONST_INIT_UNSAFE
+ def_string "$(cc-option-str,-Wno-default-const-init-unsafe)"
+
+config CC_OPT_WNO_DANGLING_POINTER
+ def_string "$(cc-option-str,-Wno-dangling-pointer)"
+
+config CC_OPT_WVLA_LARGER_THAN
+ def_string "$(cc-option-str,-Wvla-larger-than=1)"
+
+config CC_HAS_WSTRINGOP_OVERFLOW
+ def_bool $(cc-option,-Wstringop-overflow)
+
+config CC_OPT_WNO_UNTERMINATED_STRING_INITIALIZATION
+ def_string "$(cc-option-str,-Wno-unterminated-string-initialization)"
+
+config CC_OPT_WERROR_DESIGNATED_INIT
+ def_string "$(cc-option-str,-Werror=designated-init)"
+
+config CC_OPT_WENUM_CONVERSION
+ def_string "$(cc-option-str,-Wenum-conversion)"
+
+config CC_OPT_WNO_PACKED_NOT_ALIGNED
+ def_string "$(cc-option-str,-Wno-packed-not-aligned)"
+
+config CC_OPT_WNO_FORMAT_OVERFLOW
+ def_string "$(cc-option-str,-Wno-format-overflow)"
+
+config CC_OPT_WNO_STRINGOP_TRUNCATION
+ def_string "$(cc-option-str,-Wno-stringop-truncation)"
+
+config CC_HAS_COUNTED_BY
+ bool
+ # clang needs to be at least 20.1.0 to avoid potential crashes
+ # when building structures that contain __counted_by
+ # https://github.com/ClangBuiltLinux/linux/issues/2114
+ # https://github.com/llvm/llvm-project/commit/160fb1121cdf703c3ef5e61fb26c5659eb581489
+ default y if CC_IS_CLANG && CLANG_VERSION >= 200100
+ # supported since gcc 15.1.0
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108896
+ default y if CC_IS_GCC && GCC_VERSION >= 150100
+
+config CC_HAS_COUNTED_BY_PTR
+ bool
+ # supported since clang 22
+ default y if CC_IS_CLANG && CLANG_VERSION >= 220100
+ # supported since gcc 16.0.0
+ default y if CC_IS_GCC && GCC_VERSION >= 160000
+
+config CC_HAS_BROKEN_COUNTED_BY_REF
+ bool
+ # https://github.com/llvm/llvm-project/issues/182575
+ default y if CC_IS_CLANG && CLANG_VERSION < 220100
+
+config CC_HAS_ALLOC_TOKEN
+ def_bool $(cc-option,-falloc-token-max=123)
+
+config CC_HAS_MULTIDIMENSIONAL_NONSTRING
+ def_bool $(success,echo 'char tag[][4] __attribute__((__nonstring__)) = { };' | $(CC) $(CLANG_FLAGS) -x c - -c -o /dev/null -Werror)
+
+config CC_OPT_INLINE_MEMSET
+ string
+ default "-finline-stringops=memset" if $(cc-option,-finline-stringops=memset)
+ default "-mllvm -max-store-memset=4294967294" if $(cc-option,-mllvm -max-store-memset=4294967294)
+
+config LD_CAN_USE_KEEP_IN_OVERLAY
+ # ld.lld prior to 21.0.0 did not support KEEP within an overlay description
+ # https://github.com/llvm/llvm-project/pull/130661
+ def_bool LD_IS_BFD || LLD_VERSION >= 210000
+
+config RUSTC_HAS_SPAN_FILE
+ def_bool RUSTC_VERSION >= 108800
+
+config RUSTC_HAS_UNNECESSARY_TRANSMUTES
+ def_bool RUSTC_VERSION >= 108800
+
+config RUSTC_HAS_FILE_WITH_NUL
+ def_bool RUSTC_VERSION >= 108900
+
+config RUSTC_HAS_FILE_AS_C_STR
+ def_bool RUSTC_VERSION >= 109100
+
+config RUSTC_HAS_SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS
+ def_bool RUSTC_VERSION >= 109800
+
+config PAHOLE_VERSION
+ int
+ default "$(PAHOLE_VERSION)"
diff --git a/scripts/Makefile b/scripts/Makefile
index 3434a82a119f..d7fbb7ca098b 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -3,7 +3,7 @@
# scripts contains sources for various helper programs used throughout
# the kernel for the build process.
-hostprogs-always-$(CONFIG_KALLSYMS) += kallsyms
+hostprogs-always-y += kallsyms
hostprogs-always-$(BUILD_C_RECORDMCOUNT) += recordmcount
hostprogs-always-$(CONFIG_BUILDTIME_TABLE_SORT) += sorttable
hostprogs-always-$(CONFIG_ASN1) += asn1_compiler
@@ -13,6 +13,7 @@ hostprogs-always-$(CONFIG_RUST_KERNEL_DOCTESTS) += rustdoc_test_builder
hostprogs-always-$(CONFIG_RUST_KERNEL_DOCTESTS) += rustdoc_test_gen
hostprogs-always-$(CONFIG_TRACEPOINTS) += tracepoint-update
+kallsyms-objs := kallsyms.o kallsyms-sysmap.o elf-parse.o
sorttable-objs := sorttable.o elf-parse.o
tracepoint-update-objs := tracepoint-update.o elf-parse.o
@@ -30,6 +31,8 @@ rustdoc_test_builder-rust := y
rustdoc_test_gen-rust := y
HOSTCFLAGS_tracepoint-update.o = -I$(srctree)/tools/include
+HOSTCFLAGS_kallsyms.o = -I$(srctree)/tools/include
+HOSTCFLAGS_kallsyms-sysmap.o = -I$(srctree)/tools/include
HOSTCFLAGS_elf-parse.o = -I$(srctree)/tools/include
HOSTCFLAGS_sorttable.o = -I$(srctree)/tools/include
HOSTLDLIBS_sorttable = -lpthread
@@ -53,6 +56,9 @@ ifdef CONFIG_BUILDTIME_MCOUNT_SORT
HOSTCFLAGS_sorttable.o += -DMCOUNT_SORT_ENABLED
endif
+hostprogs += gen_init_cpio
+always-$(CONFIG_BLK_DEV_INITRD) += gen_init_cpio
+
# The following programs are only built on demand
hostprogs += unifdef gen_packed_field_checks
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 4349108e75e1..6127feb970c6 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -306,6 +306,27 @@ quiet_cmd_cc_lst_c = MKLST $@
$(obj)/%.lst: $(obj)/%.c FORCE
$(call if_changed_dep,cc_lst_c)
+# Compile C headers (.h) for header check
+# ---------------------------------------------------------------------------
+
+# Check a header is self-contained, has header guards, and passes kernel-doc.
+#
+# This is for 'make HEADER_CHECK=(headers|dirs) headercheck'. Not to be confused
+# with CONFIG_UAPI_HEADER_TEST.
+#
+# Please do *not* plug %.header-check into any configurable targets, unless you
+# also figure out a way to completely hide the %.header-check artefacts.
+
+quiet_cmd_header_check = HDRCHK $(patsubst %.header-check,%.h,$@)
+ cmd_header_check = \
+ $(CC) $(c_flags) -fsyntax-only -x c /dev/null -include $< -include $<; \
+ PYTHONDONTWRITEBYTECODE=1 $(PYTHON3) $(KERNELDOC) -none $(KDOCFLAGS) \
+ $(if $(findstring e, $(KBUILD_EXTRA_WARN))$(CONFIG_WERROR),-Werror) $<; \
+ touch $@
+
+$(obj)/%.header-check: $(obj)/%.h FORCE
+ $(call if_changed_dep,header_check)
+
# Compile Rust sources (.rs)
# ---------------------------------------------------------------------------
diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
index b0e1423b09c2..9b9899580571 100644
--- a/scripts/Makefile.gcc-plugins
+++ b/scripts/Makefile.gcc-plugins
@@ -8,6 +8,10 @@ ifdef CONFIG_GCC_PLUGIN_LATENT_ENTROPY
endif
export DISABLE_LATENT_ENTROPY_PLUGIN
+# Disable recording GCC options in debug info when building external modules to
+# avoid leaking absolute host build paths
+gcc-plugin-cflags-$(if $(KBUILD_EXTMOD),y) += -gno-record-gcc-switches
+
# All the plugin CFLAGS are collected here in case a build target needs to
# filter them out of the KBUILD_CFLAGS.
GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y)) -DGCC_PLUGINS
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 0a4fdd8bd975..19e96b30a122 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -333,11 +333,11 @@ quiet_cmd_ar = AR $@
quiet_cmd_objcopy = OBJCOPY $@
cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@
-# Gzip
+# Gzip, for the kernel image
# ---------------------------------------------------------------------------
quiet_cmd_gzip = GZIP $@
- cmd_gzip = cat $(real-prereqs) | $(KGZIP) -n -f -9 > $@
+ cmd_gzip = cat $(real-prereqs) | $(KPGZIP) -n -f -9 > $@
# Bzip2
# ---------------------------------------------------------------------------
diff --git a/scripts/Makefile.package b/scripts/Makefile.package
index 6d36786ba31c..948ad5b679d1 100644
--- a/scripts/Makefile.package
+++ b/scripts/Makefile.package
@@ -198,7 +198,7 @@ tar%-pkg: linux-$(KERNELRELEASE)-$(ARCH).tar.% FORCE
$(Q)$(MAKE) -f $(srctree)/Makefile INSTALL_MOD_PATH=$@/$(INSTALL_MOD_PATH) modules_install
quiet_cmd_cpio = CPIO $@
- cmd_cpio = $(CONFIG_SHELL) $(srctree)/usr/gen_initramfs.sh -o $@ $<
+ cmd_cpio = $(CONFIG_SHELL) $(srctree)/scripts/gen_initramfs.sh -o $@ $<
modules-$(KERNELRELEASE)-$(ARCH).cpio: .tmp_modules_cpio
$(call cmd,cpio)
diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
index fcae1e432d9a..95b523fe4f9e 100644
--- a/scripts/Makefile.vmlinux
+++ b/scripts/Makefile.vmlinux
@@ -89,11 +89,8 @@ remove-section-$(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS) += '.rel.*'
remove-symbols := -w --strip-unneeded-symbol='__mod_device_table__*'
-# To avoid warnings: "empty loadable segment detected at ..." from GNU objcopy,
-# it is necessary to remove the PT_LOAD flag from the segment.
quiet_cmd_strip_relocs = OBJCOPY $@
- cmd_strip_relocs = $(OBJCOPY) $(patsubst %,--set-section-flags %=noload,$(remove-section-y)) $< $@; \
- $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) $(remove-symbols) $@
+ cmd_strip_relocs = $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) $(remove-symbols) $< $@
targets += vmlinux
vmlinux: vmlinux.unstripped FORCE
@@ -102,21 +99,8 @@ vmlinux: vmlinux.unstripped FORCE
# modules.builtin.modinfo
# ---------------------------------------------------------------------------
-# .modinfo in vmlinux.unstripped is aligned to 8 bytes for compatibility with
-# tools that expect vmlinux to have sufficiently aligned sections but the
-# additional bytes used for padding .modinfo to satisfy this requirement break
-# certain versions of kmod with
-#
-# depmod: ERROR: kmod_builtin_iter_next: unexpected string without modname prefix
-#
-# Strip the trailing padding bytes after extracting .modinfo to comply with
-# what kmod expects to parse.
quiet_cmd_modules_builtin_modinfo = GEN $@
- cmd_modules_builtin_modinfo = $(cmd_objcopy); \
- sed -i 's/\x00\+$$/\x00/g' $@; \
- chmod -x $@
-
-OBJCOPYFLAGS_modules.builtin.modinfo := -j .modinfo -O binary
+ cmd_modules_builtin_modinfo = $(OBJCOPY) -O binary -j .modinfo --dump-section .modinfo=$@ $< /dev/null
targets += modules.builtin.modinfo
modules.builtin.modinfo: vmlinux.unstripped FORCE
diff --git a/scripts/Makefile.vmlinux_o b/scripts/Makefile.vmlinux_o
index 24a3a4fd271c..0b37bd5dc3a4 100644
--- a/scripts/Makefile.vmlinux_o
+++ b/scripts/Makefile.vmlinux_o
@@ -19,7 +19,7 @@ quiet_cmd_gen_initcalls_lds = GEN $@
.tmp_initcalls.lds: $(srctree)/scripts/generate_initcall_order.pl \
vmlinux.a $(KBUILD_VMLINUX_LIBS) FORCE
- $(call if_changed,gen_initcalls_lds)
+ +$(call if_changed,gen_initcalls_lds)
targets := .tmp_initcalls.lds
diff --git a/scripts/Makefile.warn b/scripts/Makefile.warn
index 35af7d6c6d18..556fa249ca07 100644
--- a/scripts/Makefile.warn
+++ b/scripts/Makefile.warn
@@ -17,7 +17,7 @@ KBUILD_CFLAGS += -Werror=strict-prototypes
KBUILD_CFLAGS += -Wno-format-security
KBUILD_CFLAGS += -Wno-trigraphs
KBUILD_CFLAGS += -Wno-frame-address
-KBUILD_CFLAGS += $(call cc-option, -Wno-address-of-packed-member)
+KBUILD_CFLAGS += $(CONFIG_CC_OPT_WNO_ADDRESS_OF_PACKED_MEMBER)
KBUILD_CFLAGS += -Wmissing-declarations
KBUILD_CFLAGS += -Wmissing-prototypes
@@ -30,8 +30,8 @@ KBUILD_CFLAGS-$(CONFIG_CC_NO_ARRAY_BOUNDS) += -Wno-array-bounds
ifdef CONFIG_CC_IS_CLANG
# 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-option, -Wno-format-overflow-non-kprintf)
-KBUILD_CFLAGS += $(call cc-option, -Wno-format-truncation-non-kprintf)
+KBUILD_CFLAGS += $(CONFIG_CC_OPT_WNO_FORMAT_OVERFLOW_NON_KPRINTF)
+KBUILD_CFLAGS += $(CONFIG_CC_OPT_WNO_FORMAT_TRUNCATION_NON_KPRINTF)
# Clang may emit a warning when a const variable, such as the dummy variables
# in typecheck(), or const member of an aggregate type are not initialized,
@@ -43,7 +43,7 @@ KBUILD_CFLAGS += $(call cc-option, -Wno-format-truncation-non-kprintf)
# disabled with this same switch, there should not be too much coverage lost
# because -Wuninitialized will still flag when an uninitialized const variable
# is used.
-KBUILD_CFLAGS += $(call cc-option, -Wno-default-const-init-unsafe)
+KBUILD_CFLAGS += $(CONFIG_CC_OPT_WNO_DEFAULT_CONST_INIT_UNSAFE)
else
# gcc inanely warns about local variables called 'main'
@@ -54,7 +54,7 @@ endif
KBUILD_CFLAGS += -Wno-type-limits
# These result in bogus false positives
-KBUILD_CFLAGS += $(call cc-option, -Wno-dangling-pointer)
+KBUILD_CFLAGS += $(CONFIG_CC_OPT_WNO_DANGLING_POINTER)
# Stack Variable Length Arrays (VLAs) must not be used in the kernel.
# Function array parameters should, however, be usable, but -Wvla will
@@ -62,7 +62,7 @@ KBUILD_CFLAGS += $(call cc-option, -Wno-dangling-pointer)
# types, so depend on GCC for now to keep stack VLAs out of the tree.
# https://github.com/llvm/llvm-project/issues/57098
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98217
-KBUILD_CFLAGS += $(call cc-option,-Wvla-larger-than=1)
+KBUILD_CFLAGS += $(CONFIG_CC_OPT_WVLA_LARGER_THAN)
# disable pointer signed / unsigned warnings in gcc 4.0
KBUILD_CFLAGS += -Wno-pointer-sign
@@ -73,11 +73,11 @@ KBUILD_CFLAGS += -Wno-pointer-sign
KBUILD_CFLAGS += -Wcast-function-type
# Currently, disable -Wstringop-overflow for GCC 11, globally.
-KBUILD_CFLAGS-$(CONFIG_CC_NO_STRINGOP_OVERFLOW) += $(call cc-option, -Wno-stringop-overflow)
-KBUILD_CFLAGS-$(CONFIG_CC_STRINGOP_OVERFLOW) += $(call cc-option, -Wstringop-overflow)
+KBUILD_CFLAGS-$(CONFIG_CC_NO_STRINGOP_OVERFLOW) += $(if $(CONFIG_CC_HAS_WSTRINGOP_OVERFLOW),-Wno-stringop-overflow)
+KBUILD_CFLAGS-$(CONFIG_CC_STRINGOP_OVERFLOW) += $(if $(CONFIG_CC_HAS_WSTRINGOP_OVERFLOW),-Wstringop-overflow)
# Currently, disable -Wunterminated-string-initialization as broken
-KBUILD_CFLAGS += $(call cc-option, -Wno-unterminated-string-initialization)
+KBUILD_CFLAGS += $(CONFIG_CC_OPT_WNO_UNTERMINATED_STRING_INITIALIZATION)
# The allocators already balk at large sizes, so silence the compiler
# warnings for bounds checks involving those possible values. While
@@ -100,10 +100,10 @@ KBUILD_CFLAGS += -Werror=date-time
KBUILD_CFLAGS += -Werror=incompatible-pointer-types
# Require designated initializers for all marked structures
-KBUILD_CFLAGS += $(call cc-option,-Werror=designated-init)
+KBUILD_CFLAGS += $(CONFIG_CC_OPT_WERROR_DESIGNATED_INIT)
# Warn if there is an enum types mismatch
-KBUILD_CFLAGS += $(call cc-option,-Wenum-conversion)
+KBUILD_CFLAGS += $(CONFIG_CC_OPT_WENUM_CONVERSION)
KBUILD_CFLAGS += -Wunused
@@ -125,12 +125,12 @@ else
# Suppress them by using -Wno... except for W=1.
KBUILD_CFLAGS += -Wno-unused-but-set-variable
KBUILD_CFLAGS += -Wno-unused-const-variable
-KBUILD_CFLAGS += $(call cc-option, -Wno-packed-not-aligned)
-KBUILD_CFLAGS += $(call cc-option, -Wno-format-overflow)
+KBUILD_CFLAGS += $(CONFIG_CC_OPT_WNO_PACKED_NOT_ALIGNED)
+KBUILD_CFLAGS += $(CONFIG_CC_OPT_WNO_FORMAT_OVERFLOW)
ifdef CONFIG_CC_IS_GCC
KBUILD_CFLAGS += -Wno-format-truncation
endif
-KBUILD_CFLAGS += $(call cc-option, -Wno-stringop-truncation)
+KBUILD_CFLAGS += $(CONFIG_CC_OPT_WNO_STRINGOP_TRUNCATION)
KBUILD_CFLAGS += -Wno-override-init # alias for -Wno-initializer-overrides in clang
diff --git a/scripts/check-function-names.sh b/scripts/check-function-names.sh
index 08071133e5a5..94883e690627 100755
--- a/scripts/check-function-names.sh
+++ b/scripts/check-function-names.sh
@@ -13,7 +13,8 @@ if [ ! -f "$objfile" ]; then
exit 1
fi
-bad_symbols=$(${NM:-nm} "$objfile" | awk '$2 ~ /^[TtWw]$/ {print $3}' | grep -E '^(startup|exit|split|unlikely|hot|unknown)(\.|$)')
+bad_symbols=$(${NM:-nm} -p "$objfile" | awk '$2 ~ /^[TtWw]$/ {print $3}' |
+ grep -E '^(startup|exit|split|unlikely|hot|unknown)(\.|$)')
if [ -n "$bad_symbols" ]; then
echo "$bad_symbols" | while read -r sym; do
diff --git a/scripts/checkkconfigsymbols.py b/scripts/checkkconfigsymbols.py
index 36c920e71313..9f7aed98d91c 100755
--- a/scripts/checkkconfigsymbols.py
+++ b/scripts/checkkconfigsymbols.py
@@ -245,7 +245,7 @@ def tree_is_dirty():
"""Return true if the current working tree is dirty (i.e., if any file has
been added, deleted, modified, renamed or copied but not committed)."""
stdout = execute(["git", "status", "--porcelain"])
- for line in stdout:
+ for line in stdout.splitlines():
if re.findall(r"[URMADC]{1}", line[:2]):
return True
return False
diff --git a/scripts/clang-tools/run-clang-tools.py b/scripts/clang-tools/run-clang-tools.py
index e78be82aa693..79d58b34e7ac 100755
--- a/scripts/clang-tools/run-clang-tools.py
+++ b/scripts/clang-tools/run-clang-tools.py
@@ -12,6 +12,7 @@ compile_commands.json.
import argparse
import json
import multiprocessing
+import os
import subprocess
import sys
diff --git a/scripts/container b/scripts/container
index b05333d8530b..a3a55180d6ed 100755
--- a/scripts/container
+++ b/scripts/container
@@ -1,17 +1,19 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-only
-# Copyright (C) 2025 Guillaume Tucker
+# Copyright (C) 2025-2026 Guillaume Tucker
"""Containerized builds"""
import abc
import argparse
+import dataclasses
import logging
import os
import pathlib
import shutil
import subprocess
import sys
+import tomllib
import uuid
@@ -20,10 +22,17 @@ class ContainerRuntime(abc.ABC):
name = None # Property defined in each implementation class
- def __init__(self, args, logger):
- self._uid = args.uid or os.getuid()
- self._gid = args.gid or args.uid or os.getgid()
- self._env_file = args.env_file
+ def __init__(self, args, config, logger):
+ def _first_not_none(*args):
+ return next((item for item in args if item is not None))
+
+ self._uid = _first_not_none(
+ args.uid, config.uid, os.getuid()
+ )
+ self._gid = _first_not_none(
+ args.gid, config.gid, args.uid, config.uid, os.getgid()
+ )
+ self._env_file = args.env_file or config.env_file
self._shell = args.shell
self._logger = logger
@@ -131,6 +140,42 @@ class Runtimes:
raise ValueError("no runtime found")
+@dataclasses.dataclass
+class Config:
+ """Container configuration"""
+ image: str = None
+ runtime: str = None
+ env_file: str = None
+ uid: int = None
+ gid: int = None
+
+ @classmethod
+ def from_toml(cls, config_file_path, profile_name):
+ """Create a config object from a TOML file"""
+ if not config_file_path:
+ config_file_path = '.container.toml'
+ if not os.path.exists(config_file_path):
+ return cls()
+ elif not os.path.exists(config_file_path):
+ raise ValueError(f"config file not found: {config_file_path}")
+ with open(config_file_path, 'rb') as config_file:
+ config = tomllib.load(config_file)
+ default = config.get('DEFAULT', {})
+ if not profile_name:
+ profile = {}
+ else:
+ profile = config.get(profile_name)
+ if profile is None:
+ raise ValueError(f"unknown profile: {profile_name}")
+ kwargs = {
+ name: type(value) for (name, type, value) in (
+ (op.name, op.type, profile.get(op.name, default.get(op.name)))
+ for op in dataclasses.fields(cls)
+ ) if value is not None
+ }
+ return cls(**kwargs)
+
+
def _get_logger(verbose):
"""Set up a logger with the appropriate level"""
logger = logging.getLogger('container')
@@ -147,13 +192,19 @@ def main(args):
"""Main entry point for the container tool"""
logger = _get_logger(args.verbose)
try:
- cls = Runtimes.get(args.runtime) if args.runtime else Runtimes.find()
+ config = Config.from_toml(args.config_file, args.config_profile)
+ runtime = args.runtime or config.runtime
+ cls = Runtimes.get(runtime) if runtime else Runtimes.find()
except ValueError as ex:
logger.error(ex)
return 1
logger.debug("runtime: %s", cls.name)
- logger.debug("image: %s", args.image)
- return cls(args, logger).run(args.image, args.cmd)
+ image = args.image or config.image
+ if not image:
+ logger.error("no image specified")
+ return 1
+ logger.debug("image: %s", image)
+ return cls(args, config, logger).run(image, args.cmd)
if __name__ == '__main__':
@@ -163,18 +214,28 @@ if __name__ == '__main__':
"https://docs.kernel.org/dev-tools/container.html"
)
parser.add_argument(
+ '-c', '--config-file',
+ help="Path to the config file. If not specified, the default is to "
+ "look for .container.toml in the current working directory."
+ )
+ parser.add_argument(
'-e', '--env-file',
help="Path to an environment file to load in the container."
)
parser.add_argument(
- '-g', '--gid',
+ '-g', '--gid', type=int,
help="Group ID to use inside the container."
)
parser.add_argument(
- '-i', '--image', required=True,
+ '-i', '--image',
help="Container image name."
)
parser.add_argument(
+ '-p', '--config-profile',
+ help="Profile section to use in the config file. This will override "
+ "any values defined in the DEFAULT section."
+ )
+ parser.add_argument(
'-r', '--runtime', choices=Runtimes.get_names(),
help="Container runtime name. If not specified, the first one found "
"on the system will be used i.e. Podman if present, otherwise Docker."
@@ -184,9 +245,10 @@ if __name__ == '__main__':
help="Run the container in an interactive shell."
)
parser.add_argument(
- '-u', '--uid',
+ '-u', '--uid', type=int,
help="User ID to use inside the container. If the -g option is not "
- "specified, the user ID will also be set as the group ID."
+ "specified and no group ID is defined in the configuration file, the "
+ "user ID will also be set as the group ID."
)
parser.add_argument(
'-v', '--verbose', action='store_true',
diff --git a/scripts/elf-parse.c b/scripts/elf-parse.c
index 99869ff91a8c..ce1c2a670244 100644
--- a/scripts/elf-parse.c
+++ b/scripts/elf-parse.c
@@ -17,15 +17,21 @@ struct elf_funcs elf_parser;
* Get the whole file as a programming convenience in order to avoid
* malloc+lseek+read+free of many pieces. If successful, then mmap
* avoids copying unused pieces; else just read the whole file.
- * Open for both read and write.
+ * Open for both read and write if writable is true, otherwise open
+ * read-only.
*/
-static void *map_file(char const *fname, size_t *size)
+static void *map_file(char const *fname, size_t *size, bool writable)
{
- int fd;
+ int fd, prot = PROT_READ, flags = MAP_PRIVATE;
struct stat sb;
void *addr = NULL;
- fd = open(fname, O_RDWR);
+ if (writable) {
+ prot |= PROT_WRITE;
+ flags = MAP_SHARED;
+ }
+
+ fd = open(fname, writable ? O_RDWR : O_RDONLY);
if (fd < 0) {
perror(fname);
return NULL;
@@ -39,7 +45,7 @@ static void *map_file(char const *fname, size_t *size)
goto out;
}
- addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ addr = mmap(0, sb.st_size, prot, flags, fd, 0);
if (addr == MAP_FAILED) {
fprintf(stderr, "Could not mmap file: %s\n", fname);
goto out;
@@ -57,6 +63,12 @@ static int elf_parse(const char *fname, void *addr, uint32_t types)
Elf_Ehdr *ehdr = addr;
uint16_t type;
+ if (memcmp(ELFMAG, ehdr->e32.e_ident, SELFMAG) != 0 ||
+ ehdr->e32.e_ident[EI_VERSION] != EV_CURRENT) {
+ fprintf(stderr, "unrecognized ELF file %s\n", fname);
+ return -1;
+ }
+
switch (ehdr->e32.e_ident[EI_DATA]) {
case ELFDATA2LSB:
elf_parser.r = rle;
@@ -78,12 +90,6 @@ static int elf_parse(const char *fname, void *addr, uint32_t types)
return -1;
}
- if (memcmp(ELFMAG, ehdr->e32.e_ident, SELFMAG) != 0 ||
- ehdr->e32.e_ident[EI_VERSION] != EV_CURRENT) {
- fprintf(stderr, "unrecognized ELF file %s\n", fname);
- return -1;
- }
-
type = elf_parser.r2(&ehdr->e32.e_type);
if (!((1 << type) & types)) {
fprintf(stderr, "Invalid ELF type file %s\n", fname);
@@ -103,7 +109,9 @@ static int elf_parse(const char *fname, void *addr, uint32_t types)
elf_parser.shdr_name = shdr32_name;
elf_parser.shdr_type = shdr32_type;
elf_parser.shdr_entsize = shdr32_entsize;
+ elf_parser.shdr_flags = shdr32_flags;
elf_parser.sym_type = sym32_type;
+ elf_parser.sym_bind = sym32_bind;
elf_parser.sym_name = sym32_name;
elf_parser.sym_value = sym32_value;
elf_parser.sym_shndx = sym32_shndx;
@@ -133,7 +141,9 @@ static int elf_parse(const char *fname, void *addr, uint32_t types)
elf_parser.shdr_name = shdr64_name;
elf_parser.shdr_type = shdr64_type;
elf_parser.shdr_entsize = shdr64_entsize;
+ elf_parser.shdr_flags = shdr64_flags;
elf_parser.sym_type = sym64_type;
+ elf_parser.sym_bind = sym64_bind;
elf_parser.sym_name = sym64_name;
elf_parser.sym_value = sym64_value;
elf_parser.sym_shndx = sym64_shndx;
@@ -174,12 +184,13 @@ int elf_map_long_size(void *addr)
return ehdr->e32.e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
}
-void *elf_map(char const *fname, size_t *size, uint32_t types)
+static void *__elf_map(char const *fname, size_t *size, uint32_t types,
+ bool writable)
{
void *addr;
int ret;
- addr = map_file(fname, size);
+ addr = map_file(fname, size, writable);
if (!addr)
return NULL;
@@ -192,7 +203,73 @@ void *elf_map(char const *fname, size_t *size, uint32_t types)
return addr;
}
+void *elf_map(char const *fname, size_t *size, uint32_t types)
+{
+ return __elf_map(fname, size, types, true);
+}
+
+/* For tools that only read the file. */
+void *elf_map_ro(char const *fname, size_t *size, uint32_t types)
+{
+ return __elf_map(fname, size, types, false);
+}
+
void elf_unmap(void *addr, size_t size)
{
munmap(addr, size);
}
+
+/*
+ * Open an ELF file and map it read-only, locating its section headers, section
+ * names and symbol table which are populated in the elf out parameter.
+ *
+ * Returns 0 on success, otherwise -1.
+ */
+int elf_open_ro(char const *fname, uint32_t types, struct elf_file *elf)
+{
+ Elf_Ehdr *ehdr;
+ Elf_Shdr *first;
+ unsigned int shstrndx, i;
+
+ memset(elf, 0, sizeof(*elf));
+ elf->base = elf_map_ro(fname, &elf->size, types);
+ if (!elf->base)
+ return -1;
+
+ ehdr = elf->base;
+ elf->shdrs = (const char *)elf->base + ehdr_shoff(ehdr);
+ elf->shentsize = ehdr_shentsize(ehdr);
+ first = elf_section(elf, 0);
+
+ /* A count or index too large for the header lives in section 0. */
+ elf->shnum = ehdr_shnum(ehdr);
+ if (elf->shnum == SHN_UNDEF)
+ elf->shnum = shdr_size(first);
+ shstrndx = ehdr_shstrndx(ehdr);
+ if (shstrndx == SHN_XINDEX)
+ shstrndx = shdr_link(first);
+ elf->shstrtab = (const char *)elf->base +
+ shdr_offset(elf_section(elf, shstrndx));
+
+ for (i = 0; i < elf->shnum && !elf->symtab; i++)
+ if (shdr_type(elf_section(elf, i)) == SHT_SYMTAB)
+ elf->symtab = elf_section(elf, i);
+
+ if (!elf->symtab) {
+ fprintf(stderr, "%s: no symbol table\n", fname);
+ elf_close(elf);
+ return -1;
+ }
+
+ elf->strtab = (const char *)elf->base +
+ shdr_offset(elf_section(elf, shdr_link(elf->symtab)));
+ elf->nr_syms = shdr_size(elf->symtab) / shdr_entsize(elf->symtab);
+
+ return 0;
+}
+
+void elf_close(struct elf_file *elf)
+{
+ elf_unmap(elf->base, elf->size);
+ elf->base = NULL;
+}
diff --git a/scripts/elf-parse.h b/scripts/elf-parse.h
index f4411e03069d..c5ad754da254 100644
--- a/scripts/elf-parse.h
+++ b/scripts/elf-parse.h
@@ -37,10 +37,12 @@ struct elf_funcs {
uint64_t (*shdr_offset)(Elf_Shdr *shdr);
uint64_t (*shdr_size)(Elf_Shdr *shdr);
uint64_t (*shdr_entsize)(Elf_Shdr *shdr);
+ uint64_t (*shdr_flags)(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);
+ uint8_t (*sym_bind)(Elf_Sym *sym);
uint32_t (*sym_name)(Elf_Sym *sym);
uint64_t (*sym_value)(Elf_Sym *sym);
uint16_t (*sym_shndx)(Elf_Sym *sym);
@@ -143,6 +145,7 @@ SHDR_ADDR(addr)
SHDR_ADDR(offset)
SHDR_ADDR(size)
SHDR_ADDR(entsize)
+SHDR_ADDR(flags)
SHDR_WORD(link)
SHDR_WORD(name)
@@ -211,6 +214,21 @@ static inline uint8_t sym_type(Elf_Sym *sym)
return elf_parser.sym_type(sym);
}
+static inline uint8_t sym64_bind(Elf_Sym *sym)
+{
+ return ELF64_ST_BIND(sym->e64.st_info);
+}
+
+static inline uint8_t sym32_bind(Elf_Sym *sym)
+{
+ return ELF32_ST_BIND(sym->e32.st_info);
+}
+
+static inline uint8_t sym_bind(Elf_Sym *sym)
+{
+ return elf_parser.sym_bind(sym);
+}
+
SYM_ADDR(value)
SYM_WORD(name)
SYM_HALF(shndx)
@@ -298,8 +316,44 @@ static inline void w8le(uint64_t val, uint64_t *x)
}
void *elf_map(char const *fname, size_t *size, uint32_t types);
+void *elf_map_ro(char const *fname, size_t *size, uint32_t types);
void elf_unmap(void *addr, size_t size);
int elf_map_machine(void *addr);
int elf_map_long_size(void *addr);
+/* A mapped file with its section headers, section names and symbol table. */
+struct elf_file {
+ void *base;
+ size_t size;
+ const char *shdrs;
+ unsigned int shnum, shentsize;
+ const char *shstrtab;
+ Elf_Shdr *symtab;
+ const char *strtab;
+ size_t nr_syms;
+};
+
+int elf_open_ro(char const *fname, uint32_t types, struct elf_file *elf);
+void elf_close(struct elf_file *elf);
+
+static inline Elf_Shdr *elf_section(const struct elf_file *elf,
+ unsigned int index)
+{
+ return (Elf_Shdr *)(elf->shdrs + (size_t)index * elf->shentsize);
+}
+
+static inline const char *elf_section_name(const struct elf_file *elf,
+ Elf_Shdr *shdr)
+{
+ return elf->shstrtab + shdr_name(shdr);
+}
+
+static inline Elf_Sym *elf_symbol(const struct elf_file *elf, size_t index)
+{
+ const char *base = elf->base;
+
+ return (Elf_Sym *)(base + shdr_offset(elf->symtab) +
+ index * shdr_entsize(elf->symtab));
+}
+
#endif /* _SCRIPTS_ELF_PARSE_H */
diff --git a/scripts/gen_init_cpio.c b/scripts/gen_init_cpio.c
new file mode 100644
index 000000000000..0f0c61ec21ca
--- /dev/null
+++ b/scripts/gen_init_cpio.c
@@ -0,0 +1,782 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <limits.h>
+
+/*
+ * Original work by Jeff Garzik
+ *
+ * External file lists, symlink, pipe and fifo support by Thayne Harbaugh
+ * Hard link support by Luciano Rocha
+ */
+
+#define xstr(s) #s
+#define str(s) xstr(s)
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define CPIO_HDR_LEN 110
+#define CPIO_TRAILER "TRAILER!!!"
+#define padlen(_off, _align) (((_align) - ((_off) & ((_align) - 1))) % (_align))
+
+/* zero-padding the filename field for data alignment is limited by PATH_MAX */
+static char padding[PATH_MAX];
+static unsigned int offset;
+static unsigned int ino = 721;
+static time_t default_mtime;
+static bool do_file_mtime;
+static bool do_csum = false;
+static int outfd = STDOUT_FILENO;
+static unsigned int dalign;
+
+struct file_handler {
+ const char *type;
+ int (*handler)(const char *line);
+};
+
+static int push_buf(const char *name, size_t name_len)
+{
+ ssize_t len;
+
+ len = write(outfd, name, name_len);
+ if (len != name_len)
+ return -1;
+
+ offset += name_len;
+ return 0;
+}
+
+static int push_pad(size_t padlen)
+{
+ ssize_t len = 0;
+
+ if (!padlen)
+ return 0;
+
+ if (padlen < sizeof(padding))
+ len = write(outfd, padding, padlen);
+ if (len != padlen)
+ return -1;
+
+ offset += padlen;
+ return 0;
+}
+
+static int push_rest(const char *name, size_t name_len)
+{
+ ssize_t len;
+
+ len = write(outfd, name, name_len);
+ if (len != name_len)
+ return -1;
+
+ offset += name_len;
+
+ return push_pad(padlen(name_len + CPIO_HDR_LEN, 4));
+}
+
+static int cpio_trailer(void)
+{
+ int len;
+ unsigned int namesize = sizeof(CPIO_TRAILER);
+
+ len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"
+ "%08X%08X%08X%08X%08X%08X%08X",
+ do_csum ? "070702" : "070701", /* magic */
+ 0, /* ino */
+ 0, /* mode */
+ (long) 0, /* uid */
+ (long) 0, /* gid */
+ 1, /* nlink */
+ (long) 0, /* mtime */
+ 0, /* filesize */
+ 0, /* major */
+ 0, /* minor */
+ 0, /* rmajor */
+ 0, /* rminor */
+ namesize, /* namesize */
+ 0); /* chksum */
+ offset += len;
+
+ if (len != CPIO_HDR_LEN ||
+ push_rest(CPIO_TRAILER, namesize) < 0 ||
+ push_pad(padlen(offset, 512)) < 0)
+ return -1;
+
+ if (fsync(outfd) < 0 && errno != EINVAL)
+ return -1;
+
+ return 0;
+}
+
+static int cpio_mkslink(const char *name, const char *target,
+ unsigned int mode, uid_t uid, gid_t gid)
+{
+ int len;
+ unsigned int namesize, targetsize = strlen(target) + 1;
+
+ if (name[0] == '/')
+ name++;
+ namesize = strlen(name) + 1;
+
+ len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"
+ "%08X%08X%08X%08X%08X%08X%08X",
+ do_csum ? "070702" : "070701", /* magic */
+ ino++, /* ino */
+ S_IFLNK | mode, /* mode */
+ (long) uid, /* uid */
+ (long) gid, /* gid */
+ 1, /* nlink */
+ (long) default_mtime, /* mtime */
+ targetsize, /* filesize */
+ 3, /* major */
+ 1, /* minor */
+ 0, /* rmajor */
+ 0, /* rminor */
+ namesize, /* namesize */
+ 0); /* chksum */
+ offset += len;
+
+ if (len != CPIO_HDR_LEN ||
+ push_buf(name, namesize) < 0 ||
+ push_pad(padlen(offset, 4)) < 0 ||
+ push_buf(target, targetsize) < 0 ||
+ push_pad(padlen(offset, 4)) < 0)
+ return -1;
+
+ return 0;
+
+}
+
+static int cpio_mkslink_line(const char *line)
+{
+ char name[PATH_MAX + 1];
+ char target[PATH_MAX + 1];
+ unsigned int mode;
+ int uid;
+ int gid;
+ int rc = -1;
+
+ if (5 != sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d", name, target, &mode, &uid, &gid)) {
+ fprintf(stderr, "Unrecognized dir format '%s'", line);
+ goto fail;
+ }
+ rc = cpio_mkslink(name, target, mode, uid, gid);
+ fail:
+ return rc;
+}
+
+static int cpio_mkgeneric(const char *name, unsigned int mode,
+ uid_t uid, gid_t gid)
+{
+ int len;
+ unsigned int namesize;
+
+ if (name[0] == '/')
+ name++;
+ namesize = strlen(name) + 1;
+
+ len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"
+ "%08X%08X%08X%08X%08X%08X%08X",
+ do_csum ? "070702" : "070701", /* magic */
+ ino++, /* ino */
+ mode, /* mode */
+ (long) uid, /* uid */
+ (long) gid, /* gid */
+ 2, /* nlink */
+ (long) default_mtime, /* mtime */
+ 0, /* filesize */
+ 3, /* major */
+ 1, /* minor */
+ 0, /* rmajor */
+ 0, /* rminor */
+ namesize, /* namesize */
+ 0); /* chksum */
+ offset += len;
+
+ if (len != CPIO_HDR_LEN ||
+ push_rest(name, namesize) < 0)
+ return -1;
+
+ return 0;
+}
+
+enum generic_types {
+ GT_DIR,
+ GT_PIPE,
+ GT_SOCK
+};
+
+struct generic_type {
+ const char *type;
+ mode_t mode;
+};
+
+static const struct generic_type generic_type_table[] = {
+ [GT_DIR] = {
+ .type = "dir",
+ .mode = S_IFDIR
+ },
+ [GT_PIPE] = {
+ .type = "pipe",
+ .mode = S_IFIFO
+ },
+ [GT_SOCK] = {
+ .type = "sock",
+ .mode = S_IFSOCK
+ }
+};
+
+static int cpio_mkgeneric_line(const char *line, enum generic_types gt)
+{
+ char name[PATH_MAX + 1];
+ unsigned int mode;
+ int uid;
+ int gid;
+ int rc = -1;
+
+ if (4 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d", name, &mode, &uid, &gid)) {
+ fprintf(stderr, "Unrecognized %s format '%s'",
+ line, generic_type_table[gt].type);
+ goto fail;
+ }
+ mode |= generic_type_table[gt].mode;
+ rc = cpio_mkgeneric(name, mode, uid, gid);
+ fail:
+ return rc;
+}
+
+static int cpio_mkdir_line(const char *line)
+{
+ return cpio_mkgeneric_line(line, GT_DIR);
+}
+
+static int cpio_mkpipe_line(const char *line)
+{
+ return cpio_mkgeneric_line(line, GT_PIPE);
+}
+
+static int cpio_mksock_line(const char *line)
+{
+ return cpio_mkgeneric_line(line, GT_SOCK);
+}
+
+static int cpio_mknod(const char *name, unsigned int mode,
+ uid_t uid, gid_t gid, char dev_type,
+ unsigned int maj, unsigned int min)
+{
+ int len;
+ unsigned int namesize;
+
+ if (dev_type == 'b')
+ mode |= S_IFBLK;
+ else
+ mode |= S_IFCHR;
+
+ if (name[0] == '/')
+ name++;
+ namesize = strlen(name) + 1;
+
+ len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"
+ "%08X%08X%08X%08X%08X%08X%08X",
+ do_csum ? "070702" : "070701", /* magic */
+ ino++, /* ino */
+ mode, /* mode */
+ (long) uid, /* uid */
+ (long) gid, /* gid */
+ 1, /* nlink */
+ (long) default_mtime, /* mtime */
+ 0, /* filesize */
+ 3, /* major */
+ 1, /* minor */
+ maj, /* rmajor */
+ min, /* rminor */
+ namesize, /* namesize */
+ 0); /* chksum */
+ offset += len;
+
+ if (len != CPIO_HDR_LEN ||
+ push_rest(name, namesize) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int cpio_mknod_line(const char *line)
+{
+ char name[PATH_MAX + 1];
+ unsigned int mode;
+ int uid;
+ int gid;
+ char dev_type;
+ unsigned int maj;
+ unsigned int min;
+ int rc = -1;
+
+ if (7 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d %c %u %u",
+ name, &mode, &uid, &gid, &dev_type, &maj, &min)) {
+ fprintf(stderr, "Unrecognized nod format '%s'", line);
+ goto fail;
+ }
+ rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min);
+ fail:
+ return rc;
+}
+
+static int cpio_mkfile_csum(int fd, unsigned long size, uint32_t *csum)
+{
+ while (size) {
+ unsigned char filebuf[65536];
+ ssize_t this_read;
+ size_t i, this_size = MIN(size, sizeof(filebuf));
+
+ this_read = read(fd, filebuf, this_size);
+ if (this_read <= 0 || this_read > this_size)
+ return -1;
+
+ for (i = 0; i < this_read; i++)
+ *csum += filebuf[i];
+
+ size -= this_read;
+ }
+ /* seek back to the start for data segment I/O */
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int cpio_mkfile(const char *name, const char *location,
+ unsigned int mode, uid_t uid, gid_t gid,
+ unsigned int nlinks)
+{
+ struct stat buf;
+ unsigned long size;
+ int file, retval, len;
+ int rc = -1;
+ time_t mtime;
+ int namesize, namepadlen;
+ unsigned int i;
+ uint32_t csum = 0;
+ ssize_t this_read;
+
+ mode |= S_IFREG;
+
+ file = open (location, O_RDONLY);
+ if (file < 0) {
+ fprintf (stderr, "File %s could not be opened for reading\n", location);
+ goto error;
+ }
+
+ retval = fstat(file, &buf);
+ if (retval) {
+ fprintf(stderr, "File %s could not be stat()'ed\n", location);
+ goto error;
+ }
+
+ if (do_file_mtime) {
+ mtime = default_mtime;
+ } else {
+ mtime = buf.st_mtime;
+ if (mtime > 0xffffffff) {
+ fprintf(stderr, "%s: Timestamp exceeds maximum cpio timestamp, clipping.\n",
+ location);
+ mtime = 0xffffffff;
+ }
+
+ if (mtime < 0) {
+ fprintf(stderr, "%s: Timestamp negative, clipping.\n",
+ location);
+ mtime = 0;
+ }
+ }
+
+ if (buf.st_size > 0xffffffff) {
+ fprintf(stderr, "%s: Size exceeds maximum cpio file size\n",
+ location);
+ goto error;
+ }
+
+ if (do_csum && cpio_mkfile_csum(file, buf.st_size, &csum) < 0) {
+ fprintf(stderr, "Failed to checksum file %s\n", location);
+ goto error;
+ }
+
+ size = 0;
+ namepadlen = 0;
+ for (i = 1; i <= nlinks; i++) {
+ if (name[0] == '/')
+ name++;
+ namesize = strlen(name) + 1;
+
+ /* data goes on last link, after any alignment padding */
+ if (i == nlinks)
+ size = buf.st_size;
+
+ if (dalign && size > dalign) {
+ namepadlen = padlen(offset + CPIO_HDR_LEN + namesize,
+ dalign);
+ if (namesize + namepadlen > PATH_MAX) {
+ fprintf(stderr,
+ "%s: best-effort alignment %u missed\n",
+ name, dalign);
+ namepadlen = 0;
+ }
+ }
+
+ len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"
+ "%08lX%08X%08X%08X%08X%08X%08X",
+ do_csum ? "070702" : "070701", /* magic */
+ ino, /* ino */
+ mode, /* mode */
+ (long) uid, /* uid */
+ (long) gid, /* gid */
+ nlinks, /* nlink */
+ (long) mtime, /* mtime */
+ size, /* filesize */
+ 3, /* major */
+ 1, /* minor */
+ 0, /* rmajor */
+ 0, /* rminor */
+ namesize + namepadlen, /* namesize */
+ size ? csum : 0); /* chksum */
+ offset += len;
+
+ if (len != CPIO_HDR_LEN ||
+ push_buf(name, namesize) < 0 ||
+ push_pad(namepadlen ? namepadlen : padlen(offset, 4)) < 0)
+ goto error;
+
+ if (size) {
+ this_read = copy_file_range(file, NULL, outfd, NULL, size, 0);
+ if (this_read > 0) {
+ if (this_read > size)
+ goto error;
+ offset += this_read;
+ size -= this_read;
+ }
+ /* short or failed copy falls back to read/write... */
+ }
+
+ while (size) {
+ unsigned char filebuf[65536];
+ size_t this_size = MIN(size, sizeof(filebuf));
+
+ this_read = read(file, filebuf, this_size);
+ if (this_read <= 0 || this_read > this_size) {
+ fprintf(stderr, "Can not read %s file\n", location);
+ goto error;
+ }
+
+ if (write(outfd, filebuf, this_read) != this_read) {
+ fprintf(stderr, "writing filebuf failed\n");
+ goto error;
+ }
+ offset += this_read;
+ size -= this_read;
+ }
+ if (push_pad(padlen(offset, 4)) < 0)
+ goto error;
+
+ name += namesize;
+ }
+ ino++;
+ rc = 0;
+
+error:
+ if (file >= 0)
+ close(file);
+ return rc;
+}
+
+static char *cpio_replace_env(char *new_location)
+{
+ char expanded[PATH_MAX + 1];
+ char *start, *end, *var;
+
+ while ((start = strstr(new_location, "${")) &&
+ (end = strchr(start + 2, '}'))) {
+ *start = *end = 0;
+ var = getenv(start + 2);
+ snprintf(expanded, sizeof expanded, "%s%s%s",
+ new_location, var ? var : "", end + 1);
+ strcpy(new_location, expanded);
+ }
+
+ return new_location;
+}
+
+static int cpio_mkfile_line(const char *line)
+{
+ char name[PATH_MAX + 1];
+ char *dname = NULL; /* malloc'ed buffer for hard links */
+ char location[PATH_MAX + 1];
+ unsigned int mode;
+ int uid;
+ int gid;
+ int nlinks = 1;
+ int end = 0, dname_len = 0;
+ int rc = -1;
+
+ if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
+ "s %o %d %d %n",
+ name, location, &mode, &uid, &gid, &end)) {
+ fprintf(stderr, "Unrecognized file format '%s'", line);
+ goto fail;
+ }
+ if (end && isgraph(line[end])) {
+ int len;
+ int nend;
+
+ dname = malloc(strlen(line));
+ if (!dname) {
+ fprintf (stderr, "out of memory (%d)\n", dname_len);
+ goto fail;
+ }
+
+ dname_len = strlen(name) + 1;
+ memcpy(dname, name, dname_len);
+
+ do {
+ nend = 0;
+ if (sscanf(line + end, "%" str(PATH_MAX) "s %n",
+ name, &nend) < 1)
+ break;
+ len = strlen(name) + 1;
+ memcpy(dname + dname_len, name, len);
+ dname_len += len;
+ nlinks++;
+ end += nend;
+ } while (isgraph(line[end]));
+ } else {
+ dname = name;
+ }
+ rc = cpio_mkfile(dname, cpio_replace_env(location),
+ mode, uid, gid, nlinks);
+ fail:
+ if (dname_len) free(dname);
+ return rc;
+}
+
+static void usage(const char *prog)
+{
+ fprintf(stderr, "Usage:\n"
+ "\t%s [-t <timestamp>] [-c] [-o <output_file>] [-a <data_align>] <cpio_list>\n"
+ "\n"
+ "<cpio_list> is a file containing newline separated entries that\n"
+ "describe the files to be included in the initramfs archive:\n"
+ "\n"
+ "# a comment\n"
+ "file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
+ "dir <name> <mode> <uid> <gid>\n"
+ "nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
+ "slink <name> <target> <mode> <uid> <gid>\n"
+ "pipe <name> <mode> <uid> <gid>\n"
+ "sock <name> <mode> <uid> <gid>\n"
+ "\n"
+ "<name> name of the file/dir/nod/etc in the archive\n"
+ "<location> location of the file in the current filesystem\n"
+ " expands shell variables quoted with ${}\n"
+ "<target> link target\n"
+ "<mode> mode/permissions of the file\n"
+ "<uid> user id (0=root)\n"
+ "<gid> group id (0=root)\n"
+ "<dev_type> device type (b=block, c=character)\n"
+ "<maj> major number of nod\n"
+ "<min> minor number of nod\n"
+ "<hard links> space separated list of other links to file\n"
+ "\n"
+ "example:\n"
+ "# A simple initramfs\n"
+ "dir /dev 0755 0 0\n"
+ "nod /dev/console 0600 0 0 c 5 1\n"
+ "dir /root 0700 0 0\n"
+ "dir /sbin 0755 0 0\n"
+ "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n"
+ "\n"
+ "<timestamp> is time in seconds since Epoch that will be used\n"
+ "as mtime for symlinks, directories, regular and special files.\n"
+ "The default is to use the current time for all files, but\n"
+ "preserve modification time for regular files.\n"
+ "-c: calculate and store 32-bit checksums for file data.\n"
+ "<output_file>: write cpio to this file instead of stdout\n"
+ "<data_align>: attempt to align file data by zero-padding the\n"
+ "filename field up to data_align. Must be a multiple of 4.\n"
+ "Alignment is best-effort; PATH_MAX limits filename padding.\n",
+ prog);
+}
+
+static const struct file_handler file_handler_table[] = {
+ {
+ .type = "file",
+ .handler = cpio_mkfile_line,
+ }, {
+ .type = "nod",
+ .handler = cpio_mknod_line,
+ }, {
+ .type = "dir",
+ .handler = cpio_mkdir_line,
+ }, {
+ .type = "slink",
+ .handler = cpio_mkslink_line,
+ }, {
+ .type = "pipe",
+ .handler = cpio_mkpipe_line,
+ }, {
+ .type = "sock",
+ .handler = cpio_mksock_line,
+ }, {
+ .type = NULL,
+ .handler = NULL,
+ }
+};
+
+#define LINE_SIZE (2 * PATH_MAX + 50)
+
+int main (int argc, char *argv[])
+{
+ FILE *cpio_list;
+ char line[LINE_SIZE];
+ char *args, *type;
+ int ec = 0;
+ int line_nr = 0;
+ const char *filename;
+
+ default_mtime = time(NULL);
+ while (1) {
+ int opt = getopt(argc, argv, "t:cho:a:");
+ char *invalid;
+
+ if (opt == -1)
+ break;
+ switch (opt) {
+ case 't':
+ default_mtime = strtol(optarg, &invalid, 10);
+ if (!*optarg || *invalid) {
+ fprintf(stderr, "Invalid timestamp: %s\n",
+ optarg);
+ usage(argv[0]);
+ exit(1);
+ }
+ do_file_mtime = true;
+ break;
+ case 'c':
+ do_csum = true;
+ break;
+ case 'o':
+ outfd = open(optarg,
+ O_WRONLY | O_CREAT | O_LARGEFILE | O_TRUNC,
+ 0600);
+ if (outfd < 0) {
+ fprintf(stderr, "failed to open %s\n", optarg);
+ usage(argv[0]);
+ exit(1);
+ }
+ break;
+ case 'a':
+ dalign = strtoul(optarg, &invalid, 10);
+ if (!*optarg || *invalid || (dalign & 3)) {
+ fprintf(stderr, "Invalid data_align: %s\n",
+ optarg);
+ usage(argv[0]);
+ exit(1);
+ }
+ break;
+ case 'h':
+ case '?':
+ usage(argv[0]);
+ exit(opt == 'h' ? 0 : 1);
+ }
+ }
+
+ /*
+ * Timestamps after 2106-02-07 06:28:15 UTC have an ascii hex time_t
+ * representation that exceeds 8 chars and breaks the cpio header
+ * specification. Negative timestamps similarly exceed 8 chars.
+ */
+ if (default_mtime > 0xffffffff || default_mtime < 0) {
+ fprintf(stderr, "ERROR: Timestamp out of range for cpio format\n");
+ exit(1);
+ }
+
+ if (argc - optind != 1) {
+ usage(argv[0]);
+ exit(1);
+ }
+ filename = argv[optind];
+ if (!strcmp(filename, "-"))
+ cpio_list = stdin;
+ else if (!(cpio_list = fopen(filename, "r"))) {
+ fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
+ filename, strerror(errno));
+ usage(argv[0]);
+ exit(1);
+ }
+
+ while (fgets(line, LINE_SIZE, cpio_list)) {
+ int type_idx;
+ size_t slen = strlen(line);
+
+ line_nr++;
+
+ if ('#' == *line) {
+ /* comment - skip to next line */
+ continue;
+ }
+
+ if (! (type = strtok(line, " \t"))) {
+ fprintf(stderr,
+ "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
+ line_nr, line);
+ ec = -1;
+ break;
+ }
+
+ if ('\n' == *type) {
+ /* a blank line */
+ continue;
+ }
+
+ if (slen == strlen(type)) {
+ /* must be an empty line */
+ continue;
+ }
+
+ if (! (args = strtok(NULL, "\n"))) {
+ fprintf(stderr,
+ "ERROR: incorrect format, newline required line %d: '%s'\n",
+ line_nr, line);
+ ec = -1;
+ break;
+ }
+
+ for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
+ int rc;
+ if (! strcmp(line, file_handler_table[type_idx].type)) {
+ if ((rc = file_handler_table[type_idx].handler(args))) {
+ ec = rc;
+ fprintf(stderr, " line %d\n", line_nr);
+ }
+ break;
+ }
+ }
+
+ if (NULL == file_handler_table[type_idx].type) {
+ fprintf(stderr, "unknown file type line %d: '%s'\n",
+ line_nr, line);
+ }
+ }
+ if (ec == 0)
+ ec = cpio_trailer();
+
+ exit(ec);
+}
diff --git a/scripts/gen_initramfs.sh b/scripts/gen_initramfs.sh
new file mode 100755
index 000000000000..9c6168a9338d
--- /dev/null
+++ b/scripts/gen_initramfs.sh
@@ -0,0 +1,250 @@
+#!/bin/sh
+# Copyright (C) Martin Schlemmer <azarah@nosferatu.za.org>
+# Copyright (C) 2006 Sam Ravnborg <sam@ravnborg.org>
+#
+# Released under the terms of the GNU GPL
+#
+# Generate a cpio packed initramfs. It uses gen_init_cpio to generate
+# the cpio archive.
+# This script assumes that gen_init_cpio is located in scripts/ directory
+
+# error out on errors
+set -e
+
+usage() {
+cat << EOF
+Usage:
+$0 [-o <file>] [-l <dep_list>] [-u <uid>] [-g <gid>] {-d | <cpio_source>} ...
+ -o <file> Create initramfs file named <file> by using gen_init_cpio
+ -l <dep_list> Create dependency list named <dep_list>
+ -u <uid> User ID to map to user ID 0 (root).
+ <uid> is only meaningful if <cpio_source> is a
+ directory. "squash" forces all files to uid 0.
+ -g <gid> Group ID to map to group ID 0 (root).
+ <gid> is only meaningful if <cpio_source> is a
+ directory. "squash" forces all files to gid 0.
+ -d <date> Use date for all file mtime values
+ <cpio_source> File list or directory for cpio archive.
+ If <cpio_source> is a .cpio file it will be used
+ as direct input to initramfs.
+
+All options except -o and -l may be repeated and are interpreted
+sequentially and immediately. -u and -g states are preserved across
+<cpio_source> options so an explicit "-u 0 -g 0" is required
+to reset the root/group mapping.
+EOF
+}
+
+# awk style field access
+# $1 - field number; rest is argument string
+field() {
+ shift $1 ; echo $1
+}
+
+filetype() {
+ local argv1="$1"
+
+ # symlink test must come before file test
+ if [ -L "${argv1}" ]; then
+ echo "slink"
+ elif [ -f "${argv1}" ]; then
+ echo "file"
+ elif [ -d "${argv1}" ]; then
+ echo "dir"
+ elif [ -b "${argv1}" -o -c "${argv1}" ]; then
+ echo "nod"
+ elif [ -p "${argv1}" ]; then
+ echo "pipe"
+ elif [ -S "${argv1}" ]; then
+ echo "sock"
+ else
+ echo "invalid"
+ fi
+ return 0
+}
+
+print_mtime() {
+ local my_mtime="0"
+
+ if [ -e "$1" ]; then
+ my_mtime=$(find "$1" -printf "%T@\n" | sort -r | head -n 1)
+ fi
+
+ echo "# Last modified: ${my_mtime}" >> $cpio_list
+ echo "" >> $cpio_list
+}
+
+list_parse() {
+ if [ -z "$dep_list" -o -L "$1" ]; then
+ return
+ fi
+ echo "$1" | sed 's/:/\\:/g; s/$/ \\/' >> $dep_list
+}
+
+# for each file print a line in following format
+# <filetype> <name> <path to file> <octal mode> <uid> <gid>
+# for links, devices etc the format differs. See gen_init_cpio for details
+parse() {
+ local location="$1"
+ local name="/${location#${srcdir}}"
+ # change '//' into '/'
+ name=$(echo "$name" | sed -e 's://*:/:g')
+ local mode="$2"
+ local uid="$3"
+ local gid="$4"
+ local ftype=$(filetype "${location}")
+ # remap uid/gid to 0 if necessary
+ [ "$root_uid" = "squash" ] && uid=0 || [ "$uid" -eq "$root_uid" ] && uid=0
+ [ "$root_gid" = "squash" ] && gid=0 || [ "$gid" -eq "$root_gid" ] && gid=0
+ local str="${mode} ${uid} ${gid}"
+
+ [ "${ftype}" = "invalid" ] && return 0
+ [ "${location}" = "${srcdir}" ] && return 0
+
+ case "${ftype}" in
+ "file")
+ str="${ftype} ${name} ${location} ${str}"
+ ;;
+ "nod")
+ local dev="`LC_ALL=C ls -l "${location}"`"
+ local maj=`field 5 ${dev}`
+ local min=`field 6 ${dev}`
+ maj=${maj%,}
+
+ [ -b "${location}" ] && dev="b" || dev="c"
+
+ str="${ftype} ${name} ${str} ${dev} ${maj} ${min}"
+ ;;
+ "slink")
+ local target=`readlink "${location}"`
+ str="${ftype} ${name} ${target} ${str}"
+ ;;
+ *)
+ str="${ftype} ${name} ${str}"
+ ;;
+ esac
+
+ echo "${str}" >> $cpio_list
+
+ return 0
+}
+
+unknown_option() {
+ printf "ERROR: unknown option \"$arg\"\n" >&2
+ printf "If the filename validly begins with '-', " >&2
+ printf "then it must be prefixed\n" >&2
+ printf "by './' so that it won't be interpreted as an option." >&2
+ printf "\n" >&2
+ usage >&2
+ exit 1
+}
+
+header() {
+ printf "\n#####################\n# $1\n" >> $cpio_list
+}
+
+# process one directory (incl sub-directories)
+dir_filelist() {
+ header "$1"
+
+ srcdir=$(echo "$1" | sed -e 's://*:/:g')
+ dirlist=$(find "${srcdir}" -printf "%p %m %U %G\n" | LC_ALL=C sort)
+
+ # If $dirlist is only one line, then the directory is empty
+ if [ "$(echo "${dirlist}" | wc -l)" -gt 1 ]; then
+ print_mtime "$1"
+
+ echo "${dirlist}" | \
+ while read x; do
+ list_parse $x
+ parse $x
+ done
+ fi
+}
+
+input_file() {
+ source="$1"
+ if [ -f "$1" ]; then
+ # If a regular file is specified, assume it is in
+ # gen_init_cpio format
+ header "$1"
+ print_mtime "$1" >> $cpio_list
+ cat "$1" >> $cpio_list
+ if [ -n "$dep_list" ]; then
+ echo "$1 \\" >> $dep_list
+ cat "$1" | while read type dir file perm ; do
+ if [ "$type" = "file" ]; then
+ echo "$file \\" >> $dep_list
+ fi
+ done
+ fi
+ elif [ -d "$1" ]; then
+ # If a directory is specified then add all files in it to fs
+ dir_filelist "$1"
+ else
+ echo " ${prog}: Cannot open '$1'" >&2
+ exit 1
+ fi
+}
+
+prog=$0
+root_uid=0
+root_gid=0
+dep_list=
+timestamp=
+cpio_list=$(mktemp ${TMPDIR:-/tmp}/cpiolist.XXXXXX)
+# gen_init_cpio writes to stdout by default
+output=""
+
+trap "rm -f $cpio_list" EXIT
+
+while [ $# -gt 0 ]; do
+ arg="$1"
+ shift
+ case "$arg" in
+ "-l") # files included in initramfs - used by kbuild
+ dep_list="$1"
+ echo "deps_initramfs := \\" > $dep_list
+ shift
+ ;;
+ "-o") # generate cpio image named $1
+ output="-o $1"
+ shift
+ ;;
+ "-u") # map $1 to uid=0 (root)
+ root_uid="$1"
+ [ "$root_uid" = "-1" ] && root_uid=$(id -u || echo 0)
+ shift
+ ;;
+ "-g") # map $1 to gid=0 (root)
+ root_gid="$1"
+ [ "$root_gid" = "-1" ] && root_gid=$(id -g || echo 0)
+ shift
+ ;;
+ "-d") # date for file mtimes
+ timestamp="$(date -d"$1" +%s || :)"
+ if test -n "$timestamp"; then
+ timestamp="-t $timestamp"
+ fi
+ shift
+ ;;
+ "-h")
+ usage
+ exit 0
+ ;;
+ *)
+ case "$arg" in
+ "-"*)
+ unknown_option
+ ;;
+ *) # input file/dir - process it
+ input_file "$arg"
+ ;;
+ esac
+ ;;
+ esac
+done
+
+# If output_file is set we will generate cpio archive
+# we are careful to delete tmp files
+scripts/gen_init_cpio $output $timestamp $cpio_list
diff --git a/scripts/jobserver-exec b/scripts/jobserver-exec
index 21b319e6c9a5..89119acb1b9e 100755
--- a/scripts/jobserver-exec
+++ b/scripts/jobserver-exec
@@ -4,7 +4,8 @@
"""
Determines how many parallel tasks "make" is expecting, as it is
not exposed via any special variables, reserves them all, runs a subprocess
-with PARALLELISM environment variable set, and releases the jobs back again.
+with PARALLELISM environment variable set and any %PARALLELISM% in its
+arguments replaced by the count, and releases the jobs back again.
See:
https://www.gnu.org/software/make/manual/html_node/POSIX-Jobserver.html#POSIX-Jobserver
diff --git a/scripts/kallsyms-sysmap.c b/scripts/kallsyms-sysmap.c
new file mode 100644
index 000000000000..a482968eba51
--- /dev/null
+++ b/scripts/kallsyms-sysmap.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Obtain symbols from vmlinux for usage by kallsyms. Replaces mksysmap.
+ *
+ * To retain compatibility, it provides the same output as nm, only faster.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <xalloc.h>
+
+#include "elf-parse.h"
+#include "kallsyms.h"
+
+/*
+ * What mksysmap dropped from System.map, by name: symbols that are not needed
+ * there, or not suitable for kallsyms.
+ */
+static const char *const sysmap_omit_prefixes[] = {
+ /* local symbols for ARM, MIPS, etc. */
+ "$",
+ /* local labels, .LBB, .Ltmpxxx, .L__unnamed_xx, .LASANPC, etc. */
+ ".L",
+ /* arm64 EFI stub namespace */
+ "__efistub_",
+ /* arm64 local symbols in PIE namespace */
+ "__pi_$",
+ "__pi_.L",
+ /* arm64 local symbols in non-VHE KVM namespace */
+ "__kvm_nvhe_$",
+ "__kvm_nvhe_.L",
+ /* CFI type identifiers */
+ "__kcfi_typeid_",
+ "__kvm_nvhe___kcfi_typeid_",
+ "__pi___kcfi_typeid_",
+ /* CRC from modversions */
+ "__crc_",
+ /* EXPORT_SYMBOL (symbol name) */
+ "__kstrtab_",
+ /* EXPORT_SYMBOL (namespace) */
+ "__kstrtabns_",
+ /* MODULE_DEVICE_TABLE (symbol name) */
+ "__mod_device_table__",
+};
+
+static const char *const sysmap_omit_suffixes[] = {
+ /* arm */
+ "_from_arm",
+ "_from_thumb",
+ "_veneer",
+};
+
+static const char *const sysmap_omit_names[] = {
+ /* for LoongArch? */
+ "L0",
+ /* ppc */
+ "_SDA_BASE_",
+ "_SDA2_BASE_",
+};
+
+/* Symbols that contain the pattern anywhere are dropped. */
+static const char *const sysmap_omit_patterns[] = {
+ /* ppc stub */
+ ".long_branch.",
+ ".plt_branch.",
+};
+
+/* __<alnum>*Thunk_: the linker's range extension thunks on arm. */
+static bool is_range_thunk(const char *name)
+{
+ const char *p;
+
+ if (!string_starts_with(name, "__"))
+ return false;
+ for (p = name + 2; isalnum((unsigned char)*p); p++)
+ ;
+ return p - name >= 7 && *p == '_' && strncmp(p - 5, "Thunk", 5) == 0;
+}
+
+/* __UNIQUE_ID_modinfo_<n>: the MODULE_INFO() strings of built-in code. */
+static bool is_modinfo_id(const char *name)
+{
+ static const char prefix[] = "__UNIQUE_ID_modinfo_";
+ const char *p;
+
+ if (!string_starts_with(name, prefix))
+ return false;
+ for (p = name + strlen(prefix); isdigit((unsigned char)*p); p++)
+ ;
+ return !*p;
+}
+
+static bool sysmap_omits(const char *name, char type)
+{
+ size_t i;
+
+ /* Absolute, undefined and debugging symbols. */
+ if (type == 'a' || type == 'N' || type == 'U' || type == 'w')
+ return true;
+
+ for (i = 0; i < ARRAY_SIZE(sysmap_omit_prefixes); i++)
+ if (string_starts_with(name, sysmap_omit_prefixes[i]))
+ return true;
+ for (i = 0; i < ARRAY_SIZE(sysmap_omit_suffixes); i++)
+ if (string_ends_with(name, sysmap_omit_suffixes[i]))
+ return true;
+ for (i = 0; i < ARRAY_SIZE(sysmap_omit_names); i++)
+ if (strcmp(name, sysmap_omit_names[i]) == 0)
+ return true;
+ for (i = 0; i < ARRAY_SIZE(sysmap_omit_patterns); i++)
+ if (strstr(name, sysmap_omit_patterns[i]))
+ return true;
+
+ return is_range_thunk(name) || is_modinfo_id(name);
+}
+
+/* nm's letter for a symbol defined in a section, as BFD classifies it. */
+static char section_symbol_type(Elf_Shdr *shdr, const char *secname)
+{
+ static const char *const debug_prefixes[] = {
+ ".debug", ".zdebug", ".gnu.debuglto_.debug_",
+ ".gnu.linkonce.wi.", ".line", ".stab",
+ };
+ const uint64_t flags = shdr_flags(shdr);
+ size_t i;
+
+ if (flags & SHF_EXECINSTR)
+ return 't';
+ if (flags & SHF_ALLOC) {
+ if (shdr_type(shdr) == SHT_NOBITS)
+ return 'b';
+ return flags & SHF_WRITE ? 'd' : 'r';
+ }
+ for (i = 0; i < ARRAY_SIZE(debug_prefixes); i++)
+ if (string_starts_with(secname, debug_prefixes[i]))
+ return 'N';
+ if (shdr_type(shdr) != SHT_NOBITS && !(flags & SHF_WRITE))
+ return 'n';
+ return '?';
+}
+
+/* The letter nm prints for a symbol, or 0 for one it leaves out. */
+static char elf_symbol_type(const struct elf_file *elf, Elf_Sym *sym)
+{
+ unsigned int bind = sym_bind(sym), type = sym_type(sym);
+ unsigned int shndx = sym_shndx(sym);
+ Elf_Shdr *shdr;
+ char c;
+
+ if (type == STT_SECTION || type == STT_FILE)
+ return 0;
+ if (shndx == SHN_COMMON)
+ return 'C';
+ if (shndx == SHN_UNDEF) {
+ if (bind == STB_WEAK)
+ return type == STT_OBJECT ? 'v' : 'w';
+ return 'U';
+ }
+ if (type == STT_GNU_IFUNC)
+ return 'i';
+ if (bind == STB_WEAK)
+ return type == STT_OBJECT ? 'V' : 'W';
+ if (bind == STB_GNU_UNIQUE)
+ return 'u';
+ if (bind != STB_GLOBAL && bind != STB_LOCAL)
+ return '?';
+
+ if (shndx == SHN_ABS) {
+ c = 'a';
+ } else if (shndx < elf->shnum) {
+ shdr = elf_section(elf, shndx);
+ c = section_symbol_type(shdr, elf_section_name(elf, shdr));
+ } else {
+ return '?';
+ }
+
+ return bind == STB_GLOBAL ? toupper(c) : c;
+}
+
+/* nm -n order: by address, then by name. */
+static int compare_symbols(const void *a, const void *b)
+{
+ const struct sysmap_symbol *sa = a, *sb = b;
+
+ if (sa->addr != sb->addr)
+ return sa->addr < sb->addr ? -1 : 1;
+ return strcmp(sa->name, sb->name);
+}
+
+/* The symbols "nm -n | mksysmap" would list, in that order. */
+static struct sysmap_symbol *elf_read_symbols(const struct elf_file *elf,
+ size_t *nr_kept)
+{
+ struct sysmap_symbol *syms = xmalloc(elf->nr_syms * sizeof(*syms));
+ size_t i, n = 0;
+
+ for (i = 1; i < elf->nr_syms; i++) {
+ Elf_Sym *sym = elf_symbol(elf, i);
+ const char *name = elf->strtab + sym_name(sym);
+ char type = elf_symbol_type(elf, sym);
+
+ if (!type || sysmap_omits(name, type))
+ continue;
+
+ syms[n].addr = sym_value(sym);
+ syms[n].name = name;
+ syms[n].type = type;
+ n++;
+ }
+
+ qsort(syms, n, sizeof(*syms), compare_symbols);
+ *nr_kept = n;
+ return syms;
+}
+
+struct sysmap *sysmap_read(const char *path)
+{
+ struct sysmap *map = xcalloc(1, sizeof(*map));
+
+ /* The names point into the mapping; keep it until the map is freed. */
+ if (elf_open_ro(path, (1 << ET_EXEC) | (1 << ET_DYN), &map->elf))
+ exit(EXIT_FAILURE);
+ map->syms = elf_read_symbols(&map->elf, &map->nr_syms);
+ map->addr_width = elf_map_long_size(map->elf.base) * 2;
+
+ return map;
+}
+
+void sysmap_write(const struct sysmap *map, FILE *out)
+{
+ size_t i;
+
+ for (i = 0; i < map->nr_syms; i++) {
+ const struct sysmap_symbol *s = &map->syms[i];
+
+ fprintf(out, "%0*llx %c %s\n", map->addr_width, s->addr, s->type,
+ s->name);
+ }
+}
+
+void sysmap_free(struct sysmap *map)
+{
+ free(map->syms);
+ elf_close(&map->elf);
+ free(map);
+}
diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
index 494852ade6d8..ecb444078c1c 100644
--- a/scripts/kallsyms.c
+++ b/scripts/kallsyms.c
@@ -5,7 +5,15 @@
* 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] in.map > out.S
+ * Usage: kallsyms [--all-symbols] [--pc-relative] [--sysmap=out.map] in out.bin > out.S
+ * kallsyms --sysmap=out.map in
+ *
+ * in is vmlinux; an empty file stands for the first link, which has no
+ * symbols yet, and gives an empty table. --sysmap also writes the symbols
+ * in System.map format.
+ *
+ * The byte tables go to out.bin and are pulled into out.S with .incbin;
+ * wider tables stay assembler source for endianness and relocations.
*
* Table compression uses all the unused char codes on the symbols and
* maps these to the most used substrings (tokens). For instance, it might
@@ -18,7 +26,6 @@
*
*/
-#include <errno.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
@@ -26,10 +33,10 @@
#include <string.h>
#include <ctype.h>
#include <limits.h>
-
+#include <sys/stat.h>
#include <xalloc.h>
-#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
+#include "kallsyms.h"
#define KSYM_NAME_LEN 512
@@ -58,20 +65,60 @@ static unsigned int table_size, table_cnt;
static int all_symbols;
static int pc_relative;
-static int token_profit[0x10000];
+/* A dynamic array of symbols, encoded by symbol index. */
+struct sym_arr {
+ unsigned int *sym_indexes;
+ unsigned int cnt, cap;
+};
+
+/* Every pair of bytes is a token. */
+#define NR_TOKENS 0x10000
+
+static int token_profit[NR_TOKENS];
+static struct sym_arr token_syms[NR_TOKENS];
/* the table that holds the result of the compression */
static unsigned char best_table[256][2];
static unsigned char best_table_len[256];
+static unsigned int sym_arr_last(const struct sym_arr *arr)
+{
+ return arr->cnt ? arr->sym_indexes[arr->cnt - 1] : UINT_MAX;
+}
+
+static void sym_arr_maybe_expand(struct sym_arr *arr)
+{
+ if (arr->cap > arr->cnt)
+ return;
+
+ arr->cap = arr->cap ? arr->cap * 2 : 16;
+ arr->sym_indexes = xrealloc(arr->sym_indexes,
+ arr->cap * sizeof(*arr->sym_indexes));
+}
+
+static void sym_arr_add(struct sym_arr *arr, unsigned int sym_idx)
+{
+ sym_arr_maybe_expand(arr);
+ arr->sym_indexes[arr->cnt++] = sym_idx;
+}
+
+static void sym_arr_free(struct sym_arr *arr)
+{
+ free(arr->sym_indexes);
+ arr->sym_indexes = NULL;
+ arr->cnt = 0;
+ arr->cap = 0;
+}
static void usage(void)
{
- fprintf(stderr, "Usage: kallsyms [--all-symbols] in.map > out.S\n");
+ fprintf(stderr, "Usage: kallsyms [--all-symbols] [--pc-relative] [--sysmap=out.map]\n"
+ " in out.bin > out.S\n"
+ " kallsyms --sysmap=out.map vmlinux\n");
exit(1);
}
-static char *sym_name(const struct sym_entry *s)
+static char *sym_entry_name(const struct sym_entry *s)
{
return (char *)s->sym + 1;
}
@@ -112,37 +159,12 @@ static void check_symbol_range(const char *sym, unsigned long long addr,
}
}
-static struct sym_entry *read_symbol(FILE *in, char **buf, size_t *buf_len)
+static struct sym_entry *add_symbol(unsigned long long addr, char type,
+ const char *name)
{
- char *name, type, *p;
- unsigned long long addr;
- size_t len;
- ssize_t readlen;
+ size_t len = strlen(name);
struct sym_entry *sym;
- errno = 0;
- readlen = getline(buf, buf_len, in);
- if (readlen < 0) {
- if (errno) {
- perror("read_symbol");
- exit(EXIT_FAILURE);
- }
- return NULL;
- }
-
- if ((*buf)[readlen - 1] == '\n')
- (*buf)[readlen - 1] = 0;
-
- addr = strtoull(*buf, &p, 16);
-
- if (*buf == p || *p++ != ' ' || !isascii((type = *p++)) || *p++ != ' ') {
- fprintf(stderr, "line format error\n");
- exit(EXIT_FAILURE);
- }
-
- name = p;
- len = strlen(name);
-
if (len >= KSYM_NAME_LEN) {
fprintf(stderr, "Symbol %s too long for kallsyms (%zu >= %d).\n"
"Please increase KSYM_NAME_LEN both in kernel and kallsyms.c\n",
@@ -167,7 +189,7 @@ static struct sym_entry *read_symbol(FILE *in, char **buf, size_t *buf_len)
sym->addr = addr;
sym->len = len;
sym->sym[0] = type;
- strcpy(sym_name(sym), name);
+ strcpy(sym_entry_name(sym), name);
return sym;
}
@@ -188,14 +210,9 @@ static int symbol_in_range(const struct sym_entry *s,
return 0;
}
-static bool string_starts_with(const char *s, const char *prefix)
-{
- return strncmp(s, prefix, strlen(prefix)) == 0;
-}
-
static int symbol_valid(const struct sym_entry *s)
{
- const char *name = sym_name(s);
+ const char *name = sym_entry_name(s);
/* if --all-symbols is not specified, then symbols outside the text
* and inittext sections are discarded */
@@ -245,36 +262,55 @@ static void shrink_table(void)
table_cnt = pos;
}
-static void read_map(const char *in)
+static void add_table_entry(struct sym_entry *sym)
{
- FILE *fp;
- struct sym_entry *sym;
- char *buf = NULL;
- size_t buflen = 0;
+ sym->seq = table_cnt;
- fp = fopen(in, "r");
- if (!fp) {
- perror(in);
- exit(1);
+ if (table_cnt >= table_size) {
+ table_size = table_size ? table_size * 2 : 10000;
+ table = xrealloc(table, sizeof(*table) * table_size);
}
- while (!feof(fp)) {
- sym = read_symbol(fp, &buf, &buflen);
- if (!sym)
- continue;
+ table[table_cnt++] = sym;
+}
- sym->seq = table_cnt;
+static bool file_is_empty(const char *path)
+{
+ struct stat st;
- if (table_cnt >= table_size) {
- table_size += 10000;
- table = xrealloc(table, sizeof(*table) * table_size);
- }
+ if (stat(path, &st)) {
+ perror(path);
+ exit(EXIT_FAILURE);
+ }
+
+ return st.st_size == 0;
+}
- table[table_cnt++] = sym;
+/*
+ * Read the symbols from vmlinux, writing System.map if asked to. The first
+ * link has no symbols yet: an empty file gives an empty table.
+ */
+static void read_elf(const char *path, FILE *sysmap_out)
+{
+ struct sysmap *map;
+ size_t i;
+
+ if (file_is_empty(path))
+ return;
+
+ map = sysmap_read(path);
+ if (sysmap_out)
+ sysmap_write(map, sysmap_out);
+
+ for (i = 0; i < map->nr_syms; i++) {
+ const struct sysmap_symbol *s = &map->syms[i];
+ struct sym_entry *sym = add_symbol(s->addr, s->type, s->name);
+
+ if (sym)
+ add_table_entry(sym);
}
- free(buf);
- fclose(fp);
+ sysmap_free(map);
}
static void output_label(const char *label)
@@ -284,6 +320,26 @@ static void output_label(const char *label)
printf("%s:\n", label);
}
+static long bin_pos(FILE *file)
+{
+ const long pos = ftell(file);
+
+ if (pos < 0) {
+ perror("kallsyms: ftell");
+ exit(EXIT_FAILURE);
+ }
+
+ return pos;
+}
+
+static void write_incbin(const char *filename, long start, long end)
+{
+ if (start >= end)
+ return;
+
+ printf("\t.incbin \"%s\", %ld, %ld\n", filename, start, end - start);
+}
+
/* 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)
@@ -317,7 +373,7 @@ static int compare_names(const void *a, const void *b)
const struct sym_entry *sa = *(const struct sym_entry **)a;
const struct sym_entry *sb = *(const struct sym_entry **)b;
- ret = strcmp(sym_name(sa), sym_name(sb));
+ ret = strcmp(sym_entry_name(sa), sym_entry_name(sb));
if (!ret) {
if (sa->addr > sb->addr)
return 1;
@@ -336,11 +392,12 @@ static void sort_symbols_by_name(void)
qsort(table, table_cnt, sizeof(table[0]), compare_names);
}
-static void write_src(void)
+static void write_src(FILE *out_bin_file, const char *out_bin_name)
{
- unsigned int i, k, off;
+ unsigned int i, off;
unsigned int best_idx[256];
unsigned int *markers, markers_cnt;
+ long bin_start;
char buf[KSYM_NAME_LEN];
printf("\t.section .rodata, \"a\"\n");
@@ -355,6 +412,7 @@ static void write_src(void)
markers = xmalloc(sizeof(*markers) * markers_cnt);
output_label("kallsyms_names");
+ bin_start = bin_pos(out_bin_file);
off = 0;
for (i = 0; i < table_cnt; i++) {
if ((i & 0xFF) == 0)
@@ -378,26 +436,24 @@ static void write_src(void)
/* Encode length with ULEB128. */
if (table[i]->len <= 0x7F) {
/* Most symbols use a single byte for the length. */
- printf("\t.byte 0x%02x", table[i]->len);
+ fputc(table[i]->len, out_bin_file);
off += table[i]->len + 1;
} else {
/* "Big" symbols use two bytes. */
- printf("\t.byte 0x%02x, 0x%02x",
- (table[i]->len & 0x7F) | 0x80,
- (table[i]->len >> 7) & 0x7F);
+ fputc((table[i]->len & 0x7F) | 0x80, out_bin_file);
+ fputc((table[i]->len >> 7) & 0x7F, out_bin_file);
off += table[i]->len + 2;
}
- for (k = 0; k < table[i]->len; k++)
- printf(", 0x%02x", table[i]->sym[k]);
+ fwrite(table[i]->sym, 1, table[i]->len, out_bin_file);
/*
* Now that we wrote out the compressed symbol name, restore the
- * original name and print it in the comment.
+ * original name for the comments below.
*/
expand_symbol(table[i]->sym, table[i]->len, buf);
strcpy((char *)table[i]->sym, buf);
- printf("\t/* %s */\n", table[i]->sym);
}
+ write_incbin(out_bin_name, bin_start, bin_pos(out_bin_file));
printf(".size kallsyms_names, . - kallsyms_names\n");
printf("\n");
@@ -410,13 +466,15 @@ static void write_src(void)
free(markers);
output_label("kallsyms_token_table");
+ bin_start = bin_pos(out_bin_file);
off = 0;
for (i = 0; i < 256; i++) {
best_idx[i] = off;
expand_symbol(best_table[i], best_table_len[i], buf);
- printf("\t.asciz\t\"%s\"\n", buf);
+ fwrite(buf, 1, strlen(buf) + 1, out_bin_file);
off += strlen(buf) + 1;
}
+ write_incbin(out_bin_name, bin_start, bin_pos(out_bin_file));
printf(".size kallsyms_token_table, . - kallsyms_token_table\n");
printf("\n");
@@ -449,15 +507,25 @@ static void write_src(void)
sort_symbols_by_name();
output_label("kallsyms_seqs_of_names");
- for (i = 0; i < table_cnt; i++)
- printf("\t.byte 0x%02x, 0x%02x, 0x%02x\t/* %s */\n",
- (unsigned char)(table[i]->seq >> 16),
- (unsigned char)(table[i]->seq >> 8),
- (unsigned char)(table[i]->seq >> 0),
- table[i]->sym);
+ bin_start = bin_pos(out_bin_file);
+ for (i = 0; i < table_cnt; i++) {
+ fputc(table[i]->seq >> 16, out_bin_file);
+ fputc(table[i]->seq >> 8, out_bin_file);
+ fputc(table[i]->seq >> 0, out_bin_file);
+ }
+ write_incbin(out_bin_name, bin_start, bin_pos(out_bin_file));
printf("\n");
}
+static unsigned int token_index(unsigned char first, unsigned char second)
+{
+ return first + (second << 8);
+}
+
+static unsigned int sym_token_index(const unsigned char *symbol, int first_idx)
+{
+ return token_index(symbol[first_idx], symbol[first_idx + 1]);
+}
/* table lookup compression functions */
@@ -467,7 +535,7 @@ static void learn_symbol(const unsigned char *symbol, int len)
int i;
for (i = 0; i < len - 1; i++)
- token_profit[ symbol[i] + (symbol[i + 1] << 8) ]++;
+ token_profit[sym_token_index(symbol, i)]++;
}
/* decrease the count for all the possible tokens in a symbol */
@@ -476,16 +544,76 @@ static void forget_symbol(const unsigned char *symbol, int len)
int i;
for (i = 0; i < len - 1; i++)
- token_profit[ symbol[i] + (symbol[i + 1] << 8) ]--;
+ token_profit[sym_token_index(symbol, i)]--;
+}
+
+static void token_add_symbol(unsigned int token_idx, unsigned int sym_idx)
+{
+ struct sym_arr *arr = &token_syms[token_idx];
+
+ /* Symbol indexes kept in sorted order, check for duplicate. */
+ if (sym_arr_last(arr) == sym_idx)
+ return;
+
+ sym_arr_add(arr, sym_idx);
+}
+
+static void symbol_index_all_tokens(const unsigned char *symbol, int len,
+ unsigned int sym_idx)
+{
+ int i;
+
+ for (i = 0; i < len - 1; i++) {
+ const unsigned int token_idx = sym_token_index(symbol, i);
+
+ token_add_symbol(token_idx, sym_idx);
+ }
+}
+
+/*
+ * The symbol just got compressed. The only parts of the symbol that changed
+ * meaningfully are those containing the newly assigned compressed char, so
+ * index those.
+ */
+static void symbol_index_new_tokens(const unsigned char *symbol, int len,
+ unsigned int sym_idx, int compressed_chr)
+{
+ int i;
+
+ for (i = 0; i < len - 1; i++) {
+ const unsigned int token_idx = sym_token_index(symbol, i);
+
+ if (symbol[i] == compressed_chr ||
+ symbol[i + 1] == compressed_chr)
+ token_add_symbol(token_idx, sym_idx);
+ }
}
-/* do the initial token count */
static void build_initial_token_table(void)
{
unsigned int i;
for (i = 0; i < table_cnt; i++)
learn_symbol(table[i]->sym, table[i]->len);
+
+ /*
+ * The initial occurrence counts tell us exactly how much memory should
+ * be reserved for each token's symbol array.
+ */
+ for (i = 0; i < ARRAY_SIZE(token_syms); i++) {
+ const int nr_syms = token_profit[i];
+
+ if (!nr_syms)
+ continue;
+
+ token_syms[i].cap = nr_syms;
+ token_syms[i].sym_indexes =
+ xmalloc(nr_syms * sizeof(unsigned int));
+ }
+
+ /* For every symbol, index every token -> symbol it is present in. */
+ for (i = 0; i < table_cnt; i++)
+ symbol_index_all_tokens(table[i]->sym, table[i]->len, i);
}
static unsigned char *find_token(unsigned char *str, int len,
@@ -502,27 +630,31 @@ static unsigned char *find_token(unsigned char *str, int len,
/* replace a given token in all the valid symbols. Use the sampled symbols
* to update the counts */
-static void compress_symbols(const unsigned char *str, int idx)
+static void compress_symbols(const unsigned char *str, int compressed_chr)
{
- unsigned int i, len, size;
+ const unsigned int token_idx = sym_token_index(str, 0);
+ struct sym_arr *arr = &token_syms[token_idx];
+ unsigned int sym_idx, j, len, size;
unsigned char *p1, *p2;
- for (i = 0; i < table_cnt; i++) {
+ /* Iterate through all symbols this token is found in and compress. */
+ for (j = 0; j < arr->cnt; j++) {
+ sym_idx = arr->sym_indexes[j];
- len = table[i]->len;
- p1 = table[i]->sym;
+ len = table[sym_idx]->len;
+ p1 = table[sym_idx]->sym;
/* find the token on the symbol */
p2 = find_token(p1, len, str);
if (!p2) continue;
/* decrease the counts for this symbol's tokens */
- forget_symbol(table[i]->sym, len);
+ forget_symbol(table[sym_idx]->sym, len);
size = len;
do {
- *p2 = idx;
+ *p2 = compressed_chr;
p2++;
size -= (p2 - p1);
memmove(p2, p2 + 1, size);
@@ -536,11 +668,15 @@ static void compress_symbols(const unsigned char *str, int idx)
} while (p2);
- table[i]->len = len;
+ table[sym_idx]->len = len;
/* increase the counts for this symbol's new tokens */
- learn_symbol(table[i]->sym, len);
+ learn_symbol(table[sym_idx]->sym, len);
+ symbol_index_new_tokens(table[sym_idx]->sym, len, sym_idx,
+ compressed_chr);
}
+
+ sym_arr_free(arr); /* No symbol contains this token any more. */
}
/* search the token with the maximum profit */
@@ -551,7 +687,7 @@ static int find_best_token(void)
bestprofit=-10000;
best = 0;
- for (i = 0; i < 0x10000; i++) {
+ for (i = 0; i < ARRAY_SIZE(token_profit); i++) {
if (token_profit[i] > bestprofit) {
best = i;
bestprofit = token_profit[i];
@@ -615,7 +751,7 @@ static void optimize_token_table(void)
/* guess for "linker script provide" symbol */
static int may_be_linker_script_provide_symbol(const struct sym_entry *se)
{
- const char *symbol = sym_name(se);
+ const char *symbol = sym_entry_name(se);
int len = se->len - 1;
if (len < 8)
@@ -672,8 +808,8 @@ static int compare_symbols(const void *a, const void *b)
return wa - wb;
/* sort by the number of prefix underscores */
- wa = strspn(sym_name(sa), "_");
- wb = strspn(sym_name(sb), "_");
+ wa = strspn(sym_entry_name(sa), "_");
+ wb = strspn(sym_entry_name(sb), "_");
if (wa != wb)
return wa - wb;
@@ -688,10 +824,14 @@ static void sort_symbols(void)
int main(int argc, char **argv)
{
+ const char *in, *sysmap = NULL, *out_bin_name;
+ FILE *sysmap_out = NULL, *out_bin_file;
+
while (1) {
static const struct option long_options[] = {
{"all-symbols", no_argument, &all_symbols, 1},
{"pc-relative", no_argument, &pc_relative, 1},
+ {"sysmap", required_argument, NULL, 's'},
{},
};
@@ -699,18 +839,55 @@ int main(int argc, char **argv)
if (c == -1)
break;
- if (c != 0)
+ if (c == 's')
+ sysmap = optarg;
+ else if (c != 0)
usage();
}
- if (optind >= argc)
+ if (optind + 2 != argc && !(sysmap && optind + 1 == argc))
usage();
- read_map(argv[optind]);
+ in = argv[optind];
+ if (sysmap) {
+ sysmap_out = fopen(sysmap, "w");
+ if (!sysmap_out) {
+ perror(sysmap);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (optind + 1 == argc) {
+ read_elf(in, sysmap_out);
+ if (ferror(sysmap_out) || fclose(sysmap_out)) {
+ perror(sysmap);
+ exit(EXIT_FAILURE);
+ }
+ return 0;
+ }
+
+ out_bin_name = argv[optind + 1];
+ out_bin_file = fopen(out_bin_name, "w");
+ if (!out_bin_file) {
+ perror(out_bin_name);
+ exit(EXIT_FAILURE);
+ }
+
+ read_elf(in, sysmap_out);
+ if (sysmap_out && (ferror(sysmap_out) || fclose(sysmap_out))) {
+ perror(sysmap);
+ exit(EXIT_FAILURE);
+ }
shrink_table();
sort_symbols();
optimize_token_table();
- write_src();
+ write_src(out_bin_file, out_bin_name);
+
+ /* Write errors are latched in the stream, so one check at the end covers them all. */
+ if (ferror(out_bin_file) || fclose(out_bin_file)) {
+ perror(out_bin_name);
+ exit(EXIT_FAILURE);
+ }
return 0;
}
diff --git a/scripts/kallsyms.h b/scripts/kallsyms.h
new file mode 100644
index 000000000000..4b2801ba6285
--- /dev/null
+++ b/scripts/kallsyms.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef KALLSYMS_H
+#define KALLSYMS_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <array_size.h>
+#include "elf-parse.h"
+
+static inline bool string_starts_with(const char *s, const char *prefix)
+{
+ return strncmp(s, prefix, strlen(prefix)) == 0;
+}
+
+static inline bool string_ends_with(const char *s, const char *suffix)
+{
+ size_t len = strlen(s), suffix_len = strlen(suffix);
+
+ return len >= suffix_len && strcmp(s + len - suffix_len, suffix) == 0;
+}
+
+/* A symbol as nm lists it. */
+struct sysmap_symbol {
+ unsigned long long addr;
+ const char *name;
+ char type;
+};
+
+/* The symbols of an ELF file that System.map lists, in its order. */
+struct sysmap {
+ struct sysmap_symbol *syms;
+ size_t nr_syms;
+ int addr_width; /* hex digits of an address */
+ struct elf_file elf; /* the mapping the names point into */
+};
+
+struct sysmap *sysmap_read(const char *path);
+void sysmap_write(const struct sysmap *map, FILE *out);
+void sysmap_free(struct sysmap *map);
+
+#endif /* KALLSYMS_H */
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index fe8ba09b0039..31c6fd25e6ec 100644
--- a/scripts/kconfig/conf.c
+++ b/scripts/kconfig/conf.c
@@ -186,12 +186,23 @@ static void conf_set_all_new_symbols(enum conf_def_mode mode)
if (mode == def_random) {
int n, p[3];
+ bool warned = false;
char *env = getenv("KCONFIG_PROBABILITY");
n = 0;
while (env && *env) {
char *endp;
- int tmp = strtol(env, &endp, 10);
+ long tmp = strtol(env, &endp, 10);
+
+ if (!isdigit((unsigned char)*env) ||
+ (*endp && *endp != ':') ||
+ (*endp == ':' && (!endp[1] || n == 2))) {
+ if (!warned) {
+ fprintf(stderr,
+ "warning: KCONFIG_PROBABILITY has malformed format\n");
+ warned = true;
+ }
+ }
if (tmp >= 0 && tmp <= 100) {
p[n++] = tmp;
@@ -227,6 +238,9 @@ static void conf_set_all_new_symbols(enum conf_def_mode mode)
perror("KCONFIG_PROBABILITY");
exit(1);
}
+
+ if (warned && getenv("KCONFIG_WERROR"))
+ exit(1);
}
menu_for_each_entry(menu) {
@@ -469,6 +483,9 @@ static void conf_choice(struct menu *menu)
}
printf("\n");
}
+ /* Nothing to ask if none of the choice values is visible. */
+ if (!cnt)
+ return;
printf("%*schoice", indent - 1, "");
if (cnt == 1) {
printf("[1]: 1\n");
diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index 4234a51d16fd..2227d89d6328 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -354,6 +354,12 @@ static int conf_set_sym_val(struct symbol *sym, int def, int def_flags, char *p)
case S_INT:
case S_HEX:
if (sym_string_valid(sym, p)) {
+ if (def != S_DEF_AUTO &&
+ !sym_string_check_bounds(sym, p))
+ /* hex uses 64-bit unsigned integer */
+ conf_warning("value '%s' for %s is outside the 64-bit %s integer bounds",
+ p, sym->name,
+ sym->type == S_INT ? "signed" : "unsigned");
sym->def[def].val = xstrdup(p);
sym->flags |= def_flags;
} else {
diff --git a/scripts/kconfig/kconfig-sym-check.pl b/scripts/kconfig/kconfig-sym-check.pl
index daa5285fdefc..c8dd07f8b27c 100755
--- a/scripts/kconfig/kconfig-sym-check.pl
+++ b/scripts/kconfig/kconfig-sym-check.pl
@@ -90,7 +90,7 @@ foreach my $file (@files) {
next;
}
- if (/^\s*(default|def_bool|def_tristate|select|depends\s+on|imply|visible\s+if|range|if|bool|tristate|int|hex|string|prompt)\s+(.+)\s*$/) {
+ if (/^\s*(default|def_bool|def_tristate|def_string|def_int|def_hex|select|depends\s+on|imply|visible\s+if|range|if|bool|tristate|int|hex|string|prompt)\s+(.+)\s*$/) {
my $s = $2;
$s =~ s/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'//g;
$s =~ s/#.*//;
diff --git a/scripts/kconfig/lexer.l b/scripts/kconfig/lexer.l
index a6155422b4a6..1fa521199d4a 100644
--- a/scripts/kconfig/lexer.l
+++ b/scripts/kconfig/lexer.l
@@ -105,6 +105,9 @@ n [A-Za-z0-9_-]
"comment" return T_COMMENT;
"config" return T_CONFIG;
"def_bool" return T_DEF_BOOL;
+"def_hex" return T_DEF_HEX;
+"def_int" return T_DEF_INT;
+"def_string" return T_DEF_STRING;
"def_tristate" return T_DEF_TRISTATE;
"default" return T_DEFAULT;
"depends" return T_DEPENDS;
diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h
index 7e6f6ca299cf..bbc99f75b416 100644
--- a/scripts/kconfig/lkc.h
+++ b/scripts/kconfig/lkc.h
@@ -89,7 +89,7 @@ struct property *menu_add_prompt(enum prop_type type, const char *prompt,
struct expr *dep);
void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep);
void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep);
-void menu_finalize(void);
+int menu_finalize(void);
void menu_set_type(int type);
extern struct menu rootmenu;
diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h
index 8914b4e8f2a8..8b436c87ba4a 100644
--- a/scripts/kconfig/lkc_proto.h
+++ b/scripts/kconfig/lkc_proto.h
@@ -31,6 +31,7 @@ bool sym_set_tristate_value(struct symbol *sym,tristate tri);
void choice_set_value(struct menu *choice, struct symbol *sym);
tristate sym_toggle_tristate_value(struct symbol *sym);
bool sym_string_valid(struct symbol *sym, const char *newval);
+bool sym_string_check_bounds(struct symbol *sym, const char *str);
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);
diff --git a/scripts/kconfig/lxdialog/textbox.c b/scripts/kconfig/lxdialog/textbox.c
index 0abaf635978f..3fe7682fc8a8 100644
--- a/scripts/kconfig/lxdialog/textbox.c
+++ b/scripts/kconfig/lxdialog/textbox.c
@@ -145,8 +145,6 @@ static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw,
/*
* Display text from a file in a dialog box.
- *
- * keys is a null-terminated array
*/
int dialog_textbox(const char *title, const char *tbuf, int initial_height,
int initial_width, int *_vscroll, int *_hscroll,
diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c
index 9c079e92a9ed..bda3e2650216 100644
--- a/scripts/kconfig/menu.c
+++ b/scripts/kconfig/menu.c
@@ -234,17 +234,42 @@ void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep)
menu_add_prop(type, expr_alloc_symbol(sym), dep);
}
-static int menu_validate_number(struct symbol *sym, struct symbol *sym2)
-{
- return sym2->type == S_INT || sym2->type == S_HEX ||
- (sym2->type == S_UNKNOWN && sym_string_valid(sym, sym2->name));
+/* Validate the sym2 value for numeric sym. */
+static int menu_validate_number(struct symbol *sym, struct symbol *sym2,
+ const struct property *prop)
+{
+ if (sym->type != S_INT && sym->type != S_HEX)
+ return 0;
+
+ if (sym2->type == sym->type)
+ return 0;
+
+ if (sym2->type != S_UNKNOWN ||
+ !sym_string_valid(sym, sym2->name)) {
+ fprintf(stderr, "%s:%d: error: '%s' is an invalid value for '%s'\n",
+ prop->filename, prop->lineno, sym2->name,
+ sym_type_name(sym->type));
+ return 1;
+ }
+
+ if (!sym_string_check_bounds(sym, sym2->name)) {
+ fprintf(stderr,
+ "%s:%d: error: %s constant '%s' is outside the 64-bit %s bounds\n",
+ prop->filename, prop->lineno, sym_type_name(sym->type),
+ sym2->name, sym->type == S_INT ? "signed" : "unsigned");
+
+ return 1;
+ }
+
+ return 0;
}
-static void sym_check_prop(struct symbol *sym)
+static int sym_check_prop(struct symbol *sym)
{
struct property *prop;
struct symbol *sym2;
char *use;
+ int errors = 0;
for (prop = sym->prop; prop; prop = prop->next) {
switch (prop->type) {
@@ -258,10 +283,7 @@ static void sym_check_prop(struct symbol *sym)
break;
sym2 = prop_get_symbol(prop);
if (sym->type == S_HEX || sym->type == S_INT) {
- if (!menu_validate_number(sym, sym2))
- prop_warn(prop,
- "'%s': number is invalid",
- sym->name);
+ errors += menu_validate_number(sym, sym2, prop);
}
if (sym_is_choice(sym)) {
struct menu *choice = sym_get_choice_menu(sym2);
@@ -287,27 +309,33 @@ static void sym_check_prop(struct symbol *sym)
"'%s' has wrong type. '%s' only "
"accept arguments of bool and "
"tristate type", sym2->name, use);
+ if (sym_is_choice_value(sym2))
+ prop_warn(prop,
+ "config symbol '%s' uses %s for '%s', but '%s' is a choice value",
+ sym->name, use, sym2->name, sym2->name);
break;
case P_RANGE:
if (sym->type != S_INT && sym->type != S_HEX)
prop_warn(prop, "range is only allowed "
"for int or hex symbols");
- if (!menu_validate_number(sym, prop->expr->left.sym) ||
- !menu_validate_number(sym, prop->expr->right.sym))
- prop_warn(prop, "range is invalid");
+ errors += menu_validate_number(sym, prop->expr->left.sym, prop);
+ errors += menu_validate_number(sym, prop->expr->right.sym, prop);
break;
default:
;
}
}
+
+ return errors;
}
-static void _menu_finalize(struct menu *parent, bool inside_choice)
+static int _menu_finalize(struct menu *parent, bool inside_choice)
{
struct menu *menu, *last_menu;
struct symbol *sym;
struct property *prop;
struct expr *basedep, *dep, *dep2;
+ int errors = 0;
sym = parent->sym;
if (parent->list) {
@@ -393,7 +421,7 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
* moving on
*/
for (menu = parent->list; menu; menu = menu->next)
- _menu_finalize(menu, sym && sym_is_choice(sym));
+ errors += _menu_finalize(menu, sym && sym_is_choice(sym));
} else if (!inside_choice && sym) {
/*
* Automatic submenu creation. If sym is a symbol and A, B, C,
@@ -461,7 +489,7 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
}
/* Superset, put in submenu */
next:
- _menu_finalize(menu, false);
+ errors += _menu_finalize(menu, false);
menu->parent = parent;
last_menu = menu;
}
@@ -519,14 +547,16 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
menu_warn(parent, "config symbol defined without type");
/* Check properties connected to this symbol */
- sym_check_prop(sym);
+ errors += sym_check_prop(sym);
sym->flags |= SYMBOL_WARNED;
}
+
+ return errors;
}
-void menu_finalize(void)
+int menu_finalize(void)
{
- _menu_finalize(&rootmenu, false);
+ return _menu_finalize(&rootmenu, false);
}
bool menu_has_prompt(const struct menu *menu)
diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y
index 5fb6f07b6ad2..ecb18c213d83 100644
--- a/scripts/kconfig/parser.y
+++ b/scripts/kconfig/parser.y
@@ -53,6 +53,9 @@ struct menu *current_menu, *current_entry, *current_choice;
%token T_CONFIG
%token T_DEFAULT
%token T_DEF_BOOL
+%token T_DEF_HEX
+%token T_DEF_INT
+%token T_DEF_STRING
%token T_DEF_TRISTATE
%token T_DEPENDS
%token T_ENDCHOICE
@@ -309,6 +312,9 @@ type:
default:
T_DEFAULT { $$ = S_UNKNOWN; }
| T_DEF_BOOL { $$ = S_BOOLEAN; }
+ | T_DEF_HEX { $$ = S_HEX; }
+ | T_DEF_INT { $$ = S_INT; }
+ | T_DEF_STRING { $$ = S_STRING; }
| T_DEF_TRISTATE { $$ = S_TRISTATE; }
/* if entry */
@@ -587,7 +593,7 @@ void conf_parse(const char *name)
menu_add_prompt(P_MENU, "Main menu", NULL);
}
- menu_finalize();
+ yynerrs += menu_finalize();
menu_for_each_entry(menu) {
struct menu *child;
diff --git a/scripts/kconfig/streamline_config.pl b/scripts/kconfig/streamline_config.pl
index 8677d1ca06a7..6a52a98eb40e 100755
--- a/scripts/kconfig/streamline_config.pl
+++ b/scripts/kconfig/streamline_config.pl
@@ -145,6 +145,7 @@ my %prompts;
my %objects;
my %config2kfile;
my %defaults;
+my %types;
my $var;
my $iflevel = 0;
my @ifdeps;
@@ -224,6 +225,7 @@ sub read_kconfig {
} elsif ($state ne "NONE" && /^\s*def(_(bool|tristate)|ault)\s+(\S.*)$/) {
my $dep = $3;
$defaults{$config} = 1;
+ $types{$config} = $2 if (defined($2));
if ($dep !~ /^\s*(y|m|n)\s*$/) {
$dep =~ s/.*\sif\s+//;
$depends{$config} .= " " . $dep;
@@ -240,7 +242,13 @@ sub read_kconfig {
}
# configs without prompts must be selected
- } elsif ($state ne "NONE" && /^\s*(tristate\s+\S|prompt\b)/) {
+ } elsif ($state ne "NONE" && /^\s*(bool|boolean)(\s+\S.*)?$/) {
+ $types{$config} = "bool";
+ $prompts{$config} = 1 if (defined($2));
+ } elsif ($state ne "NONE" && /^\s*tristate(\s+\S.*)?$/) {
+ $types{$config} = "tristate";
+ $prompts{$config} = 1 if (defined($1));
+ } elsif ($state ne "NONE" && /^\s*prompt\b/) {
# note if the config has a prompt
$prompts{$config} = 1;
@@ -707,7 +715,15 @@ foreach my $module (keys(%modules)) {
}
print STDERR "module $module did not have configs";
foreach my $conf (@arr) {
- print STDERR " " , $conf;
+ my $c = $conf;
+ $c =~ s/^CONFIG_//;
+ if (!$localyesconfig && defined($types{$c}) && $types{$c} eq "bool") {
+ print STDERR " $conf (boolean, not supported with =m)";
+ } elsif (defined($types{$c})) {
+ print STDERR " $conf ($types{$c})";
+ } else {
+ print STDERR " " , $conf;
+ }
}
print STDERR "\n";
}
diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c
index 7e81b3676ee9..4ced4dd6485b 100644
--- a/scripts/kconfig/symbol.c
+++ b/scripts/kconfig/symbol.c
@@ -5,6 +5,7 @@
#include <sys/types.h>
#include <ctype.h>
+#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <regex.h>
@@ -141,8 +142,15 @@ struct property *sym_get_range_prop(struct symbol *sym)
return NULL;
}
-static long long sym_get_range_val(struct symbol *sym, int base)
+union range_value {
+ long long s;
+ unsigned long long u;
+};
+
+static union range_value sym_get_range_val(struct symbol *sym, int base)
{
+ union range_value val;
+
sym_calc_value(sym);
switch (sym->type) {
case S_INT:
@@ -154,15 +162,25 @@ static long long sym_get_range_val(struct symbol *sym, int base)
default:
break;
}
- return strtoll(sym->curr.val, NULL, base);
+ if (base == 10)
+ val.s = strtoll(sym->curr.val, NULL, base);
+ else
+ /* HEX */
+ val.u = strtoull(sym->curr.val, NULL, base);
+ return val;
}
-static void sym_validate_range(struct symbol *sym)
+/*
+ * Return the nearest range bound for an out-of-range default value.
+ * Return NULL if the value is valid or the symbol has no active range.
+ */
+static struct symbol *sym_get_near_range_bound(struct symbol *sym,
+ const char *value)
{
struct property *prop;
struct symbol *range_sym;
int base;
- long long val, val2;
+ union range_value val, val2;
switch (sym->type) {
case S_INT:
@@ -172,21 +190,40 @@ static void sym_validate_range(struct symbol *sym)
base = 16;
break;
default:
- return;
+ return NULL;
}
prop = sym_get_range_prop(sym);
if (!prop)
- return;
- val = strtoll(sym->curr.val, NULL, base);
+ return NULL;
+ if (base == 10)
+ val.s = strtoll(value, NULL, base);
+ else
+ val.u = strtoull(value, NULL, base);
range_sym = prop->expr->left.sym;
val2 = sym_get_range_val(range_sym, base);
- if (val >= val2) {
+
+ if (base == 10 && val.s >= val2.s) {
range_sym = prop->expr->right.sym;
val2 = sym_get_range_val(range_sym, base);
- if (val <= val2)
- return;
+ if (val.s <= val2.s)
+ return NULL;
+ } else if (base == 16 && val.u >= val2.u) {
+ range_sym = prop->expr->right.sym;
+ val2 = sym_get_range_val(range_sym, base);
+ if (val.u <= val2.u)
+ return NULL;
}
- sym->curr.val = range_sym->curr.val;
+
+ return range_sym;
+}
+
+static void sym_validate_range(struct symbol *sym)
+{
+ struct symbol *range_sym;
+
+ range_sym = sym_get_near_range_bound(sym, sym->curr.val);
+ if (range_sym)
+ sym->curr.val = range_sym->curr.val;
}
static void sym_set_changed(struct symbol *sym)
@@ -711,10 +748,26 @@ bool sym_string_valid(struct symbol *sym, const char *str)
}
}
+bool sym_string_check_bounds(struct symbol *sym, const char *str)
+{
+ errno = 0;
+
+ if (sym->type == S_INT)
+ strtoll(str, NULL, 10);
+ else if (sym->type == S_HEX)
+ strtoull(str, NULL, 16);
+ else
+ /* string */
+ return true;
+
+ return errno != ERANGE;
+}
+
bool sym_string_within_range(struct symbol *sym, const char *str)
{
struct property *prop;
long long val;
+ unsigned long long uval;
switch (sym->type) {
case S_STRING:
@@ -722,21 +775,25 @@ bool sym_string_within_range(struct symbol *sym, const char *str)
case S_INT:
if (!sym_string_valid(sym, str))
return false;
+ if (!sym_string_check_bounds(sym, str))
+ return false;
prop = sym_get_range_prop(sym);
if (!prop)
return true;
val = strtoll(str, NULL, 10);
- return val >= sym_get_range_val(prop->expr->left.sym, 10) &&
- val <= sym_get_range_val(prop->expr->right.sym, 10);
+ return val >= sym_get_range_val(prop->expr->left.sym, 10).s &&
+ val <= sym_get_range_val(prop->expr->right.sym, 10).s;
case S_HEX:
if (!sym_string_valid(sym, str))
return false;
+ if (!sym_string_check_bounds(sym, str))
+ return false;
prop = sym_get_range_prop(sym);
if (!prop)
return true;
- val = strtoll(str, NULL, 16);
- return val >= sym_get_range_val(prop->expr->left.sym, 16) &&
- val <= sym_get_range_val(prop->expr->right.sym, 16);
+ uval = strtoull(str, NULL, 16);
+ return uval >= sym_get_range_val(prop->expr->left.sym, 16).u &&
+ uval <= sym_get_range_val(prop->expr->right.sym, 16).u;
case S_BOOLEAN:
case S_TRISTATE:
switch (str[0]) {
@@ -812,7 +869,7 @@ bool sym_set_string_value(struct symbol *sym, const char *newval)
const char *sym_get_string_default(struct symbol *sym)
{
struct property *prop;
- struct symbol *ds;
+ struct symbol *ds, *range_sym;
const char *str = "";
tristate val;
@@ -830,11 +887,6 @@ const char *sym_get_string_default(struct symbol *sym)
val = EXPR_AND(expr_calc_value(prop->expr), prop->visible.tri);
break;
default:
- /*
- * The following fails to handle the situation
- * where a default value is further limited by
- * the valid range.
- */
ds = prop_get_symbol(prop);
if (ds != NULL) {
sym_calc_value(ds);
@@ -878,6 +930,11 @@ const char *sym_get_string_default(struct symbol *sym)
default:
break;
}
+
+ range_sym = sym_get_near_range_bound(sym, str);
+ if (range_sym)
+ str = range_sym->curr.val;
+
return str;
}
@@ -1196,6 +1253,7 @@ static struct symbol *sym_check_expr_deps(const struct expr *e)
case E_GTH:
case E_LEQ:
case E_LTH:
+ case E_RANGE:
case E_UNEQUAL:
sym = sym_check_deps(e->left.sym);
if (sym)
@@ -1243,7 +1301,9 @@ static struct symbol *sym_check_sym_deps(struct symbol *sym)
sym2 = sym_check_expr_deps(prop->visible.expr);
if (sym2)
break;
- if (prop->type != P_DEFAULT || sym_is_choice(sym))
+ if (sym_is_choice(sym))
+ continue;
+ if (prop->type != P_DEFAULT && prop->type != P_RANGE)
continue;
stack.expr = &prop->expr;
sym2 = sym_check_expr_deps(prop->expr);
diff --git a/scripts/kconfig/tests/conftest.py b/scripts/kconfig/tests/conftest.py
index 66f95e4ed58c..d22c080a4e40 100644
--- a/scripts/kconfig/tests/conftest.py
+++ b/scripts/kconfig/tests/conftest.py
@@ -57,9 +57,12 @@ class Conf:
# Override 'srctree' environment to make the test as the top directory
extra_env['srctree'] = self._test_dir
- # Clear KCONFIG_DEFCONFIG_LIST to keep unit tests from being affected
- # by the user's environment.
- extra_env['KCONFIG_DEFCONFIG_LIST'] = ''
+ # Clear all KCONFIG_* environment variables
+ env = {
+ varname: os.environ[varname]
+ for varname in os.environ.keys()
+ if not varname.startswith("KCONFIG_")
+ }
# Run Kconfig in a temporary directory.
# This directory is automatically removed when done.
@@ -75,7 +78,7 @@ class Conf:
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=temp_dir,
- env=dict(os.environ, **extra_env))
+ env=dict(env, **extra_env))
# If input key sequence is given, feed it to stdin.
if in_keys:
@@ -156,13 +159,13 @@ class Conf:
return self._run_conf('--oldconfig', dot_config=dot_config,
interactive=True, in_keys=in_keys)
- def olddefconfig(self, dot_config=None):
+ def olddefconfig(self, dot_config=None, **kw):
"""Run olddefconfig.
dot_config: .config file to use for configuration base (optional)
returncode: exit status of the Kconfig executable
"""
- return self._run_conf('--olddefconfig', dot_config=dot_config)
+ return self._run_conf('--olddefconfig', dot_config=dot_config, **kw)
def defconfig(self, defconfig):
"""Run defconfig.
@@ -226,13 +229,15 @@ class Conf:
return self._allconfig('rand', all_config, extra_env=extra_env)
- def savedefconfig(self, dot_config):
+ def savedefconfig(self, dot_config, out_file='defconfig', **kw):
"""Run savedefconfig.
dot_config: .config file for input
+ out_file: defconfig file for output
returncode: exit status of the Kconfig executable
"""
- return self._run_conf('--savedefconfig', out_file='defconfig')
+ return self._run_conf('--savedefconfig={}'.format(out_file),
+ dot_config=dot_config, out_file=out_file, **kw)
def listnewconfig(self, dot_config=None):
"""Run listnewconfig.
@@ -312,6 +317,12 @@ class Conf:
return self._matches('stderr', expected)
+@pytest.fixture(autouse=True)
+def clear_werror(monkeypatch):
+ # extra_env can set strict mode, but cannot remove an inherited flag.
+ monkeypatch.delenv('KCONFIG_WERROR', raising=False)
+
+
@pytest.fixture(scope="module")
def conf(request):
"""Create a Conf instance and provide it to test functions."""
diff --git a/scripts/kconfig/tests/def_type/Kconfig b/scripts/kconfig/tests/def_type/Kconfig
new file mode 100644
index 000000000000..fbee37a63179
--- /dev/null
+++ b/scripts/kconfig/tests/def_type/Kconfig
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0
+# The def_<type> shorthands: a type definition plus a default value.
+
+config MODULES
+ bool "Enable loadable module support"
+ modules
+ default y
+
+config GUARD
+ bool "Guard symbol"
+
+config DEF_BOOL
+ def_bool GUARD
+
+config DEF_TRISTATE
+ def_tristate m if GUARD
+
+config DEF_STRING
+ def_string "guarded" if GUARD
+ default "fallback"
+
+config DEF_INT
+ def_int 64 if GUARD
+ default 32
+
+config DEF_HEX
+ def_hex 0xdead if GUARD
+ default 0x0
diff --git a/scripts/kconfig/tests/def_type/__init__.py b/scripts/kconfig/tests/def_type/__init__.py
new file mode 100644
index 000000000000..1ebaf5da07c8
--- /dev/null
+++ b/scripts/kconfig/tests/def_type/__init__.py
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+"""
+Set a symbol's type and its default value in one line.
+
+"def_bool", "def_tristate", "def_string", "def_int" and "def_hex" are
+shorthand for a type definition plus a "default" property. Check that
+each one sets the type, and that an "if" on the shorthand does not
+disturb the usual default cascade: the shorthand is only the first arm
+of the list, so a later "default" still applies when its condition is
+not met.
+"""
+
+def test(conf):
+ assert conf.olddefconfig(dot_config='guard_y.config') == 0
+ assert conf.config_matches('expected_guard_y')
+
+ assert conf.olddefconfig(dot_config='guard_n.config') == 0
+ assert conf.config_matches('expected_guard_n')
diff --git a/scripts/kconfig/tests/def_type/expected_guard_n b/scripts/kconfig/tests/def_type/expected_guard_n
new file mode 100644
index 000000000000..14719720a8b9
--- /dev/null
+++ b/scripts/kconfig/tests/def_type/expected_guard_n
@@ -0,0 +1,9 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Main menu
+#
+CONFIG_MODULES=y
+# CONFIG_GUARD is not set
+CONFIG_DEF_STRING="fallback"
+CONFIG_DEF_INT=32
+CONFIG_DEF_HEX=0x0
diff --git a/scripts/kconfig/tests/def_type/expected_guard_y b/scripts/kconfig/tests/def_type/expected_guard_y
new file mode 100644
index 000000000000..b2844072d0b8
--- /dev/null
+++ b/scripts/kconfig/tests/def_type/expected_guard_y
@@ -0,0 +1,11 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Main menu
+#
+CONFIG_MODULES=y
+CONFIG_GUARD=y
+CONFIG_DEF_BOOL=y
+CONFIG_DEF_TRISTATE=m
+CONFIG_DEF_STRING="guarded"
+CONFIG_DEF_INT=64
+CONFIG_DEF_HEX=0xdead
diff --git a/scripts/kconfig/tests/def_type/guard_n.config b/scripts/kconfig/tests/def_type/guard_n.config
new file mode 100644
index 000000000000..ed9ad6c1a2d4
--- /dev/null
+++ b/scripts/kconfig/tests/def_type/guard_n.config
@@ -0,0 +1 @@
+# CONFIG_GUARD is not set
diff --git a/scripts/kconfig/tests/def_type/guard_y.config b/scripts/kconfig/tests/def_type/guard_y.config
new file mode 100644
index 000000000000..afe35b084542
--- /dev/null
+++ b/scripts/kconfig/tests/def_type/guard_y.config
@@ -0,0 +1 @@
+CONFIG_GUARD=y
diff --git a/scripts/kconfig/tests/err_num_bounds/Kconfig b/scripts/kconfig/tests/err_num_bounds/Kconfig
new file mode 100644
index 000000000000..c439366c03b6
--- /dev/null
+++ b/scripts/kconfig/tests/err_num_bounds/Kconfig
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: GPL-2.0
+# Test bounds checks for 'int' and 'hex' constants
+
+config INT_SOURCE
+ int
+
+config HEX_SOURCE
+ hex
+
+config BOOL_SOURCE
+ bool
+
+# Valid values at the limits of the type
+
+config INT_MIN
+ int
+ default -9223372036854775808
+
+config INT_MAX
+ int
+ default 9223372036854775807
+
+config HEX_MIN
+ hex
+ default 0x0
+
+config HEX_MAX
+ hex
+ default 0xffffffffffffffff
+
+config INT_RANGE_LIMITS
+ int
+ range -9223372036854775808 9223372036854775807
+
+config HEX_RANGE_LIMITS
+ hex
+ range 0 0xffffffffffffffff
+
+config INT_FROM_INT
+ int
+ default INT_SOURCE
+
+config HEX_FROM_HEX
+ hex
+ default HEX_SOURCE
+
+# Constants outside the bounds
+
+config INT_DEFAULT_TOO_HIGH
+ int
+ default 10000000000000000000
+
+config INT_DEFAULT_TOO_LOW
+ int
+ default -9223372036854775809
+
+config INT_RANGE_TOO_HIGH
+ int
+ range 0 10000000000000000000
+
+config INT_RANGE_TOO_LOW
+ int
+ range -10000000000000000000 0
+
+config INT_RANGE_BOTH_OUTSIDE
+ int
+ range -9223372036854775809 10000000000000000000
+
+config HEX_DEFAULT_TOO_HIGH
+ hex
+ default 0x10000000000000000
+
+config HEX_RANGE_TOO_HIGH
+ hex
+ range 0 0x10000000000000000
+
+config HEX_RANGE_BOTH_TOO_HIGH
+ hex
+ range 0x10000000000000000 0x20000000000000000
diff --git a/scripts/kconfig/tests/err_num_bounds/__init__.py b/scripts/kconfig/tests/err_num_bounds/__init__.py
new file mode 100644
index 000000000000..72ac6aa24491
--- /dev/null
+++ b/scripts/kconfig/tests/err_num_bounds/__init__.py
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+"""
+Detect constants outside the 'int' and 'hex' bounds.
+
+An int constant must fit in a signed 64-bit integer, and a hex constant must
+fit in an unsigned 64-bit integer.
+"""
+
+
+def test(conf):
+ assert conf.olddefconfig() == 1
+ assert conf.stderr_matches('expected_stderr')
diff --git a/scripts/kconfig/tests/err_num_bounds/expected_stderr b/scripts/kconfig/tests/err_num_bounds/expected_stderr
new file mode 100644
index 000000000000..5f89cf4afff8
--- /dev/null
+++ b/scripts/kconfig/tests/err_num_bounds/expected_stderr
@@ -0,0 +1,10 @@
+Kconfig:51: error: integer constant '10000000000000000000' is outside the 64-bit signed bounds
+Kconfig:55: error: integer constant '-9223372036854775809' is outside the 64-bit signed bounds
+Kconfig:59: error: integer constant '10000000000000000000' is outside the 64-bit signed bounds
+Kconfig:63: error: integer constant '-10000000000000000000' is outside the 64-bit signed bounds
+Kconfig:67: error: integer constant '-9223372036854775809' is outside the 64-bit signed bounds
+Kconfig:67: error: integer constant '10000000000000000000' is outside the 64-bit signed bounds
+Kconfig:71: error: hex constant '0x10000000000000000' is outside the 64-bit unsigned bounds
+Kconfig:75: error: hex constant '0x10000000000000000' is outside the 64-bit unsigned bounds
+Kconfig:79: error: hex constant '0x10000000000000000' is outside the 64-bit unsigned bounds
+Kconfig:79: error: hex constant '0x20000000000000000' is outside the 64-bit unsigned bounds
diff --git a/scripts/kconfig/tests/err_num_mismatch/Kconfig b/scripts/kconfig/tests/err_num_mismatch/Kconfig
new file mode 100644
index 000000000000..8406f3bb6419
--- /dev/null
+++ b/scripts/kconfig/tests/err_num_mismatch/Kconfig
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: GPL-2.0
+# Test 'int' and 'hex' symbols that reference each other
+
+config INT_SOURCE
+ int
+
+config HEX_SOURCE
+ hex
+
+# 'hex' reference from 'int'
+
+config INT_DEFAULT_HEX
+ int
+ default HEX_SOURCE
+
+config INT_RANGE_HEX
+ int
+ range HEX_SOURCE 1
+
+# A hex symbol must not reference an int symbol
+
+config HEX_DEFAULT_INT
+ hex
+ default INT_SOURCE
+
+config HEX_RANGE_INT
+ hex
+ range INT_SOURCE 0x1
+
+# Referencing the same type is valid
+
+config INT_FROM_INT
+ int
+ default INT_SOURCE
+
+config HEX_FROM_HEX
+ hex
+ range 0 HEX_SOURCE
diff --git a/scripts/kconfig/tests/err_num_mismatch/__init__.py b/scripts/kconfig/tests/err_num_mismatch/__init__.py
new file mode 100644
index 000000000000..275f2a6e9a5b
--- /dev/null
+++ b/scripts/kconfig/tests/err_num_mismatch/__init__.py
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+"""
+Reject direct references ('default' or 'range') between int and hex options.
+"""
+
+
+def test(conf):
+ assert conf.olddefconfig() == 1
+ assert conf.stderr_matches('expected_stderr')
diff --git a/scripts/kconfig/tests/err_num_mismatch/expected_stderr b/scripts/kconfig/tests/err_num_mismatch/expected_stderr
new file mode 100644
index 000000000000..587c34467ae6
--- /dev/null
+++ b/scripts/kconfig/tests/err_num_mismatch/expected_stderr
@@ -0,0 +1,4 @@
+Kconfig:14: error: 'HEX_SOURCE' is an invalid value for 'integer'
+Kconfig:18: error: 'HEX_SOURCE' is an invalid value for 'integer'
+Kconfig:24: error: 'INT_SOURCE' is an invalid value for 'hex'
+Kconfig:28: error: 'INT_SOURCE' is an invalid value for 'hex'
diff --git a/scripts/kconfig/tests/err_num_non_numeric_ref/Kconfig b/scripts/kconfig/tests/err_num_non_numeric_ref/Kconfig
new file mode 100644
index 000000000000..0ca7a4a460f6
--- /dev/null
+++ b/scripts/kconfig/tests/err_num_non_numeric_ref/Kconfig
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: GPL-2.0
+# Test non-numeric symbol references from numeric symbols
+
+config BOOL_SOURCE
+ bool
+
+config TRISTATE_SOURCE
+ tristate
+
+config STRING_SOURCE
+ string
+
+# Invalid int defaults
+
+config INT_DEFAULT_BOOL
+ int
+ default BOOL_SOURCE
+
+config INT_DEFAULT_TRISTATE
+ int
+ default TRISTATE_SOURCE
+
+config INT_DEFAULT_STRING
+ int
+ default STRING_SOURCE
+
+# Invalid hex defaults
+
+config HEX_DEFAULT_BOOL
+ hex
+ default BOOL_SOURCE
+
+config HEX_DEFAULT_TRISTATE
+ hex
+ default TRISTATE_SOURCE
+
+config HEX_DEFAULT_STRING
+ hex
+ default STRING_SOURCE
+
+# Invalid int ranges
+
+config INT_RANGE_BOOL
+ int
+ range BOOL_SOURCE 1
+
+config INT_RANGE_TRISTATE
+ int
+ range TRISTATE_SOURCE 1
+
+config INT_RANGE_STRING
+ int
+ range STRING_SOURCE 1
+
+config INT_RANGE_MULTIPLE
+ int
+ range BOOL_SOURCE TRISTATE_SOURCE
+
+# Invalid hex ranges
+
+config HEX_RANGE_BOOL
+ hex
+ range BOOL_SOURCE 0x1
+
+config HEX_RANGE_TRISTATE
+ hex
+ range TRISTATE_SOURCE 0x1
+
+config HEX_RANGE_STRING
+ hex
+ range STRING_SOURCE 0x1
+
+config HEX_RANGE_MULTIPLE
+ hex
+ range BOOL_SOURCE TRISTATE_SOURCE
diff --git a/scripts/kconfig/tests/err_num_non_numeric_ref/__init__.py b/scripts/kconfig/tests/err_num_non_numeric_ref/__init__.py
new file mode 100644
index 000000000000..9632907abead
--- /dev/null
+++ b/scripts/kconfig/tests/err_num_non_numeric_ref/__init__.py
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+"""
+Reject nonnumeric symbol references from int and hex properties.
+"""
+
+
+def test(conf):
+ assert conf.olddefconfig() == 1
+ assert conf.stderr_matches('expected_stderr')
diff --git a/scripts/kconfig/tests/err_num_non_numeric_ref/expected_stderr b/scripts/kconfig/tests/err_num_non_numeric_ref/expected_stderr
new file mode 100644
index 000000000000..005f855ecbdd
--- /dev/null
+++ b/scripts/kconfig/tests/err_num_non_numeric_ref/expected_stderr
@@ -0,0 +1,16 @@
+Kconfig:17: error: 'BOOL_SOURCE' is an invalid value for 'integer'
+Kconfig:21: error: 'TRISTATE_SOURCE' is an invalid value for 'integer'
+Kconfig:25: error: 'STRING_SOURCE' is an invalid value for 'integer'
+Kconfig:31: error: 'BOOL_SOURCE' is an invalid value for 'hex'
+Kconfig:35: error: 'TRISTATE_SOURCE' is an invalid value for 'hex'
+Kconfig:39: error: 'STRING_SOURCE' is an invalid value for 'hex'
+Kconfig:45: error: 'BOOL_SOURCE' is an invalid value for 'integer'
+Kconfig:49: error: 'TRISTATE_SOURCE' is an invalid value for 'integer'
+Kconfig:53: error: 'STRING_SOURCE' is an invalid value for 'integer'
+Kconfig:57: error: 'BOOL_SOURCE' is an invalid value for 'integer'
+Kconfig:57: error: 'TRISTATE_SOURCE' is an invalid value for 'integer'
+Kconfig:63: error: 'BOOL_SOURCE' is an invalid value for 'hex'
+Kconfig:67: error: 'TRISTATE_SOURCE' is an invalid value for 'hex'
+Kconfig:71: error: 'STRING_SOURCE' is an invalid value for 'hex'
+Kconfig:75: error: 'BOOL_SOURCE' is an invalid value for 'hex'
+Kconfig:75: error: 'TRISTATE_SOURCE' is an invalid value for 'hex'
diff --git a/scripts/kconfig/tests/err_recursive_dep/Kconfig b/scripts/kconfig/tests/err_recursive_dep/Kconfig
index ebdb3ffd8717..bd1ff92bcab2 100644
--- a/scripts/kconfig/tests/err_recursive_dep/Kconfig
+++ b/scripts/kconfig/tests/err_recursive_dep/Kconfig
@@ -61,3 +61,27 @@ config G
bool "G"
endmenu
+
+# depends on and range
+
+config H1
+ int
+ depends on H2 < H3
+
+config H2
+ int
+ range H3 H1
+
+config H3
+ int
+ range H1 H2
+
+# range
+
+config I1
+ int
+ range I2 I2
+
+config I2
+ int
+ range I1 I1
diff --git a/scripts/kconfig/tests/err_recursive_dep/expected_stderr b/scripts/kconfig/tests/err_recursive_dep/expected_stderr
index fc2e860af082..08f843087678 100644
--- a/scripts/kconfig/tests/err_recursive_dep/expected_stderr
+++ b/scripts/kconfig/tests/err_recursive_dep/expected_stderr
@@ -36,3 +36,16 @@ error: recursive dependency detected!
symbol G depends on G
For a resolution refer to Documentation/kbuild/kconfig-language.rst
subsection "Kconfig recursive dependency limitations"
+
+error: recursive dependency detected!
+ symbol H1 depends on H2
+ symbol H2 range value contains H3
+ symbol H3 range value contains H1
+For a resolution refer to Documentation/kbuild/kconfig-language.rst
+subsection "Kconfig recursive dependency limitations"
+
+error: recursive dependency detected!
+ symbol I1 range value contains I2
+ symbol I2 range value contains I1
+For a resolution refer to Documentation/kbuild/kconfig-language.rst
+subsection "Kconfig recursive dependency limitations"
diff --git a/scripts/kconfig/tests/randconfig_probability/Kconfig b/scripts/kconfig/tests/randconfig_probability/Kconfig
new file mode 100644
index 000000000000..84f4e5fcc51e
--- /dev/null
+++ b/scripts/kconfig/tests/randconfig_probability/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config MODULES
+ bool
+ default y
+ modules
+
+config BOOL
+ bool "Bool"
+
+config TRI
+ tristate "Tristate"
diff --git a/scripts/kconfig/tests/randconfig_probability/__init__.py b/scripts/kconfig/tests/randconfig_probability/__init__.py
new file mode 100644
index 000000000000..e6e5dda10ad9
--- /dev/null
+++ b/scripts/kconfig/tests/randconfig_probability/__init__.py
@@ -0,0 +1,130 @@
+# SPDX-License-Identifier: GPL-2.0-only
+"""Validate KCONFIG_PROBABILITY without changing the supported distributions."""
+
+
+def test_malformed_warns(conf):
+ probabilities = [
+ 'invalid', ' ', '50%', '50 ', '0x32', '10 20', '10:20x',
+ '10:20:invalid', '10:20:30x',
+ ':50', '50:', '10::20', '10:20:', '10:20:30:', '10:20:30:40',
+ '-0', '+0', '+', '-', '+100:0', ' \t100:0', '0: \t100:0',
+ ]
+ for probability in probabilities:
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': probability,
+ 'KCONFIG_SEED': '0',
+ }) == 0, repr(probability)
+ assert ('warning: KCONFIG_PROBABILITY has malformed format' in
+ conf.stderr), repr(probability)
+ assert conf.stderr.count('warning:') == 1, repr(probability)
+ assert conf.config is not None, repr(probability)
+
+
+def test_malformed_preserves_config(conf):
+ probabilities = [('50%', '50:0:0'), ('-0', '0')]
+ for seed in range(20):
+ for probability, equivalent in probabilities:
+ context = 'probability={!r}, equivalent={!r}, seed={}'.format(
+ probability, equivalent, seed)
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': probability,
+ 'KCONFIG_SEED': hex(seed),
+ }) == 0, context
+ assert ('warning: KCONFIG_PROBABILITY has malformed format' in
+ conf.stderr), context
+ assert conf.stderr.count('warning:') == 1, context
+ malformed = conf.config
+
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': equivalent,
+ 'KCONFIG_SEED': hex(seed),
+ }) == 0, context
+ assert 'warning:' not in conf.stderr, context
+ assert conf.config == malformed, context
+
+
+def test_werror(conf):
+ probabilities = [
+ ('', 0), ('50', 0), ('50%', 1), ('-0', 1), ('10:20:30:40', 1),
+ ]
+ for probability, status in probabilities:
+ for werror in ['', '0', '1']:
+ context = 'probability={!r}, werror={!r}'.format(
+ probability, werror)
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': probability,
+ 'KCONFIG_SEED': '0',
+ 'KCONFIG_WERROR': werror,
+ }) == status, context
+ if status:
+ assert ('warning: KCONFIG_PROBABILITY has malformed format' in
+ conf.stderr), context
+ assert conf.stderr.count('warning:') == 1, context
+ else:
+ assert 'warning:' not in conf.stderr, context
+
+
+def test_out_of_range(conf):
+ probabilities = [
+ '-1', '101', '+101', '0:101', '0:0:101', '60:41', '0:60:41',
+ '4294967296', '-4294967296',
+ '999999999999999999999999', '-999999999999999999999999',
+ ]
+ for probability in probabilities:
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': probability,
+ 'KCONFIG_SEED': '0',
+ }) == 1, repr(probability)
+ assert 'KCONFIG_PROBABILITY:' in conf.stderr, repr(probability)
+
+
+def test_valid(conf):
+ probabilities = [
+ ('0', 'n', 'n'),
+ ('0:0', 'n', 'n'),
+ ('100:0', 'y', 'y'),
+ ('0:100', 'y', 'm'),
+ ('100:0:0', 'y', 'n'),
+ ('0:100:0', 'n', 'y'),
+ ('0:0:100', 'n', 'm'),
+ ('000:000:100', 'n', 'm'),
+ ]
+ for probability, boolean, tristate in probabilities:
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': probability,
+ 'KCONFIG_SEED': '0',
+ }) == 0, repr(probability)
+ assert 'warning:' not in conf.stderr, repr(probability)
+ for symbol, value in [('BOOL', boolean), ('TRI', tristate)]:
+ if value == 'n':
+ expected = '# CONFIG_{} is not set'.format(symbol)
+ else:
+ expected = 'CONFIG_{}={}'.format(symbol, value)
+ assert expected in conf.config.splitlines(), repr(probability)
+
+
+def test_single_probability_matches_tristate_split(conf):
+ for seed in range(20):
+ context = 'seed={}'.format(seed)
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': '50',
+ 'KCONFIG_SEED': hex(seed),
+ }) == 0, context
+ assert 'warning:' not in conf.stderr, context
+ single = conf.config
+
+ # 50% boolean y; 25% tristate y, 25% m, and 50% n.
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': '50:25:25',
+ 'KCONFIG_SEED': hex(seed),
+ }) == 0, context
+ assert 'warning:' not in conf.stderr, context
+ assert conf.config == single, context
+
+
+def test_empty(conf):
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': '',
+ 'KCONFIG_SEED': '0',
+ }) == 0
+ assert 'warning:' not in conf.stderr
diff --git a/scripts/kconfig/tests/savedefconfig_range/Kconfig b/scripts/kconfig/tests/savedefconfig_range/Kconfig
new file mode 100644
index 000000000000..d879870a5267
--- /dev/null
+++ b/scripts/kconfig/tests/savedefconfig_range/Kconfig
@@ -0,0 +1,67 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# Static default and range values
+
+config INT_DEFAULT_ABOVE_RANGE
+ int
+ range 1 1
+ default 16
+
+config INT_DEFAULT_BELOW_RANGE
+ int
+ range 4 8
+ default 2
+
+# Default and range values determined by other options
+
+config RANGE_UPPER_BOUND
+ int
+ default 3
+
+config INT_DYNAMIC_RANGE
+ int
+ range 0 RANGE_UPPER_BOUND
+ default 4
+
+# Hex
+
+config HEX_DEFAULT_ABOVE_RANGE
+ hex
+ range 0x10 0x20
+ default 0x40
+
+# Implicit default value of 0
+
+config INT_IMPLICIT_DEFAULT_ZERO
+ int
+ range 1 4
+
+# Conditional range
+
+config USE_FIRST_RANGE
+ bool
+ default y
+
+config INT_CONDITIONAL_RANGE
+ int
+ range 1 2 if USE_FIRST_RANGE
+ range 3 4 if !USE_FIRST_RANGE
+ default 3
+
+# Conditional default
+
+config USE_DEFAULT
+ bool
+ default y
+
+config INT_CONDITIONAL_DEFAULT
+ int
+ range 1 2
+ default 3 if USE_DEFAULT
+
+# Default outside of the range of a symbol with a prompt
+
+config INT_PROMPT_DEFAULT_ABOVE_RANGE
+ int "int with prompt"
+ range 1 4
+ default 8
diff --git a/scripts/kconfig/tests/savedefconfig_range/__init__.py b/scripts/kconfig/tests/savedefconfig_range/__init__.py
new file mode 100644
index 000000000000..961454be732c
--- /dev/null
+++ b/scripts/kconfig/tests/savedefconfig_range/__init__.py
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+"""Test savedefconfig with numerical defaults outside of ranges."""
+
+
+def test(conf):
+ assert conf._run_conf('--savedefconfig=defconfig', dot_config='config',
+ out_file='defconfig') == 0
+ assert conf.config_matches('expected_defconfig')
diff --git a/scripts/kconfig/tests/savedefconfig_range/config b/scripts/kconfig/tests/savedefconfig_range/config
new file mode 100644
index 000000000000..079a3107a02b
--- /dev/null
+++ b/scripts/kconfig/tests/savedefconfig_range/config
@@ -0,0 +1,8 @@
+CONFIG_INT_DEFAULT_ABOVE_RANGE=1
+CONFIG_INT_DEFAULT_BELOW_RANGE=4
+CONFIG_INT_DYNAMIC_RANGE=3
+CONFIG_HEX_DEFAULT_ABOVE_RANGE=0x20
+CONFIG_INT_IMPLICIT_DEFAULT_ZERO=1
+CONFIG_INT_CONDITIONAL_RANGE=2
+CONFIG_INT_CONDITIONAL_DEFAULT=2
+CONFIG_INT_PROMPT_DEFAULT_ABOVE_RANGE=4
diff --git a/scripts/kconfig/tests/savedefconfig_range/expected_defconfig b/scripts/kconfig/tests/savedefconfig_range/expected_defconfig
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/scripts/kconfig/tests/savedefconfig_range/expected_defconfig
diff --git a/scripts/kconfig/tests/warn_changed_input/Kconfig b/scripts/kconfig/tests/warn_changed_input/Kconfig
index 69845e2f3fb3..df4183a08420 100644
--- a/scripts/kconfig/tests/warn_changed_input/Kconfig
+++ b/scripts/kconfig/tests/warn_changed_input/Kconfig
@@ -26,6 +26,11 @@ config NUM
Kconfig resolves it to the constrained in-range value.
The warning should report that adjustment.
+config HEX_RANGE
+ hex
+ range 0 0xfffffffffffffffe
+ default 0xffffffffffffffff
+
config DUP
bool "DUP"
depends on DEP
diff --git a/scripts/kconfig/tests/warn_changed_input/__init__.py b/scripts/kconfig/tests/warn_changed_input/__init__.py
index 4c3bca6af846..17b602dd280e 100644
--- a/scripts/kconfig/tests/warn_changed_input/__init__.py
+++ b/scripts/kconfig/tests/warn_changed_input/__init__.py
@@ -8,26 +8,21 @@ KCONFIG_WARN_CHANGED_INPUT is enabled.
def test(conf):
+ warn_changed_input = {
+ "KCONFIG_WARN_CHANGED_INPUT": "1",
+ }
+
assert conf.olddefconfig('config') == 0
assert 'user-provided values changed by Kconfig' not in conf.stderr
- assert conf._run_conf('--olddefconfig', dot_config='config',
- extra_env={
- 'KCONFIG_WARN_CHANGED_INPUT': '1',
- }) == 0
+ assert conf.olddefconfig('config', extra_env=warn_changed_input) == 0
assert conf.stderr_contains('expected_stderr')
assert conf.config_matches('expected_config')
- assert conf._run_conf('--olddefconfig', dot_config='config',
- extra_env={
- 'KCONFIG_WARN_CHANGED_INPUT': '1',
- }, silent=True) == 0
+ assert conf.olddefconfig('config', extra_env=warn_changed_input,
+ silent=True) == 0
assert conf.stderr_contains('expected_stderr')
- assert conf._run_conf('--savedefconfig=defconfig', dot_config='config',
- out_file='defconfig',
- extra_env={
- 'KCONFIG_WARN_CHANGED_INPUT': '1',
- }) == 0
+ assert conf.savedefconfig('config', extra_env=warn_changed_input) == 0
assert conf.stderr_contains('expected_stderr')
assert conf.config_matches('expected_defconfig')
diff --git a/scripts/kconfig/tests/warn_changed_input/expected_config b/scripts/kconfig/tests/warn_changed_input/expected_config
index fe8bbec66c53..6a8d98a69b7e 100644
--- a/scripts/kconfig/tests/warn_changed_input/expected_config
+++ b/scripts/kconfig/tests/warn_changed_input/expected_config
@@ -4,3 +4,4 @@
#
# CONFIG_DEP is not set
CONFIG_NUM=20
+CONFIG_HEX_RANGE=0xfffffffffffffffe
diff --git a/scripts/kconfig/tests/warn_num_bounds/Kconfig b/scripts/kconfig/tests/warn_num_bounds/Kconfig
new file mode 100644
index 000000000000..a810225a58a3
--- /dev/null
+++ b/scripts/kconfig/tests/warn_num_bounds/Kconfig
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0
+
+mainmenu "Numeric bounds test"
+
+config INT_TOO_LOW
+ int "Integer below its type bounds"
+
+config INT_TOO_HIGH
+ int "Integer above its type bounds"
+
+config HEX_TOO_HIGH
+ hex "Hex value above its type bounds"
+
+config INT_MIN
+ int "Minimum valid integer"
+
+config INT_MAX
+ int "Maximum valid integer"
+
+config HEX_MIN
+ hex "Minimum valid hex value"
+
+config HEX_MAX
+ hex "Maximum valid hex value"
diff --git a/scripts/kconfig/tests/warn_num_bounds/__init__.py b/scripts/kconfig/tests/warn_num_bounds/__init__.py
new file mode 100644
index 000000000000..db7518258217
--- /dev/null
+++ b/scripts/kconfig/tests/warn_num_bounds/__init__.py
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0
+"""Test user values outside the numeric type bounds."""
+
+
+def test(conf):
+ in_keys = (
+ '-9223372036854775809\n'
+ '-1\n'
+ '9223372036854775808\n'
+ '1\n'
+ '0x10000000000000000\n'
+ '0x1\n'
+ '-9223372036854775808\n'
+ '9223372036854775807\n'
+ '0x0\n'
+ '0xffffffffffffffff\n'
+ )
+
+ assert conf.oldaskconfig(in_keys=in_keys) == 0
+ assert conf.stderr_matches('expected_frontend_stderr')
+ assert conf.config_matches('expected_frontend_config')
+
+ assert conf.olddefconfig('config') == 0
+ assert conf.stderr_matches('expected_config_stderr')
+ assert conf.config_matches('expected_config')
diff --git a/scripts/kconfig/tests/warn_num_bounds/config b/scripts/kconfig/tests/warn_num_bounds/config
new file mode 100644
index 000000000000..74bf263f99bf
--- /dev/null
+++ b/scripts/kconfig/tests/warn_num_bounds/config
@@ -0,0 +1,7 @@
+CONFIG_INT_TOO_LOW=-9223372036854775809
+CONFIG_INT_TOO_HIGH=9223372036854775808
+CONFIG_HEX_TOO_HIGH=0x10000000000000000
+CONFIG_INT_MIN=-9223372036854775808
+CONFIG_INT_MAX=9223372036854775807
+CONFIG_HEX_MIN=0x0
+CONFIG_HEX_MAX=0xffffffffffffffff
diff --git a/scripts/kconfig/tests/warn_num_bounds/expected_config b/scripts/kconfig/tests/warn_num_bounds/expected_config
new file mode 100644
index 000000000000..8b1aef612bc6
--- /dev/null
+++ b/scripts/kconfig/tests/warn_num_bounds/expected_config
@@ -0,0 +1,11 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Numeric bounds test
+#
+CONFIG_INT_TOO_LOW=-9223372036854775809
+CONFIG_INT_TOO_HIGH=9223372036854775808
+CONFIG_HEX_TOO_HIGH=0x10000000000000000
+CONFIG_INT_MIN=-9223372036854775808
+CONFIG_INT_MAX=9223372036854775807
+CONFIG_HEX_MIN=0x0
+CONFIG_HEX_MAX=0xffffffffffffffff
diff --git a/scripts/kconfig/tests/warn_num_bounds/expected_config_stderr b/scripts/kconfig/tests/warn_num_bounds/expected_config_stderr
new file mode 100644
index 000000000000..be2ed7fbf648
--- /dev/null
+++ b/scripts/kconfig/tests/warn_num_bounds/expected_config_stderr
@@ -0,0 +1,3 @@
+.config:1:warning: value '-9223372036854775809' for INT_TOO_LOW is outside the 64-bit signed integer bounds
+.config:2:warning: value '9223372036854775808' for INT_TOO_HIGH is outside the 64-bit signed integer bounds
+.config:3:warning: value '0x10000000000000000' for HEX_TOO_HIGH is outside the 64-bit unsigned integer bounds
diff --git a/scripts/kconfig/tests/warn_num_bounds/expected_frontend_config b/scripts/kconfig/tests/warn_num_bounds/expected_frontend_config
new file mode 100644
index 000000000000..a7e842517026
--- /dev/null
+++ b/scripts/kconfig/tests/warn_num_bounds/expected_frontend_config
@@ -0,0 +1,11 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Numeric bounds test
+#
+CONFIG_INT_TOO_LOW=-1
+CONFIG_INT_TOO_HIGH=1
+CONFIG_HEX_TOO_HIGH=0x1
+CONFIG_INT_MIN=-9223372036854775808
+CONFIG_INT_MAX=9223372036854775807
+CONFIG_HEX_MIN=0x0
+CONFIG_HEX_MAX=0xffffffffffffffff
diff --git a/scripts/kconfig/tests/warn_num_bounds/expected_frontend_stderr b/scripts/kconfig/tests/warn_num_bounds/expected_frontend_stderr
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/scripts/kconfig/tests/warn_num_bounds/expected_frontend_stderr
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index ab0b8125c8cb..c520741f85c2 100755
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -96,6 +96,11 @@ vmlinux_link()
ldflags="${ldflags} ${wl}--strip-debug"
fi
+ # Only the final link actually requires the relocations.
+ if [ "${output}" = "${VMLINUX}" ] && is_enabled CONFIG_ARCH_VMLINUX_NEEDS_RELOCS; then
+ ldflags="${ldflags} ${wl}--emit-relocs"
+ fi
+
if [ -n "${generate_map}" ]; then
ldflags="${ldflags} ${wl}-Map=vmlinux.map"
fi
@@ -118,11 +123,15 @@ kallsymso_changed()
! cmp -s "${kallsymso_prev}.sym" "${kallsymso}.sym"
}
-# Create ${2}.o file with all symbols from the ${1} object file
+# Create ${2}.o with the kallsyms tables for ${1} (the vmlinux, or an empty
+# listing for the first pass); list the symbols used in ${3} if given.
kallsyms()
{
local kallsymopt;
+ if [ -n "${3:-}" ]; then
+ kallsymopt="--sysmap=${3}"
+ fi
if is_enabled CONFIG_KALLSYMS_ALL; then
kallsymopt="${kallsymopt} --all-symbols"
fi
@@ -132,7 +141,7 @@ kallsyms()
fi
info KSYMS "${2}.S"
- scripts/kallsyms ${kallsymopt} "${1}" > "${2}.S"
+ scripts/kallsyms ${kallsymopt} "${1}" "${2}.bin" > "${2}.S"
info AS "${2}.o"
${CC} ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS} \
@@ -145,23 +154,20 @@ kallsyms()
# Perform kallsyms for the given temporary vmlinux.
sysmap_and_kallsyms()
{
- mksysmap "${1}" "${1}.syms"
- kallsyms "${1}.syms" "${1}.kallsyms"
-
+ kallsyms "${1}" "${1}.kallsyms" "${1}.syms"
kallsyms_sysmap=${1}.syms
}
# Create map file with all symbols from ${1}
-# See mksymap for additional details
mksysmap()
{
- info NM ${2}
- ${NM} -n "${1}" | sed -f "${srctree}/scripts/mksysmap" > "${2}"
+ info SYSMAP ${2}
+ scripts/kallsyms --sysmap="${2}" "${1}"
}
sorttable()
{
- ${NM} -S ${1} > .tmp_vmlinux.nm-sort
+ ${NM} -p -S ${1} > .tmp_vmlinux.nm-sort
${objtree}/scripts/sorttable -s .tmp_vmlinux.nm-sort ${1}
}
diff --git a/scripts/make_fit.py b/scripts/make_fit.py
index 15ba26974fd7..9bd35aa72119 100755
--- a/scripts/make_fit.py
+++ b/scripts/make_fit.py
@@ -292,7 +292,8 @@ def process_dtb(fname, args):
if 'scripts/dtc/fdtoverlay' in cmd:
# This depends on the structure of the composite DTB command
- files = cmd.split()
+ # Ignore any subsequent commands, such as schema validation
+ files = cmd.split(';', 1)[0].split()
files = files[files.index('-i') + 1:]
else:
files = [fname]
diff --git a/scripts/mkcompile_h b/scripts/mkcompile_h
index 2596f78e52ef..bf0d5669d9bc 100755
--- a/scripts/mkcompile_h
+++ b/scripts/mkcompile_h
@@ -11,7 +11,7 @@ else
LINUX_COMPILE_BY=$KBUILD_BUILD_USER
fi
if test -z "$KBUILD_BUILD_HOST"; then
- LINUX_COMPILE_HOST=`uname -n`
+ LINUX_COMPILE_HOST=$(uname -n)
else
LINUX_COMPILE_HOST=$KBUILD_BUILD_HOST
fi
diff --git a/scripts/mksysmap b/scripts/mksysmap
deleted file mode 100755
index 856b26ba2ac0..000000000000
--- a/scripts/mksysmap
+++ /dev/null
@@ -1,94 +0,0 @@
-#!/bin/sed -f
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# sed script to filter out symbols that are not needed for System.map,
-# or not suitable for kallsyms. The input should be 'nm -n <file>'.
-#
-# System.map is used by module-init tools and some debugging
-# tools to retrieve the actual addresses of symbols in the kernel.
-#
-# readprofile starts reading symbols when _stext is found, and
-# continue until it finds a symbol which is not either of 'T', 't',
-# 'W' or 'w'.
-#
-# ---------------------------------------------------------------------------
-# Ignored symbol types
-#
-
-# a: local absolute symbols
-# N: debugging symbols
-# U: undefined global symbols
-# w: local weak symbols
-/ [aNUw] /d
-
-# ---------------------------------------------------------------------------
-# Ignored prefixes
-# (do not forget a space before each pattern)
-
-# local symbols for ARM, MIPS, etc.
-/ \$/d
-
-# local labels, .LBB, .Ltmpxxx, .L__unnamed_xx, .LASANPC, etc.
-/ \.L/d
-
-# arm64 EFI stub namespace
-/ __efistub_/d
-
-# arm64 local symbols in PIE namespace
-/ __pi_\$/d
-/ __pi_\.L/d
-
-# arm64 local symbols in non-VHE KVM namespace
-/ __kvm_nvhe_\$/d
-/ __kvm_nvhe_\.L/d
-
-# lld arm/aarch64/mips thunks
-/ __[[:alnum:]]*Thunk_/d
-
-# CFI type identifiers
-/ __kcfi_typeid_/d
-/ __kvm_nvhe___kcfi_typeid_/d
-/ __pi___kcfi_typeid_/d
-
-# CRC from modversions
-/ __crc_/d
-
-# EXPORT_SYMBOL (symbol name)
-/ __kstrtab_/d
-
-# EXPORT_SYMBOL (namespace)
-/ __kstrtabns_/d
-
-# MODULE_DEVICE_TABLE (symbol name)
-/ __mod_device_table__/d
-
-# ---------------------------------------------------------------------------
-# Ignored suffixes
-# (do not forget '$' after each pattern)
-
-# arm
-/_from_arm$/d
-/_from_thumb$/d
-/_veneer$/d
-
-# ---------------------------------------------------------------------------
-# Ignored symbols (exact match)
-# (do not forget a space before and '$' after each pattern)
-
-# for LoongArch?
-/ L0$/d
-
-# ppc
-/ _SDA_BASE_$/d
-/ _SDA2_BASE_$/d
-
-# MODULE_INFO()
-/ __UNIQUE_ID_modinfo_[0-9]*$/d
-
-# ---------------------------------------------------------------------------
-# Ignored patterns
-# (symbols that contain the pattern are ignored)
-
-# ppc stub
-/\.long_branch\./d
-/\.plt_branch\./d
diff --git a/scripts/remove-stale-files b/scripts/remove-stale-files
index f1f776caae00..70a2ead4d0f1 100755
--- a/scripts/remove-stale-files
+++ b/scripts/remove-stale-files
@@ -28,3 +28,5 @@ rm -f *.spec
rm -f lib/test_fortify.log
rm -f rust/doctests_kernel_generated_kunit.c
+
+rm -f usr/gen_init_cpio
diff --git a/scripts/sbom/tests/cmd_graph/test_savedcmd_parser.py b/scripts/sbom/tests/cmd_graph/test_savedcmd_parser.py
index d7776f072e03..93787a1cd156 100644
--- a/scripts/sbom/tests/cmd_graph/test_savedcmd_parser.py
+++ b/scripts/sbom/tests/cmd_graph/test_savedcmd_parser.py
@@ -338,7 +338,7 @@ class TestSavedCmdParser(unittest.TestCase):
# gen_initramfs.sh command tests
def test_gen_initramfs(self):
- cmd = "sh ../usr/gen_initramfs.sh -o usr/initramfs_data.cpio -l usr/.initramfs_data.cpio.d ../usr/default_cpio_list"
+ cmd = "sh ../scripts/gen_initramfs.sh -o usr/initramfs_data.cpio -l usr/.initramfs_data.cpio.d ../usr/default_cpio_list"
expected = "../usr/default_cpio_list"
self._assert_parsing(cmd, expected)
diff --git a/scripts/setlocalversion b/scripts/setlocalversion
index 28169d7e143b..f264c2460ad7 100755
--- a/scripts/setlocalversion
+++ b/scripts/setlocalversion
@@ -33,7 +33,7 @@ if test $# -gt 0 -o ! -d "$srctree"; then
fi
try_tag() {
- tag="$1"
+ local tag="$1"
# Is $tag an annotated tag?
if [ "$(git cat-file -t "$tag" 2> /dev/null)" != tag ]; then
@@ -59,7 +59,6 @@ scm_version()
{
local short=false
local no_dirty=false
- local tag
while [ $# -gt 0 ];
do
diff --git a/scripts/sorttable.c b/scripts/sorttable.c
index d7b50581c732..88e49a5c4251 100644
--- a/scripts/sorttable.c
+++ b/scripts/sorttable.c
@@ -322,10 +322,23 @@ static int parse_symbols(const char *fname)
return -1;
}
- while (fscanf(fp, "%16s %16s %c %*s\n", addr_str, size_str, &type) == 3) {
+ while (fscanf(fp, "%16s %16s %c%*[^\n]", addr_str, size_str, &type) == 3) {
uint64_t addr;
uint64_t size;
+ /*
+ * nm outputs size-less entries with a missing 2nd value, which
+ * means fscanf() just misread its fields.
+ *
+ * These don't matter as a call site cannot be in an empty
+ * function, so just skip them.
+ *
+ * nm pads address and size to the same width, so if their
+ * widths differ, this is a misread.
+ */
+ if (strlen(size_str) != strlen(addr_str))
+ continue;
+
/* Only care about functions */
if (type != 't' && type != 'T' && type != 'W')
continue;
diff --git a/scripts/ver_linux b/scripts/ver_linux
index 00bdaf30d590..e06d7c650710 100755
--- a/scripts/ver_linux
+++ b/scripts/ver_linux
@@ -57,6 +57,7 @@ BEGIN {
printversion("pahole", version("pahole --version"))
printversion("Pcmcia-cs", version("cardmgr -V"))
printversion("pcmciautils", version("pccardctl -V"))
+ printversion("pigz", version("pigz --version"))
printversion("PPP", version("pppd --version"))
printversion("procps", version("ps --version"))
printversion("Python", version("python3 -V"))