问题现象
最近在交叉编译 X86-64 平台的 Linux 内核时,出现如下编译警告:
CONFIG_X86_X32 enabled but no binutils support
查看 .config
文件确实打开了 CONFIG_X86_X32
:
CONFIG_X86_X32=y
在 arch/x86/Kconfig
中找到对 CONFIG_X86_X32
的说明:
config X86_X32 bool "x32 ABI for 64-bit mode" depends on X86_64 ---help--- Include code to run binaries for the x32 native 32-bit ABI for 64-bit processors. An x32 process gets access to the full 64-bit register file and wide data path while leaving pointers at 32 bits for smaller memory footprint. You will need a recent binutils (2.22 or later) with elf32_x86_64 support enabled to compile a kernel with this option set.
由上述说明可知,CONFIG_X86_X32
选项用于在 64 位处理器上运行原生 32 位程序,需要 binutils 2.22 或更高版本(带 elf32_x86_64
支持)。
源码分析
在内核源码中搜索上述警告,定位到 arch/x86/Makefile
:
ifdef CONFIG_X86_X32 x32_ld_ok := $(call try-run,\ /bin/echo -e '1: .quad 1b' | \ $(CC) $(KBUILD_AFLAGS) -c -x assembler -o "$$TMP" - && \ $(OBJCOPY) -O elf32-x86-64 "$$TMP" "$$TMPO" && \ $(LD) -m elf32_x86_64 "$$TMPO" -o "$$TMP",y,n) ifeq ($(x32_ld_ok),y) CONFIG_X86_X32_ABI := y KBUILD_AFLAGS += -DCONFIG_X86_X32_ABI KBUILD_CFLAGS += -DCONFIG_X86_X32_ABI else $(warning CONFIG_X86_X32 enabled but no binutils support) endifendif
此段代码即是根据 try-run
的运行结果确定工具链是否支持 elf32_x86_64
,如果支持则定义 CONFIG_X86_X32_ABI
,否则输出前述编译警告。
try-run
在 scripts/Kbuild.include
中定义:
# output directory for tests belowTMPOUT := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/)# try-run# Usage: option = $(call try-run, $(CC)...-o "$$TMP",option-ok,otherwise)# Exit code chooses option. "$$TMP" serves as a temporary file and is# automatically cleaned up.try-run = $(shell set -e; \ TMP="$(TMPOUT).$$$$.tmp"; \ TMPO="$(TMPOUT).$$$$.o"; \ if ($(1)) >/dev/null 2>&1; \ then echo "$(2)"; \ else echo "$(3)"; \ fi; \ rm -f "$$TMP" "$$TMPO")
其作用是执行第一个入参指定的命令,如果成功则输出第二个入参,失败则输出第三个入参,最后删除临时目录下的两个临时文件。
结合 arch/x86/Makefile
的使用情况,完成如下三个操作:
将一行汇编语句使用
gcc
编译成.$$$$.tmp
;使用
objcopy
将.$$$$.tmp
转换为elf32-x86-64
格式的.$$$$.o
;最后使用
ld
将.$$$$.o
链接为elf32_x86_64
目标的.$$$$.tmp
(复用此文件名)。
假如三个操作都没有错误发生,表明目标工具链支持 x32 ABI 对应的选项,则 x32_ld_ok
变量赋值为 y
,否则赋值为 n
。
其中要编译的汇编语句仅有一行,作用是定义一个值为 1 的 64 比特数值,仅用于后续的选项测试,没有实际功能:
1: .quad 1b
查看工具链支持的编译目标
在 objcopy --help
的最后可查看其支持的目标,其中包括 elf32-86-64
,各个目标可作为 -O
参数传入:
objcopy: supported targets: elf64-x86-64 elf32-i386 elf32-iamcu elf32-x86-64 pei-i386 pei-x86-64 elf64-l1om elf64-k1om elf64-little elf64-big elf32-little elf32-big pe-x86-64 pe-bigobj-x86-64 pe-i386 plugin srec symbolsrec verilog tekhex binary ihex
ld -V
可查看其支持的目标,其中包括 elf32_86_64
,各个目标可作为 -m
参数传入:
# ld -VGNU ld (GNU Binutils for Ubuntu) 2.31.1 Supported emulations: elf_x86_64 elf32_x86_64 elf_i386 elf_iamcu elf_l1om elf_k1om i386pep i386pe
注意:objcopy
和 ld
参数值的不同(elf32-86-64
和 elf32_86_64
)。
解决方法
假如查看到的工具链不支持需要的目标,只需升级工具链再重新编译内核即可。
小结
64 位处理器运行原生 32 位程序,需要打开内核
CONFIG_X86_X32
选项。CONFIG_X86_X32
选项需要工具链支持编译elf32_x86_64
目标。objcopy --help
和ld -V
可查看两个命令支持的目标格式。
以上。