2 +----------------------------------------------------------------------+
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"
23 #include "folly/Format.h"
24 #include "folly/ScopeGuard.h"
26 #include "hphp/util/base.h"
27 #include "hphp/util/text-color.h"
32 // Parse the function name out of backtrace_symbols output, which
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
) {
49 int length
= fnEnd
- fnStart
;
50 char *functionName
= (char *)malloc(length
+ 1);
51 strncpy(functionName
, fnStart
, length
);
52 functionName
[length
] = '\0';
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
);
65 if (p
< demangledName
) {
72 if (p
< demangledName
) {
77 int length
= lparen
- (colon
+ 1);
78 char *methodName
= (char *)malloc(length
+ 1);
79 strncpy(methodName
, colon
+ 1, length
);
80 methodName
[length
] = '\0';
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
,
89 xed_uint32_t buffer_length
,
92 auto& memoVal
= addressToSymbolMemo
[address
];
93 if (memoVal
!= nullptr) {
94 strncpy(symbol_buffer
, memoVal
, buffer_length
- 1);
95 symbol_buffer
[buffer_length
- 1] = '\0';
100 char **symbolTable
= backtrace_symbols((void **)&address
, 1);
104 char *backtraceName
= symbolTable
[0];
105 char *mangledName
= getFunctionName(backtraceName
);
112 char *demangledName
= abi::__cxa_demangle(mangledName
, NULL
, NULL
, &status
);
113 char *methodName
= NULL
;
116 methodName
= getMethodName(demangledName
);
118 } else if (status
== -2) {
119 methodName
= mangledName
;
125 strncpy(symbol_buffer
, methodName
, buffer_length
- 1);
126 symbol_buffer
[buffer_length
- 1] = '\0';
127 memoVal
= methodName
;
135 #endif /* HAVE_LIBXED */
137 Disasm::Disasm(const Disasm::Options
& opts
)
141 xed_state_init(&m_xedState
, XED_MACHINE_MODE_LONG_64
,
142 XED_ADDRESS_WIDTH_64b
, XED_ADDRESS_WIDTH_64b
);
144 xed_register_disassembly_callback(addressToSymbol
);
145 #endif // HAVE_LIBXED
149 static void error(std::string msg
) {
150 fprintf(stderr
, "Error: %s\n", msg
.c_str());
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
) {
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
);
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
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
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
) -
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
) {
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
215 for (; posi
< instrLen
; ++posi
) {
216 out
<< folly::format("{:02x} ", (uint8_t)frontier
[posi
]);
218 for (; posi
< 16; ++posi
) {
222 out
<< codeStr
<< jmpComment
<< endClr
<< '\n';
223 frontier
+= instrLen
;
227 out
<< "This binary was compiled without disassembly support\n";
228 #endif // HAVE_LIBXED