diff options
| author | Mark Brown <broonie@kernel.org> | 2026-10-14 13:32:15 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-10-14 13:32:15 +0100 |
| commit | 2e292f8568f310eb9f8d19c0ba547633da9871f4 (patch) | |
| tree | 3128553d4293bc5f5fb77bc7e5893b564e35019d /scripts | |
| parent | 629ce8372527a66aaa92d11fd4c304032b0b48c8 (diff) | |
| parent | 56a62bca21c960b1222e39e184e550bbf1c9683a (diff) | |
| download | linux-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')
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")) |
