jjzjj

c++ - 在 Windows、TIB 和异常上修改堆栈

coder 2024-06-13 原文

我的问题的起源实际上源于想要在支持用户提供堆栈的 Windows 上提供 pthreads 的实现。具体来说,pthread_attr_setstack 应该做一些有意义的事情。我的实际要求比这要复杂一些,但这足以满足本文的目的。

没有公共(public) Win API 可以在 Fiber 或 Thread API 中提供堆栈。我四处寻找偷偷摸摸的后门、变通办法和黑客,没有任何进展。事实上,我查看了 winpthread 源代码以获得灵感,它忽略了提供给 pthread_attr_setstack 的任何堆栈。

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

(在实际情况下,我会设置比这更好的堆栈并更改 EIP,以便此步骤的行为更像 posix 函数 swapcontext,但你明白了)。

如果我在这个不同的堆栈上进行任何操作系统调用,那么我就完蛋了(例如 printf 死了)。然而这对我来说不是问题。我可以确保我永远不会在我的自定义堆栈上进行 sure 调用(因此我说我的实际要求有点复杂)。除了...我需要异常(exception)才能工作。而他们没有!具体来说,如果我尝试在修改后的堆栈上抛出并捕获异常,那么我会得到一个断言

Unhandled exception at 0xXXXXXXXX ....

所以我的(模糊的)问题是,是否有人对异常和自定义堆栈如何不能很好地协同工作有任何见解?我很欣赏这完全不受支持并且可以愉快地除了零响应或“走开”。事实上,我几乎已经决定我需要一个不同的解决方案,尽管这涉及妥协,但我很可能会使用一个。但是,好奇心战胜了我,所以我想知道为什么这行不通。

在相关说明中,我想知道 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 block !

编辑(基于@avakar 的回答)

下面的代码与您的代码非常相似,演示了同样的失败。不同之处在于我不启动第二个线程挂起而是挂起它然后尝试更改上下文。当在 foo 中命中 try-catch block 时,这段代码展示了我所描述的错误。也许这根本不合法。一件值得注意的事情是,在这种情况下,TIB 的 ExceptionList 成员在调用 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;
}

最佳答案

异常和 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);
}

关于c++ - 在 Windows、TIB 和异常上修改堆栈,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30725276/

有关c++ - 在 Windows、TIB 和异常上修改堆栈的更多相关文章

  1. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  2. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  3. ruby-on-rails - Rails - 乐观锁定总是触发 StaleObjectError 异常 - 2

    我正在学习Rails,并阅读了关于乐观锁的内容。我已将类型为integer的lock_version列添加到我的articles表中。但现在每当我第一次尝试更新记录时,我都会收到StaleObjectError异常。这是我的迁移:classAddLockVersionToArticle当我尝试通过Rails控制台更新文章时:article=Article.first=>#我这样做:article.title="newtitle"article.save我明白了:(0.3ms)begintransaction(0.3ms)UPDATE"articles"SET"title"='dwdwd

  4. ruby - #之间? Cooper 的 *Beginning Ruby* 中的错误或异常 - 2

    在Cooper的书BeginningRuby中,第166页有一个我无法重现的示例。classSongincludeComparableattr_accessor:lengthdef(other)@lengthother.lengthenddefinitialize(song_name,length)@song_name=song_name@length=lengthendenda=Song.new('Rockaroundtheclock',143)b=Song.new('BohemianRhapsody',544)c=Song.new('MinuteWaltz',60)a.betwee

  5. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  6. ruby - 在 Ruby 中重新分配常量时抛出异常? - 2

    我早就知道Ruby中的“常量”(即大写的变量名)不是真正常量。与其他编程语言一样,对对象的引用是唯一存储在变量/常量中的东西。(侧边栏:Ruby确实具有“卡住”引用对象不被修改的功能,据我所知,许多其他语言都没有提供这种功能。)所以这是我的问题:当您将一个值重新分配给常量时,您会收到如下警告:>>FOO='bar'=>"bar">>FOO='baz'(irb):2:warning:alreadyinitializedconstantFOO=>"baz"有没有办法强制Ruby抛出异常而不是打印警告?很难弄清楚为什么有时会发生重新分配。 最佳答案

  7. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  8. Vscode+Cmake配置并运行opencv环境(Windows和Ubuntu大同小异) - 2

    之前在培训新生的时候,windows环境下配置opencv环境一直教的都是网上主流的vsstudio配置属性表,但是这个似乎对新生来说难度略高(虽然个人觉得完全是他们自己的问题),加之暑假之后对cmake实在是爱不释手,且这样配置确实十分简单(其实都不需要配置),故斗胆妄言vscode下配置CV之法。其实极为简单,图比较多所以很长。如果你看此文还配不好,你应该思考一下是不是自己的问题。闲话少说,直接开始。0.CMkae简介有的人到大二了都不知道cmake是什么,我不说是谁。CMake是一个开源免费并且跨平台的构建工具,可以用简单的语句来描述所有平台的编译过程。它能够根据当前所在平台输出对应的m

  9. SPI接收数据异常问题总结 - 2

    SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手

  10. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

随机推荐