| //===- 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 newFlags, uint32_t res) { |
| if (newFlags == res) |
| return true; |
| if (newFlags == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, res)) |
| return true; |
| if (newFlags == 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 == newFlags) |
| 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 newFlags = f.flags & (EF_MIPS_ARCH | EF_MIPS_MACH); |
| |
| // Check ISA compatibility. |
| if (isArchMatched(newFlags, ret)) |
| continue; |
| if (!isArchMatched(ret, newFlags)) { |
| error("incompatible target ISA:\n>>> " + toString(files[0].file) + ": " + |
| getFullArchName(ret) + "\n>>> " + toString(f.file) + ": " + |
| getFullArchName(newFlags)); |
| return 0; |
| } |
| ret = newFlags; |
| } |
| 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()) { |
| // 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. |
| if (config->emulation.empty() || config->is64) |
| return 0; |
| return config->mipsN32Abi ? EF_MIPS_ABI2 : EF_MIPS_ABI_O32; |
| } |
| 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>(); |