/*
* Copyright 2011-2012, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "bcc/Support/Disassembler.h"
#include "bcc/Config/Config.h"
#if USE_DISASSEMBLER
#include <string>
#include <llvm/IR/LLVMContext.h>
#include <llvm/MC/MCAsmInfo.h>
#include <llvm/MC/MCDisassembler.h>
#include <llvm/MC/MCInst.h>
#include <llvm/MC/MCInstPrinter.h>
#include <llvm/MC/MCInstrInfo.h>
#include <llvm/MC/MCRegisterInfo.h>
#include <llvm/MC/MCSubtargetInfo.h>
#include <llvm/Support/MemoryObject.h>
#include <llvm/Support/TargetRegistry.h>
#include <llvm/Support/raw_ostream.h>
#include "bcc/Support/OutputFile.h"
#include "bcc/Support/Log.h"
namespace {
class BufferMemoryObject : public llvm::MemoryObject {
private:
const uint8_t *mBytes;
uint64_t mLength;
public:
BufferMemoryObject(const uint8_t *pBytes, uint64_t pLength)
: mBytes(pBytes), mLength(pLength) {
}
virtual uint64_t getBase() const { return 0; }
virtual uint64_t getExtent() const { return mLength; }
virtual int readByte(uint64_t pAddr, uint8_t *pByte) const {
if (pAddr > getExtent())
return -1;
*pByte = mBytes[pAddr];
return 0;
}
};
} // namespace anonymous
namespace bcc {
DisassembleResult Disassemble(llvm::raw_ostream &pOutput, const char *pTriple,
const char *pFuncName, const uint8_t *pFunc,
size_t pFuncSize) {
DisassembleResult result = kDisassembleSuccess;
uint64_t i = 0;
const llvm::MCSubtargetInfo *subtarget_info = nullptr;
const llvm::MCDisassembler *disassembler = nullptr;
const llvm::MCInstrInfo *mc_inst_info = nullptr;
const llvm::MCRegisterInfo *mc_reg_info = nullptr;
const llvm::MCAsmInfo *asm_info = nullptr;
llvm::MCInstPrinter *inst_printer = nullptr;
BufferMemoryObject *input_function = nullptr;
std::string error;
const llvm::Target* target =
llvm::TargetRegistry::lookupTarget(pTriple, error);
if (target == nullptr) {
ALOGE("Invalid target triple for disassembler: %s (%s)!",
pTriple, error.c_str());
return kDisassembleUnknownTarget;
}
subtarget_info =
target->createMCSubtargetInfo(pTriple, /* CPU */"", /* Features */"");;
if (subtarget_info == nullptr) {
result = kDisassembleFailedSetup;
goto bail;
}
disassembler = target->createMCDisassembler(*subtarget_info);
mc_inst_info = target->createMCInstrInfo();
mc_reg_info = target->createMCRegInfo(pTriple);
asm_info = target->createMCAsmInfo(pTriple);
if ((disassembler == nullptr) || (mc_inst_info == nullptr) ||
(mc_reg_info == nullptr) || (asm_info == nullptr)) {
result = kDisassembleFailedSetup;
goto bail;
}
inst_printer = target->createMCInstPrinter(asm_info->getAssemblerDialect(),
*asm_info, *mc_inst_info,
*mc_reg_info, *subtarget_info);
if (inst_printer == nullptr) {
result = kDisassembleFailedSetup;
goto bail;
}
input_function = new (std::nothrow) BufferMemoryObject(pFunc, pFuncSize);
if (input_function == nullptr) {
result = kDisassembleOutOfMemory;
goto bail;
}
// Disassemble the given function
pOutput << "Disassembled code: " << pFuncName << "\n";
while (i < pFuncSize) {
llvm::MCInst inst;
uint64_t inst_size;
llvm::MCDisassembler::DecodeStatus decode_result =
disassembler->getInstruction(inst, inst_size, *input_function, i,
llvm::nulls(), llvm::nulls());
switch (decode_result) {
case llvm::MCDisassembler::Fail: {
ALOGW("Invalid instruction encoding encountered at %llu of function %s "
"under %s.", i, pFuncName, pTriple);
i++;
break;
}
case llvm::MCDisassembler::SoftFail: {
ALOGW("Potentially undefined instruction encoding encountered at %llu "
"of function %s under %s.", i, pFuncName, pTriple);
// fall-through
}
case llvm::MCDisassembler::Success : {
const uint8_t *inst_addr = pFunc + i;
pOutput.indent(4);
pOutput << "0x";
pOutput.write_hex(reinterpret_cast<uintptr_t>(inst_addr));
pOutput << ": 0x";
pOutput.write_hex(*reinterpret_cast<const uint32_t *>(inst_addr));
inst_printer->printInst(&inst, pOutput, /* Annot */"");
pOutput << "\n";
i += inst_size;
break;
}
}
}
pOutput << "\n";
bail:
// Clean up
delete input_function;
delete inst_printer;
delete asm_info;
delete mc_reg_info;
delete mc_inst_info;
delete disassembler;
delete subtarget_info;
return result;
}
DisassembleResult Disassemble(OutputFile &pOutput, const char *pTriple,
const char *pFuncName, const uint8_t *pFunc,
size_t FuncSize) {
// Check the state of the specified output file.
if (pOutput.hasError()) {
return kDisassembleInvalidOutput;
}
// Open the output file decorated in llvm::raw_ostream.
llvm::raw_ostream *output = pOutput.dup();
if (output == nullptr) {
return kDisassembleFailedPrepareOutput;
}
// Delegate the request.
DisassembleResult result =
Disassemble(*output, pTriple, pFuncName, pFunc, FuncSize);
// Close the output before return.
delete output;
return result;
}
} // namespace bcc
#else
bcc::DisassembleResult Disassemble(llvm::raw_ostream &pOutput,
const char *pTriple, const char *pFuncName,
const uint8_t *pFunc, size_t pFuncSize) {
return bcc::kDisassemblerNotAvailable;
}
bcc::DisassembleResult bcc::Disassemble(OutputFile &pOutput,
const char *pTriple,
const char *pFuncName,
const uint8_t *pFunc,
size_t pFuncSize) {
return bcc::kDisassemblerNotAvailable;
}
#endif // USE_DISASSEMBLER