x86 Thin LTO + allyesconfig causes `ld.lld: error: kernel image bigger than KERNEL_IMAGE_SIZE`
emojifreak opened this issue · 3 comments
This might be a duplicate of #1424 (comment). Thin LTO + allyesconfig
gives the following build error. It does not happen with LTO_NON=y
. In addition, tools/objtool
needs 41 GiB of virtual memory and an extra swap space was needed.
+ printf %-7s %s\n LD .tmp_vmlinux.kallsyms1
LD .tmp_vmlinux.kallsyms1
+ shift
+ [ -n y ]
+ objs=vmlinux.o
+ libs=
+ [ x86 = um ]
+ wl=
+ ld=ld.lld
+ ldflags=-m elf_x86_64 --thinlto-cache-dir=.thinlto-cache -mllvm -import-instr-limit=5 --emit-relocs --discard-none -z max-page-size=0x200000 --build-id=sha1 -X --orphan-handling=warn
+ ldlibs=
+ ldflags=-m elf_x86_64 --thinlto-cache-dir=.thinlto-cache -mllvm -import-instr-limit=5 --emit-relocs --discard-none -z max-page-size=0x200000 --build-id=sha1 -X --orphan-handling=warn --script=./arch/x86/kernel/vmlinux.lds
+ [ .tmp_vmlinux.kallsyms1 != 1 ]
+ ldflags=-m elf_x86_64 --thinlto-cache-dir=.thinlto-cache -mllvm -import-instr-limit=5 --emit-relocs --discard-none -z max-page-size=0x200000 --build-id=sha1 -X --orphan-handling=warn --script=./arch/x86/kernel/vmlinux.lds --strip-debug
+ [ -n y ]
+ ldflags=-m elf_x86_64 --thinlto-cache-dir=.thinlto-cache -mllvm -import-instr-limit=5 --emit-relocs --discard-none -z max-page-size=0x200000 --build-id=sha1 -X --orphan-handling=warn --script=./arch/x86/kernel/vmlinux.lds --strip-debug -Map=.tmp_vmlinux.kallsyms1.map
+ ld.lld -m elf_x86_64 --thinlto-cache-dir=.thinlto-cache -mllvm -import-instr-limit=5 --emit-relocs --discard-none -z max-page-size=0x200000 --build-id=sha1 -X --orphan-handling=warn --script=./arch/x86/kernel/vmlinux.lds --strip-debug -Map=.tmp_vmlinux.kallsyms1.map -o .tmp_vmlinux.kallsyms1 --whole-archive vmlinux.o --no-whole-archive --start-group --end-group .btf.vmlinux.bin.o
ld.lld: error: kernel image bigger than KERNEL_IMAGE_SIZE
ld.lld: error: kernel image bigger than KERNEL_IMAGE_SIZE
ld.lld: error: kernel image bigger than KERNEL_IMAGE_SIZE
make: *** [Makefile:1161: vmlinux] Error 1
make: Target 'all' not remade because of errors.
$ LANG=C ls -l .*vmlin* *vmlinu*
-rwxr-xr-x 1 ryutaroh ryutaroh 1126946928 Dec 25 20:28 .btf.vmlinux.bin.o
-rwxr-xr-x 1 ryutaroh ryutaroh 8925382200 Dec 25 20:28 .tmp_vmlinux.btf
-rw-r--r-- 1 ryutaroh ryutaroh 255177557 Dec 25 16:57 .tmp_vmlinux.btf.map
lrwxrwxrwx 1 ryutaroh ryutaroh 60 Dec 25 11:15 vmlinux-gdb.py -> /var/tmp/tmp31/x86/linux-5.16-rc6/scripts/gdb/vmlinux-gdb.py
-rw-r--r-- 1 ryutaroh ryutaroh 8378741416 Dec 25 16:57 vmlinux.o
-rw-r--r-- 1 ryutaroh ryutaroh 1546109 Dec 25 16:57 vmlinux.symvers
A reproducing script is below:
#!/bin/sh
KVER=5.16-rc6
LANG=C.UTF-8
export LANG
cd /var/tmp/tmp31
#wget -T 10 -c https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${KVER}.tar.xz
for a in x86; do
rm -rf /var/tmp/tmp31/$a
mkdir /var/tmp/tmp31/$a
cd /var/tmp/tmp31/$a
exec </dev/null >build-log-${KVER}-${a}.txt 2>&1
set -xe
tar zxf /var/tmp/linux-${KVER}.tar.gz
cd linux-${KVER}
# sed -i 's/-O2/-O3/g' Makefile
# sed -i 's/-Os/-Oz/g' Makefile
make LLVM=1 LLVM_IAS=1 allyesconfig
cat >>.config <<EOF
CONFIG_RUNTIME_TESTING_MENU=n
CONFIG_WERROR=n
CONFIG_XFS_FS=n
CONFIG_OVERLAY_FS=n
CONFIG_EMBEDDED=n
CONFIG_FTRACE_MCOUNT_USE_RECORDMCOUNT=n
CONFIG_KASAN=n
CONFIG_GCOV_KERNEL=n
CONFIG_COMPILE_TEST=n
CONFIG_TRIM_UNUSED_KSYMS=n
CONFIG_INIT_STACK_ALL_ZERO=y
CONFIG_KVM=y
CONFIG_PREEMPT=y
CONFIG_PREEMPT_DYNAMIC=y
CONFIG_LTO_CLANG_THIN=y
CONFIG_LTO_CLANG_NONE=n
CONFIG_CFI_CLANG=y
CONFIG_CFI_CLANG_SHADOW=y
CONFIG_SHADOW_CALL_STACK=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_DWARF5=y
CONFIG_DEBUG_INFO_BTF=y
CONFIG_GDB_SCRIPTS=y
CONFIG_MATOM=y
CONFIG_X86_INTEL_TSX_MODE_ON=y
CONFIG_USB_G_DBGP_PRINTK=y
CONFIG_ROMFS_BACKED_BY_BOTH=y
CONFIG_X86_DECODER_SELFTEST=y
CONFIG_UNWINDER_FRAME_POINTER=y
EOF
yes '' |
make --keep-going -j 6 ARCH=$a LLVM=1 LLVM_IAS=1 KCFLAGS="-mllvm -polly-ast-use-context -mllvm -polly-invariant-load-hoisting -mllvm -polly-opt-fusion=max -mllvm -polly-run-inliner -mllvm -polly-vectorizer=stripmine -mllvm -polly-run-dce" oldconfig
sed -i 's/=m$/=y/g' .config
yes '' |
make --keep-going -j 6 V=1 ARCH=$a LLVM=1 LLVM_IAS=1 KCFLAGS="-mllvm -polly-ast-use-context -mllvm -polly-invariant-load-hoisting -mllvm -polly-opt-fusion=max -mllvm -polly-run-inliner -mllvm -polly-vectorizer=stripmine -mllvm -polly-run-dce" all
exec </dev/null >/dev/null 2>&1
done
wait
With LTO_NONE=y
, files sizes are much smaller as below:
$ LANG=C ls -l .*vmlin* *vmlinu*
-rwxr-xr-x 1 ryutaroh ryutaroh 318122416 Dec 25 23:27 .btf.vmlinux.bin.o
-rwxr-xr-x 1 ryutaroh ryutaroh 5056530336 Dec 25 23:27 .tmp_vmlinux.btf
-rw-r--r-- 1 ryutaroh ryutaroh 155329010 Dec 25 23:25 .tmp_vmlinux.btf.map
-rwxr-xr-x 1 ryutaroh ryutaroh 627375480 Dec 25 23:27 .tmp_vmlinux.kallsyms1
-rw-r--r-- 1 ryutaroh ryutaroh 116152114 Dec 25 23:28 .tmp_vmlinux.kallsyms1.S
-rw-r--r-- 1 ryutaroh ryutaroh 135618449 Dec 25 23:27 .tmp_vmlinux.kallsyms1.map
-rw-r--r-- 1 ryutaroh ryutaroh 19366048 Dec 25 23:28 .tmp_vmlinux.kallsyms1.o
-rwxr-xr-x 1 ryutaroh ryutaroh 646249872 Dec 25 23:28 .tmp_vmlinux.kallsyms2
-rw-r--r-- 1 ryutaroh ryutaroh 116152114 Dec 25 23:28 .tmp_vmlinux.kallsyms2.S
-rw-r--r-- 1 ryutaroh ryutaroh 135619315 Dec 25 23:28 .tmp_vmlinux.kallsyms2.map
-rw-r--r-- 1 ryutaroh ryutaroh 19366048 Dec 25 23:28 .tmp_vmlinux.kallsyms2.o
-rw-r--r-- 1 ryutaroh ryutaroh 1177 Dec 25 23:29 .vmlinux.cmd
-rwxr-xr-x 1 ryutaroh ryutaroh 5113886672 Dec 25 23:29 vmlinux
lrwxrwxrwx 1 ryutaroh ryutaroh 60 Dec 25 21:46 vmlinux-gdb.py -> /var/tmp/tmp32/x86/linux-5.16-rc6/scripts/gdb/vmlinux-gdb.py
-rw-r--r-- 1 ryutaroh ryutaroh 155329959 Dec 25 23:28 vmlinux.map
-rw-r--r-- 1 ryutaroh ryutaroh 5055556384 Dec 25 23:24 vmlinux.o
-rw-r--r-- 1 ryutaroh ryutaroh 1546109 Dec 25 23:24 vmlinux.symvers
This comes from arch/x86/kernel/vmlinux.lds.S
:
/*
* The ASSERT() sink to . is intentional, for binutils 2.14 compatibility:
*/
. = ASSERT((_end - LOAD_OFFSET <= KERNEL_IMAGE_SIZE),
"kernel image bigger than KERNEL_IMAGE_SIZE");
I am sure the objtool
memory usage is just a symptom of the fact that the kernel is super large, as objtool
has to run on vmlinux.o
since that is an ELF file, rather than LLVM bitcode.
I am just curious, does this happen without CONFIG_DEBUG_INFO
? That option is going to significantly increase the size of the binary. I see about around a five times increase (without LTO, since I don't currently have the time to allow two ThinLTO allyesconfig
kernels build):
$ git diff --no-index .build/x86_64-allyesconfig{,-debug}/.config
diff --git a/.build/x86_64-allyesconfig/.config b/.build/x86_64-allyesconfig-debug/.config
index 5b0c10f57363..92162806b0d6 100644
--- a/.build/x86_64-allyesconfig/.config
+++ b/.build/x86_64-allyesconfig-debug/.config
@@ -14548,10 +14548,16 @@ CONFIG_DEBUG_MISC=y
#
# Compile-time checks and compiler options
#
-CONFIG_DEBUG_INFO_NONE=y
+CONFIG_DEBUG_INFO=y
+# CONFIG_DEBUG_INFO_NONE is not set
# CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT is not set
# CONFIG_DEBUG_INFO_DWARF4 is not set
-# CONFIG_DEBUG_INFO_DWARF5 is not set
+CONFIG_DEBUG_INFO_DWARF5=y
+# CONFIG_DEBUG_INFO_REDUCED is not set
+# CONFIG_DEBUG_INFO_COMPRESSED is not set
+# CONFIG_DEBUG_INFO_SPLIT is not set
+CONFIG_PAHOLE_HAS_SPLIT_BTF=y
+# CONFIG_GDB_SCRIPTS is not set
CONFIG_FRAME_WARN=2048
CONFIG_STRIP_ASM_SYMS=y
CONFIG_HEADERS_INSTALL=y
$ diskus .build/x86_64-allyesconfig/vmlinux
1.34 GB (1,338,589,184 bytes)
$ diskus .build/x86_64-allyesconfig-debug/vmlinux
5.75 GB (5,748,252,672 bytes)
I am just curious, does this happen without CONFIG_DEBUG_INFO? That option is going to significantly increase the size of the binary.
You are absolutely right. With DEBUG_INFO=n
, the size of vmlinux.o
becomes 1/5, and this symptom disappears.