Arquivos
hhvm/hphp/util/disasm.cpp
T
2013-04-22 14:43:49 -07:00

218 linhas
6.8 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include "util/disasm.h"
#include <iomanip>
#include <stdlib.h>
#include <execinfo.h>
#include <cxxabi.h>
#include "folly/Format.h"
#include "folly/ScopeGuard.h"
#include "util/base.h"
#include "util/text_color.h"
namespace HPHP {
#ifdef HAVE_LIBXED
// Parse the function name out of backtrace_symbols output, which
// looks like:
// filename(function+offset) [addr]
//
// It's possible that we just have "[addr]", if there's no symbol
// associated, so return NULL in that case.
//
// Following the (bad?) example of backtrace_symbols and
// __cxa_demangle, this function allocates memory with malloc if
// successful, and doesn't allocate otherwise.
static char *getFunctionName(char *backtraceName) {
char *fnStart = strchr(backtraceName, '(');
char *fnEnd = strrchr(backtraceName, '+');
if (!fnStart || !fnEnd || fnStart > fnEnd) {
return NULL;
}
fnStart++;
int length = fnEnd - fnStart;
char *functionName = (char *)malloc(length + 1);
strncpy(functionName, fnStart, length);
functionName[length] = '\0';
return functionName;
}
// Parse the method name out of a demangled name, which looks like:
// namespace::class::method(type::args...)
// Allocates with malloc when successful, and doesn't if it isn't.
static char *getMethodName(char *demangledName) {
char *p = demangledName + strlen(demangledName);
char *lparen = NULL;
char *colon = NULL;
while (*p != '(') {
p--;
if (p < demangledName) {
return NULL;
}
}
lparen = p;
while (*p != ':') {
p--;
if (p < demangledName) {
return NULL;
}
}
colon = p;
int length = lparen - (colon + 1);
char *methodName = (char *)malloc(length + 1);
strncpy(methodName, colon + 1, length);
methodName[length] = '\0';
return methodName;
}
// XED callback function to get a symbol from an address
static int addressToSymbol(xed_uint64_t address,
char *symbol_buffer,
xed_uint32_t buffer_length,
xed_uint64_t *offset,
void *context) {
char **symbolTable = backtrace_symbols((void **)&address, 1);
if (!symbolTable) {
return 0;
}
char *backtraceName = symbolTable[0];
char *mangledName = getFunctionName(backtraceName);
free(symbolTable);
if (!mangledName) {
return 0;
}
int status;
char *demangledName = abi::__cxa_demangle(mangledName, NULL, NULL, &status);
char *methodName = NULL;
if (status == 0) {
free(mangledName);
methodName = getMethodName(demangledName);
free(demangledName);
} else if (status == -2) {
methodName = mangledName;
} else {
free(mangledName);
}
if (methodName) {
strncpy(symbol_buffer, methodName, buffer_length);
symbol_buffer[buffer_length] = '\0';
free(methodName);
} else {
return 0;
}
*offset = 0;
return 1;
}
#endif /* HAVE_LIBXED */
Disasm::Disasm(const Disasm::Options& opts)
: m_opts(opts)
{
#ifdef HAVE_LIBXED
xed_state_init(&m_xedState, XED_MACHINE_MODE_LONG_64,
XED_ADDRESS_WIDTH_64b, XED_ADDRESS_WIDTH_64b);
xed_tables_init();
xed_register_disassembly_callback(addressToSymbol);
#endif // HAVE_LIBXED
}
#ifdef HAVE_LIBXED
static void error(std::string msg) {
fprintf(stderr, "Error: %s\n", msg.c_str());
exit(1);
}
#define MAX_INSTR_ASM_LEN 128
static const xed_syntax_enum_t s_xed_syntax =
getenv("HHVM_ATT_DISAS") ? XED_SYNTAX_ATT : XED_SYNTAX_INTEL;
#endif // HAVE_LIBXED
void Disasm::disasm(std::ostream& out, uint8_t* codeStartAddr,
uint8_t* codeEndAddr) {
auto const endClr = m_opts.m_color.empty() ? "" : ANSI_COLOR_END;
#ifdef HAVE_LIBXED
char codeStr[MAX_INSTR_ASM_LEN];
xed_uint8_t *frontier;
xed_decoded_inst_t xedd;
uint64_t codeBase = uint64_t(codeStartAddr);
uint64_t ip;
// Decode and print each instruction
for (frontier = codeStartAddr, ip = (uint64_t)codeStartAddr;
frontier < codeEndAddr; ) {
xed_decoded_inst_zero_set_mode(&xedd, &m_xedState);
xed_decoded_inst_set_input_chip(&xedd, XED_CHIP_INVALID);
xed_error_enum_t xed_error = xed_decode(&xedd, frontier, 15);
if (xed_error != XED_ERROR_NONE) error("disasm error: xed_decode failed");
// Get disassembled instruction in codeStr
if (!xed_format_context(s_xed_syntax, &xedd, codeStr,
MAX_INSTR_ASM_LEN, ip, nullptr)) {
error("disasm error: xed_format_context failed");
}
uint32_t instrLen = xed_decoded_inst_get_length(&xedd);
// If it's a jump, we're printing relative offsets, and the dest
// is within the range we're printing, add the dest as a relative
// offset.
std::string jmpComment;
auto const cat = xed_decoded_inst_get_category(&xedd);
if (cat == XED_CATEGORY_COND_BR || cat == XED_CATEGORY_UNCOND_BR) {
if (m_opts.m_relativeOffset) {
auto disp = uint64_t(frontier + instrLen +
xed_decoded_inst_get_branch_displacement(&xedd) -
codeBase);
if (disp < uint64_t(codeEndAddr - codeStartAddr)) {
jmpComment = folly::format(" # {:#x}", disp).str();
}
}
}
for (int i = 0; i < m_opts.m_indentLevel; ++i) {
out << ' ';
}
out << m_opts.m_color;
const char* fmt = m_opts.m_relativeOffset ? "{:3x}: " : "{:#10x}: ";
out << folly::format(fmt, ip - (m_opts.m_relativeOffset ? codeBase : 0));
if (m_opts.m_printEncoding) {
// print encoding, like in objdump
unsigned posi = 0;
for (; posi < instrLen; ++posi) {
out << folly::format("{:02x} ", (uint8_t)frontier[posi]);
}
for (; posi < 16; ++posi) {
out << " ";
}
}
out << codeStr << jmpComment << endClr << '\n';
frontier += instrLen;
ip += instrLen;
}
#else
out << "This binary was compiled without disassembly support\n";
#endif // HAVE_LIBXED
}
} // namespace HPHP