[JITLink][i386] Adds absolute and pc relative relocation support for ELF/i386.
This commit adds support for 32 bit absolute and pc relative relocations in ELF/i386 objects, along with simple regression tests. Reviewed By: sgraenitz, lhames Differential Revision: https://reviews.llvm.org/D135523
This commit is contained in:
@@ -14,25 +14,155 @@
|
||||
#define LLVM_EXECUTIONENGINE_JITLINK_I386_H
|
||||
|
||||
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
|
||||
#include "llvm/ExecutionEngine/JITLink/TableManager.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace jitlink {
|
||||
namespace i386 {
|
||||
|
||||
namespace llvm::jitlink::i386 {
|
||||
/// Represets i386 fixups
|
||||
enum EdgeKind_i386 : Edge::Kind {
|
||||
|
||||
/// None
|
||||
None = Edge::FirstRelocation,
|
||||
|
||||
/// A plain 32-bit pointer value relocation.
|
||||
///
|
||||
/// Fixup expression:
|
||||
/// Fixup <- Target + Addend : uint32
|
||||
///
|
||||
/// Errors:
|
||||
/// - The target must reside in the low 32-bits of the address space,
|
||||
/// otherwise an out-of-range error will be returned.
|
||||
///
|
||||
Pointer32,
|
||||
|
||||
/// A 32-bit PC-relative relocation.
|
||||
///
|
||||
/// Represents a data/control flow instruction using PC-relative addressing
|
||||
/// to a target.
|
||||
///
|
||||
/// The fixup expression for this kind includes an implicit offset to account
|
||||
/// for the PC (unlike the Delta edges) so that a PCRel32 with a target
|
||||
/// T and addend zero is a call/branch to the start (offset zero) of T.
|
||||
///
|
||||
/// Fixup expression:
|
||||
/// Fixup <- Target - (Fixup + 4) + Addend : int32
|
||||
///
|
||||
/// Errors:
|
||||
/// - The result of the fixup expression must fit into an int32, otherwise
|
||||
/// an out-of-range error will be returned.
|
||||
///
|
||||
PCRel32,
|
||||
|
||||
/// A plain 16-bit pointer value relocation.
|
||||
///
|
||||
/// Fixup expression:
|
||||
/// Fixup <- Target + Addend : uint16
|
||||
///
|
||||
/// Errors:
|
||||
/// - The target must reside in the low 16-bits of the address space,
|
||||
/// otherwise an out-of-range error will be returned.
|
||||
///
|
||||
Pointer16,
|
||||
|
||||
/// A 16-bit PC-relative relocation.
|
||||
///
|
||||
/// Represents a data/control flow instruction using PC-relative addressing
|
||||
/// to a target.
|
||||
///
|
||||
/// The fixup expression for this kind includes an implicit offset to account
|
||||
/// for the PC (unlike the Delta edges) so that a PCRel16 with a target
|
||||
/// T and addend zero is a call/branch to the start (offset zero) of T.
|
||||
///
|
||||
/// Fixup expression:
|
||||
/// Fixup <- Target - (Fixup + 4) + Addend : int16
|
||||
///
|
||||
/// Errors:
|
||||
/// - The result of the fixup expression must fit into an int16, otherwise
|
||||
/// an out-of-range error will be returned.
|
||||
///
|
||||
PCRel16,
|
||||
|
||||
/// A 64-bit GOT delta.
|
||||
///
|
||||
/// Delta from the global offset table to the target.
|
||||
///
|
||||
/// Fixup expression:
|
||||
/// Fixup <- Target - GOTSymbol + Addend : int32
|
||||
///
|
||||
/// Errors:
|
||||
/// - *ASSERTION* Failure to a null pointer GOTSymbol, which the GOT section
|
||||
/// symbol was not been defined.
|
||||
Delta32FromGOT,
|
||||
};
|
||||
|
||||
/// Returns a string name for the given i386 edge. For debugging purposes
|
||||
/// only
|
||||
const char *getEdgeKindName(Edge::Kind K);
|
||||
|
||||
} // namespace i386
|
||||
} // namespace jitlink
|
||||
} // namespace llvm
|
||||
/// Returns true if the given uint32_t value is in range for a uint16_t.
|
||||
inline bool isInRangeForImmU16(uint32_t Value) {
|
||||
return Value <= std::numeric_limits<uint16_t>::max();
|
||||
}
|
||||
|
||||
/// Returns true if the given int32_t value is in range for an int16_t.
|
||||
inline bool isInRangeForImmS16(int32_t Value) {
|
||||
return (Value >= std::numeric_limits<int16_t>::min() &&
|
||||
Value <= std::numeric_limits<int16_t>::max());
|
||||
}
|
||||
|
||||
/// Apply fixup expression for edge to block content.
|
||||
inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
|
||||
using namespace i386;
|
||||
using namespace llvm::support;
|
||||
|
||||
char *BlockWorkingMem = B.getAlreadyMutableContent().data();
|
||||
char *FixupPtr = BlockWorkingMem + E.getOffset();
|
||||
auto FixupAddress = B.getAddress() + E.getOffset();
|
||||
|
||||
switch (E.getKind()) {
|
||||
case i386::None: {
|
||||
break;
|
||||
}
|
||||
|
||||
case i386::Pointer32: {
|
||||
uint32_t Value = E.getTarget().getAddress().getValue() + E.getAddend();
|
||||
*(ulittle32_t *)FixupPtr = Value;
|
||||
break;
|
||||
}
|
||||
|
||||
case i386::PCRel32: {
|
||||
int32_t Value =
|
||||
E.getTarget().getAddress() - (FixupAddress + 4) + E.getAddend();
|
||||
*(little32_t *)FixupPtr = Value;
|
||||
break;
|
||||
}
|
||||
|
||||
case i386::Pointer16: {
|
||||
uint32_t Value = E.getTarget().getAddress().getValue() + E.getAddend();
|
||||
if (LLVM_LIKELY(isInRangeForImmU16(Value)))
|
||||
*(ulittle16_t *)FixupPtr = Value;
|
||||
else
|
||||
return makeTargetOutOfRangeError(G, B, E);
|
||||
break;
|
||||
}
|
||||
|
||||
case i386::PCRel16: {
|
||||
int32_t Value =
|
||||
E.getTarget().getAddress() - (FixupAddress + 4) + E.getAddend();
|
||||
if (LLVM_LIKELY(isInRangeForImmS16(Value)))
|
||||
*(little16_t *)FixupPtr = Value;
|
||||
else
|
||||
return makeTargetOutOfRangeError(G, B, E);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return make_error<JITLinkError>(
|
||||
"In graph " + G.getName() + ", section " + B.getSection().getName() +
|
||||
"unsupported edge kind" + getEdgeKindName(E.getKind()));
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
} // namespace llvm::jitlink::i386
|
||||
|
||||
#endif // LLVM_EXECUTIONENGINE_JITLINK_I386_H
|
||||
|
||||
@@ -108,24 +108,49 @@ protected:
|
||||
Error graphifySections();
|
||||
Error graphifySymbols();
|
||||
|
||||
/// Traverse all matching relocation records in the given section. The handler
|
||||
/// function Func should be callable with this signature:
|
||||
/// Traverse all matching ELFT::Rela relocation records in the given section.
|
||||
/// The handler function Func should be callable with this signature:
|
||||
/// Error(const typename ELFT::Rela &,
|
||||
/// const typename ELFT::Shdr &, Section &)
|
||||
///
|
||||
template <typename RelocHandlerFunction>
|
||||
Error forEachRelocation(const typename ELFT::Shdr &RelSect,
|
||||
RelocHandlerFunction &&Func,
|
||||
bool ProcessDebugSections = false);
|
||||
template <typename RelocHandlerMethod>
|
||||
Error forEachRelaRelocation(const typename ELFT::Shdr &RelSect,
|
||||
RelocHandlerMethod &&Func,
|
||||
bool ProcessDebugSections = false);
|
||||
|
||||
/// Traverse all matching relocation records in the given section. Convenience
|
||||
/// wrapper to allow passing a member function for the handler.
|
||||
/// Traverse all matching ELFT::Rel relocation records in the given section.
|
||||
/// The handler function Func should be callable with this signature:
|
||||
/// Error(const typename ELFT::Rel &,
|
||||
/// const typename ELFT::Shdr &, Section &)
|
||||
///
|
||||
template <typename RelocHandlerMethod>
|
||||
Error forEachRelRelocation(const typename ELFT::Shdr &RelSect,
|
||||
RelocHandlerMethod &&Func,
|
||||
bool ProcessDebugSections = false);
|
||||
|
||||
/// Traverse all matching rela relocation records in the given section.
|
||||
/// Convenience wrapper to allow passing a member function for the handler.
|
||||
///
|
||||
template <typename ClassT, typename RelocHandlerMethod>
|
||||
Error forEachRelocation(const typename ELFT::Shdr &RelSect, ClassT *Instance,
|
||||
RelocHandlerMethod &&Method,
|
||||
bool ProcessDebugSections = false) {
|
||||
return forEachRelocation(
|
||||
Error forEachRelaRelocation(const typename ELFT::Shdr &RelSect,
|
||||
ClassT *Instance, RelocHandlerMethod &&Method,
|
||||
bool ProcessDebugSections = false) {
|
||||
return forEachRelaRelocation(
|
||||
RelSect,
|
||||
[Instance, Method](const auto &Rel, const auto &Target, auto &GS) {
|
||||
return (Instance->*Method)(Rel, Target, GS);
|
||||
},
|
||||
ProcessDebugSections);
|
||||
}
|
||||
|
||||
/// Traverse all matching rel relocation records in the given section.
|
||||
/// Convenience wrapper to allow passing a member function for the handler.
|
||||
///
|
||||
template <typename ClassT, typename RelocHandlerMethod>
|
||||
Error forEachRelRelocation(const typename ELFT::Shdr &RelSect,
|
||||
ClassT *Instance, RelocHandlerMethod &&Method,
|
||||
bool ProcessDebugSections = false) {
|
||||
return forEachRelRelocation(
|
||||
RelSect,
|
||||
[Instance, Method](const auto &Rel, const auto &Target, auto &GS) {
|
||||
return (Instance->*Method)(Rel, Target, GS);
|
||||
@@ -487,13 +512,12 @@ template <typename ELFT> Error ELFLinkGraphBuilder<ELFT>::graphifySymbols() {
|
||||
}
|
||||
|
||||
template <typename ELFT>
|
||||
template <typename RelocHandlerFunction>
|
||||
Error ELFLinkGraphBuilder<ELFT>::forEachRelocation(
|
||||
const typename ELFT::Shdr &RelSect, RelocHandlerFunction &&Func,
|
||||
template <typename RelocHandlerMethod>
|
||||
Error ELFLinkGraphBuilder<ELFT>::forEachRelaRelocation(
|
||||
const typename ELFT::Shdr &RelSect, RelocHandlerMethod &&Func,
|
||||
bool ProcessDebugSections) {
|
||||
|
||||
// Only look into sections that store relocation entries.
|
||||
if (RelSect.sh_type != ELF::SHT_RELA && RelSect.sh_type != ELF::SHT_REL)
|
||||
if (RelSect.sh_type != ELF::SHT_RELA)
|
||||
return Error::success();
|
||||
|
||||
// sh_info contains the section header index of the target (FixupSection),
|
||||
@@ -534,6 +558,53 @@ Error ELFLinkGraphBuilder<ELFT>::forEachRelocation(
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
template <typename ELFT>
|
||||
template <typename RelocHandlerMethod>
|
||||
Error ELFLinkGraphBuilder<ELFT>::forEachRelRelocation(
|
||||
const typename ELFT::Shdr &RelSect, RelocHandlerMethod &&Func,
|
||||
bool ProcessDebugSections) {
|
||||
// Only look into sections that store relocation entries.
|
||||
if (RelSect.sh_type != ELF::SHT_REL)
|
||||
return Error::success();
|
||||
|
||||
// sh_info contains the section header index of the target (FixupSection),
|
||||
// which is the section to which all relocations in RelSect apply.
|
||||
auto FixupSection = Obj.getSection(RelSect.sh_info);
|
||||
if (!FixupSection)
|
||||
return FixupSection.takeError();
|
||||
|
||||
// Target sections have names in valid ELF object files.
|
||||
Expected<StringRef> Name = Obj.getSectionName(**FixupSection);
|
||||
if (!Name)
|
||||
return Name.takeError();
|
||||
LLVM_DEBUG(dbgs() << " " << *Name << ":\n");
|
||||
|
||||
// Consider skipping these relocations.
|
||||
if (!ProcessDebugSections && isDwarfSection(*Name)) {
|
||||
LLVM_DEBUG(dbgs() << " skipped (dwarf section)\n\n");
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
// Lookup the link-graph node corresponding to the target section name.
|
||||
auto *BlockToFix = getGraphBlock(RelSect.sh_info);
|
||||
if (!BlockToFix)
|
||||
return make_error<StringError>(
|
||||
"Refencing a section that wasn't added to the graph: " + *Name,
|
||||
inconvertibleErrorCode());
|
||||
|
||||
auto RelEntries = Obj.rels(RelSect);
|
||||
if (!RelEntries)
|
||||
return RelEntries.takeError();
|
||||
|
||||
// Let the callee process relocation entries one by one.
|
||||
for (const typename ELFT::Rel &R : *RelEntries)
|
||||
if (Error Err = Func(R, **FixupSection, *BlockToFix))
|
||||
return Err;
|
||||
|
||||
LLVM_DEBUG(dbgs() << "\n");
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
} // end namespace jitlink
|
||||
} // end namespace llvm
|
||||
|
||||
|
||||
@@ -129,8 +129,8 @@ private:
|
||||
using Base = ELFLinkGraphBuilder<ELFT>;
|
||||
using Self = ELFLinkGraphBuilder_aarch64<ELFT>;
|
||||
for (const auto &RelSect : Base::Sections)
|
||||
if (Error Err = Base::forEachRelocation(RelSect, this,
|
||||
&Self::addSingleRelocation))
|
||||
if (Error Err = Base::forEachRelaRelocation(RelSect, this,
|
||||
&Self::addSingleRelocation))
|
||||
return Err;
|
||||
|
||||
return Error::success();
|
||||
|
||||
@@ -22,8 +22,7 @@
|
||||
using namespace llvm;
|
||||
using namespace llvm::jitlink;
|
||||
|
||||
namespace llvm {
|
||||
namespace jitlink {
|
||||
namespace llvm::jitlink {
|
||||
|
||||
class ELFJITLinker_i386 : public JITLinker<ELFJITLinker_i386> {
|
||||
friend class JITLinker<ELFJITLinker_i386>;
|
||||
@@ -35,15 +34,7 @@ public:
|
||||
|
||||
private:
|
||||
Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
|
||||
using namespace i386;
|
||||
using namespace llvm::support;
|
||||
|
||||
switch (E.getKind()) {
|
||||
case i386::None: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Error::success();
|
||||
return i386::applyFixup(G, B, E);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -55,6 +46,16 @@ private:
|
||||
switch (Type) {
|
||||
case ELF::R_386_NONE:
|
||||
return EdgeKind_i386::None;
|
||||
case ELF::R_386_32:
|
||||
return EdgeKind_i386::Pointer32;
|
||||
case ELF::R_386_PC32:
|
||||
return EdgeKind_i386::PCRel32;
|
||||
case ELF::R_386_16:
|
||||
return EdgeKind_i386::Pointer16;
|
||||
case ELF::R_386_PC16:
|
||||
return EdgeKind_i386::PCRel16;
|
||||
case ELF::R_386_GOTPC:
|
||||
return EdgeKind_i386::Delta32FromGOT;
|
||||
}
|
||||
|
||||
return make_error<JITLinkError>("Unsupported i386 relocation:" +
|
||||
@@ -63,6 +64,64 @@ private:
|
||||
|
||||
Error addRelocations() override {
|
||||
LLVM_DEBUG(dbgs() << "Adding relocations\n");
|
||||
using Base = ELFLinkGraphBuilder<ELFT>;
|
||||
using Self = ELFLinkGraphBuilder_i386;
|
||||
|
||||
for (const auto &RelSect : Base::Sections) {
|
||||
// Validate the section to read relocation entries from.
|
||||
if (RelSect.sh_type == ELF::SHT_RELA)
|
||||
return make_error<StringError>(
|
||||
"No SHT_RELA in valid i386 ELF object files",
|
||||
inconvertibleErrorCode());
|
||||
|
||||
if (Error Err = Base::forEachRelRelocation(RelSect, this,
|
||||
&Self::addSingleRelocation))
|
||||
return Err;
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error addSingleRelocation(const typename ELFT::Rel &Rel,
|
||||
const typename ELFT::Shdr &FixupSection,
|
||||
Block &BlockToFix) {
|
||||
using Base = ELFLinkGraphBuilder<ELFT>;
|
||||
|
||||
uint32_t SymbolIndex = Rel.getSymbol(false);
|
||||
auto ObjSymbol = Base::Obj.getRelocationSymbol(Rel, Base::SymTabSec);
|
||||
if (!ObjSymbol)
|
||||
return ObjSymbol.takeError();
|
||||
|
||||
Symbol *GraphSymbol = Base::getGraphSymbol(SymbolIndex);
|
||||
if (!GraphSymbol)
|
||||
return make_error<StringError>(
|
||||
formatv("Could not find symbol at given index, did you add it to "
|
||||
"JITSymbolTable? index: {0}, shndx: {1} Size of table: {2}",
|
||||
SymbolIndex, (*ObjSymbol)->st_shndx,
|
||||
Base::GraphSymbols.size()),
|
||||
inconvertibleErrorCode());
|
||||
|
||||
Expected<i386::EdgeKind_i386> Kind = getRelocationKind(Rel.getType(false));
|
||||
if (!Kind)
|
||||
return Kind.takeError();
|
||||
|
||||
// TODO: To be removed when GOT relative relocations are supported.
|
||||
if (*Kind == i386::EdgeKind_i386::Delta32FromGOT)
|
||||
return Error::success();
|
||||
|
||||
int64_t Addend = 0;
|
||||
|
||||
auto FixupAddress = orc::ExecutorAddr(FixupSection.sh_addr) + Rel.r_offset;
|
||||
Edge::OffsetT Offset = FixupAddress - BlockToFix.getAddress();
|
||||
Edge GE(*Kind, Offset, *GraphSymbol, Addend);
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " ";
|
||||
printEdge(dbgs(), BlockToFix, GE, i386::getEdgeKindName(*Kind));
|
||||
dbgs() << "\n";
|
||||
});
|
||||
|
||||
BlockToFix.addEdge(std::move(GE));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
@@ -110,5 +169,4 @@ void link_ELF_i386(std::unique_ptr<LinkGraph> G,
|
||||
ELFJITLinker_i386::link(std::move(Ctx), std::move(G), std::move(Config));
|
||||
}
|
||||
|
||||
} // namespace jitlink
|
||||
} // namespace llvm
|
||||
} // namespace llvm::jitlink
|
||||
|
||||
@@ -482,8 +482,8 @@ private:
|
||||
using Base = ELFLinkGraphBuilder<ELFT>;
|
||||
using Self = ELFLinkGraphBuilder_riscv<ELFT>;
|
||||
for (const auto &RelSect : Base::Sections)
|
||||
if (Error Err = Base::forEachRelocation(RelSect, this,
|
||||
&Self::addSingleRelocation))
|
||||
if (Error Err = Base::forEachRelaRelocation(RelSect, this,
|
||||
&Self::addSingleRelocation))
|
||||
return Err;
|
||||
|
||||
return Error::success();
|
||||
|
||||
@@ -166,8 +166,8 @@ private:
|
||||
"No SHT_REL in valid x64 ELF object files",
|
||||
inconvertibleErrorCode());
|
||||
|
||||
if (Error Err = Base::forEachRelocation(RelSect, this,
|
||||
&Self::addSingleRelocation))
|
||||
if (Error Err = Base::forEachRelaRelocation(RelSect, this,
|
||||
&Self::addSingleRelocation))
|
||||
return Err;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,17 +14,22 @@
|
||||
|
||||
#define DEBUG_TYPE "jitlink"
|
||||
|
||||
namespace llvm {
|
||||
namespace jitlink {
|
||||
namespace i386 {
|
||||
namespace llvm::jitlink::i386 {
|
||||
|
||||
const char *getEdgeKindName(Edge::Kind K) {
|
||||
switch (K) {
|
||||
case None:
|
||||
return "None";
|
||||
case Pointer32:
|
||||
return "Pointer32";
|
||||
case PCRel32:
|
||||
return "PCRel32";
|
||||
case Pointer16:
|
||||
return "Pointer16";
|
||||
case PCRel16:
|
||||
return "PCRel16";
|
||||
}
|
||||
return getGenericEdgeKindName(K);
|
||||
}
|
||||
} // namespace i386
|
||||
} // namespace jitlink
|
||||
} // namespace llvm
|
||||
|
||||
} // namespace llvm::jitlink::i386
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# RUN: llvm-mc -triple=i386-unknown-linux-gnu -filetype=obj -o %t.o %s
|
||||
# RUN: llvm-jitlink -noexec %t.o
|
||||
|
||||
.text
|
||||
.globl main
|
||||
.p2align 4
|
||||
.type main,@function
|
||||
main:
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
pushl %eax
|
||||
movl $0, -4(%ebp)
|
||||
movl a, %eax
|
||||
addl $4, %esp
|
||||
popl %ebp
|
||||
retl
|
||||
|
||||
.size main, .-main
|
||||
|
||||
.data
|
||||
.p2align 2
|
||||
.type a,@object
|
||||
a:
|
||||
.long 42
|
||||
.size a, 4
|
||||
@@ -0,0 +1,31 @@
|
||||
# RUN: llvm-mc -triple=i386-unknown-linux-gnu -filetype=obj -o %t.o %s
|
||||
# RUN: llvm-jitlink -noexec %t.o
|
||||
|
||||
.text
|
||||
.section .text.main
|
||||
.globl main
|
||||
.p2align 4
|
||||
.type main,@function
|
||||
main:
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
subl $8, %esp
|
||||
movl $0, -4(%ebp)
|
||||
calll foo
|
||||
addl $8, %esp
|
||||
popl %ebp
|
||||
retl
|
||||
|
||||
.size main, .-main
|
||||
|
||||
.section .text.foo
|
||||
.p2align 4
|
||||
.type foo,@function
|
||||
foo:
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
movl $42, %eax
|
||||
popl %ebp
|
||||
retl
|
||||
|
||||
.size foo, .-foo
|
||||
@@ -1,2 +1,2 @@
|
||||
if not 'i386' in config.root.targets:
|
||||
config.unsupported = True
|
||||
if not 'X86' in config.root.targets:
|
||||
config.unsupported = True
|
||||
|
||||
Reference in New Issue
Block a user