问题现象

最近在交叉编译 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-runscripts/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

注意:objcopyld 参数值的不同(elf32-86-64elf32_86_64)。

解决方法

假如查看到的工具链不支持需要的目标,只需升级工具链再重新编译内核即可。

小结

  • 64 位处理器运行原生 32 位程序,需要打开内核 CONFIG_X86_X32 选项。

  • CONFIG_X86_X32 选项需要工具链支持编译 elf32_x86_64 目标。

  • objcopy --helpld -V 可查看两个命令支持的目标格式。

以上。