Files
revng-revng/lib/Yield/Assembly/LLVMDisassemblerInterface.cpp
2025-04-30 15:08:45 +02:00

408 lines
15 KiB
C++

/// \file LLVMDisassemblerInterface.cpp
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include <map>
#include <string>
#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::MCObjectFileInfo>();
llvm::Triple Triple(Architecture);
Context = std::make_unique<llvm::MCContext>(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<std::optional<llvm::MCInst>, uint64_t>
DI::disassemble(const MetaAddress &Address,
llvm::ArrayRef<uint8_t> 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<typename LambdaType>
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<typename LambdaType>
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<DetectedMnemonic>
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<yield::Instruction::RawTag, 8> 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<uint8_t> 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");
}
}