PageRenderTime 39ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/runtime/vm/jit/unwind-x64.cpp

https://gitlab.com/Blueprint-Marketing/hhvm
C++ | 450 lines | 261 code | 64 blank | 125 comment | 45 complexity | 6ca6d169161b2be178dfbb7e34e89daf MD5 | raw file
  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. +----------------------------------------------------------------------+
  15. */
  16. #include "hphp/runtime/vm/jit/unwind-x64.h"
  17. #include <vector>
  18. #include <memory>
  19. #include <cxxabi.h>
  20. #include <boost/mpl/identity.hpp>
  21. #include "hphp/runtime/base/rds.h"
  22. #include "hphp/runtime/base/stats.h"
  23. #include "hphp/runtime/vm/bytecode.h"
  24. #include "hphp/runtime/vm/jit/abi-x64.h"
  25. #include "hphp/runtime/vm/jit/mc-generator.h"
  26. #include "hphp/runtime/vm/jit/service-requests.h"
  27. #include "hphp/runtime/vm/member-operations.h"
  28. #include "hphp/runtime/vm/runtime.h"
  29. #include "hphp/runtime/vm/unwind.h"
  30. #include "hphp/util/abi-cxx.h"
  31. // on cygwin in 64 bit/SEH adding frame information needs to be
  32. // handled with rtladdfunctiontable and rtldeletefunctiontable
  33. // or use the rtlinstallfunctiontablecallback
  34. // register_frame and deregister_frame do not exist
  35. // this is a temp solution that provides empty placeholders for linking
  36. #ifdef __CYGWIN__
  37. void __register_frame(const void*) {}
  38. void __deregister_frame(const void*) {}
  39. #else
  40. // libgcc exports this for registering eh information for
  41. // dynamically-loaded objects. The pointer is to data in the format
  42. // you find in a .eh_frame section.
  43. extern "C" void __register_frame(const void*);
  44. extern "C" void __deregister_frame(const void*);
  45. #endif
  46. TRACE_SET_MOD(unwind);
  47. namespace HPHP { namespace jit {
  48. rds::Link<UnwindRDS> unwindRdsInfo(rds::kInvalidHandle);
  49. namespace {
  50. size_t fdeIdx;
  51. template<class T>
  52. void append_vec(std::vector<char>& v,
  53. // Prevent template argument deduction:
  54. typename boost::mpl::identity<T>::type t) {
  55. size_t idx = v.size();
  56. v.resize(idx + sizeof(T));
  57. char* caddr = &v[idx];
  58. std::memcpy(caddr, &t, sizeof t);
  59. }
  60. void sync_regstate(_Unwind_Context* context) {
  61. assertx(tl_regState == VMRegState::DIRTY);
  62. uintptr_t frameRbp = _Unwind_GetGR(context, Debug::RBP);
  63. uintptr_t frameRip = _Unwind_GetIP(context);
  64. FTRACE(2, "syncing regstate for rbp: {:#x} rip: {:#x}\n", frameRbp, frameRip);
  65. /*
  66. * fixupWork expects to be looking at the first frame that is out of
  67. * the TC. We have RBP/RIP for the TC frame that called out here,
  68. * so we make a fake ActRec here to give it what it expects.
  69. *
  70. * Note: this doesn't work for IndirectFixup situations. However,
  71. * currently IndirectFixup is only used for destructors, which
  72. * aren't allowed to throw, so this is ok.
  73. */
  74. ActRec fakeAr;
  75. fakeAr.m_sfp = reinterpret_cast<ActRec*>(frameRbp);
  76. fakeAr.m_savedRip = frameRip;
  77. Stats::inc(Stats::TC_SyncUnwind);
  78. mcg->fixupMap().fixupWork(g_context.getNoCheck(), &fakeAr);
  79. tl_regState = VMRegState::CLEAN;
  80. FTRACE(2, "synced vmfp: {} vmsp: {} vmpc: {}\n", vmfp(), vmsp(), vmpc());
  81. }
  82. /*
  83. * Lookup a catch trace for the given TCA, returning nullptr if none was
  84. * found. Will abort if a nullptr catch trace was registered, meaning this call
  85. * isn't allowed to throw.
  86. */
  87. TCA lookup_catch_trace(TCA rip, _Unwind_Exception* exn) {
  88. if (auto catchTraceOpt = mcg->getCatchTrace(rip)) {
  89. if (auto catchTrace = *catchTraceOpt) return catchTrace;
  90. // A few of our optimization passes must be aware of every path out of
  91. // the trace, so throwing through jitted code without a catch block is
  92. // very bad. This is indicated with a present but nullptr entry in the
  93. // catch trace map.
  94. const size_t kCallSize = 5;
  95. const uint8_t kCallOpcode = 0xe8;
  96. auto callAddr = rip - kCallSize;
  97. TCA helperAddr = nullptr;
  98. if (*callAddr == kCallOpcode) {
  99. helperAddr = rip + *reinterpret_cast<int32_t*>(callAddr + 1);
  100. }
  101. always_assert_flog(false,
  102. "Translated call to {} threw '{}' without "
  103. "catch block, return address: {}\n",
  104. getNativeFunctionName(helperAddr),
  105. typeInfoFromUnwindException(exn).name(),
  106. rip);
  107. }
  108. return nullptr;
  109. }
  110. bool install_catch_trace(_Unwind_Context* ctx, _Unwind_Exception* exn,
  111. bool do_side_exit, TypedValue unwinder_tv) {
  112. auto const rip = (TCA)_Unwind_GetIP(ctx);
  113. auto catchTrace = lookup_catch_trace(rip, exn);
  114. if (!catchTrace) {
  115. FTRACE(1, "No catch trace entry for ip {}; bailing\n", rip);
  116. return false;
  117. }
  118. FTRACE(1, "installing catch trace {} for call {} with tv {}, "
  119. "returning _URC_INSTALL_CONTEXT\n",
  120. catchTrace, rip, unwinder_tv.pretty());
  121. // If the catch trace isn't going to finish by calling _Unwind_Resume, we
  122. // consume the exception here. Otherwise, we leave a pointer to it in RDS so
  123. // endCatchHelper can pass it to _Unwind_Resume when it's done.
  124. if (do_side_exit) {
  125. unwindRdsInfo->exn = nullptr;
  126. __cxxabiv1::__cxa_begin_catch(exn);
  127. __cxxabiv1::__cxa_end_catch();
  128. } else {
  129. unwindRdsInfo->exn = exn;
  130. }
  131. // In theory the unwind api will let us set registers in the frame before
  132. // executing our landing pad. In practice, trying to use their recommended
  133. // scratch registers results in a SEGV inside _Unwind_SetGR, so we pass
  134. // things to the handler using the RDS. This also simplifies the handler code
  135. // because it doesn't have to worry about saving its arguments somewhere
  136. // while executing the exit trace.
  137. unwindRdsInfo->doSideExit = do_side_exit;
  138. if (do_side_exit) unwindRdsInfo->unwinderTv = unwinder_tv;
  139. _Unwind_SetIP(ctx, (uint64_t)catchTrace);
  140. tl_regState = VMRegState::DIRTY;
  141. return true;
  142. }
  143. void deregister_unwind_region(std::vector<char>* p) {
  144. std::auto_ptr<std::vector<char> > del(p);
  145. __deregister_frame(&(*p)[fdeIdx]);
  146. }
  147. }
  148. _Unwind_Reason_Code
  149. tc_unwind_personality(int version,
  150. _Unwind_Action actions,
  151. uint64_t exceptionClass,
  152. _Unwind_Exception* exceptionObj,
  153. _Unwind_Context* context) {
  154. using namespace abi;
  155. // Exceptions thrown by g++-generated code will have the class "GNUCC++"
  156. // packed into a 64-bit int. libc++ has the class "CLNGC++". For now we
  157. // shouldn't be seeing exceptions from any other runtimes but this may
  158. // change in the future.
  159. DEBUG_ONLY constexpr uint64_t kMagicClass = 0x474e5543432b2b00;
  160. DEBUG_ONLY constexpr uint64_t kMagicDependentClass = 0x474e5543432b2b01;
  161. DEBUG_ONLY constexpr uint64_t kLLVMMagicClass = 0x434C4E47432B2B00;
  162. DEBUG_ONLY constexpr uint64_t kLLVMMagicDependentClass = 0x434C4E47432B2B01;
  163. assertx(exceptionClass == kMagicClass ||
  164. exceptionClass == kMagicDependentClass ||
  165. exceptionClass == kLLVMMagicClass ||
  166. exceptionClass == kLLVMMagicDependentClass);
  167. assertx(version == 1);
  168. auto const& ti = typeInfoFromUnwindException(exceptionObj);
  169. auto const std_exception = exceptionFromUnwindException(exceptionObj);
  170. InvalidSetMException* ism = nullptr;
  171. TVCoercionException* tce = nullptr;
  172. if (ti == typeid(InvalidSetMException)) {
  173. ism = static_cast<InvalidSetMException*>(std_exception);
  174. } else if (ti == typeid(TVCoercionException)) {
  175. tce = static_cast<TVCoercionException*>(std_exception);
  176. }
  177. if (Trace::moduleEnabled(TRACEMOD, 1)) {
  178. DEBUG_ONLY auto const* unwindType =
  179. (actions & _UA_SEARCH_PHASE) ? "search" : "cleanup";
  180. int status;
  181. auto* exnType = __cxa_demangle(ti.name(), nullptr, nullptr, &status);
  182. SCOPE_EXIT { free(exnType); };
  183. assertx(status == 0);
  184. FTRACE(1, "unwind {} exn {}: regState: {} ip: {} type: {}\n",
  185. unwindType, exceptionObj,
  186. tl_regState == VMRegState::DIRTY ? "dirty" : "clean",
  187. (TCA)_Unwind_GetIP(context), exnType);
  188. }
  189. /*
  190. * We don't do anything during the search phase---before attempting cleanup,
  191. * we want all deeper frames to have run their object destructors (which can
  192. * have side effects like setting tl_regState) and spilled any values they
  193. * may have been holding in callee-saved regs.
  194. */
  195. if (actions & _UA_SEARCH_PHASE) {
  196. if (ism) {
  197. FTRACE(1, "thrown value: {} returning _URC_HANDLER_FOUND\n ",
  198. ism->tv().pretty());
  199. return _URC_HANDLER_FOUND;
  200. }
  201. if (tce) {
  202. FTRACE(1, "TVCoercionException thrown, returning _URC_HANDLER_FOUND\n");
  203. return _URC_HANDLER_FOUND;
  204. }
  205. }
  206. /*
  207. * During the cleanup phase, we can either use a landing pad to perform
  208. * cleanup (with _Unwind_SetIP and _URC_INSTALL_CONTEXT), or we can do it
  209. * here. We sync the VM registers here, then optionally use a landing pad,
  210. * which is an exit trace from hhir with a few special instructions.
  211. */
  212. else if (actions & _UA_CLEANUP_PHASE) {
  213. TypedValue tv = ism ? ism->tv() : tce ? tce->tv() : TypedValue();
  214. if (tl_regState == VMRegState::DIRTY) {
  215. sync_regstate(context);
  216. }
  217. if (install_catch_trace(context, exceptionObj, ism || tce, tv)) {
  218. always_assert((ism || tce) == bool(actions & _UA_HANDLER_FRAME));
  219. return _URC_INSTALL_CONTEXT;
  220. }
  221. always_assert(!(actions & _UA_HANDLER_FRAME));
  222. auto ip = TCA(_Unwind_GetIP(context));
  223. auto& stubs = mcg->tx().uniqueStubs;
  224. if (ip == stubs.endCatchHelperPast) {
  225. FTRACE(1, "rip == endCatchHelperPast, continuing unwind\n");
  226. return _URC_CONTINUE_UNWIND;
  227. }
  228. if (ip == stubs.functionEnterHelperReturn) {
  229. FTRACE(1, "rip == functionEnterHelperReturn, continuing unwind\n");
  230. return _URC_CONTINUE_UNWIND;
  231. }
  232. FTRACE(1, "unwinder hit normal TC frame, going to tc_unwind_resume\n");
  233. unwindRdsInfo->exn = exceptionObj;
  234. _Unwind_SetIP(context, uint64_t(stubs.endCatchHelper));
  235. return _URC_INSTALL_CONTEXT;
  236. }
  237. always_assert(!(actions & _UA_HANDLER_FRAME));
  238. FTRACE(1, "returning _URC_CONTINUE_UNWIND\n");
  239. return _URC_CONTINUE_UNWIND;
  240. }
  241. TCUnwindInfo tc_unwind_resume(ActRec* fp) {
  242. while (true) {
  243. auto const newFp = fp->m_sfp;
  244. ITRACE(1, "tc_unwind_resume processing fp: {} savedRip: {:#x} newFp: {}\n",
  245. fp, fp->m_savedRip, newFp);
  246. Trace::Indent indent;
  247. always_assert_flog(isVMFrame(fp),
  248. "Unwinder got non-VM frame {} with saved rip {:#x}\n",
  249. fp, fp->m_savedRip);
  250. // When we're unwinding through a TC frame (as opposed to stopping at a
  251. // handler frame), we need to make sure that if we later return from this
  252. // VM frame in translated code, we don't resume after the bindcall that may
  253. // be expecting things to still live in its spill space. If the return
  254. // address is in functionEnterHelper or callToExit, rVmFp won't contain a
  255. // real VM frame, so we skip those.
  256. auto savedRip = reinterpret_cast<TCA>(fp->m_savedRip);
  257. if (savedRip == mcg->tx().uniqueStubs.callToExit) {
  258. ITRACE(1, "top VM frame, passing back to _Unwind_Resume\n");
  259. return {nullptr, newFp};
  260. }
  261. auto catchTrace = lookup_catch_trace(savedRip, unwindRdsInfo->exn);
  262. if (isDebuggerReturnHelper(savedRip)) {
  263. // If this frame had its return address smashed by the debugger, the real
  264. // catch trace is saved in a side table.
  265. assertx(catchTrace == nullptr);
  266. catchTrace = popDebuggerCatch(fp);
  267. }
  268. unwindPreventReturnToTC(fp);
  269. if (fp->m_savedRip != reinterpret_cast<uint64_t>(savedRip)) {
  270. ITRACE(1, "Smashed m_savedRip of fp {} from {} to {:#x}\n",
  271. fp, savedRip, fp->m_savedRip);
  272. }
  273. fp = newFp;
  274. // If there's a catch trace for this block, return it. Otherwise, keep
  275. // going up the VM stack for this nesting level.
  276. if (catchTrace) {
  277. ITRACE(1, "tc_unwind_resume returning catch trace {} with fp: {}\n",
  278. catchTrace, fp);
  279. return {catchTrace, fp};
  280. }
  281. ITRACE(1, "No catch trace entry for {}; continuing\n",
  282. mcg->tx().uniqueStubs.describe(savedRip));
  283. }
  284. }
  285. ///////////////////////////////////////////////////////////////////////////////
  286. UnwindInfoHandle
  287. register_unwind_region(unsigned char* startAddr, size_t size) {
  288. FTRACE(1, "register_unwind_region: base {}, size {}\n", startAddr, size);
  289. // The first time we're called, this will dynamically link the data
  290. // we need in the request data segment. All future JIT translations
  291. // of catch traces may use offsets based on this handle.
  292. unwindRdsInfo.bind();
  293. std::unique_ptr<std::vector<char>> bufferMem(new std::vector<char>);
  294. std::vector<char>& buffer = *bufferMem;
  295. {
  296. // This is a dwarf CIE header. Looks the same as a fde except the
  297. // second field is zero.
  298. append_vec<uint32_t>(buffer, 0); // Room for length later
  299. append_vec<int32_t>(buffer, 0); // CIE_id
  300. append_vec<uint8_t>(buffer, 1); // version
  301. /*
  302. * Null-terminated "augmentation string" (defines what the rest of
  303. * this thing is going to have.
  304. */
  305. append_vec<char>(buffer, 'z');
  306. append_vec<char>(buffer, 'P');
  307. append_vec<char>(buffer, '\0');
  308. // Code and data alignment.
  309. append_vec<uint8_t>(buffer, 1);
  310. append_vec<uint8_t>(buffer, 8); // Multiplies offsets below.
  311. // Return address column (in version 1, this is a single byte).
  312. append_vec<uint8_t>(buffer, Debug::RIP);
  313. // Length of the augmentation data.
  314. const size_t augIdx = buffer.size();
  315. append_vec<uint8_t>(buffer, 9);
  316. // Pointer to the personality routine for the TC.
  317. append_vec<uint8_t>(buffer, DW_EH_PE_absptr);
  318. append_vec<uintptr_t>(buffer, uintptr_t(tc_unwind_personality));
  319. // Fixup the augmentation data length field. Note that it doesn't include
  320. // the space for itself.
  321. void* vp = &buffer[augIdx];
  322. *static_cast<uint8_t*>(vp) = buffer.size() - augIdx - sizeof(uint8_t);
  323. /*
  324. * Define a program for the CIE. This explains to the unwinder
  325. * how to figure out where the frame pointer was, etc.
  326. *
  327. * Arguments to some of these are encoded in LEB128, so we have to
  328. * clear the high bit for the signed values.
  329. */
  330. // Previous FP (CFA) is at rbp + 16.
  331. append_vec<uint8_t>(buffer, DW_CFA_def_cfa);
  332. append_vec<uint8_t>(buffer, Debug::RBP);
  333. append_vec<uint8_t>(buffer, 16);
  334. // rip is at CFA - 1 * data_align.
  335. append_vec<uint8_t>(buffer, DW_CFA_offset_extended_sf);
  336. append_vec<uint8_t>(buffer, Debug::RIP);
  337. append_vec<uint8_t>(buffer, -1u & 0x7f);
  338. // rbp is at CFA - 2 * data_align.
  339. append_vec<uint8_t>(buffer, DW_CFA_offset_extended_sf);
  340. append_vec<uint8_t>(buffer, Debug::RBP);
  341. append_vec<uint8_t>(buffer, -2u & 0x7f);
  342. /*
  343. * Leave rsp unchanged.
  344. *
  345. * Note that some things in the translator do actually change rsp,
  346. * but we assume they cannot throw so this is ok. If rVmSp ever
  347. * changes to use rsp this code must change.
  348. */
  349. append_vec<uint8_t>(buffer, DW_CFA_same_value);
  350. append_vec<uint8_t>(buffer, Debug::RSP);
  351. // Fixup the length field. Note that it doesn't include the space
  352. // for itself.
  353. vp = &buffer[0];
  354. *static_cast<uint32_t*>(vp) = buffer.size() - sizeof(uint32_t);
  355. }
  356. fdeIdx = buffer.size();
  357. {
  358. // Reserve space for FDE length.
  359. append_vec<uint32_t>(buffer, 0);
  360. // Negative offset to the CIE for this FDE---the offset is
  361. // relative to this field.
  362. append_vec<int32_t>(buffer, int32_t(buffer.size()));
  363. // We're using the addressing mode DW_EH_PE_absptr, which means it
  364. // wants a 8 byte pointer and a 8 byte size indicating the region
  365. // this FDE applies to.
  366. append_vec<unsigned char*>(buffer, startAddr);
  367. append_vec<size_t>(buffer, size);
  368. // Length of the augmentation data in this FDE. This field must present if
  369. // 'z' is set in CIE.
  370. append_vec<uint8_t>(buffer, 0);
  371. // Fixup the length field for this FDE. Again length doesn't
  372. // include the length field itself.
  373. void* vp = &buffer[fdeIdx];
  374. *static_cast<uint32_t*>(vp) = buffer.size() - fdeIdx - sizeof(uint32_t);
  375. }
  376. // Add one more zero'd length field---this indicates that there are
  377. // no more FDEs sharing this CIE.
  378. append_vec<uint32_t>(buffer, 0);
  379. __register_frame(&buffer[fdeIdx]);
  380. return std::shared_ptr<std::vector<char>>(
  381. bufferMem.release(),
  382. deregister_unwind_region
  383. );
  384. }
  385. //////////////////////////////////////////////////////////////////////
  386. }}