diff options
Diffstat (limited to 'contrib/llvm-project/lld/ELF/Arch/MipsArchTree.cpp')
| -rw-r--r-- | contrib/llvm-project/lld/ELF/Arch/MipsArchTree.cpp | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/contrib/llvm-project/lld/ELF/Arch/MipsArchTree.cpp b/contrib/llvm-project/lld/ELF/Arch/MipsArchTree.cpp new file mode 100644 index 000000000000..20e07e1114eb --- /dev/null +++ b/contrib/llvm-project/lld/ELF/Arch/MipsArchTree.cpp @@ -0,0 +1,407 @@ +//===- MipsArchTree.cpp --------------------------------------------------===// +// +// 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 +// +//===---------------------------------------------------------------------===// +// +// This file contains a helper function for the Writer. +// +//===---------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "SymbolTable.h" +#include "Writer.h" + +#include "lld/Common/ErrorHandler.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/MipsABIFlags.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::ELF; + +using namespace lld; +using namespace lld::elf; + +namespace { +struct ArchTreeEdge { + uint32_t child; + uint32_t parent; +}; + +struct FileFlags { + InputFile *file; + uint32_t flags; +}; +} // namespace + +static StringRef getAbiName(uint32_t flags) { + switch (flags) { + case 0: + return "n64"; + case EF_MIPS_ABI2: + return "n32"; + case EF_MIPS_ABI_O32: + return "o32"; + case EF_MIPS_ABI_O64: + return "o64"; + case EF_MIPS_ABI_EABI32: + return "eabi32"; + case EF_MIPS_ABI_EABI64: + return "eabi64"; + default: + return "unknown"; + } +} + +static StringRef getNanName(bool isNan2008) { + return isNan2008 ? "2008" : "legacy"; +} + +static StringRef getFpName(bool isFp64) { return isFp64 ? "64" : "32"; } + +static void checkFlags(ArrayRef<FileFlags> files) { + assert(!files.empty() && "expected non-empty file list"); + + uint32_t abi = files[0].flags & (EF_MIPS_ABI | EF_MIPS_ABI2); + bool nan = files[0].flags & EF_MIPS_NAN2008; + bool fp = files[0].flags & EF_MIPS_FP64; + + for (const FileFlags &f : files) { + if (config->is64 && f.flags & EF_MIPS_MICROMIPS) + error(toString(f.file) + ": microMIPS 64-bit is not supported"); + + uint32_t abi2 = f.flags & (EF_MIPS_ABI | EF_MIPS_ABI2); + if (abi != abi2) + error(toString(f.file) + ": ABI '" + getAbiName(abi2) + + "' is incompatible with target ABI '" + getAbiName(abi) + "'"); + + bool nan2 = f.flags & EF_MIPS_NAN2008; + if (nan != nan2) + error(toString(f.file) + ": -mnan=" + getNanName(nan2) + + " is incompatible with target -mnan=" + getNanName(nan)); + + bool fp2 = f.flags & EF_MIPS_FP64; + if (fp != fp2) + error(toString(f.file) + ": -mfp" + getFpName(fp2) + + " is incompatible with target -mfp" + getFpName(fp)); + } +} + +static uint32_t getMiscFlags(ArrayRef<FileFlags> files) { + uint32_t ret = 0; + for (const FileFlags &f : files) + ret |= f.flags & + (EF_MIPS_ABI | EF_MIPS_ABI2 | EF_MIPS_ARCH_ASE | EF_MIPS_NOREORDER | + EF_MIPS_MICROMIPS | EF_MIPS_NAN2008 | EF_MIPS_32BITMODE); + return ret; +} + +static uint32_t getPicFlags(ArrayRef<FileFlags> files) { + // Check PIC/non-PIC compatibility. + bool isPic = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + for (const FileFlags &f : files.slice(1)) { + bool isPic2 = f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + if (isPic && !isPic2) + warn(toString(f.file) + + ": linking non-abicalls code with abicalls code " + + toString(files[0].file)); + if (!isPic && isPic2) + warn(toString(f.file) + + ": linking abicalls code with non-abicalls code " + + toString(files[0].file)); + } + + // Compute the result PIC/non-PIC flag. + uint32_t ret = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + for (const FileFlags &f : files.slice(1)) + ret &= f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + + // PIC code is inherently CPIC and may not set CPIC flag explicitly. + if (ret & EF_MIPS_PIC) + ret |= EF_MIPS_CPIC; + return ret; +} + +static ArchTreeEdge archTree[] = { + // MIPS32R6 and MIPS64R6 are not compatible with other extensions + // MIPS64R2 extensions. + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON3, EF_MIPS_ARCH_64R2}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON2, EF_MIPS_ARCH_64R2}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON, EF_MIPS_ARCH_64R2}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_LS3A, EF_MIPS_ARCH_64R2}, + // MIPS64 extensions. + {EF_MIPS_ARCH_64 | EF_MIPS_MACH_SB1, EF_MIPS_ARCH_64}, + {EF_MIPS_ARCH_64 | EF_MIPS_MACH_XLR, EF_MIPS_ARCH_64}, + {EF_MIPS_ARCH_64R2, EF_MIPS_ARCH_64}, + // MIPS V extensions. + {EF_MIPS_ARCH_64, EF_MIPS_ARCH_5}, + // R5000 extensions. + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5500, EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400}, + // MIPS IV extensions. + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400, EF_MIPS_ARCH_4}, + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_9000, EF_MIPS_ARCH_4}, + {EF_MIPS_ARCH_5, EF_MIPS_ARCH_4}, + // VR4100 extensions. + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4111, EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4120, EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100}, + // MIPS III extensions. + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4010, EF_MIPS_ARCH_3}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100, EF_MIPS_ARCH_3}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4650, EF_MIPS_ARCH_3}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_5900, EF_MIPS_ARCH_3}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2E, EF_MIPS_ARCH_3}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2F, EF_MIPS_ARCH_3}, + {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_1 | EF_MIPS_MACH_3900, EF_MIPS_ARCH_1}, + {EF_MIPS_ARCH_2, EF_MIPS_ARCH_1}, +}; + +static bool isArchMatched(uint32_t New, uint32_t res) { + if (New == res) + return true; + if (New == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, res)) + return true; + if (New == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, res)) + return true; + for (const auto &edge : archTree) { + if (res == edge.child) { + res = edge.parent; + if (res == New) + return true; + } + } + return false; +} + +static StringRef getMachName(uint32_t flags) { + switch (flags & EF_MIPS_MACH) { + case EF_MIPS_MACH_NONE: + return ""; + case EF_MIPS_MACH_3900: + return "r3900"; + case EF_MIPS_MACH_4010: + return "r4010"; + case EF_MIPS_MACH_4100: + return "r4100"; + case EF_MIPS_MACH_4650: + return "r4650"; + case EF_MIPS_MACH_4120: + return "r4120"; + case EF_MIPS_MACH_4111: + return "r4111"; + case EF_MIPS_MACH_5400: + return "vr5400"; + case EF_MIPS_MACH_5900: + return "vr5900"; + case EF_MIPS_MACH_5500: + return "vr5500"; + case EF_MIPS_MACH_9000: + return "rm9000"; + case EF_MIPS_MACH_LS2E: + return "loongson2e"; + case EF_MIPS_MACH_LS2F: + return "loongson2f"; + case EF_MIPS_MACH_LS3A: + return "loongson3a"; + case EF_MIPS_MACH_OCTEON: + return "octeon"; + case EF_MIPS_MACH_OCTEON2: + return "octeon2"; + case EF_MIPS_MACH_OCTEON3: + return "octeon3"; + case EF_MIPS_MACH_SB1: + return "sb1"; + case EF_MIPS_MACH_XLR: + return "xlr"; + default: + return "unknown machine"; + } +} + +static StringRef getArchName(uint32_t flags) { + switch (flags & EF_MIPS_ARCH) { + case EF_MIPS_ARCH_1: + return "mips1"; + case EF_MIPS_ARCH_2: + return "mips2"; + case EF_MIPS_ARCH_3: + return "mips3"; + case EF_MIPS_ARCH_4: + return "mips4"; + case EF_MIPS_ARCH_5: + return "mips5"; + case EF_MIPS_ARCH_32: + return "mips32"; + case EF_MIPS_ARCH_64: + return "mips64"; + case EF_MIPS_ARCH_32R2: + return "mips32r2"; + case EF_MIPS_ARCH_64R2: + return "mips64r2"; + case EF_MIPS_ARCH_32R6: + return "mips32r6"; + case EF_MIPS_ARCH_64R6: + return "mips64r6"; + default: + return "unknown arch"; + } +} + +static std::string getFullArchName(uint32_t flags) { + StringRef arch = getArchName(flags); + StringRef mach = getMachName(flags); + if (mach.empty()) + return arch.str(); + return (arch + " (" + mach + ")").str(); +} + +// There are (arguably too) many MIPS ISAs out there. Their relationships +// can be represented as a forest. If all input files have ISAs which +// reachable by repeated proceeding from the single child to the parent, +// these input files are compatible. In that case we need to return "highest" +// ISA. If there are incompatible input files, we show an error. +// For example, mips1 is a "parent" of mips2 and such files are compatible. +// Output file gets EF_MIPS_ARCH_2 flag. From the other side mips3 and mips32 +// are incompatible because nor mips3 is a parent for misp32, nor mips32 +// is a parent for mips3. +static uint32_t getArchFlags(ArrayRef<FileFlags> files) { + uint32_t ret = files[0].flags & (EF_MIPS_ARCH | EF_MIPS_MACH); + + for (const FileFlags &f : files.slice(1)) { + uint32_t New = f.flags & (EF_MIPS_ARCH | EF_MIPS_MACH); + + // Check ISA compatibility. + if (isArchMatched(New, ret)) + continue; + if (!isArchMatched(ret, New)) { + error("incompatible target ISA:\n>>> " + toString(files[0].file) + ": " + + getFullArchName(ret) + "\n>>> " + toString(f.file) + ": " + + getFullArchName(New)); + return 0; + } + ret = New; + } + return ret; +} + +// If we don't have any input files, we'll have to rely on the information we +// can derive from emulation information, since this at least gets us ABI. +static uint32_t getFlagsFromEmulation() { + uint32_t ret = 0; + + if (config->emulation.empty()) + return 0; + + if (config->ekind == ELF32BEKind || config->ekind == ELF32LEKind) { + if (config->mipsN32Abi) + ret |= EF_MIPS_ABI2; + else + ret |= EF_MIPS_ABI_O32; + } + + return ret; +} + +template <class ELFT> uint32_t elf::calcMipsEFlags() { + std::vector<FileFlags> v; + for (InputFile *f : objectFiles) + v.push_back({f, cast<ObjFile<ELFT>>(f)->getObj().getHeader()->e_flags}); + if (v.empty()) + return getFlagsFromEmulation(); + checkFlags(v); + return getMiscFlags(v) | getPicFlags(v) | getArchFlags(v); +} + +static int compareMipsFpAbi(uint8_t fpA, uint8_t fpB) { + if (fpA == fpB) + return 0; + if (fpB == Mips::Val_GNU_MIPS_ABI_FP_ANY) + return 1; + if (fpB == Mips::Val_GNU_MIPS_ABI_FP_64A && + fpA == Mips::Val_GNU_MIPS_ABI_FP_64) + return 1; + if (fpB != Mips::Val_GNU_MIPS_ABI_FP_XX) + return -1; + if (fpA == Mips::Val_GNU_MIPS_ABI_FP_DOUBLE || + fpA == Mips::Val_GNU_MIPS_ABI_FP_64 || + fpA == Mips::Val_GNU_MIPS_ABI_FP_64A) + return 1; + return -1; +} + +static StringRef getMipsFpAbiName(uint8_t fpAbi) { + switch (fpAbi) { + case Mips::Val_GNU_MIPS_ABI_FP_ANY: + return "any"; + case Mips::Val_GNU_MIPS_ABI_FP_DOUBLE: + return "-mdouble-float"; + case Mips::Val_GNU_MIPS_ABI_FP_SINGLE: + return "-msingle-float"; + case Mips::Val_GNU_MIPS_ABI_FP_SOFT: + return "-msoft-float"; + case Mips::Val_GNU_MIPS_ABI_FP_OLD_64: + return "-mgp32 -mfp64 (old)"; + case Mips::Val_GNU_MIPS_ABI_FP_XX: + return "-mfpxx"; + case Mips::Val_GNU_MIPS_ABI_FP_64: + return "-mgp32 -mfp64"; + case Mips::Val_GNU_MIPS_ABI_FP_64A: + return "-mgp32 -mfp64 -mno-odd-spreg"; + default: + return "unknown"; + } +} + +uint8_t elf::getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag, + StringRef fileName) { + if (compareMipsFpAbi(newFlag, oldFlag) >= 0) + return newFlag; + if (compareMipsFpAbi(oldFlag, newFlag) < 0) + error(fileName + ": floating point ABI '" + getMipsFpAbiName(newFlag) + + "' is incompatible with target floating point ABI '" + + getMipsFpAbiName(oldFlag) + "'"); + return oldFlag; +} + +template <class ELFT> static bool isN32Abi(const InputFile *f) { + if (auto *ef = dyn_cast<ELFFileBase>(f)) + return ef->template getObj<ELFT>().getHeader()->e_flags & EF_MIPS_ABI2; + return false; +} + +bool elf::isMipsN32Abi(const InputFile *f) { + switch (config->ekind) { + case ELF32LEKind: + return isN32Abi<ELF32LE>(f); + case ELF32BEKind: + return isN32Abi<ELF32BE>(f); + case ELF64LEKind: + return isN32Abi<ELF64LE>(f); + case ELF64BEKind: + return isN32Abi<ELF64BE>(f); + default: + llvm_unreachable("unknown Config->EKind"); + } +} + +bool elf::isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; } + +bool elf::isMipsR6() { + uint32_t arch = config->eflags & EF_MIPS_ARCH; + return arch == EF_MIPS_ARCH_32R6 || arch == EF_MIPS_ARCH_64R6; +} + +template uint32_t elf::calcMipsEFlags<ELF32LE>(); +template uint32_t elf::calcMipsEFlags<ELF32BE>(); +template uint32_t elf::calcMipsEFlags<ELF64LE>(); +template uint32_t elf::calcMipsEFlags<ELF64BE>(); |
