修改Windows、TIB和异常上的堆栈
Modifying the stack on Windows, TIB and exceptions
我的问题的根源实际上是想在Windows上提供一个支持用户提供堆栈的pthreads实现。具体来说,pthread_attr_setstack
应该做一些有意义的事情。我的实际需求比这更复杂一些,但这对于这篇文章的目的来说已经足够了。
没有公共的Win api可以在光纤或线程api中提供堆栈。我找了很多后门,变通办法和黑客手段,都没找到。事实上,我看了winpthread源代码的灵感,它忽略了任何堆栈提供给pthread_attr_setstack
。
相反,我尝试了下面的"解决方案",看看它是否有效。我使用ConvertThreadToFiber
, CreateFiberEx
和SwitchToFiber
的通常组合创建一个光纤。在CreateFiberEx
中,我提供了最小的堆栈大小。然后,在光纤的入口点,我为堆栈分配内存,适当地更改TIB字段:"堆栈基础"answers"堆栈限制"(见这里:http://en.wikipedia.org/wiki/Win32_Thread_Information_Block),然后将ESP设置为堆栈的高地址。
(在现实世界的情况下,我将设置堆栈比这更好,并改变EIP,使这一步更像posix函数swapcontext
,但你明白了)。
如果我在这个不同的堆栈上进行任何操作系统调用,那么我就完蛋了(例如printf
就死了)。然而,这对我来说不是问题。我可以确保在我的自定义堆栈上我永远不会确保调用(因此我说我的实际需求有点复杂)。除了……我需要例外来工作。他们没有!具体来说,如果我试图在修改后的堆栈上抛出并捕获异常,那么我将得到一个assert
0xXXXXXXXX ....未处理异常
所以我(模糊)的问题是,有没有人有任何见解异常和自定义堆栈可能不会很好地发挥在一起?我明白这是完全不支持的,可以高兴地除了零回应或"走开"。事实上,我已经决定我需要一个不同的解决方案,尽管这涉及到妥协,我可能会使用一个。然而,好奇心控制了我,所以我想知道为什么这不起作用。
在一个相关的说明,我想知道Cygwin如何处理这个无上下文。这里的源代码http://szupervigyor.ddsi.hu/source/in/openjdk-6-6b18-1.8.13/cacao-0.99.4/src/vm/jit/i386/cygwin/ucontext.c使用GetThreadContext
/SetThreadContext
来实现ucontext。然而,从实验中我发现,当从新上下文内部抛出异常时,这种方法也会失败。事实上,SetThreadContext
调用甚至不更新TIB块!
编辑(基于@avakar的答案)
下面的代码(与您的代码非常相似)演示了相同的失败。不同的是,我不启动第二个线程暂停,但暂停它,然后尝试改变上下文。这段代码显示了我在foo
中遇到try-catch块时所描述的错误。也许这就是不合法的。值得注意的是,在这种情况下,当调用modifyThreadContext
时,TIB的ExceptionList
成员是一个有效的指针,而在您的示例中它是-1。手动编辑这个没有帮助。
正如我在对你的回答的评论中提到的。这不是我想要的。我想从我当前的线程切换上下文。但是,SetThreadContext
的文档警告不要在活动线程上调用此方法。所以我猜,如果下面的代码不工作,那么我没有机会让它在一个线程上工作。
namespace
{
HANDLE ghSemaphore = 0;
void foo()
{
try
{
throw 6;
}
catch(...){}
ExitThread(0);
}
void modifyThreadContext(HANDLE thread)
{
typedef NTSTATUS WINAPI NtQueryInformationThread_t(HANDLE ThreadHandle, DWORD ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength, PULONG ReturnLength);
HMODULE hNtdll = LoadLibraryW(L"ntdll.dll");
auto NtQueryInformationThread = (NtQueryInformationThread_t *)GetProcAddress(hNtdll, "NtQueryInformationThread");
DWORD stackSize = 1024 * 1024;
void * mystack = VirtualAlloc(0, stackSize, MEM_COMMIT, PAGE_READWRITE);
DWORD threadInfo[7];
NtQueryInformationThread(thread, 0, threadInfo, sizeof threadInfo, 0);
NT_TIB * tib = (NT_TIB *)threadInfo[1];
CONTEXT ctx = {};
ctx.ContextFlags = CONTEXT_ALL;
GetThreadContext(thread, &ctx);
ctx.Esp = (DWORD)mystack + stackSize - ((DWORD)tib->StackBase - ctx.Esp);
ctx.Eip = (DWORD)&foo;
tib->StackBase = (PVOID)((DWORD)mystack + stackSize);
tib->StackLimit = (PVOID)((DWORD)mystack);
SetThreadContext(thread, &ctx);
}
DWORD CALLBACK threadMain(LPVOID)
{
ReleaseSemaphore(ghSemaphore, 1, NULL);
while (1)
Sleep(10000);
// Never gets here
return 1;
}
} // namespace
int main()
{
ghSemaphore = CreateSemaphore(NULL, 0, 1, NULL);
HANDLE th = CreateThread(0, 0, threadMain, 0, 0, 0);
while (WaitForSingleObject(ghSemaphore, INFINITE) != WAIT_OBJECT_0);
SuspendThread(th);
modifyThreadContext(th);
ResumeThread(th);
while (WaitForSingleObject(th, 10) != WAIT_OBJECT_0);
return 0;
}
例外和printf
都为我工作,我不明白为什么他们不应该。如果你把你的代码贴出来,我们可以试着查明发生了什么。
#include <windows.h>
#include <stdio.h>
DWORD CALLBACK ThreadProc(LPVOID)
{
try
{
throw 1;
}
catch (int i)
{
printf("%dn", i);
}
return 0;
}
typedef NTSTATUS WINAPI NtQueryInformationThread_t(HANDLE ThreadHandle, DWORD ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength, PULONG ReturnLength);
int main()
{
HMODULE hNtdll = LoadLibraryW(L"ntdll.dll");
auto NtQueryInformationThread = (NtQueryInformationThread_t *)GetProcAddress(hNtdll, "NtQueryInformationThread");
DWORD stackSize = 1024 * 1024;
void * mystack = VirtualAlloc(0, stackSize, MEM_COMMIT, PAGE_READWRITE);
DWORD dwThreadId;
HANDLE hThread = CreateThread(0, 0, &ThreadProc, 0, CREATE_SUSPENDED, &dwThreadId);
DWORD threadInfo[7];
NtQueryInformationThread(hThread, 0, threadInfo, sizeof threadInfo, 0);
NT_TIB * tib = (NT_TIB *)threadInfo[1];
CONTEXT ctx = {};
ctx.ContextFlags = CONTEXT_ALL;
GetThreadContext(hThread, &ctx);
ctx.Esp = (DWORD)mystack + stackSize - ((DWORD)tib->StackBase - ctx.Esp);
tib->StackBase = (PVOID)((DWORD)mystack + stackSize);
tib->StackLimit = (PVOID)((DWORD)mystack);
SetThreadContext(hThread, &ctx);
ResumeThread(hThread);
WaitForSingleObject(hThread, INFINITE);
}
- 对象接收堆栈溢出异常 c++ 的排序向量
- 堆栈上的 C++ 访问冲突写入异常
- 主函数执行时C++堆栈溢出异常
- Hinnant的堆栈分配器和异常
- 在使用 In Order 遍历成员函数时引发异常(堆栈溢出)时出现问题
- 在 Myfile.exe 中0x00831D39时未处理的异常:0xC00000FD:堆栈溢出(参数:0x0000000
- CUDA 编程未处理的异常和堆栈溢出
- C 堆栈溢出异常,可能是由于递归引起的
- 启用优化时的堆栈指针比较异常
- throw() 函数应该总是在异常时展开堆栈并允许捕获异常还是必须调用 std::terminate ?
- std::list 和 std::vector 中的堆栈溢出异常
- c++ 的除法的其余部分出现"堆栈溢出"异常
- 如何在Visual Studio(2015)中增加堆栈尺寸的堆栈尺寸?异常代码:C00000FD
- 捕获异常后的堆栈跟踪
- 我在程序中遇到堆栈溢出异常C++
- 堆栈溢出异常中c++和c#之间的差异
- 是否可以对构造函数抛出异常的对象进行异常处理,该对象的异常处理接近其基于堆栈的代码创建
- 在 C++/STL/MFC 应用程序启动早期发生的致命异常中,是否可以信任堆栈回溯符号名称?
- 程序在函数声明时崩溃,出现未经处理的异常:堆栈溢出
- 堆栈异常处理和析构函数展开.如何使用这些信息