1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
|
//===--------- MachO_x86_64.cpp - Tests for JITLink MachO/x86-64 ----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "JITLinkTestCommon.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ExecutionEngine/JITLink/MachO_x86_64.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
using namespace llvm;
using namespace llvm::jitlink;
using namespace llvm::jitlink::MachO_x86_64_Edges;
namespace {
class JITLinkTest_MachO_x86_64 : public JITLinkTestCommon,
public testing::Test {
public:
using BasicVerifyGraphFunction =
std::function<void(AtomGraph &, const MCDisassembler &)>;
void runBasicVerifyGraphTest(StringRef AsmSrc, StringRef Triple,
StringMap<JITEvaluatedSymbol> Externals,
bool PIC, bool LargeCodeModel,
MCTargetOptions Options,
BasicVerifyGraphFunction RunGraphTest) {
auto TR = getTestResources(AsmSrc, Triple, PIC, LargeCodeModel,
std::move(Options));
if (!TR) {
dbgs() << "Skipping JITLInk unit test: " << toString(TR.takeError())
<< "\n";
return;
}
auto JTCtx = llvm::make_unique<TestJITLinkContext>(
**TR, [&](AtomGraph &G) { RunGraphTest(G, (*TR)->getDisassembler()); });
JTCtx->externals() = std::move(Externals);
jitLink_MachO_x86_64(std::move(JTCtx));
}
protected:
static void verifyIsPointerTo(AtomGraph &G, DefinedAtom &A, Atom &Target) {
EXPECT_EQ(A.edges_size(), 1U) << "Incorrect number of edges for pointer";
if (A.edges_size() != 1U)
return;
auto &E = *A.edges().begin();
EXPECT_EQ(E.getKind(), Pointer64)
<< "Expected pointer to have a pointer64 relocation";
EXPECT_EQ(&E.getTarget(), &Target) << "Expected edge to point at target";
EXPECT_THAT_EXPECTED(readInt<uint64_t>(G, A), HasValue(Target.getAddress()))
<< "Pointer does not point to target";
}
static void verifyGOTLoad(AtomGraph &G, DefinedAtom &A, Edge &E,
Atom &Target) {
EXPECT_EQ(E.getAddend(), 0U) << "Expected GOT load to have a zero addend";
EXPECT_TRUE(E.getTarget().isDefined())
<< "GOT entry should be a defined atom";
if (!E.getTarget().isDefined())
return;
verifyIsPointerTo(G, static_cast<DefinedAtom &>(E.getTarget()), Target);
}
static void verifyCall(const MCDisassembler &Dis, AtomGraph &G,
DefinedAtom &Caller, Edge &E, Atom &Callee) {
EXPECT_EQ(E.getKind(), Branch32) << "Edge is not a Branch32";
EXPECT_EQ(E.getAddend(), 0U) << "Expected no addend on stub call";
EXPECT_EQ(&E.getTarget(), &Callee)
<< "Edge does not point at expected callee";
JITTargetAddress FixupAddress = Caller.getAddress() + E.getOffset();
uint64_t PCRelDelta = Callee.getAddress() - (FixupAddress + 4);
EXPECT_THAT_EXPECTED(
decodeImmediateOperand(Dis, Caller, 0, E.getOffset() - 1),
HasValue(PCRelDelta));
}
static void verifyIndirectCall(const MCDisassembler &Dis, AtomGraph &G,
DefinedAtom &Caller, Edge &E, Atom &Callee) {
EXPECT_EQ(E.getKind(), PCRel32) << "Edge is not a PCRel32";
EXPECT_EQ(E.getAddend(), 0) << "Expected no addend on stub cal";
EXPECT_TRUE(E.getTarget().isDefined()) << "Target is not a defined atom";
if (!E.getTarget().isDefined())
return;
verifyIsPointerTo(G, static_cast<DefinedAtom &>(E.getTarget()), Callee);
JITTargetAddress FixupAddress = Caller.getAddress() + E.getOffset();
uint64_t PCRelDelta = E.getTarget().getAddress() - (FixupAddress + 4);
EXPECT_THAT_EXPECTED(
decodeImmediateOperand(Dis, Caller, 3, E.getOffset() - 2),
HasValue(PCRelDelta));
}
static void verifyCallViaStub(const MCDisassembler &Dis, AtomGraph &G,
DefinedAtom &Caller, Edge &E, Atom &Callee) {
verifyCall(Dis, G, Caller, E, E.getTarget());
if (!E.getTarget().isDefined()) {
ADD_FAILURE() << "Edge target is not a stub";
return;
}
auto &StubAtom = static_cast<DefinedAtom &>(E.getTarget());
EXPECT_EQ(StubAtom.edges_size(), 1U)
<< "Expected one edge from stub to target";
auto &StubEdge = *StubAtom.edges().begin();
verifyIndirectCall(Dis, G, static_cast<DefinedAtom &>(StubAtom), StubEdge,
Callee);
}
};
} // end anonymous namespace
// Test each operation on LegacyObjectTransformLayer.
TEST_F(JITLinkTest_MachO_x86_64, BasicRelocations) {
runBasicVerifyGraphTest(
R"(
.section __TEXT,__text,regular,pure_instructions
.build_version macos, 10, 14
.globl _bar
.p2align 4, 0x90
_bar:
callq _baz
.globl _foo
.p2align 4, 0x90
_foo:
callq _bar
_foo.1:
movq _y@GOTPCREL(%rip), %rcx
_foo.2:
movq _p(%rip), %rdx
.section __DATA,__data
.globl _x
.p2align 2
_x:
.long 42
.globl _p
.p2align 3
_p:
.quad _x
.subsections_via_symbols)",
"x86_64-apple-macosx10.14",
{{"_y", JITEvaluatedSymbol(0xdeadbeef, JITSymbolFlags::Exported)},
{"_baz", JITEvaluatedSymbol(0xcafef00d, JITSymbolFlags::Exported)}},
true, false, MCTargetOptions(),
[](AtomGraph &G, const MCDisassembler &Dis) {
// Name the atoms in the asm above.
auto &Baz = atom(G, "_baz");
auto &Y = atom(G, "_y");
auto &Bar = definedAtom(G, "_bar");
auto &Foo = definedAtom(G, "_foo");
auto &Foo_1 = definedAtom(G, "_foo.1");
auto &Foo_2 = definedAtom(G, "_foo.2");
auto &X = definedAtom(G, "_x");
auto &P = definedAtom(G, "_p");
// Check unsigned reloc for _p
{
EXPECT_EQ(P.edges_size(), 1U) << "Unexpected number of relocations";
EXPECT_EQ(P.edges().begin()->getKind(), Pointer64)
<< "Unexpected edge kind for _p";
EXPECT_THAT_EXPECTED(readInt<uint64_t>(G, P),
HasValue(X.getAddress()))
<< "Unsigned relocation did not apply correctly";
}
// Check that _bar is a call-via-stub to _baz.
// This will check that the call goes to a stub, that the stub is an
// indirect call, and that the pointer for the indirect call points to
// baz.
{
EXPECT_EQ(Bar.edges_size(), 1U)
<< "Incorrect number of edges for bar";
EXPECT_EQ(Bar.edges().begin()->getKind(), Branch32)
<< "Unexpected edge kind for _bar";
verifyCallViaStub(Dis, G, Bar, *Bar.edges().begin(), Baz);
}
// Check that _foo is a direct call to _bar.
{
EXPECT_EQ(Foo.edges_size(), 1U)
<< "Incorrect number of edges for foo";
EXPECT_EQ(Foo.edges().begin()->getKind(), Branch32);
verifyCall(Dis, G, Foo, *Foo.edges().begin(), Bar);
}
// Check .got load in _foo.1
{
EXPECT_EQ(Foo_1.edges_size(), 1U)
<< "Incorrect number of edges for foo_1";
EXPECT_EQ(Foo_1.edges().begin()->getKind(), PCRel32);
verifyGOTLoad(G, Foo_1, *Foo_1.edges().begin(), Y);
}
// Check PCRel ref to _p in _foo.2
{
EXPECT_EQ(Foo_2.edges_size(), 1U)
<< "Incorrect number of edges for foo_2";
EXPECT_EQ(Foo_2.edges().begin()->getKind(), PCRel32);
JITTargetAddress FixupAddress =
Foo_2.getAddress() + Foo_2.edges().begin()->getOffset();
uint64_t PCRelDelta = P.getAddress() - (FixupAddress + 4);
EXPECT_THAT_EXPECTED(decodeImmediateOperand(Dis, Foo_2, 4, 0),
HasValue(PCRelDelta))
<< "PCRel load does not reference expected target";
}
});
}
|