aboutsummaryrefslogtreecommitdiff
path: root/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp
blob: 9b10c2f160f41d38b9b6e174e7df6ec30348a9dc (plain) (blame)
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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
//===- lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp --------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "HexagonExecutableWriter.h"
#include "HexagonDynamicLibraryWriter.h"
#include "HexagonLinkingContext.h"
#include "HexagonTargetHandler.h"

using namespace lld;
using namespace elf;
using namespace llvm::ELF;

using llvm::makeArrayRef;

HexagonTargetHandler::HexagonTargetHandler(HexagonLinkingContext &context)
    : _hexagonLinkingContext(context),
      _hexagonRuntimeFile(new HexagonRuntimeFile<HexagonELFType>(context)),
      _hexagonTargetLayout(new HexagonTargetLayout<HexagonELFType>(context)),
      _hexagonRelocationHandler(new HexagonTargetRelocationHandler(
          *_hexagonTargetLayout.get())) {}

std::unique_ptr<Writer> HexagonTargetHandler::getWriter() {
  switch (_hexagonLinkingContext.getOutputELFType()) {
  case llvm::ELF::ET_EXEC:
    return std::unique_ptr<Writer>(
        new elf::HexagonExecutableWriter<HexagonELFType>(
            _hexagonLinkingContext, *_hexagonTargetLayout.get()));
  case llvm::ELF::ET_DYN:
    return std::unique_ptr<Writer>(
        new elf::HexagonDynamicLibraryWriter<HexagonELFType>(
            _hexagonLinkingContext, *_hexagonTargetLayout.get()));
  case llvm::ELF::ET_REL:
    llvm_unreachable("TODO: support -r mode");
  default:
    llvm_unreachable("unsupported output type");
  }
}

using namespace llvm::ELF;

// .got atom
const uint8_t hexagonGotAtomContent[4] = { 0 };
// .got.plt atom (entry 0)
const uint8_t hexagonGotPlt0AtomContent[16] = { 0 };
// .got.plt atom (all other entries)
const uint8_t hexagonGotPltAtomContent[4] = { 0 };
// .plt (entry 0)
const uint8_t hexagonPlt0AtomContent[28] = {
  0x00, 0x40, 0x00, 0x00, // { immext (#0)
  0x1c, 0xc0, 0x49, 0x6a, //   r28 = add (pc, ##GOT0@PCREL) } # address of GOT0
  0x0e, 0x42, 0x9c, 0xe2, // { r14 -= add (r28, #16)  # offset of GOTn from GOTa
  0x4f, 0x40, 0x9c, 0x91, //   r15 = memw (r28 + #8)  # object ID at GOT2
  0x3c, 0xc0, 0x9c, 0x91, //   r28 = memw (r28 + #4) }# dynamic link at GOT1
  0x0e, 0x42, 0x0e, 0x8c, // { r14 = asr (r14, #2)    # index of PLTn
  0x00, 0xc0, 0x9c, 0x52, //   jumpr r28 }            # call dynamic linker
};

// .plt (other entries)
const uint8_t hexagonPltAtomContent[16] = {
  0x00, 0x40, 0x00, 0x00, // { immext (#0)
  0x0e, 0xc0, 0x49, 0x6a, //   r14 = add (pc, ##GOTn@PCREL) } # address of GOTn
  0x1c, 0xc0, 0x8e, 0x91, // r28 = memw (r14)                 # contents of GOTn
  0x00, 0xc0, 0x9c, 0x52, // jumpr r28                        # call it
};

class HexagonGOTAtom : public GOTAtom {
public:
  HexagonGOTAtom(const File &f) : GOTAtom(f, ".got") {}

  ArrayRef<uint8_t> rawContent() const override {
    return makeArrayRef(hexagonGotAtomContent);
  }

  Alignment alignment() const override { return Alignment(2); }
};

class HexagonGOTPLTAtom : public GOTAtom {
public:
  HexagonGOTPLTAtom(const File &f) : GOTAtom(f, ".got.plt") {}

  ArrayRef<uint8_t> rawContent() const override {
    return makeArrayRef(hexagonGotPltAtomContent);
  }

  Alignment alignment() const override { return Alignment(2); }
};

class HexagonGOTPLT0Atom : public GOTAtom {
public:
  HexagonGOTPLT0Atom(const File &f) : GOTAtom(f, ".got.plt") {}

  ArrayRef<uint8_t> rawContent() const override {
    return makeArrayRef(hexagonGotPlt0AtomContent);
  }

  Alignment alignment() const override { return Alignment(3); }
};

class HexagonPLT0Atom : public PLT0Atom {
public:
  HexagonPLT0Atom(const File &f) : PLT0Atom(f) {}

  ArrayRef<uint8_t> rawContent() const override {
    return makeArrayRef(hexagonPlt0AtomContent);
  }
};

class HexagonPLTAtom : public PLTAtom {

public:
  HexagonPLTAtom(const File &f, StringRef secName) : PLTAtom(f, secName) {}

  ArrayRef<uint8_t> rawContent() const override {
    return makeArrayRef(hexagonPltAtomContent);
  }
};

class ELFPassFile : public SimpleFile {
public:
  ELFPassFile(const ELFLinkingContext &eti) : SimpleFile("ELFPassFile") {
    setOrdinal(eti.getNextOrdinalAndIncrement());
  }

  llvm::BumpPtrAllocator _alloc;
};

/// \brief Create GOT and PLT entries for relocations. Handles standard GOT/PLT
template <class Derived> class GOTPLTPass : public Pass {
  /// \brief Handle a specific reference.
  void handleReference(const DefinedAtom &atom, const Reference &ref) {
    if (ref.kindNamespace() != Reference::KindNamespace::ELF)
      return;
    assert(ref.kindArch() == Reference::KindArch::Hexagon);
    switch (ref.kindValue()) {
    case R_HEX_PLT_B22_PCREL:
    case R_HEX_B22_PCREL:
      static_cast<Derived *>(this)->handlePLT32(ref);
      break;
    case R_HEX_GOT_LO16:
    case R_HEX_GOT_HI16:
    case R_HEX_GOT_32_6_X:
    case R_HEX_GOT_16_X:
    case R_HEX_GOT_11_X:
      static_cast<Derived *>(this)->handleGOTREL(ref);
      break;
    }
  }

protected:
  /// \brief Create a GOT entry containing 0.
  const GOTAtom *getNullGOT() {
    if (!_null) {
      _null = new (_file._alloc) HexagonGOTPLTAtom(_file);
#ifndef NDEBUG
      _null->_name = "__got_null";
#endif
    }
    return _null;
  }

public:
  GOTPLTPass(const ELFLinkingContext &ctx)
      : _file(ctx), _null(nullptr), _PLT0(nullptr), _got0(nullptr) {}

  /// \brief Do the pass.
  ///
  /// The goal here is to first process each reference individually. Each call
  /// to handleReference may modify the reference itself and/or create new
  /// atoms which must be stored in one of the maps below.
  ///
  /// After all references are handled, the atoms created during that are all
  /// added to mf.
  void perform(std::unique_ptr<MutableFile> &mf) override {
    // Process all references.
    for (const auto &atom : mf->defined())
      for (const auto &ref : *atom)
        handleReference(*atom, *ref);

    // Add all created atoms to the link.
    uint64_t ordinal = 0;
    if (_PLT0) {
      _PLT0->setOrdinal(ordinal++);
      mf->addAtom(*_PLT0);
    }
    for (auto &plt : _pltVector) {
      plt->setOrdinal(ordinal++);
      mf->addAtom(*plt);
    }
    if (_null) {
      _null->setOrdinal(ordinal++);
      mf->addAtom(*_null);
    }
    if (_got0) {
      _got0->setOrdinal(ordinal++);
      mf->addAtom(*_got0);
    }
    for (auto &got : _gotVector) {
      got->setOrdinal(ordinal++);
      mf->addAtom(*got);
    }
  }

protected:
  /// \brief Owner of all the Atoms created by this pass.
  ELFPassFile _file;

  /// \brief Map Atoms to their GOT entries.
  llvm::DenseMap<const Atom *, GOTAtom *> _gotMap;

  /// \brief Map Atoms to their PLT entries.
  llvm::DenseMap<const Atom *, PLTAtom *> _pltMap;

  /// \brief the list of GOT/PLT atoms
  std::vector<GOTAtom *> _gotVector;
  std::vector<PLTAtom *> _pltVector;

  /// \brief GOT entry that is always 0. Used for undefined weaks.
  GOTAtom *_null;

  /// \brief The got and plt entries for .PLT0. This is used to call into the
  /// dynamic linker for symbol resolution.
  /// @{
  PLT0Atom *_PLT0;
  GOTAtom *_got0;
  /// @}
};

class DynamicGOTPLTPass final : public GOTPLTPass<DynamicGOTPLTPass> {
public:
  DynamicGOTPLTPass(const elf::HexagonLinkingContext &ctx) : GOTPLTPass(ctx) {
    _got0 = new (_file._alloc) HexagonGOTPLT0Atom(_file);
#ifndef NDEBUG
    _got0->_name = "__got0";
#endif
  }

  const PLT0Atom *getPLT0() {
    if (_PLT0)
      return _PLT0;
    _PLT0 = new (_file._alloc) HexagonPLT0Atom(_file);
    _PLT0->addReferenceELF_Hexagon(R_HEX_B32_PCREL_X, 0, _got0, 0);
    _PLT0->addReferenceELF_Hexagon(R_HEX_6_PCREL_X, 4, _got0, 4);
    DEBUG_WITH_TYPE("PLT", llvm::dbgs() << "[ PLT0/GOT0 ] "
                                        << "Adding plt0/got0 \n");
    return _PLT0;
  }

  const PLTAtom *getPLTEntry(const Atom *a) {
    auto plt = _pltMap.find(a);
    if (plt != _pltMap.end())
      return plt->second;
    auto ga = new (_file._alloc) HexagonGOTPLTAtom(_file);
    ga->addReferenceELF_Hexagon(R_HEX_JMP_SLOT, 0, a, 0);
    auto pa = new (_file._alloc) HexagonPLTAtom(_file, ".plt");
    pa->addReferenceELF_Hexagon(R_HEX_B32_PCREL_X, 0, ga, 0);
    pa->addReferenceELF_Hexagon(R_HEX_6_PCREL_X, 4, ga, 4);

    // Point the got entry to the PLT0 atom initially
    ga->addReferenceELF_Hexagon(R_HEX_32, 0, getPLT0(), 0);
#ifndef NDEBUG
    ga->_name = "__got_";
    ga->_name += a->name();
    pa->_name = "__plt_";
    pa->_name += a->name();
    DEBUG_WITH_TYPE("PLT", llvm::dbgs() << "[" << a->name() << "] "
                                        << "Adding plt/got: " << pa->_name
                                        << "/" << ga->_name << "\n");
#endif
    _gotMap[a] = ga;
    _pltMap[a] = pa;
    _gotVector.push_back(ga);
    _pltVector.push_back(pa);
    return pa;
  }

  const GOTAtom *getGOTEntry(const Atom *a) {
    auto got = _gotMap.find(a);
    if (got != _gotMap.end())
      return got->second;
    auto ga = new (_file._alloc) HexagonGOTAtom(_file);
    ga->addReferenceELF_Hexagon(R_HEX_GLOB_DAT, 0, a, 0);

#ifndef NDEBUG
    ga->_name = "__got_";
    ga->_name += a->name();
    DEBUG_WITH_TYPE("GOT", llvm::dbgs() << "[" << a->name() << "] "
                                        << "Adding got: " << ga->_name << "\n");
#endif
    _gotMap[a] = ga;
    _gotVector.push_back(ga);
    return ga;
  }

  std::error_code handleGOTREL(const Reference &ref) {
    // Turn this so that the target is set to the GOT entry
    const_cast<Reference &>(ref).setTarget(getGOTEntry(ref.target()));
    return std::error_code();
  }

  std::error_code handlePLT32(const Reference &ref) {
    // Turn this into a PC32 to the PLT entry.
    assert(ref.kindNamespace() == Reference::KindNamespace::ELF);
    assert(ref.kindArch() == Reference::KindArch::Hexagon);
    const_cast<Reference &>(ref).setKindValue(R_HEX_B22_PCREL);
    const_cast<Reference &>(ref).setTarget(getPLTEntry(ref.target()));
    return std::error_code();
  }
};

void elf::HexagonLinkingContext::addPasses(PassManager &pm) {
  if (isDynamic())
    pm.add(llvm::make_unique<DynamicGOTPLTPass>(*this));
  ELFLinkingContext::addPasses(pm);
}

void HexagonTargetHandler::registerRelocationNames(Registry &registry) {
  registry.addKindTable(Reference::KindNamespace::ELF,
                        Reference::KindArch::Hexagon, kindStrings);
}

#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name),

const Registry::KindStrings HexagonTargetHandler::kindStrings[] = {
#include "llvm/Support/ELFRelocs/Hexagon.def"
  LLD_KIND_STRING_END
};

#undef ELF_RELOC