aboutsummaryrefslogtreecommitdiffstats
path: root/lib/Target/Mips/MCTargetDesc/MipsELFObjectWriter.cpp
blob: 8d9e3e31105ea72bdc6a9999be383ef82f4678b5 (plain)
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
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
//===-- MipsELFObjectWriter.cpp - Mips ELF Writer -------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "MCTargetDesc/MipsBaseInfo.h"
#include "MCTargetDesc/MipsFixupKinds.h"
#include "MCTargetDesc/MipsMCTargetDesc.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCELF.h"
#include "llvm/MC/MCELFObjectWriter.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCSection.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/ErrorHandling.h"
#include <list>

using namespace llvm;

namespace {
// A helper structure based on ELFRelocationEntry, used for sorting entries in
// the relocation table.
struct MipsRelocationEntry {
  MipsRelocationEntry(const ELFRelocationEntry &R)
      : R(R), SortOffset(R.Offset), HasMatchingHi(false) {}
  const ELFRelocationEntry R;
  // SortOffset equals R.Offset except for the *HI16 relocations, for which it
  // will be set based on the R.Offset of the matching *LO16 relocation.
  int64_t SortOffset;
  // True when this is a *LO16 relocation chosen as a match for a *HI16
  // relocation.
  bool HasMatchingHi;
};

  class MipsELFObjectWriter : public MCELFObjectTargetWriter {
  public:
    MipsELFObjectWriter(bool _is64Bit, uint8_t OSABI,
                        bool _isN64, bool IsLittleEndian);

    ~MipsELFObjectWriter() override;

    unsigned GetRelocType(const MCValue &Target, const MCFixup &Fixup,
                          bool IsPCRel) const override;
    bool needsRelocateWithSymbol(const MCSymbolData &SD,
                                 unsigned Type) const override;
    virtual void sortRelocs(const MCAssembler &Asm,
                            std::vector<ELFRelocationEntry> &Relocs) override;
  };
}

MipsELFObjectWriter::MipsELFObjectWriter(bool _is64Bit, uint8_t OSABI,
                                         bool _isN64, bool IsLittleEndian)
    : MCELFObjectTargetWriter(_is64Bit, OSABI, ELF::EM_MIPS,
                              /*HasRelocationAddend*/ _isN64,
                              /*IsN64*/ _isN64) {}

MipsELFObjectWriter::~MipsELFObjectWriter() {}

unsigned MipsELFObjectWriter::GetRelocType(const MCValue &Target,
                                           const MCFixup &Fixup,
                                           bool IsPCRel) const {
  // determine the type of the relocation
  unsigned Type = (unsigned)ELF::R_MIPS_NONE;
  unsigned Kind = (unsigned)Fixup.getKind();

  switch (Kind) {
  default:
    llvm_unreachable("invalid fixup kind!");
  case Mips::fixup_Mips_32:
  case FK_Data_4:
    Type = ELF::R_MIPS_32;
    break;
  case Mips::fixup_Mips_64:
  case FK_Data_8:
    Type = ELF::R_MIPS_64;
    break;
  case FK_GPRel_4:
    if (isN64()) {
      Type = setRType((unsigned)ELF::R_MIPS_GPREL32, Type);
      Type = setRType2((unsigned)ELF::R_MIPS_64, Type);
      Type = setRType3((unsigned)ELF::R_MIPS_NONE, Type);
    }
    else
      Type = ELF::R_MIPS_GPREL32;
    break;
  case Mips::fixup_Mips_GPREL16:
    Type = ELF::R_MIPS_GPREL16;
    break;
  case Mips::fixup_Mips_26:
    Type = ELF::R_MIPS_26;
    break;
  case Mips::fixup_Mips_CALL16:
    Type = ELF::R_MIPS_CALL16;
    break;
  case Mips::fixup_Mips_GOT_Global:
  case Mips::fixup_Mips_GOT_Local:
    Type = ELF::R_MIPS_GOT16;
    break;
  case Mips::fixup_Mips_HI16:
    Type = ELF::R_MIPS_HI16;
    break;
  case Mips::fixup_Mips_LO16:
    Type = ELF::R_MIPS_LO16;
    break;
  case Mips::fixup_Mips_TLSGD:
    Type = ELF::R_MIPS_TLS_GD;
    break;
  case Mips::fixup_Mips_GOTTPREL:
    Type = ELF::R_MIPS_TLS_GOTTPREL;
    break;
  case Mips::fixup_Mips_TPREL_HI:
    Type = ELF::R_MIPS_TLS_TPREL_HI16;
    break;
  case Mips::fixup_Mips_TPREL_LO:
    Type = ELF::R_MIPS_TLS_TPREL_LO16;
    break;
  case Mips::fixup_Mips_TLSLDM:
    Type = ELF::R_MIPS_TLS_LDM;
    break;
  case Mips::fixup_Mips_DTPREL_HI:
    Type = ELF::R_MIPS_TLS_DTPREL_HI16;
    break;
  case Mips::fixup_Mips_DTPREL_LO:
    Type = ELF::R_MIPS_TLS_DTPREL_LO16;
    break;
  case Mips::fixup_Mips_Branch_PCRel:
  case Mips::fixup_Mips_PC16:
    Type = ELF::R_MIPS_PC16;
    break;
  case Mips::fixup_Mips_GOT_PAGE:
    Type = ELF::R_MIPS_GOT_PAGE;
    break;
  case Mips::fixup_Mips_GOT_OFST:
    Type = ELF::R_MIPS_GOT_OFST;
    break;
  case Mips::fixup_Mips_GOT_DISP:
    Type = ELF::R_MIPS_GOT_DISP;
    break;
  case Mips::fixup_Mips_GPOFF_HI:
    Type = setRType((unsigned)ELF::R_MIPS_GPREL16, Type);
    Type = setRType2((unsigned)ELF::R_MIPS_SUB, Type);
    Type = setRType3((unsigned)ELF::R_MIPS_HI16, Type);
    break;
  case Mips::fixup_Mips_GPOFF_LO:
    Type = setRType((unsigned)ELF::R_MIPS_GPREL16, Type);
    Type = setRType2((unsigned)ELF::R_MIPS_SUB, Type);
    Type = setRType3((unsigned)ELF::R_MIPS_LO16, Type);
    break;
  case Mips::fixup_Mips_HIGHER:
    Type = ELF::R_MIPS_HIGHER;
    break;
  case Mips::fixup_Mips_HIGHEST:
    Type = ELF::R_MIPS_HIGHEST;
    break;
  case Mips::fixup_Mips_GOT_HI16:
    Type = ELF::R_MIPS_GOT_HI16;
    break;
  case Mips::fixup_Mips_GOT_LO16:
    Type = ELF::R_MIPS_GOT_LO16;
    break;
  case Mips::fixup_Mips_CALL_HI16:
    Type = ELF::R_MIPS_CALL_HI16;
    break;
  case Mips::fixup_Mips_CALL_LO16:
    Type = ELF::R_MIPS_CALL_LO16;
    break;
  case Mips::fixup_MICROMIPS_26_S1:
    Type = ELF::R_MICROMIPS_26_S1;
    break;
  case Mips::fixup_MICROMIPS_HI16:
    Type = ELF::R_MICROMIPS_HI16;
    break;
  case Mips::fixup_MICROMIPS_LO16:
    Type = ELF::R_MICROMIPS_LO16;
    break;
  case Mips::fixup_MICROMIPS_GOT16:
    Type = ELF::R_MICROMIPS_GOT16;
    break;
  case Mips::fixup_MICROMIPS_PC7_S1:
    Type = ELF::R_MICROMIPS_PC7_S1;
    break;
  case Mips::fixup_MICROMIPS_PC10_S1:
    Type = ELF::R_MICROMIPS_PC10_S1;
    break;
  case Mips::fixup_MICROMIPS_PC16_S1:
    Type = ELF::R_MICROMIPS_PC16_S1;
    break;
  case Mips::fixup_MICROMIPS_CALL16:
    Type = ELF::R_MICROMIPS_CALL16;
    break;
  case Mips::fixup_MICROMIPS_GOT_DISP:
    Type = ELF::R_MICROMIPS_GOT_DISP;
    break;
  case Mips::fixup_MICROMIPS_GOT_PAGE:
    Type = ELF::R_MICROMIPS_GOT_PAGE;
    break;
  case Mips::fixup_MICROMIPS_GOT_OFST:
    Type = ELF::R_MICROMIPS_GOT_OFST;
    break;
  case Mips::fixup_MICROMIPS_TLS_GD:
    Type = ELF::R_MICROMIPS_TLS_GD;
    break;
  case Mips::fixup_MICROMIPS_TLS_LDM:
    Type = ELF::R_MICROMIPS_TLS_LDM;
    break;
  case Mips::fixup_MICROMIPS_TLS_DTPREL_HI16:
    Type = ELF::R_MICROMIPS_TLS_DTPREL_HI16;
    break;
  case Mips::fixup_MICROMIPS_TLS_DTPREL_LO16:
    Type = ELF::R_MICROMIPS_TLS_DTPREL_LO16;
    break;
  case Mips::fixup_MICROMIPS_TLS_TPREL_HI16:
    Type = ELF::R_MICROMIPS_TLS_TPREL_HI16;
    break;
  case Mips::fixup_MICROMIPS_TLS_TPREL_LO16:
    Type = ELF::R_MICROMIPS_TLS_TPREL_LO16;
    break;
  case Mips::fixup_MIPS_PC19_S2:
    Type = ELF::R_MIPS_PC19_S2;
    break;
  case Mips::fixup_MIPS_PC18_S3:
    Type = ELF::R_MIPS_PC18_S3;
    break;
  case Mips::fixup_MIPS_PC21_S2:
    Type = ELF::R_MIPS_PC21_S2;
    break;
  case Mips::fixup_MIPS_PC26_S2:
    Type = ELF::R_MIPS_PC26_S2;
    break;
  case Mips::fixup_MIPS_PCHI16:
    Type = ELF::R_MIPS_PCHI16;
    break;
  case Mips::fixup_MIPS_PCLO16:
    Type = ELF::R_MIPS_PCLO16;
    break;
  }
  return Type;
}

// Sort entries by SortOffset in descending order.
// When there are more *HI16 relocs paired with one *LO16 reloc, the 2nd rule
// sorts them in ascending order of R.Offset.
static int cmpRelMips(const MipsRelocationEntry *AP,
                      const MipsRelocationEntry *BP) {
  const MipsRelocationEntry &A = *AP;
  const MipsRelocationEntry &B = *BP;
  if (A.SortOffset != B.SortOffset)
    return B.SortOffset - A.SortOffset;
  if (A.R.Offset != B.R.Offset)
    return A.R.Offset - B.R.Offset;
  if (B.R.Type != A.R.Type)
    return B.R.Type - A.R.Type;
  //llvm_unreachable("ELFRelocs might be unstable!");
  return 0;
}

// For the given Reloc.Type, return the matching relocation type, as in the
// table below.
static unsigned getMatchingLoType(const MCAssembler &Asm,
                                  const ELFRelocationEntry &Reloc) {
  unsigned Type = Reloc.Type;
  if (Type == ELF::R_MIPS_HI16)
    return ELF::R_MIPS_LO16;
  if (Type == ELF::R_MICROMIPS_HI16)
    return ELF::R_MICROMIPS_LO16;
  if (Type == ELF::R_MIPS16_HI16)
    return ELF::R_MIPS16_LO16;

  const MCSymbolData &SD = Asm.getSymbolData(*Reloc.Symbol);

  if (MCELF::GetBinding(SD) != ELF::STB_LOCAL)
    return ELF::R_MIPS_NONE;

  if (Type == ELF::R_MIPS_GOT16)
    return ELF::R_MIPS_LO16;
  if (Type == ELF::R_MICROMIPS_GOT16)
    return ELF::R_MICROMIPS_LO16;
  if (Type == ELF::R_MIPS16_GOT16)
    return ELF::R_MIPS16_LO16;

  return ELF::R_MIPS_NONE;
}

// Return true if First needs a matching *LO16, its matching *LO16 type equals
// Second's type and both relocations are against the same symbol.
static bool areMatchingHiAndLo(const MCAssembler &Asm,
                               const ELFRelocationEntry &First,
                               const ELFRelocationEntry &Second) {
  return getMatchingLoType(Asm, First) != ELF::R_MIPS_NONE &&
         getMatchingLoType(Asm, First) == Second.Type &&
         First.Symbol && First.Symbol == Second.Symbol;
}

// Return true if MipsRelocs[Index] is a *LO16 preceded by a matching *HI16.
static bool
isPrecededByMatchingHi(const MCAssembler &Asm, uint32_t Index,
                       std::vector<MipsRelocationEntry> &MipsRelocs) {
  return Index < MipsRelocs.size() - 1 &&
         areMatchingHiAndLo(Asm, MipsRelocs[Index + 1].R, MipsRelocs[Index].R);
}

// Return true if MipsRelocs[Index] is a *LO16 not preceded by a matching *HI16
// and not chosen by a *HI16 as a match.
static bool isFreeLo(const MCAssembler &Asm, uint32_t Index,
                     std::vector<MipsRelocationEntry> &MipsRelocs) {
  return Index < MipsRelocs.size() && !MipsRelocs[Index].HasMatchingHi &&
         !isPrecededByMatchingHi(Asm, Index, MipsRelocs);
}

// Lo is chosen as a match for Hi, set their fields accordingly.
// Mips instructions have fixed length of at least two bytes (two for
// micromips/mips16, four for mips32/64), so we can set HI's SortOffset to
// matching LO's Offset minus one to simplify the sorting function.
static void setMatch(MipsRelocationEntry &Hi, MipsRelocationEntry &Lo) {
  Lo.HasMatchingHi = true;
  Hi.SortOffset = Lo.R.Offset - 1;
}

// We sort relocation table entries by offset, except for one additional rule
// required by MIPS ABI: every *HI16 relocation must be immediately followed by
// the corresponding *LO16 relocation. We also support a GNU extension that
// allows more *HI16s paired with one *LO16.
//
// *HI16 relocations and their matching *LO16 are:
//
// +---------------------------------------------+-------------------+
// |               *HI16                         |  matching *LO16   |
// |---------------------------------------------+-------------------|
// |  R_MIPS_HI16, local R_MIPS_GOT16            |    R_MIPS_LO16    |
// |  R_MICROMIPS_HI16, local R_MICROMIPS_GOT16  | R_MICROMIPS_LO16  |
// |  R_MIPS16_HI16, local R_MIPS16_GOT16        |  R_MIPS16_LO16    |
// +---------------------------------------------+-------------------+
//
// (local R_*_GOT16 meaning R_*_GOT16 against the local symbol.)
//
// To handle *HI16 and *LO16 relocations, the linker needs a combined addend
// ("AHL") calculated from both *HI16 ("AHI") and *LO16 ("ALO") relocations:
// AHL = (AHI << 16) + (short)ALO;
//
// We are reusing gnu as sorting algorithm so we are emitting the relocation
// table sorted the same way as gnu as would sort it, for easier comparison of
// the generated .o files.
//
// The logic is:
// search the table (starting from the highest offset and going back to zero)
// for all *HI16 relocations that don't have a matching *LO16.
// For every such HI, find a matching LO with highest offset that isn't already
// matched with another HI. If there are no free LOs, match it with the first
// found (starting from lowest offset).
// When there are more HIs matched with one LO, sort them in descending order by
// offset.
//
// In other words, when searching for a matching LO:
// - don't look for a 'better' match for the HIs that are already followed by a
//   matching LO;
// - prefer LOs without a pair;
// - prefer LOs with higher offset;
void MipsELFObjectWriter::sortRelocs(const MCAssembler &Asm,
                                     std::vector<ELFRelocationEntry> &Relocs) {
  if (Relocs.size() < 2)
    return;

  // The default function sorts entries by Offset in descending order.
  MCELFObjectTargetWriter::sortRelocs(Asm, Relocs);

  // Init MipsRelocs from Relocs.
  std::vector<MipsRelocationEntry> MipsRelocs;
  for (unsigned I = 0, E = Relocs.size(); I != E; ++I)
    MipsRelocs.push_back(MipsRelocationEntry(Relocs[I]));

  // Find a matching LO for all HIs that need it.
  for (int32_t I = 0, E = MipsRelocs.size(); I != E; ++I) {
    if (getMatchingLoType(Asm, MipsRelocs[I].R) == ELF::R_MIPS_NONE ||
        (I > 0 && isPrecededByMatchingHi(Asm, I - 1, MipsRelocs)))
      continue;

    int32_t MatchedLoIndex = -1;

    // Search the list in the ascending order of Offset.
    for (int32_t J = MipsRelocs.size() - 1, N = -1; J != N; --J) {
      // check for a match
      if (areMatchingHiAndLo(Asm, MipsRelocs[I].R, MipsRelocs[J].R) &&
          (MatchedLoIndex == -1 || // first match
           // or we already have a match,
           // but this one is with higher offset and it's free
           (MatchedLoIndex > J && isFreeLo(Asm, J, MipsRelocs))))
        MatchedLoIndex = J;
    }

    if (MatchedLoIndex != -1)
      // We have a match.
      setMatch(MipsRelocs[I], MipsRelocs[MatchedLoIndex]);
  }

  // SortOffsets are calculated, call the sorting function.
  array_pod_sort(MipsRelocs.begin(), MipsRelocs.end(), cmpRelMips);

  // Copy sorted MipsRelocs back to Relocs.
  for (unsigned I = 0, E = MipsRelocs.size(); I != E; ++I)
    Relocs[I] = MipsRelocs[I].R;
}

bool
MipsELFObjectWriter::needsRelocateWithSymbol(const MCSymbolData &SD,
                                             unsigned Type) const {
  // FIXME: This is extremely conservative. This really needs to use a
  // whitelist with a clear explanation for why each realocation needs to
  // point to the symbol, not to the section.
  switch (Type) {
  default:
    return true;

  case ELF::R_MIPS_GOT16:
  case ELF::R_MIPS16_GOT16:
  case ELF::R_MICROMIPS_GOT16:
    llvm_unreachable("Should have been handled already");

  // These relocations might be paired with another relocation. The pairing is
  // done by the static linker by matching the symbol. Since we only see one
  // relocation at a time, we have to force them to relocate with a symbol to
  // avoid ending up with a pair where one points to a section and another
  // points to a symbol.
  case ELF::R_MIPS_HI16:
  case ELF::R_MIPS16_HI16:
  case ELF::R_MICROMIPS_HI16:
  case ELF::R_MIPS_LO16:
  case ELF::R_MIPS16_LO16:
  case ELF::R_MICROMIPS_LO16:
    return true;

  case ELF::R_MIPS_32:
    if (MCELF::getOther(SD) & (ELF::STO_MIPS_MICROMIPS >> 2))
      return true;
    // falltrough
  case ELF::R_MIPS_26:
  case ELF::R_MIPS_64:
  case ELF::R_MIPS_GPREL16:
    return false;
  }
}

MCObjectWriter *llvm::createMipsELFObjectWriter(raw_pwrite_stream &OS,
                                                uint8_t OSABI,
                                                bool IsLittleEndian,
                                                bool Is64Bit) {
  MCELFObjectTargetWriter *MOTW =
      new MipsELFObjectWriter(Is64Bit, OSABI, Is64Bit, IsLittleEndian);
  return createELFObjectWriter(MOTW, OS, IsLittleEndian);
}