Skip to content

Commit 2a3b257

Browse files
jkshtjlhames
authored andcommitted
[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
1 parent 66fdfff commit 2a3b257

File tree

10 files changed

+371
-51
lines changed

10 files changed

+371
-51
lines changed

llvm/include/llvm/ExecutionEngine/JITLink/i386.h

Lines changed: 137 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,155 @@
1414
#define LLVM_EXECUTIONENGINE_JITLINK_I386_H
1515

1616
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
17+
#include "llvm/ExecutionEngine/JITLink/TableManager.h"
1718

18-
namespace llvm {
19-
namespace jitlink {
20-
namespace i386 {
21-
19+
namespace llvm::jitlink::i386 {
2220
/// Represets i386 fixups
2321
enum EdgeKind_i386 : Edge::Kind {
2422

2523
/// None
2624
None = Edge::FirstRelocation,
2725

26+
/// A plain 32-bit pointer value relocation.
27+
///
28+
/// Fixup expression:
29+
/// Fixup <- Target + Addend : uint32
30+
///
31+
/// Errors:
32+
/// - The target must reside in the low 32-bits of the address space,
33+
/// otherwise an out-of-range error will be returned.
34+
///
35+
Pointer32,
36+
37+
/// A 32-bit PC-relative relocation.
38+
///
39+
/// Represents a data/control flow instruction using PC-relative addressing
40+
/// to a target.
41+
///
42+
/// The fixup expression for this kind includes an implicit offset to account
43+
/// for the PC (unlike the Delta edges) so that a PCRel32 with a target
44+
/// T and addend zero is a call/branch to the start (offset zero) of T.
45+
///
46+
/// Fixup expression:
47+
/// Fixup <- Target - (Fixup + 4) + Addend : int32
48+
///
49+
/// Errors:
50+
/// - The result of the fixup expression must fit into an int32, otherwise
51+
/// an out-of-range error will be returned.
52+
///
53+
PCRel32,
54+
55+
/// A plain 16-bit pointer value relocation.
56+
///
57+
/// Fixup expression:
58+
/// Fixup <- Target + Addend : uint16
59+
///
60+
/// Errors:
61+
/// - The target must reside in the low 16-bits of the address space,
62+
/// otherwise an out-of-range error will be returned.
63+
///
64+
Pointer16,
65+
66+
/// A 16-bit PC-relative relocation.
67+
///
68+
/// Represents a data/control flow instruction using PC-relative addressing
69+
/// to a target.
70+
///
71+
/// The fixup expression for this kind includes an implicit offset to account
72+
/// for the PC (unlike the Delta edges) so that a PCRel16 with a target
73+
/// T and addend zero is a call/branch to the start (offset zero) of T.
74+
///
75+
/// Fixup expression:
76+
/// Fixup <- Target - (Fixup + 4) + Addend : int16
77+
///
78+
/// Errors:
79+
/// - The result of the fixup expression must fit into an int16, otherwise
80+
/// an out-of-range error will be returned.
81+
///
82+
PCRel16,
83+
84+
/// A 64-bit GOT delta.
85+
///
86+
/// Delta from the global offset table to the target.
87+
///
88+
/// Fixup expression:
89+
/// Fixup <- Target - GOTSymbol + Addend : int32
90+
///
91+
/// Errors:
92+
/// - *ASSERTION* Failure to a null pointer GOTSymbol, which the GOT section
93+
/// symbol was not been defined.
94+
Delta32FromGOT,
2895
};
2996

3097
/// Returns a string name for the given i386 edge. For debugging purposes
3198
/// only
3299
const char *getEdgeKindName(Edge::Kind K);
33100

34-
} // namespace i386
35-
} // namespace jitlink
36-
} // namespace llvm
101+
/// Returns true if the given uint32_t value is in range for a uint16_t.
102+
inline bool isInRangeForImmU16(uint32_t Value) {
103+
return Value <= std::numeric_limits<uint16_t>::max();
104+
}
105+
106+
/// Returns true if the given int32_t value is in range for an int16_t.
107+
inline bool isInRangeForImmS16(int32_t Value) {
108+
return (Value >= std::numeric_limits<int16_t>::min() &&
109+
Value <= std::numeric_limits<int16_t>::max());
110+
}
111+
112+
/// Apply fixup expression for edge to block content.
113+
inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
114+
using namespace i386;
115+
using namespace llvm::support;
116+
117+
char *BlockWorkingMem = B.getAlreadyMutableContent().data();
118+
char *FixupPtr = BlockWorkingMem + E.getOffset();
119+
auto FixupAddress = B.getAddress() + E.getOffset();
120+
121+
switch (E.getKind()) {
122+
case i386::None: {
123+
break;
124+
}
125+
126+
case i386::Pointer32: {
127+
uint32_t Value = E.getTarget().getAddress().getValue() + E.getAddend();
128+
*(ulittle32_t *)FixupPtr = Value;
129+
break;
130+
}
131+
132+
case i386::PCRel32: {
133+
int32_t Value =
134+
E.getTarget().getAddress() - (FixupAddress + 4) + E.getAddend();
135+
*(little32_t *)FixupPtr = Value;
136+
break;
137+
}
138+
139+
case i386::Pointer16: {
140+
uint32_t Value = E.getTarget().getAddress().getValue() + E.getAddend();
141+
if (LLVM_LIKELY(isInRangeForImmU16(Value)))
142+
*(ulittle16_t *)FixupPtr = Value;
143+
else
144+
return makeTargetOutOfRangeError(G, B, E);
145+
break;
146+
}
147+
148+
case i386::PCRel16: {
149+
int32_t Value =
150+
E.getTarget().getAddress() - (FixupAddress + 4) + E.getAddend();
151+
if (LLVM_LIKELY(isInRangeForImmS16(Value)))
152+
*(little16_t *)FixupPtr = Value;
153+
else
154+
return makeTargetOutOfRangeError(G, B, E);
155+
break;
156+
}
157+
158+
default:
159+
return make_error<JITLinkError>(
160+
"In graph " + G.getName() + ", section " + B.getSection().getName() +
161+
"unsupported edge kind" + getEdgeKindName(E.getKind()));
162+
}
163+
164+
return Error::success();
165+
}
166+
} // namespace llvm::jitlink::i386
37167

38168
#endif // LLVM_EXECUTIONENGINE_JITLINK_I386_H

llvm/lib/ExecutionEngine/JITLink/ELFLinkGraphBuilder.h

Lines changed: 88 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -108,24 +108,49 @@ class ELFLinkGraphBuilder : public ELFLinkGraphBuilderBase {
108108
Error graphifySections();
109109
Error graphifySymbols();
110110

111-
/// Traverse all matching relocation records in the given section. The handler
112-
/// function Func should be callable with this signature:
111+
/// Traverse all matching ELFT::Rela relocation records in the given section.
112+
/// The handler function Func should be callable with this signature:
113113
/// Error(const typename ELFT::Rela &,
114114
/// const typename ELFT::Shdr &, Section &)
115115
///
116-
template <typename RelocHandlerFunction>
117-
Error forEachRelocation(const typename ELFT::Shdr &RelSect,
118-
RelocHandlerFunction &&Func,
119-
bool ProcessDebugSections = false);
116+
template <typename RelocHandlerMethod>
117+
Error forEachRelaRelocation(const typename ELFT::Shdr &RelSect,
118+
RelocHandlerMethod &&Func,
119+
bool ProcessDebugSections = false);
120+
121+
/// Traverse all matching ELFT::Rel relocation records in the given section.
122+
/// The handler function Func should be callable with this signature:
123+
/// Error(const typename ELFT::Rel &,
124+
/// const typename ELFT::Shdr &, Section &)
125+
///
126+
template <typename RelocHandlerMethod>
127+
Error forEachRelRelocation(const typename ELFT::Shdr &RelSect,
128+
RelocHandlerMethod &&Func,
129+
bool ProcessDebugSections = false);
130+
131+
/// Traverse all matching rela relocation records in the given section.
132+
/// Convenience wrapper to allow passing a member function for the handler.
133+
///
134+
template <typename ClassT, typename RelocHandlerMethod>
135+
Error forEachRelaRelocation(const typename ELFT::Shdr &RelSect,
136+
ClassT *Instance, RelocHandlerMethod &&Method,
137+
bool ProcessDebugSections = false) {
138+
return forEachRelaRelocation(
139+
RelSect,
140+
[Instance, Method](const auto &Rel, const auto &Target, auto &GS) {
141+
return (Instance->*Method)(Rel, Target, GS);
142+
},
143+
ProcessDebugSections);
144+
}
120145

121-
/// Traverse all matching relocation records in the given section. Convenience
122-
/// wrapper to allow passing a member function for the handler.
146+
/// Traverse all matching rel relocation records in the given section.
147+
/// Convenience wrapper to allow passing a member function for the handler.
123148
///
124149
template <typename ClassT, typename RelocHandlerMethod>
125-
Error forEachRelocation(const typename ELFT::Shdr &RelSect, ClassT *Instance,
126-
RelocHandlerMethod &&Method,
127-
bool ProcessDebugSections = false) {
128-
return forEachRelocation(
150+
Error forEachRelRelocation(const typename ELFT::Shdr &RelSect,
151+
ClassT *Instance, RelocHandlerMethod &&Method,
152+
bool ProcessDebugSections = false) {
153+
return forEachRelRelocation(
129154
RelSect,
130155
[Instance, Method](const auto &Rel, const auto &Target, auto &GS) {
131156
return (Instance->*Method)(Rel, Target, GS);
@@ -487,13 +512,12 @@ template <typename ELFT> Error ELFLinkGraphBuilder<ELFT>::graphifySymbols() {
487512
}
488513

489514
template <typename ELFT>
490-
template <typename RelocHandlerFunction>
491-
Error ELFLinkGraphBuilder<ELFT>::forEachRelocation(
492-
const typename ELFT::Shdr &RelSect, RelocHandlerFunction &&Func,
515+
template <typename RelocHandlerMethod>
516+
Error ELFLinkGraphBuilder<ELFT>::forEachRelaRelocation(
517+
const typename ELFT::Shdr &RelSect, RelocHandlerMethod &&Func,
493518
bool ProcessDebugSections) {
494-
495519
// Only look into sections that store relocation entries.
496-
if (RelSect.sh_type != ELF::SHT_RELA && RelSect.sh_type != ELF::SHT_REL)
520+
if (RelSect.sh_type != ELF::SHT_RELA)
497521
return Error::success();
498522

499523
// sh_info contains the section header index of the target (FixupSection),
@@ -534,6 +558,53 @@ Error ELFLinkGraphBuilder<ELFT>::forEachRelocation(
534558
return Error::success();
535559
}
536560

561+
template <typename ELFT>
562+
template <typename RelocHandlerMethod>
563+
Error ELFLinkGraphBuilder<ELFT>::forEachRelRelocation(
564+
const typename ELFT::Shdr &RelSect, RelocHandlerMethod &&Func,
565+
bool ProcessDebugSections) {
566+
// Only look into sections that store relocation entries.
567+
if (RelSect.sh_type != ELF::SHT_REL)
568+
return Error::success();
569+
570+
// sh_info contains the section header index of the target (FixupSection),
571+
// which is the section to which all relocations in RelSect apply.
572+
auto FixupSection = Obj.getSection(RelSect.sh_info);
573+
if (!FixupSection)
574+
return FixupSection.takeError();
575+
576+
// Target sections have names in valid ELF object files.
577+
Expected<StringRef> Name = Obj.getSectionName(**FixupSection);
578+
if (!Name)
579+
return Name.takeError();
580+
LLVM_DEBUG(dbgs() << " " << *Name << ":\n");
581+
582+
// Consider skipping these relocations.
583+
if (!ProcessDebugSections && isDwarfSection(*Name)) {
584+
LLVM_DEBUG(dbgs() << " skipped (dwarf section)\n\n");
585+
return Error::success();
586+
}
587+
588+
// Lookup the link-graph node corresponding to the target section name.
589+
auto *BlockToFix = getGraphBlock(RelSect.sh_info);
590+
if (!BlockToFix)
591+
return make_error<StringError>(
592+
"Refencing a section that wasn't added to the graph: " + *Name,
593+
inconvertibleErrorCode());
594+
595+
auto RelEntries = Obj.rels(RelSect);
596+
if (!RelEntries)
597+
return RelEntries.takeError();
598+
599+
// Let the callee process relocation entries one by one.
600+
for (const typename ELFT::Rel &R : *RelEntries)
601+
if (Error Err = Func(R, **FixupSection, *BlockToFix))
602+
return Err;
603+
604+
LLVM_DEBUG(dbgs() << "\n");
605+
return Error::success();
606+
}
607+
537608
} // end namespace jitlink
538609
} // end namespace llvm
539610

llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@ class ELFLinkGraphBuilder_aarch64 : public ELFLinkGraphBuilder<ELFT> {
129129
using Base = ELFLinkGraphBuilder<ELFT>;
130130
using Self = ELFLinkGraphBuilder_aarch64<ELFT>;
131131
for (const auto &RelSect : Base::Sections)
132-
if (Error Err = Base::forEachRelocation(RelSect, this,
133-
&Self::addSingleRelocation))
132+
if (Error Err = Base::forEachRelaRelocation(RelSect, this,
133+
&Self::addSingleRelocation))
134134
return Err;
135135

136136
return Error::success();

0 commit comments

Comments
 (0)