使用-O0编译Linux内核

文章目录

  • 1. 编译内核
    • 1.1. 修改gcc优化等级
    • 1.2. 防止`modpost: Section mismatches detected.`错误
    • 1.3. 根据需要编译内核
    • 1.4. 修改子目录Makefile
    • 1.5. 重新执行编译
  • 2. 参考资料

1. 编译内核

进入kernel 源码目录

1.1. 修改gcc优化等级

diff --git a/Makefile b/Makefile
index d4d36c619..1047c83c6 100644
--- a/Makefile
+++ b/Makefile
@@ -701,11 +701,11 @@ KBUILD_CFLAGS     += $(call cc-disable-warning, format-overflow)
 KBUILD_CFLAGS  += $(call cc-disable-warning, address-of-packed-member)

 ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE
-KBUILD_CFLAGS += -O2
+KBUILD_CFLAGS += -O0
 else ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3
-KBUILD_CFLAGS += -O3
+KBUILD_CFLAGS += -O0
 else ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
-KBUILD_CFLAGS += -Os
+KBUILD_CFLAGS += -O0
 endif

 ifdef CONFIG_CC_DISABLE_WARN_MAYBE_UNINITIALIZED

1.2. 防止modpost: Section mismatches detected.错误

如果此时直接执行make进行编译,会出现modpost: Section mismatches detected.错误。

解决方法是修改scripts/mod/modpost.c

diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index d2a30a7b3..58e2237c2 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -2670,7 +2670,7 @@ int main(int argc, char **argv)

        if (dump_write)
                write_dump(dump_write);
-       if (sec_mismatch_count && sec_mismatch_fatal)
+       if (0 && sec_mismatch_count && sec_mismatch_fatal)
                fatal("modpost: Section mismatches detected.\n"
                      "Set CONFIG_SECTION_MISMATCH_WARN_ONLY=y to allow them.\n");
        for (n = 0; n < SYMBOL_HASH_SIZE; n++) {

1.3. 根据需要编译内核

make tinyconfig ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- 2>&1 | tee -a build.log
make vmlinux -j4 ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- 2>&1 | tee -a build_error.log

此时编译失败,原因是部分函数未定义,具体原因可以参考宋宝华: 关于Linux编译优化几个必须掌握的姿势

  MODINFO modules.builtin.modinfo
  LD      vmlinux
mm/page-writeback.o: In function `page_index':
page-writeback.c:(.text+0x21a4): undefined reference to `__page_file_index'
page-writeback.c:(.text+0x21a4): relocation truncated to fit: R_AARCH64_CALL26 against undefined symbol `__page_file_index'
mm/truncate.o: In function `truncate_exceptional_pvec_entries':
truncate.c:(.text+0x199c): undefined reference to `dax_delete_mapping_entry'
truncate.c:(.text+0x199c): relocation truncated to fit: R_AARCH64_CALL26 against undefined symbol `dax_delete_mapping_entry'
......
mm/rmap.o: In function `linear_page_index':
rmap.c:(.text+0x3110): undefined reference to `linear_hugepage_index'
Makefile:1077: recipe for target 'vmlinux' failed
make: *** [vmlinux] Error 1

1.4. 修改子目录Makefile

修改方法就是单独为提示存在未定义符号的文件修改gcc优化等级。方法是在Makefile中添加CFLAGS_file.o += -O

拷贝如下内容新建脚本enbale_O0.sh,使用enabel_O0.sh和上述编译失败的build_error.log文件,执行./enable_O0.sh build_error.log

#!/bin/bash

# make clean -j16
# make all -j4 ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- 2>&1 |tee -a build_error.log

MAKE_LOG=""
BUILD_PATH="$(pwd -P)"

if [ $# -ge 1 ]; then
	MAKE_LOG=$1
else
	echo "Usage: $(basename $0) build_error.log [build_path]"
	exit -1
fi

if [ $# -ge 2 ]; then
	if [ -d $2 ]; then
		BUILD_PATH=$2
		BUILD_PATH=${BUILD_PATH%*/}
	else
		echo "$2 No such directory"
		exit -1
	fi
fi

if [ ! -f ${MAKE_LOG} ]; then
	echo "${MAKE_LOG}: No such file"
	exit -1
fi



add_cflag()
{
	make_file=$1
	obj_file=$2

	if [ -e ${make_file} ]; then
		line_no=$(grep -snv '^#' ${make_file}	\
					| cut -d : -f 1 | head -1)
		str_cflags="CFLAGS_${obj_file}"
		if [[ "" == "$(grep ${str_cflags}.*O ${make_file})" ]]; then
			cmd="${line_no}i\\${str_cflags} += -O"
			sed -i "${cmd}" ${make_file}

			echo "${make_file}: add ${str_cflags}"
		fi
	fi
}

vmlinux_add_cflag()
{
	log_file=$1

	filelist=$(grep -shw -B1 'undefined reference' ${log_file}		\
			| grep 'In function' | cut -d : -f 1 | sort -u)

	for f in ${filelist}
	do
		path=$(dirname ${f})
		obj_file=$(basename ${f})

		make_file=${path}/Makefile

		add_cflag ${make_file} ${obj_file}

	done
}

process_one()
{
	# ERROR: "alloc_test_extent_buffer" [fs/btrfs/btrfs.ko] undefined!
	str=$1

	symbol=$(echo ${str} | awk '{print $2}')
	symbol=${symbol:1:0-1}		# 删除 ""
	
	ko=$(echo ${str} | awk '{print $3}')
	ko=${ko:1:0-1}				# 删除 []

	path=$(dirname ${ko})
	filelist=$(grep -rl ${symbol} ${BUILD_PATH}/${path}/*.o)
	echo $filelist

	make_file=${path}/Makefile

	for f in ${filelist}
	do
		f=${f#${BUILD_PATH}/}		# 从左边删除 "${BUILD_PATH}/"
		obj=$(basename ${f})

		add_cflag ${make_file} ${obj}
	done
}

module_add_cflag()
{
	log_file=$1

	IFS=$'\n'		# 按行遍历
	str_list=$(grep -sh "^ERROR:.*undefined!" ${log_file} | sort -u)
	for str in ${str_list}
	do
		process_one ${str}
	done
}

vmlinux_add_cflag ${MAKE_LOG}
module_add_cflag ${MAKE_LOG}

执行结束后,子目录Makefile改动如下:

diff --git a/mm/Makefile b/mm/Makefile
index d99684669..52a1962ea 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -2,6 +2,14 @@
 #
 # Makefile for the linux memory manager.
 #
+CFLAGS_truncate.o += -O
+CFLAGS_rmap.o += -O
+CFLAGS_page-writeback.o += -O
+CFLAGS_mremap.o += -O
+CFLAGS_mprotect.o += -O
+CFLAGS_mincore.o += -O
+CFLAGS_memory.o += -O
+CFLAGS_gup.o += -O

 KASAN_SANITIZE_slab_common.o := n
 KASAN_SANITIZE_slab.o := n

1.5. 重新执行编译

make clean
make vmlinux -j4 ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- 2>&1 | tee -a build_ok.log

此时可正常编译通过。

  MODINFO modules.builtin.modinfo
  LD      vmlinux
  SORTEX  vmlinux
  SYSMAP  System.map

2. 参考资料

  1. 宋宝华: 关于Linux编译优化几个必须掌握的姿势
  2. runninglinuxkernel_4

你可能感兴趣的:(make,kernel,代码阅读)