问题现象
最近在交叉编译 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)
endif
endif
此段代码即是根据 try-run
的运行结果确定工具链是否支持 elf32_x86_64
,如果支持则定义 CONFIG_X86_X32_ABI
,否则输出前述编译警告。
try-run
在 scripts/Kbuild.include
中定义:
# output directory for tests below
TMPOUT := $(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 -V
GNU 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
可查看两个命令支持的目标格式。
以上。