2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/jit/unwind-x64.h"
22 #include <boost/mpl/identity.hpp>
24 #include "hphp/util/abi-cxx.h"
25 #include "hphp/runtime/vm/jit/abi-x64.h"
26 #include "hphp/runtime/base/rds.h"
27 #include "hphp/runtime/vm/jit/mc-generator.h"
28 #include "hphp/runtime/base/stats.h"
29 #include "hphp/runtime/vm/runtime.h"
30 #include "hphp/runtime/vm/member-operations.h"
32 // libgcc exports this for registering eh information for
33 // dynamically-loaded objects. The pointer is to data in the format
34 // you find in a .eh_frame section.
35 extern "C" void __register_frame(void*);
36 extern "C" void __deregister_frame(void*);
38 TRACE_SET_MOD(unwind
);
40 namespace HPHP
{ namespace JIT
{
42 RDS::Link
<UnwindRDS
> unwindRdsInfo(RDS::kInvalidHandle
);
47 void append_vec(std::vector
<char>& v
,
48 // Prevent template argument deduction:
49 typename
boost::mpl::identity
<T
>::type t
) {
50 size_t idx
= v
.size();
51 v
.resize(idx
+ sizeof(T
));
52 char* caddr
= &v
[idx
];
53 std::memcpy(caddr
, &t
, sizeof t
);
56 void sync_regstate(_Unwind_Context
* context
) {
57 assert(tl_regState
== VMRegState::DIRTY
);
59 uintptr_t frameRbp
= _Unwind_GetGR(context
, Debug::RBP
);
60 uintptr_t frameRip
= _Unwind_GetGR(context
, Debug::RIP
);
63 * fixupWork expects to be looking at the first frame that is out of
64 * the TC. We have RBP/RIP for the TC frame that called out here,
65 * so we make a fake ActRec here to give it what it expects.
67 * Note: this doesn't work for IndirectFixup situations. However,
68 * currently IndirectFixup is only used for destructors, which
69 * aren't allowed to throw, so this is ok.
72 fakeAr
.m_sfp
= reinterpret_cast<ActRec
*>(frameRbp
);
73 fakeAr
.m_savedRip
= frameRip
;
75 Stats::inc(Stats::TC_SyncUnwind
);
76 mcg
->fixupMap().fixupWork(g_context
.getNoCheck(), &fakeAr
);
77 tl_regState
= VMRegState::CLEAN
;
80 bool install_catch_trace(_Unwind_Context
* ctx
, _Unwind_Exception
* exn
,
81 InvalidSetMException
* ism
) {
82 auto const rip
= (TCA
)_Unwind_GetIP(ctx
);
83 auto catchTraceOpt
= mcg
->getCatchTrace(rip
);
84 if (!catchTraceOpt
) return false;
86 auto catchTrace
= *catchTraceOpt
;
88 // A few of our optimization passes must be aware of every path out of the
89 // trace, so throwing through jitted code without a catch block is very
90 // bad. This is indicated with a present but nullptr entry in the catch
92 const size_t kCallSize
= 5;
93 const uint8_t kCallOpcode
= 0xe8;
95 auto callAddr
= rip
- kCallSize
;
96 TCA helperAddr
= nullptr;
97 std::string helperName
;
98 if (*callAddr
== kCallOpcode
) {
99 helperAddr
= rip
+ *reinterpret_cast<int32_t*>(callAddr
+ 1);
102 always_assert_flog(false,
103 "Translated call to {} threw '{}' without "
104 "catch block, return address: {}\n",
105 getNativeFunctionName(helperAddr
),
106 exceptionFromUnwindException(exn
)->what(),
111 FTRACE(1, "installing catch trace {} for call {} with ism {}, "
112 "returning _URC_INSTALL_CONTEXT\n",
113 catchTrace
, rip
, ism
);
115 // In theory the unwind api will let us set registers in the frame
116 // before executing our landing pad. In practice, trying to use
117 // their recommended scratch registers results in a SEGV inside
118 // _Unwind_SetGR, so we pass things to the handler using the
119 // RDS. This also simplifies the handler code because it doesn't
120 // have to worry about saving its arguments somewhere while
121 // executing the exit trace.
122 unwindRdsInfo
->unwinderScratch
= (int64_t)exn
;
123 unwindRdsInfo
->doSideExit
= ism
;
125 unwindRdsInfo
->unwinderTv
= ism
->tv();
127 _Unwind_SetIP(ctx
, (uint64_t)catchTrace
);
128 tl_regState
= VMRegState::DIRTY
;
134 tc_unwind_personality(int version
,
135 _Unwind_Action actions
,
136 uint64_t exceptionClass
,
137 _Unwind_Exception
* exceptionObj
,
138 _Unwind_Context
* context
) {
140 // Exceptions thrown by g++-generated code will have the class "GNUCC++"
141 // packed into a 64-bit int. For now we shouldn't be seeing exceptions from
142 // any other runtimes but this may change in the future.
143 DEBUG_ONLY
constexpr uint64_t kMagicClass
= 0x474e5543432b2b00;
144 DEBUG_ONLY
constexpr uint64_t kMagicDependentClass
= 0x474e5543432b2b01;
145 assert(exceptionClass
== kMagicClass
|| exceptionClass
== kMagicDependentClass
);
146 assert(version
== 1);
148 auto const& ti
= typeInfoFromUnwindException(exceptionObj
);
149 InvalidSetMException
* ism
= nullptr;
150 if (ti
== typeid(InvalidSetMException
)) {
151 ism
= static_cast<InvalidSetMException
*>(
152 exceptionFromUnwindException(exceptionObj
));
155 if (Trace::moduleEnabled(TRACEMOD
, 1)) {
156 DEBUG_ONLY
auto const* unwindType
=
157 (actions
& _UA_SEARCH_PHASE
) ? "search" : "cleanup";
159 auto* exnType
= __cxa_demangle(ti
.name(), nullptr, nullptr, &status
);
160 SCOPE_EXIT
{ free(exnType
); };
162 FTRACE(1, "unwind {} exn {}: regState: {} ip: {} type: {}. ",
163 unwindType
, exceptionObj
,
164 tl_regState
== VMRegState::DIRTY
? "dirty" : "clean",
165 (TCA
)_Unwind_GetIP(context
), exnType
);
169 * We don't do anything during the search phase---before attempting
170 * cleanup, we want all deeper frames to have run their object
171 * destructors (which can have side effects like setting
172 * tl_regState) and spilled any values they may have been holding in
175 if (actions
& _UA_SEARCH_PHASE
) {
177 FTRACE(1, "thrown value: {} returning _URC_HANDLER_FOUND\n ",
179 return _URC_HANDLER_FOUND
;
184 * During the cleanup phase, we can either use a landing pad to perform
185 * cleanup (with _Unwind_SetIP and _URC_INSTALL_CONTEXT), or we can do it
186 * here. We sync the VM registers here, then optionally use a landing pad,
187 * which is an exit trace from hhir with a few special instructions.
189 else if (actions
& _UA_CLEANUP_PHASE
) {
190 if (tl_regState
== VMRegState::DIRTY
) {
191 sync_regstate(context
);
193 if (install_catch_trace(context
, exceptionObj
, ism
)) {
194 return _URC_INSTALL_CONTEXT
;
198 FTRACE(1, "returning _URC_CONTINUE_UNWIND\n");
199 return _URC_CONTINUE_UNWIND
;
202 void deregister_unwind_region(std::vector
<char>* p
) {
203 std::auto_ptr
<std::vector
<char> > del(p
);
204 __deregister_frame(&(*p
)[0]);
209 ///////////////////////////////////////////////////////////////////////////////
212 register_unwind_region(unsigned char* startAddr
, size_t size
) {
213 FTRACE(1, "register_unwind_region: base {}, size {}\n", startAddr
, size
);
214 // The first time we're called, this will dynamically link the data
215 // we need in the request data segment. All future JIT translations
216 // of catch traces may use offsets based on this handle.
217 unwindRdsInfo
.bind();
219 std::unique_ptr
<std::vector
<char>> bufferMem(new std::vector
<char>);
220 std::vector
<char>& buffer
= *bufferMem
;
223 // This is a dwarf CIE header. Looks the same as a fde except the
224 // second field is zero.
225 append_vec
<uint32_t>(buffer
, 0); // Room for length later
226 append_vec
<int32_t>(buffer
, 0); // CIE_id
227 append_vec
<uint8_t>(buffer
, 1); // version
230 * Null-terminated "augmentation string" (defines what the rest of
231 * this thing is going to have.
233 * TODO: probably we should have a 'z' field to indicate length.
235 append_vec
<char>(buffer
, 'P');
236 append_vec
<char>(buffer
, '\0');
238 // Code and data alignment.
239 append_vec
<uint8_t>(buffer
, 1);
240 append_vec
<uint8_t>(buffer
, 8); // Multiplies offsets below.
242 // Return address column (in version 1, this is a single byte).
243 append_vec
<uint8_t>(buffer
, Debug::RIP
);
245 // Pointer to the personality routine for the TC.
246 append_vec
<uint8_t>(buffer
, DW_EH_PE_absptr
);
247 append_vec
<uintptr_t>(buffer
, uintptr_t(tc_unwind_personality
));
250 * Define a program for the CIE. This explains to the unwinder
251 * how to figure out where the frame pointer was, etc.
253 * Arguments to some of these are encoded in LEB128, so we have to
254 * clear the high bit for the signed values.
256 // Previous FP (CFA) is at rbp + 16.
257 append_vec
<uint8_t>(buffer
, DW_CFA_def_cfa
);
258 append_vec
<uint8_t>(buffer
, Debug::RBP
);
259 append_vec
<uint8_t>(buffer
, 16);
260 // rip is at CFA - 1 * data_align.
261 append_vec
<uint8_t>(buffer
, DW_CFA_offset_extended_sf
);
262 append_vec
<uint8_t>(buffer
, Debug::RIP
);
263 append_vec
<uint8_t>(buffer
, -1u & 0x7f);
264 // rbp is at CFA - 2 * data_align.
265 append_vec
<uint8_t>(buffer
, DW_CFA_offset_extended_sf
);
266 append_vec
<uint8_t>(buffer
, Debug::RBP
);
267 append_vec
<uint8_t>(buffer
, -2u & 0x7f);
269 * Leave rsp unchanged.
271 * Note that some things in the translator do actually change rsp,
272 * but we assume they cannot throw so this is ok. If rVmSp ever
273 * changes to use rsp this code must change.
275 append_vec
<uint8_t>(buffer
, DW_CFA_same_value
);
276 append_vec
<uint8_t>(buffer
, Debug::RSP
);
278 // Fixup the length field. Note that it doesn't include the space
280 void* vp
= &buffer
[0];
281 *static_cast<uint32_t*>(vp
) = buffer
.size() - sizeof(uint32_t);
283 const size_t fdeIdx
= buffer
.size();
285 // Reserve space for FDE length.
286 append_vec
<uint32_t>(buffer
, 0);
288 // Negative offset to the CIE for this FDE---the offset is
289 // relative to this field.
290 append_vec
<int32_t>(buffer
, int32_t(buffer
.size()));
292 // We're using the addressing mode DW_EH_PE_absptr, which means it
293 // wants a 8 byte pointer and a 8 byte size indicating the region
294 // this FDE applies to.
295 append_vec
<unsigned char*>(buffer
, startAddr
);
296 append_vec
<size_t>(buffer
, size
);
298 // Fixup the length field for this FDE. Again length doesn't
299 // include the length field itself.
300 void* vp
= &buffer
[fdeIdx
];
301 *static_cast<uint32_t*>(vp
) = buffer
.size() - fdeIdx
- sizeof(uint32_t);
303 // Add one more zero'd length field---this indicates that there are
304 // no more FDEs sharing this CIE.
305 append_vec
<uint32_t>(buffer
, 0);
307 __register_frame(&buffer
[0]);
309 return std::shared_ptr
<std::vector
<char> >(
311 deregister_unwind_region
315 //////////////////////////////////////////////////////////////////////