Skip to content

Commit

Permalink
[jitlink/rtdydl][checker] Add TargetFlag dependent disassembler switc…
Browse files Browse the repository at this point in the history
…hing support

Some targets such as AArch32 make use of TargetFlags to indicate ISA mode. Depending
on the TargetFlag, MCDisassembler and similar target specific objects should be
reinitialized with the correct Target Triple. Backends with similar needs can
easily extend this implementation for their usecase.

The drivers llvm-rtdyld and llvm-jitlink have their SymbolInfo's extended to take
TargetFlag into account. RuntimeDyldChecker can now create necessary TargetInfo
to reinitialize MCDisassembler and MCInstPrinter. The required triple is obtained
from the new getTripleFromTargetFlag function by checking the TargetFlag.

In addition, breaking changes for RuntimeDyld COFF Thumb tests are fixed by making
the backend emit a TargetFlag.

Reviewed By: lhames, sgraenitz

Differential Revision: https://reviews.llvm.org/D158280
  • Loading branch information
eymay authored and weliveindetail committed Sep 8, 2023
1 parent b0ea279 commit 4b17c81
Show file tree
Hide file tree
Showing 15 changed files with 253 additions and 63 deletions.
6 changes: 2 additions & 4 deletions llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h
Original file line number Diff line number Diff line change
Expand Up @@ -623,10 +623,8 @@ class Symbol {
this->S = static_cast<uint8_t>(S);
}

/// Check whether the given target flags are set for this Symbol.
bool hasTargetFlags(TargetFlagsType Flags) const {
return static_cast<TargetFlagsType>(TargetFlags) & Flags;
}
/// Get the target flags of this Symbol.
TargetFlagsType getTargetFlags() const { return TargetFlags; }

/// Set the target flags for this Symbol.
void setTargetFlags(TargetFlagsType Flags) {
Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ namespace llvm {
namespace jitlink {
namespace aarch32 {

/// Check whether the given target flags are set for this Symbol.
bool hasTargetFlags(Symbol &Sym, TargetFlagsType Flags);

/// JITLink-internal AArch32 fixup kinds
enum EdgeKind_aarch32 : Edge::Kind {

Expand Down
27 changes: 21 additions & 6 deletions llvm/include/llvm/ExecutionEngine/RuntimeDyldChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ExecutionEngine/JITSymbol.h"
#include "llvm/Support/Endian.h"
#include "llvm/TargetParser/SubtargetFeature.h"
#include "llvm/TargetParser/Triple.h"
#include <optional>

#include <cstdint>
Expand All @@ -29,6 +31,9 @@ class RuntimeDyld;
class RuntimeDyldCheckerImpl;
class raw_ostream;

/// Holds target-specific properties for a symbol.
using TargetFlagsType = uint8_t;

/// RuntimeDyld invariant checker for verifying that RuntimeDyld has
/// correctly applied relocations.
///
Expand Down Expand Up @@ -78,10 +83,11 @@ class RuntimeDyldChecker {
public:
MemoryRegionInfo() = default;

/// Constructor for symbols/sections with content.
MemoryRegionInfo(ArrayRef<char> Content, JITTargetAddress TargetAddress)
/// Constructor for symbols/sections with content and TargetFlag.
MemoryRegionInfo(ArrayRef<char> Content, JITTargetAddress TargetAddress,
TargetFlagsType TargetFlags)
: ContentPtr(Content.data()), Size(Content.size()),
TargetAddress(TargetAddress) {}
TargetAddress(TargetAddress), TargetFlags(TargetFlags) {}

/// Constructor for zero-fill symbols/sections.
MemoryRegionInfo(uint64_t Size, JITTargetAddress TargetAddress)
Expand Down Expand Up @@ -127,10 +133,20 @@ class RuntimeDyldChecker {
/// Return the target address for this region.
JITTargetAddress getTargetAddress() const { return TargetAddress; }

/// Get the target flags for this Symbol.
TargetFlagsType getTargetFlags() const { return TargetFlags; }

/// Set the target flags for this Symbol.
void setTargetFlags(TargetFlagsType Flags) {
assert(Flags <= 1 && "Add more bits to store more than one flag");
TargetFlags = Flags;
}

private:
const char *ContentPtr = nullptr;
uint64_t Size = 0;
JITTargetAddress TargetAddress = 0;
TargetFlagsType TargetFlags = 0;
};

using IsSymbolValidFunction = std::function<bool(StringRef Symbol)>;
Expand All @@ -148,9 +164,8 @@ class RuntimeDyldChecker {
GetSectionInfoFunction GetSectionInfo,
GetStubInfoFunction GetStubInfo,
GetGOTInfoFunction GetGOTInfo,
support::endianness Endianness,
MCDisassembler *Disassembler, MCInstPrinter *InstPrinter,
raw_ostream &ErrStream);
support::endianness Endianness, Triple TT,
SubtargetFeatures TF, raw_ostream &ErrStream);
~RuntimeDyldChecker();

/// Check a single expression against the attached RuntimeDyld
Expand Down
13 changes: 9 additions & 4 deletions llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ namespace llvm {
namespace jitlink {
namespace aarch32 {

/// Check whether the given target flags are set for this Symbol.
bool hasTargetFlags(Symbol &Sym, TargetFlagsType Flags) {
return static_cast<TargetFlagsType>(Sym.getTargetFlags()) & Flags;
}

/// Encode 22-bit immediate value for branch instructions without J1J2 range
/// extension (formats B T4, BL T1 and BLX T2).
///
Expand Down Expand Up @@ -287,7 +292,7 @@ Error applyFixupData(LinkGraph &G, Block &B, const Edge &E) {
int64_t Addend = E.getAddend();
Symbol &TargetSymbol = E.getTarget();
uint64_t TargetAddress = TargetSymbol.getAddress().getValue();
assert(!TargetSymbol.hasTargetFlags(ThumbSymbol));
assert(!hasTargetFlags(TargetSymbol, ThumbSymbol));

// Regular data relocations have size 4, alignment 1 and write the full 32-bit
// result to the place; no need for overflow checking. There are three
Expand Down Expand Up @@ -341,14 +346,14 @@ Error applyFixupThumb(LinkGraph &G, Block &B, const Edge &E,
int64_t Addend = E.getAddend();
Symbol &TargetSymbol = E.getTarget();
uint64_t TargetAddress = TargetSymbol.getAddress().getValue();
if (TargetSymbol.hasTargetFlags(ThumbSymbol))
if (hasTargetFlags(TargetSymbol, ThumbSymbol))
TargetAddress |= 0x01;

switch (Kind) {
case Thumb_Jump24: {
if (!checkOpcode<Thumb_Jump24>(R))
return makeUnexpectedOpcodeError(G, R, Kind);
if (!(TargetSymbol.hasTargetFlags(ThumbSymbol)))
if (!hasTargetFlags(TargetSymbol, ThumbSymbol))
return make_error<JITLinkError>("Branch relocation needs interworking "
"stub when bridging to ARM: " +
StringRef(G.getEdgeKindName(Kind)));
Expand All @@ -375,7 +380,7 @@ Error applyFixupThumb(LinkGraph &G, Block &B, const Edge &E,

// The call instruction itself is Thumb. The call destination can either be
// Thumb or Arm. We use BL to stay in Thumb and BLX to change to Arm.
bool TargetIsArm = !TargetSymbol.hasTargetFlags(ThumbSymbol);
bool TargetIsArm = !hasTargetFlags(TargetSymbol, ThumbSymbol);
bool InstrIsBlx = (R.Lo & FixupInfo<Thumb_Call>::LoBitNoBlx) == 0;
if (TargetIsArm != InstrIsBlx) {
if (LLVM_LIKELY(TargetIsArm)) {
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/ExecutionEngine/Orc/ObjectLinkingLayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ ExecutorAddr getJITSymbolPtrForSymbol(Symbol &Sym, const Triple &TT) {
case Triple::armeb:
case Triple::thumb:
case Triple::thumbeb:
if (Sym.hasTargetFlags(aarch32::ThumbSymbol)) {
if (hasTargetFlags(Sym, aarch32::ThumbSymbol)) {
// Set LSB to indicate thumb target
assert(Sym.isCallable() && "Only callable symbols can have thumb flag");
assert((Sym.getAddress().getValue() & 0x01) == 0 && "LSB is clear");
Expand Down
162 changes: 147 additions & 15 deletions llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,16 @@
#include "RuntimeDyldCheckerImpl.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCDisassembler/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/MC/MCTargetOptions.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/MSVCErrorWorkarounds.h"
#include "llvm/Support/MemoryBuffer.h"
Expand All @@ -25,6 +32,19 @@

using namespace llvm;

namespace {
struct TargetInfo {
const Target *TheTarget;
std::unique_ptr<MCSubtargetInfo> STI;
std::unique_ptr<MCRegisterInfo> MRI;
std::unique_ptr<MCAsmInfo> MAI;
std::unique_ptr<MCContext> Ctx;
std::unique_ptr<MCDisassembler> Disassembler;
std::unique_ptr<MCInstrInfo> MII;
std::unique_ptr<MCInstPrinter> InstPrinter;
};
} // anonymous namespace

namespace llvm {

// Helper class that implements the language evaluated by RuntimeDyldChecker.
Expand Down Expand Up @@ -276,6 +296,20 @@ class RuntimeDyldCheckerExprEval {
"");

unsigned OpIdx = OpIdxExpr.getValue();

auto printInst = [this](StringRef Symbol, MCInst Inst,
raw_string_ostream &ErrMsgStream) {
auto TI = getTargetInfo(
Checker.getTripleFromTargetFlag(Checker.getTargetFlag(Symbol)));
if (auto E = TI.takeError()) {
errs() << "Error obtaining instruction printer: "
<< toString(std::move(E)) << "\n";
return std::make_pair(EvalResult(ErrMsgStream.str()), "");
}
Inst.dump_pretty(ErrMsgStream, TI->InstPrinter.get());
return std::make_pair(EvalResult(ErrMsgStream.str()), "");
};

if (OpIdx >= Inst.getNumOperands()) {
std::string ErrMsg;
raw_string_ostream ErrMsgStream(ErrMsg);
Expand All @@ -284,8 +318,8 @@ class RuntimeDyldCheckerExprEval {
<< "'. Instruction has only "
<< format("%i", Inst.getNumOperands())
<< " operands.\nInstruction is:\n ";
Inst.dump_pretty(ErrMsgStream, Checker.InstPrinter);
return std::make_pair(EvalResult(ErrMsgStream.str()), "");

return printInst(Symbol, Inst, ErrMsgStream);
}

const MCOperand &Op = Inst.getOperand(OpIdx);
Expand All @@ -294,9 +328,8 @@ class RuntimeDyldCheckerExprEval {
raw_string_ostream ErrMsgStream(ErrMsg);
ErrMsgStream << "Operand '" << format("%i", OpIdx) << "' of instruction '"
<< Symbol << "' is not an immediate.\nInstruction is:\n ";
Inst.dump_pretty(ErrMsgStream, Checker.InstPrinter);

return std::make_pair(EvalResult(ErrMsgStream.str()), "");
return printInst(Symbol, Inst, ErrMsgStream);
}

return std::make_pair(EvalResult(Op.getImm()), RemainingExpr);
Expand Down Expand Up @@ -687,31 +720,101 @@ class RuntimeDyldCheckerExprEval {

bool decodeInst(StringRef Symbol, MCInst &Inst, uint64_t &Size,
int64_t Offset) const {
MCDisassembler *Dis = Checker.Disassembler;
auto TI = getTargetInfo(
Checker.getTripleFromTargetFlag(Checker.getTargetFlag(Symbol)));

if (auto E = TI.takeError()) {
errs() << "Error obtaining disassembler: " << toString(std::move(E))
<< "\n";
return false;
}

StringRef SymbolMem = Checker.getSymbolContent(Symbol);
ArrayRef<uint8_t> SymbolBytes(SymbolMem.bytes_begin() + Offset,
SymbolMem.size() - Offset);

MCDisassembler::DecodeStatus S =
Dis->getInstruction(Inst, Size, SymbolBytes, 0, nulls());
TI->Disassembler->getInstruction(Inst, Size, SymbolBytes, 0, nulls());

return (S == MCDisassembler::Success);
}

Expected<TargetInfo>
getTargetInfo(const Triple &TT,
const SubtargetFeatures &TF = SubtargetFeatures()) const {

auto TripleName = TT.str();
std::string ErrorStr;
const Target *TheTarget =
TargetRegistry::lookupTarget(TripleName, ErrorStr);
if (!TheTarget)
return make_error<StringError>("Error accessing target '" + TripleName +
"': " + ErrorStr,
inconvertibleErrorCode());

std::unique_ptr<MCSubtargetInfo> STI(
TheTarget->createMCSubtargetInfo(TripleName, "", TF.getString()));
if (!STI)
return make_error<StringError>("Unable to create subtarget for " +
TripleName,
inconvertibleErrorCode());

std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
if (!MRI)
return make_error<StringError>("Unable to create target register info "
"for " +
TripleName,
inconvertibleErrorCode());

MCTargetOptions MCOptions;
std::unique_ptr<MCAsmInfo> MAI(
TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
if (!MAI)
return make_error<StringError>("Unable to create target asm info " +
TripleName,
inconvertibleErrorCode());

auto Ctx = std::make_unique<MCContext>(Triple(TripleName), MAI.get(),
MRI.get(), STI.get());

std::unique_ptr<MCDisassembler> Disassembler(
TheTarget->createMCDisassembler(*STI, *Ctx));
if (!Disassembler)
return make_error<StringError>("Unable to create disassembler for " +
TripleName,
inconvertibleErrorCode());

std::unique_ptr<MCInstrInfo> MII(TheTarget->createMCInstrInfo());
if (!MII)
return make_error<StringError>("Unable to create instruction info for" +
TripleName,
inconvertibleErrorCode());

std::unique_ptr<MCInstPrinter> InstPrinter(TheTarget->createMCInstPrinter(
Triple(TripleName), 0, *MAI, *MII, *MRI));
if (!InstPrinter)
return make_error<StringError>(
"Unable to create instruction printer for" + TripleName,
inconvertibleErrorCode());

return TargetInfo({TheTarget, std::move(STI), std::move(MRI),
std::move(MAI), std::move(Ctx), std::move(Disassembler),
std::move(MII), std::move(InstPrinter)});
}
};
} // namespace llvm

RuntimeDyldCheckerImpl::RuntimeDyldCheckerImpl(
IsSymbolValidFunction IsSymbolValid, GetSymbolInfoFunction GetSymbolInfo,
GetSectionInfoFunction GetSectionInfo, GetStubInfoFunction GetStubInfo,
GetGOTInfoFunction GetGOTInfo, support::endianness Endianness,
MCDisassembler *Disassembler, MCInstPrinter *InstPrinter,
raw_ostream &ErrStream)
GetGOTInfoFunction GetGOTInfo, support::endianness Endianness, Triple TT,
SubtargetFeatures TF, raw_ostream &ErrStream)
: IsSymbolValid(std::move(IsSymbolValid)),
GetSymbolInfo(std::move(GetSymbolInfo)),
GetSectionInfo(std::move(GetSectionInfo)),
GetStubInfo(std::move(GetStubInfo)), GetGOTInfo(std::move(GetGOTInfo)),
Endianness(Endianness), Disassembler(Disassembler),
InstPrinter(InstPrinter), ErrStream(ErrStream) {}
Endianness(Endianness), TT(std::move(TT)), TF(std::move(TF)),
ErrStream(ErrStream) {}

bool RuntimeDyldCheckerImpl::check(StringRef CheckExpr) const {
CheckExpr = CheckExpr.trim();
Expand Down Expand Up @@ -822,6 +925,36 @@ StringRef RuntimeDyldCheckerImpl::getSymbolContent(StringRef Symbol) const {
return {SymInfo->getContent().data(), SymInfo->getContent().size()};
}

TargetFlagsType RuntimeDyldCheckerImpl::getTargetFlag(StringRef Symbol) const {
auto SymInfo = GetSymbolInfo(Symbol);
if (!SymInfo) {
logAllUnhandledErrors(SymInfo.takeError(), errs(), "RTDyldChecker: ");
return TargetFlagsType{};
}
return SymInfo->getTargetFlags();
}

Triple
RuntimeDyldCheckerImpl::getTripleFromTargetFlag(TargetFlagsType Flag) const {
Triple TheTriple = TT;

switch (TT.getArch()) {
case Triple::ArchType::arm:
if (~Flag & 0x1)
return TT;
TheTriple.setArchName((Twine("thumb") + TT.getArchName().substr(3)).str());
return TheTriple;
case Triple::ArchType::thumb:
if (Flag & 0x1)
return TT;
TheTriple.setArchName((Twine("arm") + TT.getArchName().substr(5)).str());
return TheTriple;

default:
return TT;
}
}

std::pair<uint64_t, std::string> RuntimeDyldCheckerImpl::getSectionAddr(
StringRef FileName, StringRef SectionName, bool IsInsideLoad) const {

Expand Down Expand Up @@ -884,13 +1017,12 @@ std::pair<uint64_t, std::string> RuntimeDyldCheckerImpl::getStubOrGOTAddrFor(
RuntimeDyldChecker::RuntimeDyldChecker(
IsSymbolValidFunction IsSymbolValid, GetSymbolInfoFunction GetSymbolInfo,
GetSectionInfoFunction GetSectionInfo, GetStubInfoFunction GetStubInfo,
GetGOTInfoFunction GetGOTInfo, support::endianness Endianness,
MCDisassembler *Disassembler, MCInstPrinter *InstPrinter,
raw_ostream &ErrStream)
GetGOTInfoFunction GetGOTInfo, support::endianness Endianness, Triple TT,
SubtargetFeatures TF, raw_ostream &ErrStream)
: Impl(::std::make_unique<RuntimeDyldCheckerImpl>(
std::move(IsSymbolValid), std::move(GetSymbolInfo),
std::move(GetSectionInfo), std::move(GetStubInfo),
std::move(GetGOTInfo), Endianness, Disassembler, InstPrinter,
std::move(GetGOTInfo), Endianness, std::move(TT), std::move(TF),
ErrStream)) {}

RuntimeDyldChecker::~RuntimeDyldChecker() = default;
Expand Down

0 comments on commit 4b17c81

Please sign in to comment.