Remove JIT::RuntimeType
[hiphop-php.git] / hphp / runtime / vm / jit / unwind-x64.cpp
blob00ffb90e2f12f328bce06fa2a5c0dd50fb43289d
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
19 #include <vector>
20 #include <memory>
21 #include <cxxabi.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);
44 namespace {
46 template<class T>
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.
71 ActRec fakeAr;
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;
87 if (!catchTrace) {
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
91 // trace map.
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(),
107 rip);
108 return false;
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;
124 if (ism) {
125 unwindRdsInfo->unwinderTv = ism->tv();
127 _Unwind_SetIP(ctx, (uint64_t)catchTrace);
128 tl_regState = VMRegState::DIRTY;
130 return true;
133 _Unwind_Reason_Code
134 tc_unwind_personality(int version,
135 _Unwind_Action actions,
136 uint64_t exceptionClass,
137 _Unwind_Exception* exceptionObj,
138 _Unwind_Context* context) {
139 using namespace abi;
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";
158 int status;
159 auto* exnType = __cxa_demangle(ti.name(), nullptr, nullptr, &status);
160 SCOPE_EXIT { free(exnType); };
161 assert(status == 0);
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
173 * callee-saved regs.
175 if (actions & _UA_SEARCH_PHASE) {
176 if (ism) {
177 FTRACE(1, "thrown value: {} returning _URC_HANDLER_FOUND\n ",
178 ism->tv().pretty());
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 ///////////////////////////////////////////////////////////////////////////////
211 UnwindInfoHandle
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
279 // for itself.
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> >(
310 bufferMem.release(),
311 deregister_unwind_region
315 //////////////////////////////////////////////////////////////////////