我一直在binfmt
为 Ubuntu Linux 12.04、Intel x86_64 架构开发一个自定义 PE 处理程序(如果这听起来很熟悉,我已经发布了一些与该项目相关的问题)。如果我提供的信息量过大,我会提前道歉。
binfmt
处理程序非常标准;我读入 PE 头和节,然后将这些节写入用户空间内存中节表中指定的地址。然后,当一切准备就绪时,我打电话
start_thread(regs, entry_addr, current->mm->start_stack);
就像内置的 Linux 处理程序一样;在我的情况下,regs = 0xcf7dffb4
,entry_addr = 0x401000
和start_stack = 0xbffff59b
。
我在 Intel x86 程序集中有以下代码:
push ebp
mov ebp, esp
mov eax, 4
add eax, 5
pop ebp
ret
我使用fasm将此程序编译为 PE 格式的可执行文件(math1.exe),并binfmt
使用insmod
. 如果我在 中调试这个程序gdb
,我会看到:
(gdb) set disassembly-flavor intel
(gdb) x/6i 0x401000
0x401000: push ebp
0x401001: mov ebp,esp
0x401003: mov eax,0x4
0x401008: add eax,0x5
0x40100b: pop ebp
0x40100c: ret
所以我知道代码已加载到正确的地址。然后:
(gdb) run
Starting program: /media/sf_Sandbox/math1.exe
Program received signal SIGSEGV, Segmentation fault.
0x0040100c in ?? ()
当我进行寄存器转储时:
(gdb) info registers
eax 0x9 9
ecx 0x81394e8 135501032
edx 0x8137808 135493640
ebx 0x8139548 135501128
esp 0xbfffe59b 0xbfffe59b
ebp 0x0 0x0
esi 0x81394e8 135501032
edi 0x2f7ff4 3112948
eip 0x40100c 0x40100c
...other registers...
您可以看到代码确实执行了eax = 0x9
,因为它应该执行。不过,从表面上看,我找不到任何理由在ret
声明中出现段错误。检查dmesg
,我发现
math1.exe[1864] general protection ip:40100c sp:bffff5bd error:0
但我发现很少有关于可能导致这种情况的文档。我知道问题不在于代码本身,因为使用相同的汇编程序编译为 ELF 格式的相同代码运行起来没有任何问题。
我目前关于这个问题的理论是:
- 我并没有真正弄乱处理程序中的堆栈指针。内置的 Linux 处理程序(用于 ELF、a.out 和平面格式,仅举三例)具有
create_*_tables()
处理argc
、argv
和envp
参数的函数。我一开始没有包含这个函数,因为测试程序不接受任何输入,但是实现这个create_flat_tables()
函数(从平面处理程序)到目前为止并没有解决问题。(我知道从其他模块中盲目粘贴和调用函数是个坏主意,但是该函数的 a.out 和 flat 版本本质上是相同的,所以它似乎不太依赖于可执行格式;我以为我会试一试。) - 我发现这篇文章是关于在
main()
.objdump
math1.exe 的 仅包含上面给出的汇编代码,但同一objdump
程序在汇编为 ELF 格式(生成*.o
文件)和链接gcc
(以获取 ELF 二进制文件)后包含文章中提到的其他函数 (_start()
,__libc_start_main()
, ETC。)。也许这些功能在 Linux 平台上比我之前想象的更加强制性。
我正在寻找可以采取的任何解释/建议/进一步的故障排除步骤。提前致谢!