go Windows: SEH 和 VEH 一起使用时效果不佳

fumotvh3  于 4个月前  发布在  Go
关注(0)|答案(9)|浏览(47)

在Windows上,有两种类型的异常处理:结构化异常处理(SEH)和向量异常处理(VEH)。
MSVC生成的二进制文件使用SEH,其中单个函数都有附加的异常处理程序。在64位系统上,这些存储在PE的.pdata部分的表中。当一个函数抛出异常(例如,由于非法指令或除以零),如果有的话,它的SEH将被调用。
Go运行时使用VEH,其中整个运行时具有一系列处理器,VEH通过它们跳转,直到达到终止点。
当抛出异常时,ntdll在执行VEH之前先执行SEH。这是合理的行为,但对我们使用VEH的方式构成了一个问题。
最近的MSVC工具链生成arm64 DLL,通过测试一些指令来探测ARMv8.1原子性,并使用SEH捕获潜在的非法指令异常,指示这些指令不存在。由最近的MSVC工具链生成的ARM64 DLL在初始化期间无条件地执行此操作。
当Go运行时加载其中一个这样的DLL(像正常一样使用LoadLibrary)时,其VEH处理程序将为探测到的非法指令异常生成的异常调用。然后Go VEH处理程序决定它是致命的,退出。这是一个大问题。
我看到了几种解决这个问题的方法:

  1. 我们对从Go的.text部分之外抛出的非法指令异常进行特殊处理(如我在https://go-review.googlesource.com/c/go/+/340070中所做的那样)。这工作得很好(我现在正在发货),似乎是最不侵入性的更改。然而,这可能会掩盖从Go的.text部分之外抛出的未被捕获和处理的其他非法指令异常,在这种情况下,二进制文件将在没有输出堆栈跟踪的情况下终止。这似乎是一个不太可能且无关紧要的边缘情况。
  2. 我们总是允许我们的VEH在来自Go的.text部分之外的异常情况下继续执行到SEH(或什么都不做)。这会起作用,但也意味着由库函数引起的合法崩溃不会导致打印Go堆栈跟踪。我有点喜欢这个解决方案,但我可以想象其他人可能不喜欢。而且那些堆栈跟踪可能非常有用。这意味着删除我们在runtime/中拥有的TestRaiseException单元测试。
  3. 如果异常来自Go的.text部分之外并且存在该函数的SEH,我们允许VEH继续执行到SEH。这是(1)的一个更一般的情况,也许是一个非常好的解决方案。我可以完成必要的逆向工程来实现这一点,尽管这并不容易。然而,如果SEH决定不继续执行而是崩溃,我们就不会打印堆栈跟踪。那边缘情况会在什么地方被触发还有待观察。
  4. 当我们允许VEH继续执行到SEH时,如果SEH存在并且被调用了,我们就直接让我们的VEH调用一个SEH,只有在SEH被调用后才用堆栈跟踪崩溃。这非常棘手,但可能会给我们带来最好的结果。
  5. 我们做(2),但然后安装一个SEH(使用RtlAddFunctionTable)用于syscall.Syscall的IP打印堆栈跟踪,这样我们最终确实会得到由于(2)传递给VEH的未处理异常。这似乎简单一些,尽管我怀疑我们可能会错过由非Go代码创建的线程引起的堆栈跟踪。
    在我进一步考虑这个问题之前,我想我们应该先讨论一下所有这些。无论如何,https://go-review.googlesource.com/c/go/+/340070目前作为一个临时解决方案可以工作。
    CC @rsc@ianlancetaylor@aclements@cherrymui@bradfitz@alexbrainman@jstarks
1cosmwyk

1cosmwyk1#

https://golang.org/cl/340070提到了这个问题:runtime: allow arm64 SEH to be called if illegal instruction

esyap4oy

esyap4oy2#

如果你从Windows的Angular 有关于什么行为最符合规范的意见,那将具有很大的分量。我们当前的处理程序迷宫位于 https://github.com/golang/go/blob/master/src/runtime/signal_windows.go

velaa5lx

velaa5lx3#

我还没有确定。但有一点我想指出的是,Go的向量异常处理器(exceptionhandler)已经陷入SEH。在VEH和SEH处理之后,向量继续处理器无条件运行,直到其中一个返回EXCEPTION_CONTINUE_EXECUTION

我认为理想情况下,你的VCH只有在VEH或SEH处理器都没有返回EXCEPTION_CONTINUE_EXECUTION时才会发生恐慌。但我还不知道这是否对你可用。

看起来Go真的只是希望在未处理的异常情况下被调用,但未处理的异常过滤器不会在调试进程中运行,这是有问题的。对吗?

8i9zcol2

8i9zcol24#

哦,这很有趣。我没有考虑到在SEH之后继续处理程序仍在运行。原因是尽管它是“最后一个”,但最后一个继续处理程序似乎在arm64 DLL的SEH之前被调用。这很奇怪...

txu3uszq

txu3uszq5#

嗯。我立刻看不出这怎么可能。

mm9b1k5b

mm9b1k5b6#

Bug?尝试回滚70546f6并加载由最新EWDK 22000(here's one)生成的DLL。您会发现当探测时它会死掉。然后将该提交恢复,您会看到它会触发互斥探测异常处理程序。至少我认为这就是发生的情况?

ohfgkhjo

ohfgkhjo7#

我将在接下来的几天内尝试调试这个问题。

mcdcgff0

mcdcgff08#

https://go.dev/cl/457875提到了这个问题:runtime,cmd/link: allow SEH tramps handle non-Go exception

9njqaruj

9njqaruj9#

我已经在CL 457875中实现了选项5。仍然处于WIP状态,但看起来很有前途。编辑:未捕获的非go线程异常可能会使此选项无法使用。

相关问题