我在linux上遇到了一些关于ELF可执行文件的奇怪问题。
这是我的系统(uname -a
):
Linux 3.16.0-4-amd64 #1 SMP Debian 3.16.7-ckt20-1+deb8u2 (2016-01-02) x86_64 GNU/Linux
我有以下程序(test.asm),我使用NASM组装它:
; program just exits with code 0 using linux INT 80H
SECTION .data
SECTION .text
GLOBAL _start
_start:
MOV EAX, 1
XOR EBX, EBX
INT 0x80
我创建了三个不同的可执行文件:
nasm -f elf32 -o test32-i386.o test.asm
ld -m elf_i386 -o test32-i386 test32-i386.o
nasm -f elfx32 -o test32-x86_64.o test.asm
ld -m elf32_x86_64 -o test32-x86_64 test32-x86_64.o
nasm -f elf64 -o test64-x86_64.o test.asm
ld -m elf_x86_64 -o test64-x86_64 test64-x86_64.o
这是file
命令的输出:
test32-i386: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped
test32-x86_64: ELF 32-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
test64-x86_64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
对我有意义。但是,运行它们会带来麻烦。
./test32-i386
:没问题,运行正常。./test64-x86_64
:同样,运行正常。./test32-x86_64
但是,bash: ./test32-x86_64: cannot execute binary file: Exec format error
另外,Valgrind会产生......有趣的结果。
valgrind ./test32-i386
:好的valgrind ./test64-x86_64
:提升SIGILL
(?!)valgrind ./test32-x86_64
:给了我./test32-x86_64: 1: ./test32-x86_64: Syntax error: word unexpected (expecting ")")
问题1:为什么Valgrind在运行SIGILL
时会引发./test64-x86_64
,即使程序在没有Valgrind的情况下似乎工作正常?
问题2:为什么我无法运行./test32-x86_64
? Valgrind为该二进制文件提供的错误非常模糊......
答案 0 :(得分:2)
首先,我无法在./test32-x86_64
上重现您的错误。虽然我使用完全相同的代码和命令行来编译它。
我在Linux 4.3.3 x86_64(Debian)上运行。
问题1 :为什么Valgrind在运行
SIGILL
时会引发./test64-x86_64
,即使程序在没有Valgrind的情况下似乎工作正常?
我的Valgrind版本(3.11.0)不会在此版本上引发SIGILL
,但会引发此消息(然后按预期执行该程序):
valgrind: wrong ELF executable class (eg. 32-bit instead of 64-bit)
但是,当运行text64-x86_64
时,Valgrind会抛出以下消息:
vex amd64->IR: unhandled instruction bytes: 0xCD 0x80 0x0 0x0 0x0 0x0 0x0 0x0
vex amd64->IR: REX=0 REX.W=0 REX.R=0 REX.X=0 REX.B=0
vex amd64->IR: VEX=0 VEX.L=0 VEX.nVVVV=0x0 ESC=NONE
vex amd64->IR: PFX.66=0 PFX.F2=0 PFX.F3=0
如Valgrind FAQ中所述:
<强> 3.3。我的程序死了,在此过程中打印出这样的消息:
vex x86->IR: unhandled instruction bytes: 0x66 0xF 0x2E 0x5
一种可能是您的程序有错误并错误地跳转到非代码地址,在这种情况下您将获得SIGILL信号。 Memcheck可能会在此之前发出警告,但如果跳转发生在可寻址内存中,则可能不会发出警告。
另一种可能性是Valgrind不处理该指令。如果您使用的是旧版Valgrind,则较新版本可能会处理该指令。但是,所有指令集都有一些模糊的,很少使用的指令。此外,在amd64上,冗余指令前缀的组合数量几乎无限,其中许多没有记录,但被CPU接受。因此Valgrind将不时有解码失败。如果发生这种情况,请提交错误报告。
在我们的确切情况下,它只是意味着VEX中间语言没有将int 0x80
指令识别为x86_64
架构的一部分。
问题2 :为什么我无法运行
./test32-x86_64
? Valgrind为该二进制文件提供的错误非常模糊......
不幸的是,我无法用我的Valgrind重现你的错误。
答案 1 :(得分:2)
对于问题1:针对valgrind打开了一个错误,它不支持int80 instruction in x86_64。我能够在我自己的valgrind(v3.11.0)下重现这一点,并且从浏览源看起来好像它不受支持。
问题2:ELF加载程序不支持文件类型。为了在linux上提供32位二进制文件的兼容性,它必须在尝试执行二进制文件时对二进制文件进行一些检查。
当我们在test32-x86_64上使用readelf时,会显示一个标题:
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x400060
Start of program headers: 52 (bytes into file)
Start of section headers: 288 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 1
Size of section headers: 40 (bytes)
Number of section headers: 5
Section header string table index: 2
即。 class是32位,机器类型是x86_64。即它是一个x32 ABI二进制文件
问题是,这需要使用CONFIG_X86_X32_ABI
配置您的内核,否则您将失败foul of the check:
#define compat_elf_check_arch(x) \
(elf_check_arch_ia32(x) || \
(IS_ENABLED(CONFIG_X86_X32_ABI) && (x)->e_machine == EM_X86_64))
仅支持没有配置选项的32位二进制文件。如果您有内核选项,则设置此选项:CONFIG_X86_X32 = y和CONFIG_X86_X32_DISABLED未设置(这是我正在查看的linux内核4.3源代码)。
所以你需要你的内核配置这个支持来运行代码 - perror之所以没有看到问题是他的内核似乎是用正确的运行x32代码的选项编译的。
valgrind可能无法用二进制格式混淆 - 它不被认为特别常见。