1/*
2 * Copyright 2012-present Facebook, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <folly/experimental/exception_tracer/ExceptionTracerLib.h>
18
19#include <dlfcn.h>
20
21#include <vector>
22
23#include <folly/Indestructible.h>
24#include <folly/Portability.h>
25#include <folly/SharedMutex.h>
26#include <folly/Synchronized.h>
27
28namespace __cxxabiv1 {
29
30extern "C" {
31void __cxa_throw(
32 void* thrownException,
33 std::type_info* type,
34 void (*destructor)(void*)) __attribute__((__noreturn__));
35void* __cxa_begin_catch(void* excObj) throw();
36void __cxa_rethrow(void) __attribute__((__noreturn__));
37void __cxa_end_catch(void);
38}
39
40} // namespace __cxxabiv1
41
42using namespace folly::exception_tracer;
43
44namespace {
45
46template <typename Function>
47class CallbackHolder {
48 public:
49 void registerCallback(Function f) {
50 callbacks_.wlock()->push_back(std::move(f));
51 }
52
53 // always inline to enforce kInternalFramesNumber
54 template <typename... Args>
55 FOLLY_ALWAYS_INLINE void invoke(Args... args) {
56 auto callbacksLock = callbacks_.rlock();
57 for (auto& cb : *callbacksLock) {
58 cb(args...);
59 }
60 }
61
62 private:
63 folly::Synchronized<std::vector<Function>> callbacks_;
64};
65
66} // namespace
67
68namespace folly {
69namespace exception_tracer {
70
71#define DECLARE_CALLBACK(NAME) \
72 CallbackHolder<NAME##Type>& get##NAME##Callbacks() { \
73 static Indestructible<CallbackHolder<NAME##Type>> Callbacks; \
74 return *Callbacks; \
75 } \
76 void register##NAME##Callback(NAME##Type callback) { \
77 get##NAME##Callbacks().registerCallback(callback); \
78 }
79
80DECLARE_CALLBACK(CxaThrow)
81DECLARE_CALLBACK(CxaBeginCatch)
82DECLARE_CALLBACK(CxaRethrow)
83DECLARE_CALLBACK(CxaEndCatch)
84DECLARE_CALLBACK(RethrowException)
85
86} // namespace exception_tracer
87} // namespace folly
88
89// Clang is smart enough to understand that the symbols we're loading
90// are [[noreturn]], but GCC is not. In order to be able to build with
91// -Wunreachable-code enable for Clang, these __builtin_unreachable()
92// calls need to go away. Everything else is messy though, so just
93// #define it to an empty macro under Clang and be done with it.
94#ifdef __clang__
95#define __builtin_unreachable()
96#endif
97
98namespace __cxxabiv1 {
99
100void __cxa_throw(
101 void* thrownException,
102 std::type_info* type,
103 void (*destructor)(void*)) {
104 static auto orig_cxa_throw =
105 reinterpret_cast<decltype(&__cxa_throw)>(dlsym(RTLD_NEXT, "__cxa_throw"));
106 getCxaThrowCallbacks().invoke(thrownException, type, destructor);
107 orig_cxa_throw(thrownException, type, destructor);
108 __builtin_unreachable(); // orig_cxa_throw never returns
109}
110
111void __cxa_rethrow() {
112 // __cxa_rethrow leaves the current exception on the caught stack,
113 // and __cxa_begin_catch recognizes that case. We could do the same, but
114 // we'll implement something simpler (and slower): we pop the exception from
115 // the caught stack, and push it back onto the active stack; this way, our
116 // implementation of __cxa_begin_catch doesn't have to do anything special.
117 static auto orig_cxa_rethrow = reinterpret_cast<decltype(&__cxa_rethrow)>(
118 dlsym(RTLD_NEXT, "__cxa_rethrow"));
119 getCxaRethrowCallbacks().invoke();
120 orig_cxa_rethrow();
121 __builtin_unreachable(); // orig_cxa_rethrow never returns
122}
123
124void* __cxa_begin_catch(void* excObj) throw() {
125 // excObj is a pointer to the unwindHeader in __cxa_exception
126 static auto orig_cxa_begin_catch =
127 reinterpret_cast<decltype(&__cxa_begin_catch)>(
128 dlsym(RTLD_NEXT, "__cxa_begin_catch"));
129 getCxaBeginCatchCallbacks().invoke(excObj);
130 return orig_cxa_begin_catch(excObj);
131}
132
133void __cxa_end_catch() {
134 static auto orig_cxa_end_catch = reinterpret_cast<decltype(&__cxa_end_catch)>(
135 dlsym(RTLD_NEXT, "__cxa_end_catch"));
136 getCxaEndCatchCallbacks().invoke();
137 orig_cxa_end_catch();
138}
139
140} // namespace __cxxabiv1
141
142namespace std {
143
144void rethrow_exception(std::exception_ptr ep) {
145 // Mangled name for std::rethrow_exception
146 // TODO(tudorb): Dicey, as it relies on the fact that std::exception_ptr
147 // is typedef'ed to a type in namespace __exception_ptr
148 static auto orig_rethrow_exception =
149 reinterpret_cast<decltype(&rethrow_exception)>(dlsym(
150 RTLD_NEXT,
151 "_ZSt17rethrow_exceptionNSt15__exception_ptr13exception_ptrE"));
152 getRethrowExceptionCallbacks().invoke(ep);
153 orig_rethrow_exception(ep);
154 __builtin_unreachable(); // orig_rethrow_exception never returns
155}
156
157} // namespace std
158