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(LinkGraph
&, 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
, [&](LinkGraph
&G
) { RunGraphTest(G
, (*TR
)->getDisassembler()); });
45 JTCtx
->externals() = std::move(Externals
);
47 jitLink_MachO_x86_64(std::move(JTCtx
));
51 static void verifyIsPointerTo(LinkGraph
&G
, Block
&B
, Symbol
&Target
) {
52 EXPECT_EQ(B
.edges_size(), 1U) << "Incorrect number of edges for pointer";
53 if (B
.edges_size() != 1U)
55 auto &E
= *B
.edges().begin();
56 EXPECT_EQ(E
.getOffset(), 0U) << "Expected edge offset of zero";
57 EXPECT_EQ(E
.getKind(), Pointer64
)
58 << "Expected pointer to have a pointer64 relocation";
59 EXPECT_EQ(&E
.getTarget(), &Target
) << "Expected edge to point at target";
60 EXPECT_THAT_EXPECTED(readInt
<uint64_t>(G
, B
), HasValue(Target
.getAddress()))
61 << "Pointer does not point to target";
64 static void verifyGOTLoad(LinkGraph
&G
, Edge
&E
, Symbol
&Target
) {
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 symbol";
68 if (!E
.getTarget().isDefined())
71 verifyIsPointerTo(G
, E
.getTarget().getBlock(), Target
);
74 static void verifyCall(const MCDisassembler
&Dis
, LinkGraph
&G
,
75 Block
&CallerBlock
, Edge
&E
, Symbol
&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
= CallerBlock
.getAddress() + E
.getOffset();
82 uint64_t PCRelDelta
= Callee
.getAddress() - (FixupAddress
+ 4);
85 decodeImmediateOperand(Dis
, CallerBlock
, 0, E
.getOffset() - 1),
86 HasValue(PCRelDelta
));
89 static void verifyIndirectCall(const MCDisassembler
&Dis
, LinkGraph
&G
,
90 Block
&CallerBlock
, Edge
&E
, Symbol
&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 symbol";
94 if (!E
.getTarget().isDefined())
96 verifyIsPointerTo(G
, E
.getTarget().getBlock(), Callee
);
98 JITTargetAddress FixupAddress
= CallerBlock
.getAddress() + E
.getOffset();
99 uint64_t PCRelDelta
= E
.getTarget().getAddress() - (FixupAddress
+ 4);
101 EXPECT_THAT_EXPECTED(
102 decodeImmediateOperand(Dis
, CallerBlock
, 3, E
.getOffset() - 2),
103 HasValue(PCRelDelta
));
106 static void verifyCallViaStub(const MCDisassembler
&Dis
, LinkGraph
&G
,
107 Block
&CallerBlock
, Edge
&E
, Symbol
&Callee
) {
108 verifyCall(Dis
, G
, CallerBlock
, E
, E
.getTarget());
110 if (!E
.getTarget().isDefined()) {
111 ADD_FAILURE() << "Edge target is not a stub";
115 auto &StubBlock
= E
.getTarget().getBlock();
116 EXPECT_EQ(StubBlock
.edges_size(), 1U)
117 << "Expected one edge from stub to target";
119 auto &StubEdge
= *StubBlock
.edges().begin();
121 verifyIndirectCall(Dis
, G
, StubBlock
, StubEdge
, Callee
);
125 } // end anonymous namespace
127 // Test each operation on LegacyObjectTransformLayer.
128 TEST_F(JITLinkTest_MachO_x86_64
, BasicRelocations
) {
129 runBasicVerifyGraphTest(
131 .section __TEXT,__text,regular,pure_instructions
132 .build_version macos, 10, 14
143 movq _y@GOTPCREL(%rip), %rcx
147 .section __DATA,__data
158 .subsections_via_symbols)",
159 "x86_64-apple-macosx10.14",
160 {{"_y", JITEvaluatedSymbol(0xdeadbeef, JITSymbolFlags::Exported
)},
161 {"_baz", JITEvaluatedSymbol(0xcafef00d, JITSymbolFlags::Exported
)}},
162 true, false, MCTargetOptions(),
163 [](LinkGraph
&G
, const MCDisassembler
&Dis
) {
164 // Name the symbols in the asm above.
165 auto &Baz
= symbol(G
, "_baz");
166 auto &Y
= symbol(G
, "_y");
167 auto &Bar
= symbol(G
, "_bar");
168 auto &Foo
= symbol(G
, "_foo");
169 auto &Foo_1
= symbol(G
, "_foo.1");
170 auto &Foo_2
= symbol(G
, "_foo.2");
171 auto &X
= symbol(G
, "_x");
172 auto &P
= symbol(G
, "_p");
174 // Check unsigned reloc for _p
176 EXPECT_EQ(P
.getBlock().edges_size(), 1U)
177 << "Unexpected number of relocations";
178 EXPECT_EQ(P
.getBlock().edges().begin()->getKind(), Pointer64
)
179 << "Unexpected edge kind for _p";
180 EXPECT_THAT_EXPECTED(readInt
<uint64_t>(G
, P
.getBlock()),
181 HasValue(X
.getAddress()))
182 << "Unsigned relocation did not apply correctly";
185 // Check that _bar is a call-via-stub to _baz.
186 // This will check that the call goes to a stub, that the stub is an
187 // indirect call, and that the pointer for the indirect call points to
190 EXPECT_EQ(Bar
.getBlock().edges_size(), 1U)
191 << "Incorrect number of edges for bar";
192 EXPECT_EQ(Bar
.getBlock().edges().begin()->getKind(), Branch32
)
193 << "Unexpected edge kind for _bar";
194 verifyCallViaStub(Dis
, G
, Bar
.getBlock(),
195 *Bar
.getBlock().edges().begin(), Baz
);
198 // Check that _foo is a direct call to _bar.
200 EXPECT_EQ(Foo
.getBlock().edges_size(), 1U)
201 << "Incorrect number of edges for foo";
202 EXPECT_EQ(Foo
.getBlock().edges().begin()->getKind(), Branch32
);
203 verifyCall(Dis
, G
, Foo
.getBlock(), *Foo
.getBlock().edges().begin(),
207 // Check .got load in _foo.1
209 EXPECT_EQ(Foo_1
.getBlock().edges_size(), 1U)
210 << "Incorrect number of edges for foo_1";
211 EXPECT_EQ(Foo_1
.getBlock().edges().begin()->getKind(), PCRel32
);
212 verifyGOTLoad(G
, *Foo_1
.getBlock().edges().begin(), Y
);
215 // Check PCRel ref to _p in _foo.2
217 EXPECT_EQ(Foo_2
.getBlock().edges_size(), 1U)
218 << "Incorrect number of edges for foo_2";
219 EXPECT_EQ(Foo_2
.getBlock().edges().begin()->getKind(), PCRel32
);
221 JITTargetAddress FixupAddress
=
222 Foo_2
.getBlock().getAddress() +
223 Foo_2
.getBlock().edges().begin()->getOffset();
224 uint64_t PCRelDelta
= P
.getAddress() - (FixupAddress
+ 4);
226 EXPECT_THAT_EXPECTED(
227 decodeImmediateOperand(Dis
, Foo_2
.getBlock(), 4, 0),
228 HasValue(PCRelDelta
))
229 << "PCRel load does not reference expected target";