当出现panic时再次发生panic为什么会导致非法指令?

3
考虑下面有意引起双重panic的代码:
use scopeguard::defer; // 1.1.0

fn main() {
    defer!{ panic!() };
    defer!{ panic!() };
}

我知道这通常是在先前的panic进行展开时Drop实现发生panic时发生的,但为什么会导致程序发出非法指令的错误?这听起来像代码已损坏或跳到了意外的地方。我想这可能与系统或代码生成有关,但我在各个平台上都进行了测试,并且它们都以相同的原因发出类似的错误:

  • Linux:

    thread panicked while panicking. aborting.
    Illegal instruction (core dumped)
    
  • Windows (with cargo run):

    thread panicked while panicking. aborting.
    error: process didn't exit successfully: `target\debug\tests.exe` (exit code: 0xc000001d, STATUS_ILLEGAL_INSTRUCTION)
    
  • The Rust Playground:

    thread panicked while panicking. aborting.
    timeout: the monitored command dumped core
    /playground/tools/entrypoint.sh: line 11:     8 Illegal instruction     timeout --signal=KILL ${timeout} "$@"
    

发生了什么?是什么原因导致了这种情况?


可能是因为你慌了,但最终还是抓住了机会,然后又慌了一次,因为第一次的慌乱没有起作用。我猜这就像 Rust 里的那句话:“好吧,我自己来做。”(视频链接) - Stargateur
1个回答

7

这种行为是有意的。

来自 Jonas Schievink 在 Why does panicking in a Drop impl cause SIGILL?评论 中:

它调用 intrinsics::abort(),LLVM 将其转换为一个非法的 ub2 指令,因此出现 SIGILL

我找不到任何有关如何处理双重 panic 的文档,但 std::intrinsics::abort() 的一段落与此行为相符:

当前实现的 intrinsics::abort 是在大多数平台上调用无效指令。在Unix上,进程可能会以类似于 SIGABRTSIGILLSIGTRAPSIGSEGVSIGBUS 的信号终止。精确的行为不能保证且不稳定。
有趣的是,这种行为与调用 std::process::abort() 不同,后者总是以 SIGABRT 终止。
在x86上,选择的非法指令是 UD2(我认为上面的注释中有一个错别字),也就是所谓的未定义指令,这个指令悖论地被保留并记录为不是一条指令。因此,没有破坏或无效的跳转,只是一种快速而响亮的方式告诉操作系统出了很大的问题。

5
我猜测core::intrinsics::abortstd::intrinsics::abort是它的重新导出)在双重panic期间被使用是因为它必须即使在#![no_std]的情况下也能正常工作,而core::instrinsics::abort使用非法指令是因为core库不能使用操作系统功能。 - Frxstrem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接