ELF 可执行问题

ELF executable issues

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),我 assemble 使用 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

对我来说很有意义。但是,运行宁可惹麻烦。

另外,Valgrind 产生...有趣的结果。

所以,总结一下:

问题 1:为什么 Valgrind 在 运行s ./test64-x86_64 时会引发 SIGILL,即使程序在没有 Valgrind 的情况下似乎运行良好?

问题2:为什么我不能运行./test32-x86_64? Valgrind 为该二进制文件给出的错误非常模糊...

首先,我无法在 ./test32-x86_64 上重现您的错误。虽然我使用完全相同的代码和命令行来编译它。

我是 运行 Linux 4.3.3 x86_64 (Debian)。

Question 1: Why does Valgrind raise SIGILL when it runs ./test64-x86_64 even though the program seems to work fine without 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. My program dies, printing a message like this along the way:

vex x86->IR: unhandled instruction bytes: 0x66 0xF 0x2E 0x5

One possibility is that your program has a bug and erroneously jumps to a non-code address, in which case you'll get a SIGILL signal. Memcheck may issue a warning just before this happens, but it might not if the jump happens to land in addressable memory.

Another possibility is that Valgrind does not handle the instruction. If you are using an older Valgrind, a newer version might handle the instruction. However, all instruction sets have some obscure, rarely used instructions. Also, on amd64 there are an almost limitless number of combinations of redundant instruction prefixes, many of them undocumented but accepted by CPUs. So Valgrind will still have decoding failures from time to time. If this happens, please file a bug report.

在我们的精确案例中,它只是意味着 VEX 中间语言没有将 int 0x80 指令识别为 x86_64 架构的一部分。

Question 2: Why can't I run ./test32-x86_64? The error Valgrind gives for that binary is very obscure...

不幸的是,我无法用我的 Valgrind 重现您的错误。

对于问题 1:有一个针对 valgrind 的漏洞,它不支持 int80 instruction in x86_64。我能够在我自己的 valgrind (v3.11.0) 下重现它,并且通过浏览源代码,它似乎不受支持。

对于问题2:您的ELF加载器不支持该文件类型。为了在 linux 上提供 32 位二进制文​​件的兼容性,它必须在尝试执行时对二进制文件进行一些检查。

当我们在 test32-x86_64 上使用 readelf 时,它会显示一个 header of:

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 没有发现问题的原因是他的内核似乎是使用 [=39] 的正确选项编译的=]宁 x32 代码。

valgrind 可能被二进制格式弄糊涂了——它并不被认为特别常见。