summaryrefslogtreecommitdiff
path: root/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.cpp')
-rw-r--r--lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.cpp149
1 files changed, 149 insertions, 0 deletions
diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.cpp b/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.cpp
new file mode 100644
index 0000000000000..0ef2c70b81564
--- /dev/null
+++ b/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.cpp
@@ -0,0 +1,149 @@
+//===- 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::merge(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();
+}