在 64 位上执行 32 位二进制时的 SIGSEGV Linux
SIGSEGV when executing 32 bit binary on 64 bit Linux
您可能听说过 kragen 的一个项目 StoneKnifeForth:https://github.com/kragen/stoneknifeforth。它是一个充当小型 Forth 解释器的 Python 程序和一个充当 Forth 编译器的 Forth 程序。因此,您可以同时使用这两个来构建 Forth 编译器二进制文件。
将 StoneKnifeForth 移植到 C++ (https://github.com/tekknolagi/stoneknifecpp) 后,我注意到 StoneKnifeForth(任一变体)生成的所有二进制文件在 64 位上都存在段错误 Linux。也就是说,如果您克隆 stoneknifecpp 和 运行:
make
./l01compiler # produced by the Forth program
您将获得以下内容:
willow% ./l01compiler
[1] 31614 segmentation fault ./l01compiler
显然,这不是一个非常有趣的错误消息,所以我想我会跟踪它:
willow% strace ./l01compiler
execve("./l01compiler", ["./l01compiler"], [/* 110 vars */]) = -1 EPERM (Operation not permitted)
--- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} ---
+++ killed by SIGSEGV +++
[1] 31615 segmentation fault (core dumped) strace ./l01compiler
并获得了...更多信息。看来ELFheader是错的不知何故,除了下面两个有趣的花絮:
- 在 32 位 qemu 下运行没问题
- 如果我
sudo ./l01compiler
运行没问题
即使在互联网上搜索 32 位和 64 位 Linux 内核之间的 ELF header 格式之间可能存在的差异等之后,我也有点不知所措。如果谁有任何信息,我会很高兴。
我附上了下面的header:
willow% readelf -h l01compiler
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: Intel 80386
Version: 0x1
Entry point address: 0x1e39
Start of program headers: 52 (bytes into file)
Start of section headers: 0 (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: 0
Section header string table index: 0
非常感谢 Tom Hebb (tchebb) 证实了我的怀疑并解决了这个问题。从 this commit 中可以看出,问题是原始地址太低了。它与 32 位或 64 位无关,而是较早的内核与较新的内核。
较新的内核增加了vm.mmap_min_addr
sysctl 参数,这意味着旧的内核将完全禁止程序启动。这解释了为什么 sudo
有效。正如汤姆所解释的那样,"Unless you invoke qemu with KVM support, qemu is an emulator not a hypervisor, so it simulates the entire address space and virtual memory subsystem in software and presumably doesn't impose any load address restrictions."
您可能听说过 kragen 的一个项目 StoneKnifeForth:https://github.com/kragen/stoneknifeforth。它是一个充当小型 Forth 解释器的 Python 程序和一个充当 Forth 编译器的 Forth 程序。因此,您可以同时使用这两个来构建 Forth 编译器二进制文件。
将 StoneKnifeForth 移植到 C++ (https://github.com/tekknolagi/stoneknifecpp) 后,我注意到 StoneKnifeForth(任一变体)生成的所有二进制文件在 64 位上都存在段错误 Linux。也就是说,如果您克隆 stoneknifecpp 和 运行:
make
./l01compiler # produced by the Forth program
您将获得以下内容:
willow% ./l01compiler
[1] 31614 segmentation fault ./l01compiler
显然,这不是一个非常有趣的错误消息,所以我想我会跟踪它:
willow% strace ./l01compiler
execve("./l01compiler", ["./l01compiler"], [/* 110 vars */]) = -1 EPERM (Operation not permitted)
--- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} ---
+++ killed by SIGSEGV +++
[1] 31615 segmentation fault (core dumped) strace ./l01compiler
并获得了...更多信息。看来ELFheader是错的不知何故,除了下面两个有趣的花絮:
- 在 32 位 qemu 下运行没问题
- 如果我
sudo ./l01compiler
运行没问题
即使在互联网上搜索 32 位和 64 位 Linux 内核之间的 ELF header 格式之间可能存在的差异等之后,我也有点不知所措。如果谁有任何信息,我会很高兴。
我附上了下面的header:
willow% readelf -h l01compiler
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: Intel 80386
Version: 0x1
Entry point address: 0x1e39
Start of program headers: 52 (bytes into file)
Start of section headers: 0 (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: 0
Section header string table index: 0
非常感谢 Tom Hebb (tchebb) 证实了我的怀疑并解决了这个问题。从 this commit 中可以看出,问题是原始地址太低了。它与 32 位或 64 位无关,而是较早的内核与较新的内核。
较新的内核增加了vm.mmap_min_addr
sysctl 参数,这意味着旧的内核将完全禁止程序启动。这解释了为什么 sudo
有效。正如汤姆所解释的那样,"Unless you invoke qemu with KVM support, qemu is an emulator not a hypervisor, so it simulates the entire address space and virtual memory subsystem in software and presumably doesn't impose any load address restrictions."