FS 寄存器为空
FS register is null
我必须实现基于 SEH 的异常处理程序。
首先,我编写了以下示例代码,我在其中尝试使用 fs register.
注册异常处理程序
#include <iostream>
#include <exception>
#include <windows.h>
using namespace std;
EXCEPTION_DISPOSITION myHandler(
_EXCEPTION_RECORD *ExcRecord,
void * EstablisherFrame,
_CONTEXT *ContextRecord,
void * DispatcherContext)
{
cout << "In the exception handler" << endl;
cout << "Just a demo. exiting..." << endl;
return ExceptionContinueExecution;
}
int main()
{
cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!
EXCEPTION_REGISTRATION myExceptReg;
EXCEPTION_REGISTRATION *pReg = &myExceptReg;
myExceptReg.handler = myHandler;
DWORD prev;
asm("movl %fs:0 , %eax");
asm("movl %%eax , %0": "=r" (prev));
myExceptReg.prev = (EXCEPTION_REGISTRATION*) prev;
asm ("movl %0, %%eax" : "=m" (pReg));
asm("movl %eax , %fs:0");
// int* ptr = 0;
// exception e;
return 0;
}
当我调试代码时,我看到 fs 寄存器的值是 zero。程序在执行 asm("movl %fs:0 , %eax");
后崩溃
以下是此代码的等效程序集示例。
000000000000401626: mov %rax,%rcx
000000000000401629: callq 0x44d7a0 <std::ostream::operator<<(std::ostream& (*)(std::ostream&))>
32 EXCEPTION_REGISTRATION *pReg = &myExceptReg;
00000000000040162e: lea 0x20(%rbp),%rax
000000000000401632: mov %rax,0x18(%rbp)
33 myExceptReg.handler = myHandler;
000000000000401636: lea -0x13d(%rip),%rax # 0x401500 <myHandler(_EXCEPTION_RECORD*, void*, _CONTEXT*, void*)>
00000000000040163d: mov %rax,0x28(%rbp)
36 asm("movl %fs:0 , %eax");
000000000000401641: mov %fs:0x0,%eax
37 asm("movl %%eax , %0": "=r" (prev));
000000000000401649: mov %eax,%ebx
00000000000040164b: mov %ebx,0x3c(%rbp)
39 myExceptReg.prev = (EXCEPTION_REGISTRATION*) prev;
00000000000040164e: mov 0x3c(%rbp),%eax
000000000000401651: mov %rax,0x20(%rbp)
41 asm ("movl %0, %%eax" : "=m" (pReg));
000000000000401655: mov 0x18(%rbp),%eax
42 asm("movl %eax , %fs:0");
000000000000401658: mov %eax,%fs:0x0
50 return 0;
可能是什么问题?
总结一下:
调试输出显示正在为 64 位编译代码,并且(正如 Hans 指出的那样)所使用的异常处理样式仅对 32 位有效。确保代码针对 32 位编译可以解决问题。
如果这回答了您的问题,请单击左侧的复选标记以奖励业力。
除了 64 位问题之外,您还在无故破坏 %eax
(没有告诉编译器)。
我很惊讶它能与您的原始来源一起使用。幸运的是,编译器立即覆盖了 %eax
,这可能是因为您使用 -O0
编写了永远不会在寄存器中保存任何内容的讨厌代码。因此,一旦您在优化编译时,您的代码就会崩溃。
您还很幸运,编译器没有在破坏 %eax
的两个 asm 语句之间插入任何指令。永远不要指望两个 asm 块之间存在的寄存器或标志:在一个块中使用多个指令。
除此之外,asm ("movl %0, %%eax" : "=m" (pReg));
告诉编译器 asm 语句覆盖内存中的 pReg
,而不读取旧值。同样,只有 -O0
使您免于此错误,因为它没有优化掉 pReg = &myExceptReg;
,如果 pReg
无论如何都将被覆盖,则不需要计算它。对于 -O1
或更高版本,您应该期望 pReg
未初始化。
使用类似
的东西
void* prev;
asm volatile("mov %%fs:0, %0": "=r" (prev));
...
asm volatile("movl %0, %%fs:0" : : "re" (pReg) : "memory");
// reg or immediate source, but not memory, are encodable with a memory destination
当被询问时,编译器将负责让变量存在于寄存器中。如果它想在之后立即将 prev
存储回内存,它会这样做。没有必要强迫它这样做,"=r" (prev)
只是生成了一个冗余的 reg-reg 从 %eax
移动到任何其他 reg gcc 决定保留 prev
的地方。
由于(根据Hans的说法),这段代码只在32bit中有用,我在存储到内存的时候留下了movl
后缀,所以编译器可以在没有的时候判断operand-size注册涉及确定它。 (即当值是全局或静态符号的 compile-time-constant 地址时。)
除此之外,此源代码可以编译为正确的 64 位代码,因为它没有明确命名任何寄存器。
您可以删除约束的 "e"
部分和 l
后缀,以便在 32 位和 64 位之间轻松移植第二个 asm 语句,但代价是强制编译器会浪费一条先将地址放入寄存器的指令,即使它不需要这样做。在 64 位模式下,mov r/m64, imm32
(Intel 语法)就是您所需要的。我使用 an "e"
constraint 而不是 "i"
约束,因为无法对任意 64 位常量进行编码,只能对 sign-extended 32 位进行编码。幸运的是,在默认代码模型中,符号地址可以安全地假设在低 2GB 中。链接器可以将地址填充到 32 位重定位中。
为了使其在 32 位和 64 位之间可移植地使用立即数操作数,您需要让它输出 64 位的“movq”。我认为您必须使用预处理器来测试 i386 与 amd64。 if(sizeof(void*) == 4) { asm("movl ..."); } else { asm("movq..."); }
也可能有效。这会使带有 -O0
的代码变得非常丑陋,但分支的伪造端仍然 assemble (至 mov r/m32, imm32
)并且永远不会 运行.
我必须实现基于 SEH 的异常处理程序。 首先,我编写了以下示例代码,我在其中尝试使用 fs register.
注册异常处理程序#include <iostream>
#include <exception>
#include <windows.h>
using namespace std;
EXCEPTION_DISPOSITION myHandler(
_EXCEPTION_RECORD *ExcRecord,
void * EstablisherFrame,
_CONTEXT *ContextRecord,
void * DispatcherContext)
{
cout << "In the exception handler" << endl;
cout << "Just a demo. exiting..." << endl;
return ExceptionContinueExecution;
}
int main()
{
cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!
EXCEPTION_REGISTRATION myExceptReg;
EXCEPTION_REGISTRATION *pReg = &myExceptReg;
myExceptReg.handler = myHandler;
DWORD prev;
asm("movl %fs:0 , %eax");
asm("movl %%eax , %0": "=r" (prev));
myExceptReg.prev = (EXCEPTION_REGISTRATION*) prev;
asm ("movl %0, %%eax" : "=m" (pReg));
asm("movl %eax , %fs:0");
// int* ptr = 0;
// exception e;
return 0;
}
当我调试代码时,我看到 fs 寄存器的值是 zero。程序在执行 asm("movl %fs:0 , %eax");
后崩溃以下是此代码的等效程序集示例。
000000000000401626: mov %rax,%rcx
000000000000401629: callq 0x44d7a0 <std::ostream::operator<<(std::ostream& (*)(std::ostream&))>
32 EXCEPTION_REGISTRATION *pReg = &myExceptReg;
00000000000040162e: lea 0x20(%rbp),%rax
000000000000401632: mov %rax,0x18(%rbp)
33 myExceptReg.handler = myHandler;
000000000000401636: lea -0x13d(%rip),%rax # 0x401500 <myHandler(_EXCEPTION_RECORD*, void*, _CONTEXT*, void*)>
00000000000040163d: mov %rax,0x28(%rbp)
36 asm("movl %fs:0 , %eax");
000000000000401641: mov %fs:0x0,%eax
37 asm("movl %%eax , %0": "=r" (prev));
000000000000401649: mov %eax,%ebx
00000000000040164b: mov %ebx,0x3c(%rbp)
39 myExceptReg.prev = (EXCEPTION_REGISTRATION*) prev;
00000000000040164e: mov 0x3c(%rbp),%eax
000000000000401651: mov %rax,0x20(%rbp)
41 asm ("movl %0, %%eax" : "=m" (pReg));
000000000000401655: mov 0x18(%rbp),%eax
42 asm("movl %eax , %fs:0");
000000000000401658: mov %eax,%fs:0x0
50 return 0;
可能是什么问题?
总结一下:
调试输出显示正在为 64 位编译代码,并且(正如 Hans 指出的那样)所使用的异常处理样式仅对 32 位有效。确保代码针对 32 位编译可以解决问题。
如果这回答了您的问题,请单击左侧的复选标记以奖励业力。
除了 64 位问题之外,您还在无故破坏 %eax
(没有告诉编译器)。
我很惊讶它能与您的原始来源一起使用。幸运的是,编译器立即覆盖了 %eax
,这可能是因为您使用 -O0
编写了永远不会在寄存器中保存任何内容的讨厌代码。因此,一旦您在优化编译时,您的代码就会崩溃。
您还很幸运,编译器没有在破坏 %eax
的两个 asm 语句之间插入任何指令。永远不要指望两个 asm 块之间存在的寄存器或标志:在一个块中使用多个指令。
除此之外,asm ("movl %0, %%eax" : "=m" (pReg));
告诉编译器 asm 语句覆盖内存中的 pReg
,而不读取旧值。同样,只有 -O0
使您免于此错误,因为它没有优化掉 pReg = &myExceptReg;
,如果 pReg
无论如何都将被覆盖,则不需要计算它。对于 -O1
或更高版本,您应该期望 pReg
未初始化。
使用类似
的东西void* prev;
asm volatile("mov %%fs:0, %0": "=r" (prev));
...
asm volatile("movl %0, %%fs:0" : : "re" (pReg) : "memory");
// reg or immediate source, but not memory, are encodable with a memory destination
当被询问时,编译器将负责让变量存在于寄存器中。如果它想在之后立即将 prev
存储回内存,它会这样做。没有必要强迫它这样做,"=r" (prev)
只是生成了一个冗余的 reg-reg 从 %eax
移动到任何其他 reg gcc 决定保留 prev
的地方。
由于(根据Hans的说法),这段代码只在32bit中有用,我在存储到内存的时候留下了movl
后缀,所以编译器可以在没有的时候判断operand-size注册涉及确定它。 (即当值是全局或静态符号的 compile-time-constant 地址时。)
除此之外,此源代码可以编译为正确的 64 位代码,因为它没有明确命名任何寄存器。
您可以删除约束的 "e"
部分和 l
后缀,以便在 32 位和 64 位之间轻松移植第二个 asm 语句,但代价是强制编译器会浪费一条先将地址放入寄存器的指令,即使它不需要这样做。在 64 位模式下,mov r/m64, imm32
(Intel 语法)就是您所需要的。我使用 an "e"
constraint 而不是 "i"
约束,因为无法对任意 64 位常量进行编码,只能对 sign-extended 32 位进行编码。幸运的是,在默认代码模型中,符号地址可以安全地假设在低 2GB 中。链接器可以将地址填充到 32 位重定位中。
为了使其在 32 位和 64 位之间可移植地使用立即数操作数,您需要让它输出 64 位的“movq”。我认为您必须使用预处理器来测试 i386 与 amd64。 if(sizeof(void*) == 4) { asm("movl ..."); } else { asm("movq..."); }
也可能有效。这会使带有 -O0
的代码变得非常丑陋,但分支的伪造端仍然 assemble (至 mov r/m32, imm32
)并且永远不会 运行.