1 //===--------- MachO_x86_64.cpp - Tests for JITLink MachO/x86-64 ----------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "JITLinkTestCommon.h"
11 #include "llvm/ADT/DenseSet.h"
12 #include "llvm/ExecutionEngine/JITLink/MachO_x86_64.h"
13 #include "llvm/Testing/Support/Error.h"
15 #include "gtest/gtest.h"
18 using namespace llvm::jitlink
;
19 using namespace llvm::jitlink::MachO_x86_64_Edges
;
23 class JITLinkTest_MachO_x86_64
: public JITLinkTestCommon
,
24 public testing::Test
{
26 using BasicVerifyGraphFunction
=
27 std::function
<void(AtomGraph
&, const MCDisassembler
&)>;
29 void runBasicVerifyGraphTest(StringRef AsmSrc
, StringRef Triple
,
30 StringMap
<JITEvaluatedSymbol
> Externals
,
31 bool PIC
, bool LargeCodeModel
,
32 MCTargetOptions Options
,
33 BasicVerifyGraphFunction RunGraphTest
) {
34 auto TR
= getTestResources(AsmSrc
, Triple
, PIC
, LargeCodeModel
,
37 dbgs() << "Skipping JITLInk unit test: " << toString(TR
.takeError())
42 auto JTCtx
= std::make_unique
<TestJITLinkContext
>(
43 **TR
, [&](AtomGraph
&G
) { RunGraphTest(G
, (*TR
)->getDisassembler()); });
45 JTCtx
->externals() = std::move(Externals
);
47 jitLink_MachO_x86_64(std::move(JTCtx
));
51 static void verifyIsPointerTo(AtomGraph
&G
, DefinedAtom
&A
, Atom
&Target
) {
52 EXPECT_EQ(A
.edges_size(), 1U) << "Incorrect number of edges for pointer";
53 if (A
.edges_size() != 1U)
55 auto &E
= *A
.edges().begin();
56 EXPECT_EQ(E
.getKind(), Pointer64
)
57 << "Expected pointer to have a pointer64 relocation";
58 EXPECT_EQ(&E
.getTarget(), &Target
) << "Expected edge to point at target";
59 EXPECT_THAT_EXPECTED(readInt
<uint64_t>(G
, A
), HasValue(Target
.getAddress()))
60 << "Pointer does not point to target";
63 static void verifyGOTLoad(AtomGraph
&G
, DefinedAtom
&A
, Edge
&E
,
65 EXPECT_EQ(E
.getAddend(), 0U) << "Expected GOT load to have a zero addend";
66 EXPECT_TRUE(E
.getTarget().isDefined())
67 << "GOT entry should be a defined atom";
68 if (!E
.getTarget().isDefined())
71 verifyIsPointerTo(G
, static_cast<DefinedAtom
&>(E
.getTarget()), Target
);
74 static void verifyCall(const MCDisassembler
&Dis
, AtomGraph
&G
,
75 DefinedAtom
&Caller
, Edge
&E
, Atom
&Callee
) {
76 EXPECT_EQ(E
.getKind(), Branch32
) << "Edge is not a Branch32";
77 EXPECT_EQ(E
.getAddend(), 0U) << "Expected no addend on stub call";
78 EXPECT_EQ(&E
.getTarget(), &Callee
)
79 << "Edge does not point at expected callee";
81 JITTargetAddress FixupAddress
= Caller
.getAddress() + E
.getOffset();
82 uint64_t PCRelDelta
= Callee
.getAddress() - (FixupAddress
+ 4);
85 decodeImmediateOperand(Dis
, Caller
, 0, E
.getOffset() - 1),
86 HasValue(PCRelDelta
));
89 static void verifyIndirectCall(const MCDisassembler
&Dis
, AtomGraph
&G
,
90 DefinedAtom
&Caller
, Edge
&E
, Atom
&Callee
) {
91 EXPECT_EQ(E
.getKind(), PCRel32
) << "Edge is not a PCRel32";
92 EXPECT_EQ(E
.getAddend(), 0) << "Expected no addend on stub cal";
93 EXPECT_TRUE(E
.getTarget().isDefined()) << "Target is not a defined atom";
94 if (!E
.getTarget().isDefined())
96 verifyIsPointerTo(G
, static_cast<DefinedAtom
&>(E
.getTarget()), Callee
);
98 JITTargetAddress FixupAddress
= Caller
.getAddress() + E
.getOffset();
99 uint64_t PCRelDelta
= E
.getTarget().getAddress() - (FixupAddress
+ 4);
101 EXPECT_THAT_EXPECTED(
102 decodeImmediateOperand(Dis
, Caller
, 3, E
.getOffset() - 2),
103 HasValue(PCRelDelta
));
106 static void verifyCallViaStub(const MCDisassembler
&Dis
, AtomGraph
&G
,
107 DefinedAtom
&Caller
, Edge
&E
, Atom
&Callee
) {
108 verifyCall(Dis
, G
, Caller
, E
, E
.getTarget());
110 if (!E
.getTarget().isDefined()) {
111 ADD_FAILURE() << "Edge target is not a stub";
115 auto &StubAtom
= static_cast<DefinedAtom
&>(E
.getTarget());
116 EXPECT_EQ(StubAtom
.edges_size(), 1U)
117 << "Expected one edge from stub to target";
119 auto &StubEdge
= *StubAtom
.edges().begin();
121 verifyIndirectCall(Dis
, G
, static_cast<DefinedAtom
&>(StubAtom
), StubEdge
,
126 } // end anonymous namespace
128 // Test each operation on LegacyObjectTransformLayer.
129 TEST_F(JITLinkTest_MachO_x86_64
, BasicRelocations
) {
130 runBasicVerifyGraphTest(
132 .section __TEXT,__text,regular,pure_instructions
133 .build_version macos, 10, 14
144 movq _y@GOTPCREL(%rip), %rcx
148 .section __DATA,__data
159 .subsections_via_symbols)",
160 "x86_64-apple-macosx10.14",
161 {{"_y", JITEvaluatedSymbol(0xdeadbeef, JITSymbolFlags::Exported
)},
162 {"_baz", JITEvaluatedSymbol(0xcafef00d, JITSymbolFlags::Exported
)}},
163 true, false, MCTargetOptions(),
164 [](AtomGraph
&G
, const MCDisassembler
&Dis
) {
165 // Name the atoms in the asm above.
166 auto &Baz
= atom(G
, "_baz");
167 auto &Y
= atom(G
, "_y");
169 auto &Bar
= definedAtom(G
, "_bar");
170 auto &Foo
= definedAtom(G
, "_foo");
171 auto &Foo_1
= definedAtom(G
, "_foo.1");
172 auto &Foo_2
= definedAtom(G
, "_foo.2");
173 auto &X
= definedAtom(G
, "_x");
174 auto &P
= definedAtom(G
, "_p");
176 // Check unsigned reloc for _p
178 EXPECT_EQ(P
.edges_size(), 1U) << "Unexpected number of relocations";
179 EXPECT_EQ(P
.edges().begin()->getKind(), Pointer64
)
180 << "Unexpected edge kind for _p";
181 EXPECT_THAT_EXPECTED(readInt
<uint64_t>(G
, P
),
182 HasValue(X
.getAddress()))
183 << "Unsigned relocation did not apply correctly";
186 // Check that _bar is a call-via-stub to _baz.
187 // This will check that the call goes to a stub, that the stub is an
188 // indirect call, and that the pointer for the indirect call points to
191 EXPECT_EQ(Bar
.edges_size(), 1U)
192 << "Incorrect number of edges for bar";
193 EXPECT_EQ(Bar
.edges().begin()->getKind(), Branch32
)
194 << "Unexpected edge kind for _bar";
195 verifyCallViaStub(Dis
, G
, Bar
, *Bar
.edges().begin(), Baz
);
198 // Check that _foo is a direct call to _bar.
200 EXPECT_EQ(Foo
.edges_size(), 1U)
201 << "Incorrect number of edges for foo";
202 EXPECT_EQ(Foo
.edges().begin()->getKind(), Branch32
);
203 verifyCall(Dis
, G
, Foo
, *Foo
.edges().begin(), Bar
);
206 // Check .got load in _foo.1
208 EXPECT_EQ(Foo_1
.edges_size(), 1U)
209 << "Incorrect number of edges for foo_1";
210 EXPECT_EQ(Foo_1
.edges().begin()->getKind(), PCRel32
);
211 verifyGOTLoad(G
, Foo_1
, *Foo_1
.edges().begin(), Y
);
214 // Check PCRel ref to _p in _foo.2
216 EXPECT_EQ(Foo_2
.edges_size(), 1U)
217 << "Incorrect number of edges for foo_2";
218 EXPECT_EQ(Foo_2
.edges().begin()->getKind(), PCRel32
);
220 JITTargetAddress FixupAddress
=
221 Foo_2
.getAddress() + Foo_2
.edges().begin()->getOffset();
222 uint64_t PCRelDelta
= P
.getAddress() - (FixupAddress
+ 4);
224 EXPECT_THAT_EXPECTED(decodeImmediateOperand(Dis
, Foo_2
, 4, 0),
225 HasValue(PCRelDelta
))
226 << "PCRel load does not reference expected target";