[llvm] [cmake] Add possibility to use ChooseMSVCCRT.cmake when include LLVM library
[llvm-core.git] / unittests / ExecutionEngine / JITLink / MachO_x86_64_Tests.cpp
blobe051ad551c757d8d98d0025d55c1ef35ab08bfe0
1 //===--------- MachO_x86_64.cpp - Tests for JITLink MachO/x86-64 ----------===//
2 //
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
6 //
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"
17 using namespace llvm;
18 using namespace llvm::jitlink;
19 using namespace llvm::jitlink::MachO_x86_64_Edges;
21 namespace {
23 class JITLinkTest_MachO_x86_64 : public JITLinkTestCommon,
24 public testing::Test {
25 public:
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,
35 std::move(Options));
36 if (!TR) {
37 dbgs() << "Skipping JITLInk unit test: " << toString(TR.takeError())
38 << "\n";
39 return;
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));
50 protected:
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)
54 return;
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,
64 Atom &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 atom";
68 if (!E.getTarget().isDefined())
69 return;
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);
84 EXPECT_THAT_EXPECTED(
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())
95 return;
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";
112 return;
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,
122 Callee);
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
134 .globl _bar
135 .p2align 4, 0x90
136 _bar:
137 callq _baz
139 .globl _foo
140 .p2align 4, 0x90
141 _foo:
142 callq _bar
143 _foo.1:
144 movq _y@GOTPCREL(%rip), %rcx
145 _foo.2:
146 movq _p(%rip), %rdx
148 .section __DATA,__data
149 .globl _x
150 .p2align 2
152 .long 42
154 .globl _p
155 .p2align 3
157 .quad _x
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
189 // baz.
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";