/// \file LLVMDisassemblerInterface.cpp // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include "llvm/ADT/StringRef.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCTargetOptions.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "revng/Support/Debug.h" #include "revng/Yield/Assembly/LLVMDisassemblerInterface.h" #include "revng/Yield/Function.h" /// \note: this might cause multithreading problems. static void ensureDisassemblersWereInitializedOnce() { static bool WereTheyInitialized = false; if (!WereTheyInitialized) { llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllDisassemblers(); WereTheyInitialized = true; } } using DI = LLVMDisassemblerInterface; DI::LLVMDisassemblerInterface(MetaAddressType::Values AddrType, const model::DisassemblyConfiguration &Config) { ensureDisassemblersWereInitializedOnce(); auto LLVMArchitecture = MetaAddressType::arch(AddrType); revng_assert(LLVMArchitecture.has_value(), "Impossible to create a disassembler for a non-code section"); auto Architecture = llvm::Triple::getArchTypeName(*LLVMArchitecture); // Workaround for ARM if (*LLVMArchitecture == llvm::Triple::ArchType::arm) Architecture = "armv7"; std::string ErrorMessage; using Registry = llvm::TargetRegistry; auto *LLVMTarget = Registry::lookupTarget(Architecture.str(), ErrorMessage); revng_assert(LLVMTarget != nullptr, "Requested target is not available"); llvm::StringRef CPUDefinition; llvm::StringRef CPUFeatures = MetaAddressType::getLLVMCPUFeatures(AddrType); SubtargetInformation.reset(LLVMTarget->createMCSubtargetInfo(Architecture, CPUDefinition, CPUFeatures)); revng_assert(SubtargetInformation != nullptr, "Subtarget information object creation failed."); RegisterInformation.reset(LLVMTarget->createMCRegInfo(Architecture)); revng_assert(RegisterInformation != nullptr, "Register information object creation failed."); llvm::MCTargetOptions TargetOptions; AssemblyInformation.reset(LLVMTarget->createMCAsmInfo(*RegisterInformation, Architecture, TargetOptions)); revng_assert(AssemblyInformation != nullptr, "yield information object creation failed."); ObjectFileInformation = std::make_unique(); llvm::Triple Triple(Architecture); Context = std::make_unique(Triple, AssemblyInformation.get(), RegisterInformation.get(), SubtargetInformation.get()); bool IsPIC = false; ObjectFileInformation->initMCObjectFileInfo(*Context, IsPIC); auto &SI = *SubtargetInformation; Disassembler.reset(LLVMTarget->createMCDisassembler(SI, *Context)); revng_assert(Disassembler != nullptr, "Disassembler object creation failed."); InstructionInformation.reset(LLVMTarget->createMCInstrInfo()); unsigned AssemblyDialect = 0; if (*LLVMArchitecture == llvm::Triple::ArchType::x86 || *LLVMArchitecture == llvm::Triple::ArchType::x86_64) { if (not Config.UseX86ATTSyntax()) AssemblyDialect = 1; } Printer.reset(LLVMTarget->createMCInstPrinter(Triple, AssemblyDialect, *AssemblyInformation, *InstructionInformation, *RegisterInformation)); revng_assert(Printer != nullptr, "Printer object creation failed."); namespace Style = model::DisassemblyConfigurationImmediateStyle; Style::Values ImmediateStyle = Config.ImmediateStyle(); if (ImmediateStyle == Style::Invalid) { // TODO: introduce a better way to handle default configuration values. ImmediateStyle = Style::CHexadecimal; } if (ImmediateStyle == Style::Decimal) Printer->setPrintImmHex(false); else Printer->setPrintImmHex(true); if (ImmediateStyle == Style::CHexadecimal) Printer->setPrintHexStyle(llvm::HexStyle::C); else if (ImmediateStyle == Style::AsmHexadecimal) Printer->setPrintHexStyle(llvm::HexStyle::Asm); Printer->setPrintBranchImmAsAddress(false); Printer->setSymbolizeOperands(false); Printer->setUseMarkup(true); } std::pair, uint64_t> DI::disassemble(const MetaAddress &Address, llvm::ArrayRef RawBytes, const llvm::MCDisassembler &Disassembler) { llvm::MCInst Result; llvm::raw_null_ostream NullStream; uint64_t LocalSize = 0; auto ResultCode = Disassembler.getInstruction(Result, LocalSize, RawBytes, Address.asPC(), NullStream); if (LocalSize == 0) return { std::nullopt, 0 }; switch (ResultCode) { case llvm::MCDisassembler::Success: return std::pair{ std::move(Result), LocalSize }; case llvm::MCDisassembler::SoftFail: return std::pair{ std::nullopt, LocalSize }; case llvm::MCDisassembler::Fail: return std::pair{ std::nullopt, 0 }; default: revng_abort("Unsupported MCDisassembler::getInstruction result."); } } static yield::TagType::Values parseMarkupTag(llvm::StringRef Input) { if (Input == "imm") return yield::TagType::Immediate; else if (Input == "mem") return yield::TagType::Memory; else if (Input == "reg") return yield::TagType::Register; else if (Input == "addr") return yield::TagType::Address; else if (Input == "pcrel") return yield::TagType::PCRelativeAddress; else if (Input == "absolute") return yield::TagType::AbsoluteAddress; else revng_abort(("Unknown llvm markup tag: `" + Input.str() + '`').c_str()); } /// Counts the number of consecutive characters satisfying \p Lambda predicate /// in the \p String starting from \p StartFrom and going forwards. template size_t getConsecutiveCount(llvm::StringRef String, LambdaType Lambda, size_t StartFrom = 0) { for (size_t Index = StartFrom; Index < String.size(); ++Index) if (!Lambda(String[Index])) return Index - StartFrom; return String.size() - StartFrom; } /// Counts the number of consecutive characters satisfying \p Lambda predicate /// in the \p String starting from \p StartFrom and going backwards. template size_t getBackwardsConsecutiveCount(llvm::StringRef String, LambdaType Lambda, size_t StartFrom) { for (size_t Index = StartFrom - 1; Index != size_t(-1); --Index) if (!Lambda(String[Index])) return StartFrom - Index - 1; return StartFrom; } static DI::Disassembled makeInvalidInstruction(MetaAddress Where, size_t Size, std::string Reason) { DI::Disassembled Result; Result.Address = Where; Result.Size = Size; Result.Text = "(invalid)"; Result.Tags.emplace_back(yield::TagType::Mnemonic, 0, 9); Result.Comment = std::to_string(Size) + " bytes"; Result.Error = std::move(Reason); return Result; } static llvm::StringRef cleanStringUp(std::string &Input) { Input = llvm::StringRef(Input).trim().str(); size_t Position = Input.find_first_of('\t'); while (Position != std::string::npos) { Input.replace(Position, 1, " "); Position = Input.find_first_of('\t', Position); } return Input; } // TODO: this is but a temporary measure. LLVM MCInstPrinter needs to be // patched. constexpr std::array CommonlyMisdetectedMnemonics = { "mov", "mvn", "or", "push", "pop", "cmp", "cmn", "asr", "lsl", "lsr", "ror", "rrx", "mul", "neg", "sbfiz", "sbfx", "sxtb", "sxth", "sxtw", "cset", "cinc", "tst", "nop", "b.", "b" }; struct DetectedMnemonic { size_t Position = llvm::StringRef::npos; size_t FullPosition = llvm::StringRef::npos; size_t Size = 0; size_t PrefixSize = 0; size_t SuffixSize = 0; size_t FullSize = 0; }; static std::optional tryDetectMnemonic(llvm::StringRef Text, llvm::StringRef Mnemonic) { if (Mnemonic.empty()) return std::nullopt; // Workaround for improper mnemonics being returned by the printer. // This explicitly limits them to only contain letters and numbers. auto AlphaNumCheck = [](char C) { return std::isalnum(C) || C == '.'; }; size_t AlphaNumCount = getConsecutiveCount(Mnemonic, AlphaNumCheck); if (AlphaNumCount < Mnemonic.size()) Mnemonic = Mnemonic.take_front(AlphaNumCount); if (Mnemonic.empty()) return std::nullopt; DetectedMnemonic Result; Result.Position = Text.find(Mnemonic); bool WasMnemonicDetected = Result.Position != llvm::StringRef::npos; if (WasMnemonicDetected == false) { // Try to find one of the commonly misdetected mnemonics. // TODO: patch llvm's printers so that we no longer need such ugly solutions for (const auto &CommonMnemonic : CommonlyMisdetectedMnemonics) { Result.Position = Text.find(CommonMnemonic); if ((WasMnemonicDetected = (Result.Position != llvm::StringRef::npos))) { Mnemonic = CommonMnemonic; break; } } if (WasMnemonicDetected == false) return std::nullopt; } Result.Size = Mnemonic.size(); Result.PrefixSize = getBackwardsConsecutiveCount(Text, AlphaNumCheck, Result.Position); Result.SuffixSize = getConsecutiveCount(Text, AlphaNumCheck, Result.Position + Result.Size); revng_assert(Result.Position >= Result.PrefixSize); Result.FullPosition = Result.Position - Result.PrefixSize; Result.FullSize = Result.Size + Result.PrefixSize + Result.SuffixSize; return Result; } DI::Disassembled DI::parse(const llvm::MCInst &Instruction, const MetaAddress &Address, llvm::MCInstPrinter &Printer, const llvm::MCSubtargetInfo &SI) { DI::Disassembled Result; Result.Address = Address; // Save the opcode for future use. if (auto Opcode = Printer.getOpcodeName(Instruction.getOpcode()); !Opcode.empty()) Result.OpcodeIdentifier = Opcode.str(); std::string MarkupStorage; llvm::raw_string_ostream MarkupStream(MarkupStorage); Printer.printInst(&Instruction, 0, "", SI, MarkupStream); if (MarkupStorage.empty()) { Result.Error = "Printer failed."; return Result; } llvm::StringRef Markup = cleanStringUp(MarkupStorage); auto Mnemonic = tryDetectMnemonic(Markup, Printer.getMnemonic(&Instruction).first); if (!Mnemonic.has_value()) Result.Error = "Impossible to detect mnemonic."; auto WhitespaceCheck = [](char C) { constexpr llvm::StringRef Whitespaces = " \t\n\v\f\r"; return Whitespaces.contains(C); }; // Investigate the llvm-provided tags. constexpr llvm::StringRef TagBoundaries = "<>"; llvm::SmallVector OpenTagStack; for (size_t Position = 0; Position < Markup.size(); ++Position) { // Mark the whitespaces so that the client can easily remove them if needed. size_t WhitespaceCount = getConsecutiveCount(Markup, WhitespaceCheck, Position); if (WhitespaceCount != 0) { Result.Tags.emplace_back(yield::TagType::Whitespace, Result.Text.size(), Result.Text.size() + WhitespaceCount); Result.Text += Markup.substr(Position, WhitespaceCount); Position += WhitespaceCount - 1; continue; } if (Markup[Position] == '<') { // Opens a new markup tag. auto TagEndPosition = Markup.find(':', Position + 1); llvm::StringRef Tag = Markup.slice(Position + 1, TagEndPosition); yield::TagType::Values TagType = parseMarkupTag(Tag); OpenTagStack.emplace_back(TagType, Result.Text.size(), 0); Position = TagEndPosition; } else if (Markup[Position] == '>') { // Closes the current markup tag revng_assert(not OpenTagStack.empty()); yield::Instruction::RawTag CurrentTag = OpenTagStack.back(); CurrentTag.To = Result.Text.size(); OpenTagStack.pop_back(); Result.Tags.emplace_back(CurrentTag); } else if (Mnemonic.has_value() && Position == Mnemonic->FullPosition) { // Mnemonic if (!OpenTagStack.empty()) { Result.Error = "Mnemonic could not be detected correctly"; Result.Text += Markup[Position]; continue; } size_t MnemonicFullStart = Result.Text.size(); size_t MnemonicPrefixEnd = MnemonicFullStart + Mnemonic->PrefixSize; size_t MnemonicSuffixStart = MnemonicPrefixEnd + Mnemonic->Size; size_t MnemonicFullEnd = MnemonicSuffixStart + Mnemonic->SuffixSize; Result.Tags.emplace_back(yield::TagType::Mnemonic, Result.Text.size(), MnemonicFullEnd); if (Mnemonic->PrefixSize != 0) Result.Tags.emplace_back(yield::TagType::MnemonicPrefix, Result.Text.size(), MnemonicPrefixEnd); if (Mnemonic->SuffixSize != 0) Result.Tags.emplace_back(yield::TagType::MnemonicSuffix, MnemonicSuffixStart, MnemonicFullEnd); Result.Text += Markup.substr(Mnemonic->FullPosition, Mnemonic->FullSize); Position += Mnemonic->FullSize - 1; } else { // Nothing special, just a character. Result.Text += Markup[Position]; } } if (!OpenTagStack.empty()) Result.Error = "A tag doesn't have a closing bracket."; return Result; } DI::Disassembled DI::instruction(const MetaAddress &Where, llvm::ArrayRef RawBytes) { revng_assert(Where.isValid() && !RawBytes.empty()); auto &&[Instruction, Size] = disassemble(Where, RawBytes, *Disassembler); if (Instruction.has_value()) { revng_assert(Size != 0); auto Result = parse(*Instruction, Where, *Printer, *SubtargetInformation); Result.Size = Size; const auto &Info = InstructionInformation->get(Instruction->getOpcode()); Result.HasDelaySlot = Info.hasDelaySlot(); return Result; } else { if (Size == 0) Size = RawBytes.size(); return makeInvalidInstruction(Where, Size, "MCDisassembler failed"); } }