summaryrefslogtreecommitdiffstats
path: root/lld/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.cpp
blob: 68138fffd0a383fb83de6f7425e0b1b7bef72ac2 (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
//===- lib/ReaderWriter/ELF/MipsELFFlagsMerger.cpp ------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "MipsELFFlagsMerger.h"
#include "lld/Core/Error.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/ELF.h"
#include "llvm/Support/raw_ostream.h"

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

struct MipsISATreeEdge {
  unsigned child;
  unsigned parent;
};

static MipsISATreeEdge isaTree[] = {
    // MIPS32R6 and MIPS64R6 are not compatible with other extensions

    // MIPS64 extensions.
    {EF_MIPS_ARCH_64R2, EF_MIPS_ARCH_64},
    // MIPS V extensions.
    {EF_MIPS_ARCH_64, EF_MIPS_ARCH_5},
    // MIPS IV extensions.
    {EF_MIPS_ARCH_5, EF_MIPS_ARCH_4},
    // MIPS III extensions.
    {EF_MIPS_ARCH_4, EF_MIPS_ARCH_3},
    // MIPS32 extensions.
    {EF_MIPS_ARCH_32R2, EF_MIPS_ARCH_32},
    // MIPS II extensions.
    {EF_MIPS_ARCH_3, EF_MIPS_ARCH_2},
    {EF_MIPS_ARCH_32, EF_MIPS_ARCH_2},
    // MIPS I extensions.
    {EF_MIPS_ARCH_2, EF_MIPS_ARCH_1},
};

static bool matchMipsISA(unsigned base, unsigned ext) {
  if (base == ext)
    return true;
  if (base == EF_MIPS_ARCH_32 && matchMipsISA(EF_MIPS_ARCH_64, ext))
    return true;
  if (base == EF_MIPS_ARCH_32R2 && matchMipsISA(EF_MIPS_ARCH_64R2, ext))
    return true;
  for (const auto &edge : isaTree) {
    if (ext == edge.child) {
      ext = edge.parent;
      if (ext == base)
        return true;
    }
  }
  return false;
}

MipsELFFlagsMerger::MipsELFFlagsMerger(bool is64Bits)
    : _is64Bit(is64Bits), _flags(0) {}

uint32_t MipsELFFlagsMerger::getMergedELFFlags() const { return _flags; }

std::error_code MipsELFFlagsMerger::mergeHeaderFlags(uint8_t newClass,
                                                     uint32_t newFlags) {
  // Check bitness.
  if (_is64Bit != (newClass == ELFCLASS64))
    return make_dynamic_error_code(
        Twine("Bitness is incompatible with that of the selected target"));

  // We support two ABI: O32 and N64. The last one does not have
  // the corresponding ELF flag.
  uint32_t inAbi = newFlags & EF_MIPS_ABI;
  uint32_t supportedAbi = _is64Bit ? 0 : uint32_t(EF_MIPS_ABI_O32);
  if (inAbi != supportedAbi)
    return make_dynamic_error_code(Twine("Unsupported ABI"));

  // ... and reduced set of architectures ...
  uint32_t newArch = newFlags & EF_MIPS_ARCH;
  switch (newArch) {
  case EF_MIPS_ARCH_1:
  case EF_MIPS_ARCH_2:
  case EF_MIPS_ARCH_3:
  case EF_MIPS_ARCH_4:
  case EF_MIPS_ARCH_5:
  case EF_MIPS_ARCH_32:
  case EF_MIPS_ARCH_64:
  case EF_MIPS_ARCH_32R2:
  case EF_MIPS_ARCH_64R2:
  case EF_MIPS_ARCH_32R6:
  case EF_MIPS_ARCH_64R6:
    break;
  default:
    return make_dynamic_error_code(Twine("Unsupported instruction set"));
  }

  // ... and still do not support MIPS-16 extension.
  if (newFlags & EF_MIPS_ARCH_ASE_M16)
    return make_dynamic_error_code(Twine("Unsupported extension: MIPS16"));

  // PIC code is inherently CPIC and may not set CPIC flag explicitly.
  // Ensure that this flag will exist in the linked file.
  if (newFlags & EF_MIPS_PIC)
    newFlags |= EF_MIPS_CPIC;

  std::lock_guard<std::mutex> lock(_mutex);

  // If the old set of flags is empty, use the new one as a result.
  if (!_flags) {
    _flags = newFlags;
    return std::error_code();
  }

  // Check PIC / CPIC flags compatibility.
  uint32_t newPic = newFlags & (EF_MIPS_PIC | EF_MIPS_CPIC);
  uint32_t oldPic = _flags & (EF_MIPS_PIC | EF_MIPS_CPIC);

  if ((newPic != 0) != (oldPic != 0))
    llvm::errs() << "lld warning: linking abicalls and non-abicalls files\n";

  if (!(newPic & EF_MIPS_PIC))
    _flags &= ~EF_MIPS_PIC;
  if (newPic)
    _flags |= EF_MIPS_CPIC;

  // Check mixing -mnan=2008 / -mnan=legacy modules.
  if ((newFlags & EF_MIPS_NAN2008) != (_flags & EF_MIPS_NAN2008))
    return make_dynamic_error_code(
        Twine("Linking -mnan=2008 and -mnan=legacy modules"));

  // Check ISA compatibility and update the extension flag.
  uint32_t oldArch = _flags & EF_MIPS_ARCH;
  if (!matchMipsISA(newArch, oldArch)) {
    if (!matchMipsISA(oldArch, newArch))
      return make_dynamic_error_code(
          Twine("Linking modules with incompatible ISA"));
    _flags &= ~EF_MIPS_ARCH;
    _flags |= newArch;
  }

  _flags |= newFlags & EF_MIPS_NOREORDER;
  _flags |= newFlags & EF_MIPS_MICROMIPS;
  _flags |= newFlags & EF_MIPS_NAN2008;
  _flags |= newFlags & EF_MIPS_32BITMODE;

  return std::error_code();
}
OpenPOWER on IntegriCloud