在Windows上修改堆栈,TIB和exception

我的问题的由来源于想要提供在Windows上支持用户提供堆栈的pthreads的实现。 具体来说, pthread_attr_setstack应该做一些有意义的事情。 我的实际需求比这个多一点,但是这对于这个post来说足够好了。

没有用于在光纤或线程API中提供堆栈的公共Win API。 我search了偷偷摸摸的后门,解决方法和黑客,没有任何进展。 实际上,我看到了winpthread源代码的灵感,忽略了任何提供给pthread_attr_setstack堆栈。

相反,我尝试了下面的“解决scheme”,看看它是否会工作。 我使用ConvertThreadToFiberCreateFiberExSwitchToFiber的常用组合创build一个光纤。 在CreateFiberEx我提供了最小的堆栈大小。 在光纤的入口点,然后为堆栈分配内存,相应地更改TIB字段:“Stack Base”和“Stack Limit”(请参见http://en.wikipedia.org/wiki/Win32_Thread_Information_Block ),然后设置ESP到我的堆栈的高地址。

(在现实世界的情况下,我会设置堆栈比这更好,并改变EIP,以便这一步的行为更像POSIXfunction的swapcontext ,但你明白了)。

如果我在这个不同的堆栈上做任何操作系统调用,那么我几乎搞砸了(例如printf死亡)。 然而,这对我来说不是问题。 我可以确保我在自定义堆栈上从不确定调用(因此为什么我说我的实际需求有一点涉及)。 除了…我需要例外的工作。 而他们不! 具体来说,如果我尝试抛出并捕获exception,我修改的堆栈,然后我得到一个断言

未处理的exception在0xXXXXXXXX ….

所以我的(模糊)问题是,有没有人有任何洞察力,如何exception和自定义堆栈可能不是很好地一起玩? 我明白,这是完全不受支持的,除了零响应或“走开”以外,可以愉快地开展工作。 事实上,我已经决定了,我需要一个不同的解决scheme,尽pipe这涉及妥协,我可能会使用一个。 然而,好奇心越来越好,所以我想知道为什么这不起作用。

在一个相关的笔记,我想知道Cygwin如何处理这个ucontext。 源代码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。 但是,从实验中我看到,当从新的上下文中抛出exception时,这也失败了。 实际上, SetThreadContext调用甚至不更新TIB块!

编辑(根据@avakar的答案)

下面的代码与您的代码非常相似,performance出同样的错误。 不同的是,我不启动第二个线程暂停,但暂停它,然后尝试更改上下文。 这段代码展示了我在描述try-catch块在foo 。 也许这根本不合法。 值得注意的是,在这种情况下,当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; } 

Solutions Collecting From Web of "在Windows上修改堆栈,TIB和exception"

例外和printf都为我工作,我不明白他们为什么不应该这样做。 如果您发布代码,我们可以尝试查明发生了什么事情。

 #include <windows.h> #include <stdio.h> DWORD CALLBACK ThreadProc(LPVOID) { try { throw 1; } catch (int i) { printf("%d\n", 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); }