summaryrefslogtreecommitdiff
path: root/libcxxabi/src/cxa_default_handlers.cpp
blob: 7bcfca069fb7f71be94159762a425a067fde02c5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//
// This file implements the default terminate_handler, unexpected_handler and
// new_handler.
//===----------------------------------------------------------------------===//

#include <exception>
#include <memory>
#include <stdlib.h>
#include "abort_message.h"
#include "cxxabi.h"
#include "cxa_handlers.h"
#include "cxa_exception.h"
#include "private_typeinfo.h"
#include "include/atomic_support.h" // from libc++

#if !defined(LIBCXXABI_SILENT_TERMINATE)

static constinit const char* cause = "uncaught";

#ifndef _LIBCXXABI_NO_EXCEPTIONS
// Demangle the given string, or return the string as-is in case of an error.
static std::unique_ptr<char const, void (*)(char const*)> demangle(char const* str)
{
#if !defined(LIBCXXABI_NON_DEMANGLING_TERMINATE)
    if (const char* result = __cxxabiv1::__cxa_demangle(str, nullptr, nullptr, nullptr))
        return {result, [](char const* p) { std::free(const_cast<char*>(p)); }};
#endif
    return {str, [](char const*) { /* nothing to free */ }};
}

__attribute__((noreturn))
static void demangling_terminate_handler()
{
    using namespace __cxxabiv1;
    __cxa_eh_globals* globals = __cxa_get_globals_fast();

    // If there is no uncaught exception, just note that we're terminating
    if (!globals)
        abort_message("terminating");

    __cxa_exception* exception_header = globals->caughtExceptions;
    if (!exception_header)
        abort_message("terminating");

    _Unwind_Exception* unwind_exception =
        reinterpret_cast<_Unwind_Exception*>(exception_header + 1) - 1;

    // If we're terminating due to a foreign exception
    if (!__isOurExceptionClass(unwind_exception))
        abort_message("terminating due to %s foreign exception", cause);

    void* thrown_object =
        __getExceptionClass(unwind_exception) == kOurDependentExceptionClass ?
            ((__cxa_dependent_exception*)exception_header)->primaryException :
            exception_header + 1;
    const __shim_type_info* thrown_type =
        static_cast<const __shim_type_info*>(exception_header->exceptionType);
    auto name = demangle(thrown_type->name());
    // If the uncaught exception can be caught with std::exception&
    const __shim_type_info* catch_type =
        static_cast<const __shim_type_info*>(&typeid(std::exception));
    if (catch_type->can_catch(thrown_type, thrown_object))
    {
        // Include the what() message from the exception
        const std::exception* e = static_cast<const std::exception*>(thrown_object);
        abort_message("terminating due to %s exception of type %s: %s", cause, name.get(), e->what());
    }
    else
    {
        // Else just note that we're terminating due to an exception
        abort_message("terminating due to %s exception of type %s", cause, name.get());
    }
}
#else // !_LIBCXXABI_NO_EXCEPTIONS
__attribute__((noreturn))
static void demangling_terminate_handler()
{
    abort_message("terminating");
}
#endif // !_LIBCXXABI_NO_EXCEPTIONS

__attribute__((noreturn))
static void demangling_unexpected_handler()
{
    cause = "unexpected";
    std::terminate();
}

static constexpr std::terminate_handler default_terminate_handler = demangling_terminate_handler;
static constexpr std::terminate_handler default_unexpected_handler = demangling_unexpected_handler;
#else // !LIBCXXABI_SILENT_TERMINATE
static constexpr std::terminate_handler default_terminate_handler = ::abort;
static constexpr std::terminate_handler default_unexpected_handler = std::terminate;
#endif // !LIBCXXABI_SILENT_TERMINATE

//
// Global variables that hold the pointers to the current handler
//
_LIBCXXABI_DATA_VIS
constinit std::terminate_handler __cxa_terminate_handler = default_terminate_handler;

_LIBCXXABI_DATA_VIS
constinit std::unexpected_handler __cxa_unexpected_handler = default_unexpected_handler;

_LIBCXXABI_DATA_VIS
constinit std::new_handler __cxa_new_handler = nullptr;

namespace std
{

unexpected_handler
set_unexpected(unexpected_handler func) noexcept
{
    if (func == 0)
        func = default_unexpected_handler;
    return __libcpp_atomic_exchange(&__cxa_unexpected_handler, func,
                                    _AO_Acq_Rel);
}

terminate_handler
set_terminate(terminate_handler func) noexcept
{
    if (func == 0)
        func = default_terminate_handler;
    return __libcpp_atomic_exchange(&__cxa_terminate_handler, func,
                                    _AO_Acq_Rel);
}

new_handler
set_new_handler(new_handler handler) noexcept
{
    return __libcpp_atomic_exchange(&__cxa_new_handler, handler, _AO_Acq_Rel);
}

}