| /* |
| * 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/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 = NULL; |
| const llvm::MCDisassembler *disassembler = NULL; |
| const llvm::MCInstrInfo *mc_inst_info = NULL; |
| const llvm::MCRegisterInfo *mc_reg_info = NULL; |
| const llvm::MCAsmInfo *asm_info = NULL; |
| llvm::MCInstPrinter *inst_printer = NULL; |
| |
| BufferMemoryObject *input_function = NULL; |
| |
| std::string error; |
| const llvm::Target* target = |
| llvm::TargetRegistry::lookupTarget(pTriple, error); |
| |
| if (target == NULL) { |
| ALOGE("Invalid target triple for disassembler: %s (%s)!", |
| pTriple, error.c_str()); |
| return kDisassembleUnknownTarget; |
| } |
| |
| subtarget_info = |
| target->createMCSubtargetInfo(pTriple, /* CPU */"", /* Features */"");; |
| |
| if (subtarget_info == NULL) { |
| 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 == NULL) || (mc_inst_info == NULL) || |
| (mc_reg_info == NULL) || (asm_info == NULL)) { |
| result = kDisassembleFailedSetup; |
| goto bail; |
| } |
| |
| inst_printer = target->createMCInstPrinter(asm_info->getAssemblerDialect(), |
| *asm_info, *mc_inst_info, |
| *mc_reg_info, *subtarget_info); |
| |
| if (inst_printer == NULL) { |
| result = kDisassembleFailedSetup; |
| goto bail; |
| } |
| |
| input_function = new (std::nothrow) BufferMemoryObject(pFunc, pFuncSize); |
| |
| if (input_function == NULL) { |
| 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 == NULL) { |
| 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 |