Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustc: Use custom personality functions on MSVC #48567

Closed
wants to merge 1 commit into from

Commits on Feb 27, 2018

  1. rustc: Use custom personality functions on MSVC

    Exception handling on MSVC for Rust has at this point a long and storied
    history to it. We currently use the exception mechanisms on MSVC to implement
    panics in Rust. On MSVC both 32 and 64-bit exceptions are based on SEH,
    structured exception handling. LLVM long ago added special instructions for
    MSVC, and we've been using those for as long as MSVC panics have been
    implemented!
    
    Exception handling at the system layer is typically guided by "personality
    functions" which are in theory intended to be language specific and allow
    programming languages to implement custom logic. Since the beginning, however,
    LLVM has had a hardcoded list of "known personalities" as they behave quite
    differently. As a result, Rust has historically shoehorned our desired panic
    behavior into preexisting personality functions defined on MSVC.
    
    Originally Rust actually used the functions `__C_specific_handler` (64-bit) and
    `_except_handler3` (32-bit). Using these personalities was relatively easy in
    Rust and only required a "filter function" on our "catch" equivalent to only
    catch Rust exceptions. Unfortunately these personalities suffered two
    [fatal][f1] [flaws][f2], which caused us to subsequently [switch] to the
    `__CxxFrameHandler3` personality.
    
    This personality is intended for C++, but we're mostly like C++ in any case so
    it worked quite well for a long time! The default C++ personality didn't run
    cleanups on faults and LLVM optimized invokes of nounwind functions well. The
    only downside at this point was that the support was [sort of scary][scary].
    
    Fast forward to the 1.24.0 release and another [fatal flaw][f3] is found in our
    strategy. Historically we've always declared "unwinding into Rust code from
    other languages is undefined behavior" (or unwinding out of Rust code). In
    1.24.0 we changed `extern` functions defined in Rust to enforce this behavior,
    forcibly aborting the process if the function panics. Everything was ship shape
    until it was discovered that `longjmp` across Rust frames caused the process to
    abort!
    
    It turns out that on MSVC `longjmp` is implemented with SEH! Furthermore it
    turns out that the personality we're using, `__CxxFrameHandler3`, recognized the
    `longjmp` exception enough to run cleanups. Consequently, when SEH ran past an
    `extern` function in Rust it aborted the process. Sounds like "cleanups run on
    segfaults" v2!
    
    Well in any case, that's a long-winded way of describing how shoehorning Rust's
    desired behavior into existing personality functions is getting more and more
    difficult. As a result, this commit starts taking a new strategy of defining
    custom personality functions in Rust (like we do for all other platforms) and
    teaching LLVM about these personalities so they're classified correctly and
    don't suffer from [old bugs][f2].
    
    Alright, so with all that information in your head now this commit can be
    described with:
    
    * New personality functions are added for MSVC: `rust_seh{32,64}_personality`.
    * These functions are shims around `__C_specific_handler` and `_except_handler3`
      like how on Unix we're typically a shim around a gcc personality.
    * Rust's personality functions defer on all exceptions that *aren't*
      Rust-related. We choose an arbitrary code to represent a Rust exception and
      only exceptions with matching codes execute our cleanups/catches. (the
      prevents [previous bugs][f1] with the personalities these functions are
      wrapping).
    * LLVM is updated with a small-ish commit to learn about these personality
      functions. The largest change here is, again, [ensuring old bugs don't
      resurface][f2] by teaching LLVM that it can simplify invokes of nounwind
      functions in Rust.
    * Finally, bedbad6 is partially reverted to restore the old translation
      behavior of the `try` intrinsic, bringing some scary code back into the
      compiler about `llvm.localescape` and such.
    
    Overall the intent of this commit is to preserve all existing behavior with
    panics on MSVC (aka keep old bugs closed and use the same system in general) but
    enable longjmps across Rust code. To this effect a test is checked in which
    asserts that we can indeed longjmp across Rust code with destructors and such.
    
    [f1]: rust-lang#33112
    [f2]: rust-lang#33116
    [switch]: rust-lang@38e6e5d0
    [scary]: https://github.com/rust-lang/rust/blob/bedbad61195d2eae69b43eca49c6d3e2aee8f208/src/libpanic_unwind/seh.rs#L107-L294
    [f3]: rust-lang#48251
    alexcrichton committed Feb 27, 2018
    Configuration menu
    Copy the full SHA
    1613fe2 View commit details
    Browse the repository at this point in the history