在Windows,TIB和异常

时间:2015-06-09 07:12:54

标签: c++ windows multithreading exception fibers

我的问题的起源有效地源于想要在Windows上提供支持用户提供堆栈的pthread的实现。具体来说,pthread_attr_setstack应该做一些有意义的事情。我的实际要求比这更复杂,但这对于帖子来说已经足够了。

在Fiber或Thread API中没有用于提供堆栈的公共Win API。我四处搜寻偷偷摸摸的后门,变通办法和黑客行为,没有什么可去的。事实上,我看起来是winpthread的灵感源,它忽略了提供给pthread_attr_setstack的任何堆栈。

相反,我尝试了以下"解决方案"看看它是否有效。我使用ConvertThreadToFiberCreateFiberExSwitchToFiber的常规组合创建了一个光纤。在CreateFiberEx中,我提供了最小的堆栈大小。在光纤的入口点,然后为堆栈分配内存,更改TIB字段:" Stack Base"和#34; Stack Limit"适当(见这里:http://en.wikipedia.org/wiki/Win32_Thread_Information_Block)然后将ESP设置为我的堆栈的高位地址。

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

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

  

0xXXXXXXXX处的未处理异常....

所以我的(含糊)问题是,是否有人对异常和自定义堆栈如何在一起很好地协作方面有任何见解?我感谢这完全没有支持,并且可以愉快地除了零响应或者#34;离开"。事实上,我几乎已经决定我需要一个不同的解决方案,尽管这涉及妥协,但我可能会使用一个。然而,好奇心让我变得更好,所以我想知道为什么这样做不起作用。

在一个相关的说明中,我想知道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。但是,从实验中我发现,当从新上下文中抛出异常时,这也会失败。实际上,SetThreadContext调用甚至不会更新TIB块!

编辑(根据@avakar的回答)

以下代码与您的代码非常相似,表现出同样的失败。不同之处在于我没有启动第二个线程暂停,但暂停它然后尝试更改上下文。这段代码展示了我在foo中遇到try-catch块时所描述的错误。也许这根本不合法。值得注意的是,在这种情况下,调用ExceptionList时,TIB的modifyThreadContext成员是有效指针,而在您的示例中,它是-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;
}

1 个答案:

答案 0 :(得分:1)

两个例外和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);
}