【问题标题】:How is an exception transferred to find a handler?异常如何转移到寻找处理程序?
【发布时间】:2014-10-30 12:26:18
【问题描述】:

当抛出异常时,会启动堆栈展开,直到遇到处理代码,但我对整个过程的机制有点不清楚。

1 - 异常存储在哪里?我不是指实际的异常对象,它可能很大,例如有一个消息字符串或其他东西,但如果你愿意的话,是实际的引用或指针。它必须是某个统一的存储位置,这样它才能在堆栈展开并到达处理位置时保持下来?

2 - 程序流如何确定它是否必须展开特定的函数框架并调用与程序计数器指示的位置相关的适当析构函数,或者在进一步展开之前寻求异常处理?

3 - 如何在抛出的内容和正在发生的异常之间进行实际检查?

我知道答案可能包括特定于平台的内容,在这种情况下,我们将不胜感激。不过,无需超越 x86/x64 和 ARM。

【问题讨论】:

  • 所有问题都有完全特定于实现的答案,特别是对于 Linux/GCC 构建和 32 位 Microsoft Windows 构建有非常不同的答案。
  • 但供参考:这里是标准Linux C++ ABI异常处理机制的描述。请注意,ARM 略有不同。 mentorembedded.github.io/cxx-abi/abi-eh.html
  • @SebastianRedl - 只要它们提供信息,我对不同的答案没有问题。我将不胜感激任何内容丰富的答案,无论它解决的是哪种实现。
  • 问题是,我认为这在某种程度上违反了指导方针。
  • @SebastianRedl - 询问具体实施问题是否违反指南?或者也许问“多实现”问题?如果是后者 - 为每个实现提出相同的问题真的会更好吗?

标签: c++ exception exception-handling implementation


【解决方案1】:

这些都是实现细节,在设计异常处理机制的(非平凡的)过程中决定。我只能简要说明人们可能(或可能不会)选择如何实现这一点。

如果您想详细了解一种实现,可以阅读 GCC 和其他流行编译器使用的 Itanium ABI 规范。

1 - 异常对象存储在未指定的位置,该位置必须持续到异常处理完毕。指针或引用像任何其他变量一样在异常处理代码中传递,然后通过类似于传递函数参数的某种机制传递给处理程序(如果它接受引用)。

2 - 有两种常见的方法:静态数据结构,将程序位置映射到有关堆栈帧的信息;或一个动态的类似堆栈的数据结构,其中包含有关活动处理程序和需要销毁的重要堆栈对象的信息。

在第一种情况下,在抛出时它会查看该信息以查看是否有任何本地对象要销毁,以及任何本地处理程序;如果没有,它将在本地堆栈帧上找到函数返回地址,并将相同的过程应用于调用函数的堆栈帧,直到找到处理程序。一旦找到处理程序,CPU 寄存器就会更新以引用该堆栈帧,程序可以跳转到处理程序的代码。

在第二种情况下,它将从堆栈结构中弹出条目,使用它们告诉它如何销毁堆栈对象,直到找到合适的处理程序。一旦找到处理程序,并且所有未展开的堆栈对象都被销毁,它可以使用longjmp 或类似的机制跳转到处理程序。

其他方法也是可能的。

3 - 异常处理代码将使用某种数据结构来识别类型,允许它比较抛出的类型和处理程序的类型。继承有点复杂。测试不能是简单的比较。我不知道任何特定实现的细节。

【讨论】:

  • 我专注于表格方法,即“调用与程序计数器相关的适当析构函数”。但目前还不清楚“处理程序”是如何找到的。我的意思是搜索处理程序涉及从存储的 PC 中“继续执行”,而帧展开只会调用“此刻”存在的对象的析构函数并转到前一帧。
  • @user3735658:数据结构中将有函数内活动处理程序的描述符,以及要销毁的对象。一旦识别出处理程序,“继续执行”涉及设置 CPU 寄存器以引用适当的堆栈帧,该堆栈帧是通过在抛出时读取运行时堆栈找到的,然后跳转到处理程序的适当位置。这与使用setjmp/longjmp 存储和检索执行上下文的机制类似。
【解决方案2】:

来源:How do exceptions work (behind the scenes) in c++(我阅读了汇编,并根据我的理解回答了问题)

问题1#:

movl    $1, (%esp)
call    __cxa_allocate_exception
movl    $_ZN11MyExceptionD1Ev, 8(%esp)
movl    $_ZTI11MyException, 4(%esp)

_ZTI11MyException 是个例外。它看起来好像它有自己的分配而不是在堆栈中,它将指针放在名为eax的寄存器中。

问题2#:

.LFE9:
.size   _Z20my_catching_functionv, .-_Z20my_catching_functionv
.section    .gcc_except_table,"a",@progbits
.align 4

它看起来像在程序中存储在静态数据中的表。所以它可以知道它可以捕捉到哪里。没有关于对象在展开帧后如何自毁的内容,所以这是来自 Visual Studio:(顶部的链接来自 Linux)

        MyClass s, s2, s3, s4;
 mov         dword ptr [ebp-4],3  
        try {
            {
                MyClass s, s2, s3, s4;
 mov         byte ptr [ebp-4],7  
            }

看起来它保存了要销毁的对象的数量。例如当它完成时:

call        MyClass::~MyClass (0DC1163h)
mov         dword ptr [ebp-4],0FFFFFFFFh

0FFFFFFFFh 意味着没有什么可破坏的。如果我发现了一些关于它是如何发现并销毁它们的,我会在这里添加。

问题3#:

与上一个问题一样,您会看到它的表格,它可以知道正确函数中的任何内容。

【讨论】:

    猜你喜欢
    • 2011-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-06
    • 2012-03-04
    • 1970-01-01
    相关资源
    最近更新 更多