Optional Two-phase heap tracing
[hiphop-php.git] / hphp / util / disasm.cpp
blob171daaf6749ef394d38fef421ae64d16422d2377
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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 +----------------------------------------------------------------------+
16 #include "hphp/util/disasm.h"
18 #include <iomanip>
19 #include <stdlib.h>
21 #include <boost/algorithm/string.hpp>
23 #include <folly/Format.h>
25 #include "hphp/util/abi-cxx.h"
26 #include "hphp/util/text-color.h"
28 namespace HPHP {
30 static uintptr_t excludeLow, excludeLen;
32 #ifdef HAVE_LIBXED
34 // XED callback function to get a symbol from an address
35 static int addressToSymbol(xed_uint64_t address, char* symbolBuffer,
36 xed_uint32_t bufferLength, xed_uint64_t* offset,
37 void* /*context*/) {
38 if (address - excludeLow < excludeLen) return 0;
40 auto name = boost::trim_copy(getNativeFunctionName((void*)address));
41 if (boost::starts_with(name, "0x")) {
42 return 0;
44 auto pos = name.find_first_of('(');
45 auto copyLength = pos != std::string::npos
46 ? std::min(pos, size_t(bufferLength - 1))
47 : bufferLength - 1;
48 strncpy(symbolBuffer, name.c_str(), copyLength);
49 symbolBuffer[copyLength] = '\0';
50 *offset = 0;
51 return 1;
53 #endif /* HAVE_LIBXED */
55 void Disasm::ExcludedAddressRange(void* low, size_t len) {
56 excludeLow = uintptr_t(low);
57 excludeLen = len;
60 Disasm::Disasm(const Disasm::Options& opts)
61 : m_opts(opts)
63 #ifdef HAVE_LIBXED
64 xed_state_init(&m_xedState, XED_MACHINE_MODE_LONG_64,
65 XED_ADDRESS_WIDTH_64b, XED_ADDRESS_WIDTH_64b);
66 xed_tables_init();
67 #if XED_ENCODE_ORDER_MAX_ENTRIES == 28 // Older version of XED library
68 xed_register_disassembly_callback(addressToSymbol);
69 #endif
70 #endif // HAVE_LIBXED
73 #ifdef HAVE_LIBXED
75 #define MAX_INSTR_ASM_LEN 128
77 static const xed_syntax_enum_t s_xed_syntax =
78 getenv("HHVM_INTEL_DISAS") ? XED_SYNTAX_INTEL : XED_SYNTAX_ATT;
79 #endif // HAVE_LIBXED
81 void Disasm::disasm(std::ostream& out, uint8_t* codeStartAddr,
82 uint8_t* codeEndAddr) {
84 #ifdef HAVE_LIBXED
85 auto const endClr = m_opts.m_color.empty() ? "" : ANSI_COLOR_END;
86 char codeStr[MAX_INSTR_ASM_LEN];
87 xed_uint8_t *frontier;
88 xed_decoded_inst_t xedd;
89 uint64_t codeBase = uint64_t(codeStartAddr);
90 uint64_t ip;
92 // Decode and print each instruction
93 for (frontier = codeStartAddr, ip = (uint64_t)codeStartAddr;
94 frontier < codeEndAddr; ) {
95 for (int i = 0; i < m_opts.m_indentLevel; ++i) {
96 out << ' ';
99 xed_decoded_inst_zero_set_mode(&xedd, &m_xedState);
100 xed_decoded_inst_set_input_chip(&xedd, XED_CHIP_INVALID);
101 xed_error_enum_t xed_error = xed_decode(&xedd, frontier, 15);
102 if (xed_error != XED_ERROR_NONE) {
103 out << folly::format("xed_decode failed at address {}\n", frontier);
104 return;
107 // Get disassembled instruction in codeStr
108 auto const syntax = m_opts.m_forceAttSyntax ? XED_SYNTAX_ATT
109 : s_xed_syntax;
110 if (!xed_format_context(syntax, &xedd, codeStr,
111 MAX_INSTR_ASM_LEN, ip, nullptr
112 #if XED_ENCODE_ORDER_MAX_ENTRIES != 28 // Newer version of XED library
113 , addressToSymbol
114 #endif
115 )) {
116 out << folly::format("xed_format_context failed at address {}\n",
117 frontier);
118 return;
120 uint32_t instrLen = xed_decoded_inst_get_length(&xedd);
122 // If it's a jump, we're printing relative offsets, and the dest
123 // is within the range we're printing, add the dest as a relative
124 // offset.
125 std::string jmpComment;
126 auto const cat = xed_decoded_inst_get_category(&xedd);
127 if (cat == XED_CATEGORY_COND_BR || cat == XED_CATEGORY_UNCOND_BR) {
128 if (m_opts.m_relativeOffset) {
129 auto disp = uint64_t(frontier + instrLen +
130 xed_decoded_inst_get_branch_displacement(&xedd) -
131 codeBase);
132 if (disp < uint64_t(codeEndAddr - codeStartAddr)) {
133 jmpComment = folly::format(" # {:#x}", disp).str();
138 out << m_opts.m_color;
139 if (m_opts.m_addresses) {
140 const char* fmt = m_opts.m_relativeOffset ? "{:3x}: " : "{:#10x}: ";
141 out << folly::format(fmt, ip - (m_opts.m_relativeOffset ? codeBase : 0));
143 if (m_opts.m_printEncoding) {
144 // print encoding, like in objdump
145 unsigned posi = 0;
146 for (; posi < instrLen; ++posi) {
147 out << folly::format("{:02x} ", (uint8_t)frontier[posi]);
149 for (; posi < 16; ++posi) {
150 out << " ";
153 out << codeStr << jmpComment << endClr << '\n';
154 frontier += instrLen;
155 ip += instrLen;
157 #else
158 out << "This binary was compiled without disassembly support\n";
159 #endif // HAVE_LIBXED
162 } // namespace HPHP