reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    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
  230
  231
  232
//===--------- 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(LinkGraph &, 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 = std::make_unique<TestJITLinkContext>(
        **TR, [&](LinkGraph &G) { RunGraphTest(G, (*TR)->getDisassembler()); });

    JTCtx->externals() = std::move(Externals);

    jitLink_MachO_x86_64(std::move(JTCtx));
  }

protected:
  static void verifyIsPointerTo(LinkGraph &G, Block &B, Symbol &Target) {
    EXPECT_EQ(B.edges_size(), 1U) << "Incorrect number of edges for pointer";
    if (B.edges_size() != 1U)
      return;
    auto &E = *B.edges().begin();
    EXPECT_EQ(E.getOffset(), 0U) << "Expected edge offset of zero";
    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, B), HasValue(Target.getAddress()))
        << "Pointer does not point to target";
  }

  static void verifyGOTLoad(LinkGraph &G, Edge &E, Symbol &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 symbol";
    if (!E.getTarget().isDefined())
      return;

    verifyIsPointerTo(G, E.getTarget().getBlock(), Target);
  }

  static void verifyCall(const MCDisassembler &Dis, LinkGraph &G,
                         Block &CallerBlock, Edge &E, Symbol &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 = CallerBlock.getAddress() + E.getOffset();
    uint64_t PCRelDelta = Callee.getAddress() - (FixupAddress + 4);

    EXPECT_THAT_EXPECTED(
        decodeImmediateOperand(Dis, CallerBlock, 0, E.getOffset() - 1),
        HasValue(PCRelDelta));
  }

  static void verifyIndirectCall(const MCDisassembler &Dis, LinkGraph &G,
                                 Block &CallerBlock, Edge &E, Symbol &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 symbol";
    if (!E.getTarget().isDefined())
      return;
    verifyIsPointerTo(G, E.getTarget().getBlock(), Callee);

    JITTargetAddress FixupAddress = CallerBlock.getAddress() + E.getOffset();
    uint64_t PCRelDelta = E.getTarget().getAddress() - (FixupAddress + 4);

    EXPECT_THAT_EXPECTED(
        decodeImmediateOperand(Dis, CallerBlock, 3, E.getOffset() - 2),
        HasValue(PCRelDelta));
  }

  static void verifyCallViaStub(const MCDisassembler &Dis, LinkGraph &G,
                                Block &CallerBlock, Edge &E, Symbol &Callee) {
    verifyCall(Dis, G, CallerBlock, E, E.getTarget());

    if (!E.getTarget().isDefined()) {
      ADD_FAILURE() << "Edge target is not a stub";
      return;
    }

    auto &StubBlock = E.getTarget().getBlock();
    EXPECT_EQ(StubBlock.edges_size(), 1U)
        << "Expected one edge from stub to target";

    auto &StubEdge = *StubBlock.edges().begin();

    verifyIndirectCall(Dis, G, StubBlock, 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(),
      [](LinkGraph &G, const MCDisassembler &Dis) {
        // Name the symbols in the asm above.
        auto &Baz = symbol(G, "_baz");
        auto &Y = symbol(G, "_y");
        auto &Bar = symbol(G, "_bar");
        auto &Foo = symbol(G, "_foo");
        auto &Foo_1 = symbol(G, "_foo.1");
        auto &Foo_2 = symbol(G, "_foo.2");
        auto &X = symbol(G, "_x");
        auto &P = symbol(G, "_p");

        // Check unsigned reloc for _p
        {
          EXPECT_EQ(P.getBlock().edges_size(), 1U)
              << "Unexpected number of relocations";
          EXPECT_EQ(P.getBlock().edges().begin()->getKind(), Pointer64)
              << "Unexpected edge kind for _p";
          EXPECT_THAT_EXPECTED(readInt<uint64_t>(G, P.getBlock()),
                               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.getBlock().edges_size(), 1U)
              << "Incorrect number of edges for bar";
          EXPECT_EQ(Bar.getBlock().edges().begin()->getKind(), Branch32)
              << "Unexpected edge kind for _bar";
          verifyCallViaStub(Dis, G, Bar.getBlock(),
                            *Bar.getBlock().edges().begin(), Baz);
        }

        // Check that _foo is a direct call to _bar.
        {
          EXPECT_EQ(Foo.getBlock().edges_size(), 1U)
              << "Incorrect number of edges for foo";
          EXPECT_EQ(Foo.getBlock().edges().begin()->getKind(), Branch32);
          verifyCall(Dis, G, Foo.getBlock(), *Foo.getBlock().edges().begin(),
                     Bar);
        }

        // Check .got load in _foo.1
        {
          EXPECT_EQ(Foo_1.getBlock().edges_size(), 1U)
              << "Incorrect number of edges for foo_1";
          EXPECT_EQ(Foo_1.getBlock().edges().begin()->getKind(), PCRel32);
          verifyGOTLoad(G, *Foo_1.getBlock().edges().begin(), Y);
        }

        // Check PCRel ref to _p in _foo.2
        {
          EXPECT_EQ(Foo_2.getBlock().edges_size(), 1U)
              << "Incorrect number of edges for foo_2";
          EXPECT_EQ(Foo_2.getBlock().edges().begin()->getKind(), PCRel32);

          JITTargetAddress FixupAddress =
              Foo_2.getBlock().getAddress() +
              Foo_2.getBlock().edges().begin()->getOffset();
          uint64_t PCRelDelta = P.getAddress() - (FixupAddress + 4);

          EXPECT_THAT_EXPECTED(
              decodeImmediateOperand(Dis, Foo_2.getBlock(), 4, 0),
              HasValue(PCRelDelta))
              << "PCRel load does not reference expected target";
        }
      });
}