Skip to content

Commit 1b123d9

Browse files
committed
Adds support for GOT relocations to i386/ELF backend
This CR adds support for GOT relocations to the JITLink i386/ELF backend. Reviewed By: lhames Differential Revision: https://reviews.llvm.org/D140279
1 parent 03ed35b commit 1b123d9

File tree

5 files changed

+319
-9
lines changed

5 files changed

+319
-9
lines changed

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

Lines changed: 122 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,19 @@ enum EdgeKind_i386 : Edge::Kind {
8181
///
8282
PCRel16,
8383

84-
/// A 64-bit GOT delta.
84+
/// A 32-bit delta.
85+
///
86+
/// Delta from the fixup to the target.
87+
///
88+
/// Fixup expression:
89+
/// Fixup <- Target - Fixup + Addend : int64
90+
///
91+
/// Errors:
92+
/// - The result of the fixup expression must fit into an int32, otherwise
93+
/// an out-of-range error will be returned.
94+
Delta32,
95+
96+
/// A 32-bit GOT delta.
8597
///
8698
/// Delta from the global offset table to the target.
8799
///
@@ -92,6 +104,26 @@ enum EdgeKind_i386 : Edge::Kind {
92104
/// - *ASSERTION* Failure to a null pointer GOTSymbol, which the GOT section
93105
/// symbol was not been defined.
94106
Delta32FromGOT,
107+
108+
/// A GOT entry offset within GOT getter/constructor, transformed to
109+
/// Delta32FromGOT pointing at the GOT entry for the original target.
110+
///
111+
/// Indicates that this edge should be transformed into a Delta32FromGOT
112+
/// targeting the GOT entry for the edge's current target, maintaining the
113+
/// same addend.
114+
/// A GOT entry for the target should be created if one does not already
115+
/// exist.
116+
///
117+
/// Edges of this kind are usually handled by a GOT builder pass inserted by
118+
/// default
119+
///
120+
/// Fixup expression:
121+
/// NONE
122+
///
123+
/// Errors:
124+
/// - *ASSERTION* Failure to handle edges of this kind prior to the fixup
125+
/// phase will result in an assert/unreachable during the fixup phase
126+
RequestGOTAndTransformToDelta32FromGOT,
95127
};
96128

97129
/// Returns a string name for the given i386 edge. For debugging purposes
@@ -110,7 +142,8 @@ inline bool isInRangeForImmS16(int32_t Value) {
110142
}
111143

112144
/// Apply fixup expression for edge to block content.
113-
inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
145+
inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
146+
const Symbol *GOTSymbol) {
114147
using namespace i386;
115148
using namespace llvm::support;
116149

@@ -155,6 +188,20 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
155188
break;
156189
}
157190

191+
case i386::Delta32: {
192+
int32_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend();
193+
*(little32_t *)FixupPtr = Value;
194+
break;
195+
}
196+
197+
case i386::Delta32FromGOT: {
198+
assert(GOTSymbol && "No GOT section symbol");
199+
int32_t Value =
200+
E.getTarget().getAddress() - GOTSymbol->getAddress() + E.getAddend();
201+
*(little32_t *)FixupPtr = Value;
202+
break;
203+
}
204+
158205
default:
159206
return make_error<JITLinkError>(
160207
"In graph " + G.getName() + ", section " + B.getSection().getName() +
@@ -163,6 +210,79 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
163210

164211
return Error::success();
165212
}
213+
214+
/// i386 pointer size.
215+
constexpr uint32_t PointerSize = 4;
216+
217+
/// i386 null pointer content.
218+
extern const char NullPointerContent[PointerSize];
219+
220+
/// Creates a new pointer block in the given section and returns an anonymous
221+
/// symbol pointing to it.
222+
///
223+
/// If InitialTarget is given then an Pointer32 relocation will be added to the
224+
/// block pointing at InitialTarget.
225+
///
226+
/// The pointer block will have the following default values:
227+
/// alignment: 32-bit
228+
/// alignment-offset: 0
229+
/// address: highest allowable (~7U)
230+
inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection,
231+
Symbol *InitialTarget = nullptr,
232+
uint64_t InitialAddend = 0) {
233+
auto &B = G.createContentBlock(PointerSection, NullPointerContent,
234+
orc::ExecutorAddr(), 8, 0);
235+
if (InitialTarget)
236+
B.addEdge(Pointer32, 0, *InitialTarget, InitialAddend);
237+
return G.addAnonymousSymbol(B, 0, PointerSize, false, false);
238+
}
239+
240+
/// Global Offset Table Builder.
241+
class GOTTableManager : public TableManager<GOTTableManager> {
242+
public:
243+
static StringRef getSectionName() { return "$__GOT"; }
244+
245+
bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
246+
Edge::Kind KindToSet = Edge::Invalid;
247+
switch (E.getKind()) {
248+
case i386::Delta32FromGOT: {
249+
// we need to make sure that the GOT section exists, but don't otherwise
250+
// need to fix up this edge
251+
getGOTSection(G);
252+
return false;
253+
}
254+
case i386::RequestGOTAndTransformToDelta32FromGOT:
255+
KindToSet = i386::Delta32FromGOT;
256+
break;
257+
default:
258+
return false;
259+
}
260+
assert(KindToSet != Edge::Invalid &&
261+
"Fell through switch, but no new kind to set");
262+
DEBUG_WITH_TYPE("jitlink", {
263+
dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
264+
<< B->getFixupAddress(E) << " (" << B->getAddress() << " + "
265+
<< formatv("{0:x}", E.getOffset()) << ")\n";
266+
});
267+
E.setKind(KindToSet);
268+
E.setTarget(getEntryForTarget(G, E.getTarget()));
269+
return true;
270+
}
271+
272+
Symbol &createEntry(LinkGraph &G, Symbol &Target) {
273+
return createAnonymousPointer(G, getGOTSection(G), &Target);
274+
}
275+
276+
private:
277+
Section &getGOTSection(LinkGraph &G) {
278+
if (!GOTSection)
279+
GOTSection = &G.createSection(getSectionName(), orc::MemProt::Read);
280+
return *GOTSection;
281+
}
282+
283+
Section *GOTSection = nullptr;
284+
};
285+
166286
} // namespace llvm::jitlink::i386
167287

168288
#endif // LLVM_EXECUTIONENGINE_JITLINK_I386_H

llvm/lib/ExecutionEngine/JITLink/ELF_i386.cpp

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,25 @@
1717
#include "llvm/ExecutionEngine/JITLink/i386.h"
1818
#include "llvm/Object/ELFObjectFile.h"
1919

20+
#include "DefineExternalSectionStartAndEndSymbols.h"
21+
2022
#define DEBUG_TYPE "jitlink"
2123

2224
using namespace llvm;
2325
using namespace llvm::jitlink;
2426

27+
namespace {
28+
constexpr StringRef ELFGOTSymbolName = "_GLOBAL_OFFSET_TABLE_";
29+
30+
Error buildTables_ELF_i386(LinkGraph &G) {
31+
LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n");
32+
33+
i386::GOTTableManager GOT;
34+
visitExistingEdges(G, GOT);
35+
return Error::success();
36+
}
37+
} // namespace
38+
2539
namespace llvm::jitlink {
2640

2741
class ELFJITLinker_i386 : public JITLinker<ELFJITLinker_i386> {
@@ -30,11 +44,68 @@ class ELFJITLinker_i386 : public JITLinker<ELFJITLinker_i386> {
3044
public:
3145
ELFJITLinker_i386(std::unique_ptr<JITLinkContext> Ctx,
3246
std::unique_ptr<LinkGraph> G, PassConfiguration PassConfig)
33-
: JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {}
47+
: JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {
48+
getPassConfig().PostAllocationPasses.push_back(
49+
[this](LinkGraph &G) { return getOrCreateGOTSymbol(G); });
50+
}
3451

3552
private:
53+
Symbol *GOTSymbol = nullptr;
54+
55+
Error getOrCreateGOTSymbol(LinkGraph &G) {
56+
auto DefineExternalGOTSymbolIfPresent =
57+
createDefineExternalSectionStartAndEndSymbolsPass(
58+
[&](LinkGraph &LG, Symbol &Sym) -> SectionRangeSymbolDesc {
59+
if (Sym.getName() == ELFGOTSymbolName)
60+
if (auto *GOTSection = G.findSectionByName(
61+
i386::GOTTableManager::getSectionName())) {
62+
GOTSymbol = &Sym;
63+
return {*GOTSection, true};
64+
}
65+
return {};
66+
});
67+
68+
// Try to attach _GLOBAL_OFFSET_TABLE_ to the GOT if it's defined as an
69+
// external.
70+
if (auto Err = DefineExternalGOTSymbolIfPresent(G))
71+
return Err;
72+
73+
// If we succeeded then we're done.
74+
if (GOTSymbol)
75+
return Error::success();
76+
77+
// Otherwise look for a GOT section: If it already has a start symbol we'll
78+
// record it, otherwise we'll create our own.
79+
// If there's a GOT section but we didn't find an external GOT symbol...
80+
if (auto *GOTSection =
81+
G.findSectionByName(i386::GOTTableManager::getSectionName())) {
82+
83+
// Check for an existing defined symbol.
84+
for (auto *Sym : GOTSection->symbols())
85+
if (Sym->getName() == ELFGOTSymbolName) {
86+
GOTSymbol = Sym;
87+
return Error::success();
88+
}
89+
90+
// If there's no defined symbol then create one.
91+
SectionRange SR(*GOTSection);
92+
93+
if (SR.empty()) {
94+
GOTSymbol =
95+
&G.addAbsoluteSymbol(ELFGOTSymbolName, orc::ExecutorAddr(), 0,
96+
Linkage::Strong, Scope::Local, true);
97+
} else {
98+
GOTSymbol =
99+
&G.addDefinedSymbol(*SR.getFirstBlock(), 0, ELFGOTSymbolName, 0,
100+
Linkage::Strong, Scope::Local, false, true);
101+
}
102+
}
103+
104+
return Error::success();
105+
}
106+
36107
Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
37-
return i386::applyFixup(G, B, E);
108+
return i386::applyFixup(G, B, E, GOTSymbol);
38109
}
39110
};
40111

@@ -54,7 +125,11 @@ class ELFLinkGraphBuilder_i386 : public ELFLinkGraphBuilder<ELFT> {
54125
return EdgeKind_i386::Pointer16;
55126
case ELF::R_386_PC16:
56127
return EdgeKind_i386::PCRel16;
128+
case ELF::R_386_GOT32:
129+
return EdgeKind_i386::RequestGOTAndTransformToDelta32FromGOT;
57130
case ELF::R_386_GOTPC:
131+
return EdgeKind_i386::Delta32;
132+
case ELF::R_386_GOTOFF:
58133
return EdgeKind_i386::Delta32FromGOT;
59134
}
60135

@@ -105,10 +180,6 @@ class ELFLinkGraphBuilder_i386 : public ELFLinkGraphBuilder<ELFT> {
105180
if (!Kind)
106181
return Kind.takeError();
107182

108-
// TODO: To be removed when GOT relative relocations are supported.
109-
if (*Kind == i386::EdgeKind_i386::Delta32FromGOT)
110-
return Error::success();
111-
112183
int64_t Addend = 0;
113184

114185
auto FixupAddress = orc::ExecutorAddr(FixupSection.sh_addr) + Rel.r_offset;
@@ -121,7 +192,6 @@ class ELFLinkGraphBuilder_i386 : public ELFLinkGraphBuilder<ELFT> {
121192
});
122193

123194
BlockToFix.addEdge(std::move(GE));
124-
125195
return Error::success();
126196
}
127197

@@ -162,6 +232,9 @@ void link_ELF_i386(std::unique_ptr<LinkGraph> G,
162232
Config.PrePrunePasses.push_back(std::move(MarkLive));
163233
else
164234
Config.PrePrunePasses.push_back(markAllSymbolsLive);
235+
236+
// Add an in-place GOT build pass.
237+
Config.PostPrunePasses.push_back(buildTables_ELF_i386);
165238
}
166239
if (auto Err = Ctx->modifyPassConfig(*G, Config))
167240
return Ctx->notifyFailed(std::move(Err));

llvm/lib/ExecutionEngine/JITLink/i386.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,16 @@ const char *getEdgeKindName(Edge::Kind K) {
2828
return "Pointer16";
2929
case PCRel16:
3030
return "PCRel16";
31+
case Delta32:
32+
return "Delta32";
33+
case Delta32FromGOT:
34+
return "Delta32FromGOT";
35+
case RequestGOTAndTransformToDelta32FromGOT:
36+
return "RequestGOTAndTransformToDelta32FromGOT";
3137
}
38+
3239
return getGenericEdgeKindName(K);
3340
}
3441

42+
const char NullPointerContent[PointerSize] = {0x00, 0x00, 0x00, 0x00};
3543
} // namespace llvm::jitlink::i386
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# RUN: llvm-mc -triple=i386-unknown-linux-gnu -filetype=obj -o %t %s
2+
# RUN: llvm-jitlink -noexec %t
3+
#
4+
# Check that symbol scope is demoted to local when external symbols are
5+
# converted to absolutes. This is demotion is necessary to avoid "unexpected
6+
# definition" errors.
7+
#
8+
# The reference to _GLOBAL_OFFSET_TABLE_ will trigger creation of an external
9+
# _GLOBAL_OFFSET_TABLE_ symbol, and the GOTOFF relocation will force creation
10+
# of a GOT symbol without actually introducing any GOT entries. Together these
11+
# should cause the external _GLOBAL_OFFSET_TABLE_ symbol to be converted to an
12+
# absolute symbol with address zero. If the scope is not demoted correctly this
13+
# will trigger an "unexpected definition" error.
14+
15+
.text
16+
.globl main
17+
.p2align 4, 0x90
18+
.type main,@function
19+
main:
20+
pushl %ebp
21+
movl %esp, %ebp
22+
pushl %eax
23+
calll .L0$pb
24+
.L0$pb:
25+
popl %eax
26+
.Ltmp0:
27+
addl $_GLOBAL_OFFSET_TABLE_+(.Ltmp0-.L0$pb), %eax
28+
movl $0, -4(%ebp)
29+
movl a@GOTOFF(%eax), %eax
30+
addl $4, %esp
31+
popl %ebp
32+
retl
33+
.size main, .-main
34+
35+
36+
.type a,@object # @a
37+
.data
38+
.p2align 2
39+
a:
40+
.long 42 # 0x2a
41+
.size a, 4

0 commit comments

Comments
 (0)