Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kbuild: build init/built-in.a just once

Kbuild builds init/built-in.a twice; first during the ordinary
directory descending, second from scripts/link-vmlinux.sh.

We do this because UTS_VERSION contains the build version and the
timestamp. We cannot update it during the normal directory traversal
since we do not yet know if we need to update vmlinux. UTS_VERSION is
temporarily calculated, but omitted from the update check. Otherwise,
vmlinux would be rebuilt every time.

When Kbuild results in running link-vmlinux.sh, it increments the
version number in the .version file and takes the timestamp at that
time to really fix UTS_VERSION.

However, updating the same file twice is a footgun. To avoid nasty
timestamp issues, all build artifacts that depend on init/built-in.a
are atomically generated in link-vmlinux.sh, where some of them do not
need rebuilding.

To fix this issue, this commit changes as follows:

[1] Split UTS_VERSION out to include/generated/utsversion.h from
include/generated/compile.h

include/generated/utsversion.h is generated just before the
vmlinux link. It is generated under include/generated/ because
some decompressors (s390, x86) use UTS_VERSION.

[2] Split init_uts_ns and linux_banner out to init/version-timestamp.c
from init/version.c

init_uts_ns and linux_banner contain UTS_VERSION. During the ordinary
directory descending, they are compiled with __weak and used to
determine if vmlinux needs relinking. Just before the vmlinux link,
they are compiled without __weak to embed the real version and
timestamp.

Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>

+120 -131
+1 -1
arch/powerpc/boot/wrapper
··· 433 433 # Extract kernel version information, some platforms want to include 434 434 # it in the image header 435 435 version=`${CROSS}strings "$kernel" | grep '^Linux version [-0-9.]' | \ 436 - cut -d' ' -f3` 436 + head -n1 | cut -d' ' -f3` 437 437 if [ -n "$version" ]; then 438 438 uboot_version="-n Linux-$version" 439 439 fi
+1
arch/s390/boot/version.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 + #include <generated/utsversion.h> 2 3 #include <generated/utsrelease.h> 3 4 #include <generated/compile.h> 4 5 #include "boot.h"
+1
arch/x86/boot/compressed/kaslr.c
··· 29 29 #include <linux/uts.h> 30 30 #include <linux/utsname.h> 31 31 #include <linux/ctype.h> 32 + #include <generated/utsversion.h> 32 33 #include <generated/utsrelease.h> 33 34 34 35 #define _SETUP
+1
arch/x86/boot/version.c
··· 11 11 */ 12 12 13 13 #include "boot.h" 14 + #include <generated/utsversion.h> 14 15 #include <generated/utsrelease.h> 15 16 #include <generated/compile.h> 16 17
+2
init/.gitignore
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + /utsversion-tmp.h
+42 -13
init/Makefile
··· 19 19 mounts-$(CONFIG_BLK_DEV_RAM) += do_mounts_rd.o 20 20 mounts-$(CONFIG_BLK_DEV_INITRD) += do_mounts_initrd.o 21 21 22 - # dependencies on generated files need to be listed explicitly 23 - $(obj)/version.o: include/generated/compile.h 22 + # 23 + # UTS_VERSION 24 + # 24 25 25 - # compile.h changes depending on hostname, generation number, etc, 26 - # so we regenerate it always. 27 - # mkcompile_h will make sure to only update the 28 - # actual file if its content has changed. 26 + smp-flag-$(CONFIG_SMP) := SMP 27 + preempt-flag-$(CONFIG_PREEMPT_BUILD) := PREEMPT 28 + preempt-flag-$(CONFIG_PREEMPT_DYNAMIC) := PREEMPT_DYNAMIC 29 + preempt-flag-$(CONFIG_PREEMPT_RT) := PREEMPT_RT 29 30 30 - quiet_cmd_compile.h = CHK $@ 31 - cmd_compile.h = \ 32 - $(CONFIG_SHELL) $(srctree)/scripts/mkcompile_h $@ \ 33 - "$(UTS_MACHINE)" "$(CONFIG_SMP)" "$(CONFIG_PREEMPT_BUILD)" \ 34 - "$(CONFIG_PREEMPT_DYNAMIC)" "$(CONFIG_PREEMPT_RT)" \ 35 - "$(CONFIG_CC_VERSION_TEXT)" "$(LD)" 31 + build-version = $(or $(KBUILD_BUILD_VERSION), $(build-version-auto)) 32 + build-timestamp = $(or $(KBUILD_BUILD_TIMESTAMP), $(build-timestamp-auto)) 33 + 34 + # Maximum length of UTS_VERSION is 64 chars 35 + filechk_uts_version = \ 36 + utsver=$$(echo '$(pound)'"$(build-version)" $(smp-flag-y) $(preempt-flag-y) "$(build-timestamp)" | cut -b -64); \ 37 + echo '$(pound)'define UTS_VERSION \""$${utsver}"\" 38 + 39 + # 40 + # Build version.c with temporary UTS_VERSION 41 + # 42 + 43 + $(obj)/utsversion-tmp.h: FORCE 44 + $(call filechk,uts_version) 45 + 46 + clean-files += utsversion-tmp.h 47 + 48 + $(obj)/version.o: include/generated/compile.h $(obj)/utsversion-tmp.h 49 + CFLAGS_version.o := -include $(obj)/utsversion-tmp.h 50 + 51 + filechk_compile.h = $(srctree)/scripts/mkcompile_h \ 52 + "$(UTS_MACHINE)" "$(CONFIG_CC_VERSION_TEXT)" "$(LD)" 36 53 37 54 include/generated/compile.h: FORCE 38 - $(call cmd,compile.h) 55 + $(call filechk,compile.h) 56 + 57 + # 58 + # Build version-timestamp.c with final UTS_VERSION 59 + # 60 + 61 + include/generated/utsversion.h: build-version-auto = $(shell $(srctree)/$(src)/build-version) 62 + include/generated/utsversion.h: build-timestamp-auto = $(shell LC_ALL=C date) 63 + include/generated/utsversion.h: FORCE 64 + $(call filechk,uts_version) 65 + 66 + $(obj)/version-timestamp.o: include/generated/utsversion.h 67 + CFLAGS_version-timestamp.o := -include include/generated/utsversion.h
+10
init/build-version
··· 1 + #!/bin/sh 2 + # SPDX-License-Identifier: GPL-2.0-only 3 + 4 + prev_ver=$(cat .version 2>/dev/null) && 5 + ver=$(expr ${prev_ver} + 1 2>/dev/null) || 6 + ver=1 7 + 8 + echo ${ver} > .version 9 + 10 + echo ${ver}
+31
init/version-timestamp.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + 3 + #include <generated/compile.h> 4 + #include <generated/utsrelease.h> 5 + #include <linux/version.h> 6 + #include <linux/proc_ns.h> 7 + #include <linux/refcount.h> 8 + #include <linux/uts.h> 9 + #include <linux/utsname.h> 10 + 11 + struct uts_namespace init_uts_ns = { 12 + .ns.count = REFCOUNT_INIT(2), 13 + .name = { 14 + .sysname = UTS_SYSNAME, 15 + .nodename = UTS_NODENAME, 16 + .release = UTS_RELEASE, 17 + .version = UTS_VERSION, 18 + .machine = UTS_MACHINE, 19 + .domainname = UTS_DOMAINNAME, 20 + }, 21 + .user_ns = &init_user_ns, 22 + .ns.inum = PROC_UTS_INIT_INO, 23 + #ifdef CONFIG_UTS_NS 24 + .ns.ops = &utsns_operations, 25 + #endif 26 + }; 27 + 28 + /* FIXED STRINGS! Don't touch! */ 29 + const char linux_banner[] = 30 + "Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@" 31 + LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n";
+13 -23
init/version.c
··· 18 18 #include <generated/utsrelease.h> 19 19 #include <linux/proc_ns.h> 20 20 21 - struct uts_namespace init_uts_ns = { 22 - .ns.count = REFCOUNT_INIT(2), 23 - .name = { 24 - .sysname = UTS_SYSNAME, 25 - .nodename = UTS_NODENAME, 26 - .release = UTS_RELEASE, 27 - .version = UTS_VERSION, 28 - .machine = UTS_MACHINE, 29 - .domainname = UTS_DOMAINNAME, 30 - }, 31 - .user_ns = &init_user_ns, 32 - .ns.inum = PROC_UTS_INIT_INO, 33 - #ifdef CONFIG_UTS_NS 34 - .ns.ops = &utsns_operations, 35 - #endif 36 - }; 37 - EXPORT_SYMBOL_GPL(init_uts_ns); 38 - 39 21 static int __init early_hostname(char *arg) 40 22 { 41 23 size_t bufsize = sizeof(init_uts_ns.name.nodename); ··· 33 51 } 34 52 early_param("hostname", early_hostname); 35 53 36 - /* FIXED STRINGS! Don't touch! */ 37 - const char linux_banner[] = 38 - "Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@" 39 - LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n"; 40 - 41 54 const char linux_proc_banner[] = 42 55 "%s version %s" 43 56 " (" LINUX_COMPILE_BY "@" LINUX_COMPILE_HOST ")" ··· 40 63 41 64 BUILD_SALT; 42 65 BUILD_LTO_INFO; 66 + 67 + /* 68 + * init_uts_ns and linux_banner contain the build version and timestamp, 69 + * which are really fixed at the very last step of build process. 70 + * They are compiled with __weak first, and without __weak later. 71 + */ 72 + 73 + struct uts_namespace init_uts_ns __weak; 74 + const char linux_banner[] __weak; 75 + 76 + #include "version-timestamp.c" 77 + 78 + EXPORT_SYMBOL_GPL(init_uts_ns);
+3 -3
kernel/gen_kheaders.sh
··· 31 31 fi 32 32 all_dirs="$all_dirs $dir_list" 33 33 34 - # include/generated/compile.h is ignored because it is touched even when none 35 - # of the source files changed. 34 + # include/generated/utsversion.h is ignored because it is generated after this 35 + # script is executed. (utsversion.h is unneeded for kheaders) 36 36 # 37 37 # When Kconfig regenerates include/generated/autoconf.h, its timestamp is 38 38 # updated, but the contents might be still the same. When any CONFIG option is ··· 42 42 # 43 43 # Ignore them for md5 calculation to avoid pointless regeneration. 44 44 headers_md5="$(find $all_dirs -name "*.h" | 45 - grep -v "include/generated/compile.h" | 45 + grep -v "include/generated/utsversion.h" | 46 46 grep -v "include/generated/autoconf.h" | 47 47 xargs ls -l | md5sum | cut -d ' ' -f1)" 48 48
+11 -78
scripts/mkcompile_h
··· 1 1 #!/bin/sh 2 2 # SPDX-License-Identifier: GPL-2.0 3 3 4 - TARGET=$1 5 - ARCH=$2 6 - SMP=$3 7 - PREEMPT=$4 8 - PREEMPT_DYNAMIC=$5 9 - PREEMPT_RT=$6 10 - CC_VERSION="$7" 11 - LD=$8 4 + UTS_MACHINE=$1 5 + CC_VERSION="$2" 6 + LD=$3 12 7 13 8 # Do not expand names 14 9 set -f ··· 12 17 LC_ALL=C 13 18 export LC_ALL 14 19 15 - if [ -z "$KBUILD_BUILD_VERSION" ]; then 16 - VERSION=$(cat .version 2>/dev/null || echo 1) 17 - else 18 - VERSION=$KBUILD_BUILD_VERSION 19 - fi 20 - 21 - if [ -z "$KBUILD_BUILD_TIMESTAMP" ]; then 22 - TIMESTAMP=`date` 23 - else 24 - TIMESTAMP=$KBUILD_BUILD_TIMESTAMP 25 - fi 26 20 if test -z "$KBUILD_BUILD_USER"; then 27 21 LINUX_COMPILE_BY=$(whoami | sed 's/\\/\\\\/') 28 22 else ··· 23 39 LINUX_COMPILE_HOST=$KBUILD_BUILD_HOST 24 40 fi 25 41 26 - UTS_VERSION="#$VERSION" 27 - CONFIG_FLAGS="" 28 - if [ -n "$SMP" ] ; then CONFIG_FLAGS="SMP"; fi 42 + LD_VERSION=$($LD -v | head -n1 | sed 's/(compatible with [^)]*)//' \ 43 + | sed 's/[[:space:]]*$//') 29 44 30 - if [ -n "$PREEMPT_RT" ] ; then 31 - CONFIG_FLAGS="$CONFIG_FLAGS PREEMPT_RT" 32 - elif [ -n "$PREEMPT_DYNAMIC" ] ; then 33 - CONFIG_FLAGS="$CONFIG_FLAGS PREEMPT_DYNAMIC" 34 - elif [ -n "$PREEMPT" ] ; then 35 - CONFIG_FLAGS="$CONFIG_FLAGS PREEMPT" 36 - fi 37 - 38 - # Truncate to maximum length 39 - UTS_LEN=64 40 - UTS_VERSION="$(echo $UTS_VERSION $CONFIG_FLAGS $TIMESTAMP | cut -b -$UTS_LEN)" 41 - 42 - # Generate a temporary compile.h 43 - 44 - { echo /\* This file is auto generated, version $VERSION \*/ 45 - if [ -n "$CONFIG_FLAGS" ] ; then echo "/* $CONFIG_FLAGS */"; fi 46 - 47 - echo \#define UTS_MACHINE \"$ARCH\" 48 - 49 - echo \#define UTS_VERSION \"$UTS_VERSION\" 50 - 51 - printf '#define LINUX_COMPILE_BY "%s"\n' "$LINUX_COMPILE_BY" 52 - echo \#define LINUX_COMPILE_HOST \"$LINUX_COMPILE_HOST\" 53 - 54 - LD_VERSION=$($LD -v | head -n1 | sed 's/(compatible with [^)]*)//' \ 55 - | sed 's/[[:space:]]*$//') 56 - printf '#define LINUX_COMPILER "%s"\n' "$CC_VERSION, $LD_VERSION" 57 - } > .tmpcompile 58 - 59 - # Only replace the real compile.h if the new one is different, 60 - # in order to preserve the timestamp and avoid unnecessary 61 - # recompilations. 62 - # We don't consider the file changed if only the date/time changed, 63 - # unless KBUILD_BUILD_TIMESTAMP was explicitly set (e.g. for 64 - # reproducible builds with that value referring to a commit timestamp). 65 - # A kernel config change will increase the generation number, thus 66 - # causing compile.h to be updated (including date/time) due to the 67 - # changed comment in the 68 - # first line. 69 - 70 - if [ -z "$KBUILD_BUILD_TIMESTAMP" ]; then 71 - IGNORE_PATTERN="UTS_VERSION" 72 - else 73 - IGNORE_PATTERN="NOT_A_PATTERN_TO_BE_MATCHED" 74 - fi 75 - 76 - if [ -r $TARGET ] && \ 77 - grep -v $IGNORE_PATTERN $TARGET > .tmpver.1 && \ 78 - grep -v $IGNORE_PATTERN .tmpcompile > .tmpver.2 && \ 79 - cmp -s .tmpver.1 .tmpver.2; then 80 - rm -f .tmpcompile 81 - else 82 - echo " UPD $TARGET" 83 - mv -f .tmpcompile $TARGET 84 - fi 85 - rm -f .tmpver.1 .tmpver.2 45 + cat <<EOF 46 + #define UTS_MACHINE "${UTS_MACHINE}" 47 + #define LINUX_COMPILE_BY "${LINUX_COMPILE_BY}" 48 + #define LINUX_COMPILE_HOST "${LINUX_COMPILE_HOST}" 49 + #define LINUX_COMPILER "${CC_VERSION}, ${LD_VERSION}" 50 + EOF