fixup yaml to be c++ compliant
[hiphop-php.git] / hphp / util / disasm.cpp
blob5b4b50ce840262972ed997e57bc922e437a741fd
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 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>
20 #include <execinfo.h>
21 #include <cxxabi.h>
23 #include "folly/Format.h"
24 #include "folly/ScopeGuard.h"
26 #include "hphp/util/base.h"
27 #include "hphp/util/text-color.h"
29 namespace HPHP {
31 #ifdef HAVE_LIBXED
32 // Parse the function name out of backtrace_symbols output, which
33 // looks like:
34 // filename(function+offset) [addr]
36 // It's possible that we just have "[addr]", if there's no symbol
37 // associated, so return NULL in that case.
39 // Following the (bad?) example of backtrace_symbols and
40 // __cxa_demangle, this function allocates memory with malloc if
41 // successful, and doesn't allocate otherwise.
42 static char *getFunctionName(char *backtraceName) {
43 char *fnStart = strchr(backtraceName, '(');
44 char *fnEnd = strrchr(backtraceName, '+');
45 if (!fnStart || !fnEnd || fnStart > fnEnd) {
46 return NULL;
48 fnStart++;
49 int length = fnEnd - fnStart;
50 char *functionName = (char *)malloc(length + 1);
51 strncpy(functionName, fnStart, length);
52 functionName[length] = '\0';
53 return functionName;
56 // Parse the method name out of a demangled name, which looks like:
57 // namespace::class::method(type::args...)
58 // Allocates with malloc when successful, and doesn't if it isn't.
59 static char *getMethodName(char *demangledName) {
60 char *p = demangledName + strlen(demangledName);
61 char *lparen = NULL;
62 char *colon = NULL;
63 while (*p != '(') {
64 p--;
65 if (p < demangledName) {
66 return NULL;
69 lparen = p;
70 while (*p != ':') {
71 p--;
72 if (p < demangledName) {
73 return NULL;
76 colon = p;
77 int length = lparen - (colon + 1);
78 char *methodName = (char *)malloc(length + 1);
79 strncpy(methodName, colon + 1, length);
80 methodName[length] = '\0';
81 return methodName;
84 static hphp_hash_map<xed_uint64_t,const char*> addressToSymbolMemo;
86 // XED callback function to get a symbol from an address
87 static int addressToSymbol(xed_uint64_t address,
88 char *symbol_buffer,
89 xed_uint32_t buffer_length,
90 xed_uint64_t *offset,
91 void *context) {
92 auto& memoVal = addressToSymbolMemo[address];
93 if (memoVal != nullptr) {
94 strncpy(symbol_buffer, memoVal, buffer_length - 1);
95 symbol_buffer[buffer_length - 1] = '\0';
96 *offset = 0;
97 return 1;
100 char **symbolTable = backtrace_symbols((void **)&address, 1);
101 if (!symbolTable) {
102 return 0;
104 char *backtraceName = symbolTable[0];
105 char *mangledName = getFunctionName(backtraceName);
106 free(symbolTable);
107 if (!mangledName) {
108 return 0;
111 int status;
112 char *demangledName = abi::__cxa_demangle(mangledName, NULL, NULL, &status);
113 char *methodName = NULL;
114 if (status == 0) {
115 free(mangledName);
116 methodName = getMethodName(demangledName);
117 free(demangledName);
118 } else if (status == -2) {
119 methodName = mangledName;
120 } else {
121 free(mangledName);
124 if (methodName) {
125 strncpy(symbol_buffer, methodName, buffer_length - 1);
126 symbol_buffer[buffer_length - 1] = '\0';
127 memoVal = methodName;
128 } else {
129 return 0;
132 *offset = 0;
133 return 1;
135 #endif /* HAVE_LIBXED */
137 Disasm::Disasm(const Disasm::Options& opts)
138 : m_opts(opts)
140 #ifdef HAVE_LIBXED
141 xed_state_init(&m_xedState, XED_MACHINE_MODE_LONG_64,
142 XED_ADDRESS_WIDTH_64b, XED_ADDRESS_WIDTH_64b);
143 xed_tables_init();
144 xed_register_disassembly_callback(addressToSymbol);
145 #endif // HAVE_LIBXED
148 #ifdef HAVE_LIBXED
149 static void error(std::string msg) {
150 fprintf(stderr, "Error: %s\n", msg.c_str());
151 exit(1);
154 #define MAX_INSTR_ASM_LEN 128
156 static const xed_syntax_enum_t s_xed_syntax =
157 getenv("HHVM_ATT_DISAS") ? XED_SYNTAX_ATT : XED_SYNTAX_INTEL;
158 #endif // HAVE_LIBXED
160 void Disasm::disasm(std::ostream& out, uint8_t* codeStartAddr,
161 uint8_t* codeEndAddr) {
163 #ifdef HAVE_LIBXED
164 auto const endClr = m_opts.m_color.empty() ? "" : ANSI_COLOR_END;
165 char codeStr[MAX_INSTR_ASM_LEN];
166 xed_uint8_t *frontier;
167 xed_decoded_inst_t xedd;
168 uint64_t codeBase = uint64_t(codeStartAddr);
169 uint64_t ip;
171 // Decode and print each instruction
172 for (frontier = codeStartAddr, ip = (uint64_t)codeStartAddr;
173 frontier < codeEndAddr; ) {
174 xed_decoded_inst_zero_set_mode(&xedd, &m_xedState);
175 xed_decoded_inst_set_input_chip(&xedd, XED_CHIP_INVALID);
176 xed_error_enum_t xed_error = xed_decode(&xedd, frontier, 15);
177 if (xed_error != XED_ERROR_NONE) error("disasm error: xed_decode failed");
179 // Get disassembled instruction in codeStr
180 auto const syntax = m_opts.m_forceAttSyntax ? XED_SYNTAX_ATT
181 : s_xed_syntax;
182 if (!xed_format_context(syntax, &xedd, codeStr,
183 MAX_INSTR_ASM_LEN, ip, nullptr)) {
184 error("disasm error: xed_format_context failed");
186 uint32_t instrLen = xed_decoded_inst_get_length(&xedd);
188 // If it's a jump, we're printing relative offsets, and the dest
189 // is within the range we're printing, add the dest as a relative
190 // offset.
191 std::string jmpComment;
192 auto const cat = xed_decoded_inst_get_category(&xedd);
193 if (cat == XED_CATEGORY_COND_BR || cat == XED_CATEGORY_UNCOND_BR) {
194 if (m_opts.m_relativeOffset) {
195 auto disp = uint64_t(frontier + instrLen +
196 xed_decoded_inst_get_branch_displacement(&xedd) -
197 codeBase);
198 if (disp < uint64_t(codeEndAddr - codeStartAddr)) {
199 jmpComment = folly::format(" # {:#x}", disp).str();
204 for (int i = 0; i < m_opts.m_indentLevel; ++i) {
205 out << ' ';
207 out << m_opts.m_color;
208 if (m_opts.m_addresses) {
209 const char* fmt = m_opts.m_relativeOffset ? "{:3x}: " : "{:#10x}: ";
210 out << folly::format(fmt, ip - (m_opts.m_relativeOffset ? codeBase : 0));
212 if (m_opts.m_printEncoding) {
213 // print encoding, like in objdump
214 unsigned posi = 0;
215 for (; posi < instrLen; ++posi) {
216 out << folly::format("{:02x} ", (uint8_t)frontier[posi]);
218 for (; posi < 16; ++posi) {
219 out << " ";
222 out << codeStr << jmpComment << endClr << '\n';
223 frontier += instrLen;
224 ip += instrLen;
226 #else
227 out << "This binary was compiled without disassembly support\n";
228 #endif // HAVE_LIBXED
231 } // namespace HPHP