//===- 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/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 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 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 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 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 uint32_t elf::calcMipsEFlags() { std::vector v; for (InputFile *f : ctx.objectFiles) v.push_back({f, cast>(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 static bool isN32Abi(const InputFile *f) { if (auto *ef = dyn_cast(f)) return ef->template getObj().getHeader().e_flags & EF_MIPS_ABI2; return false; } bool elf::isMipsN32Abi(const InputFile *f) { switch (config->ekind) { case ELF32LEKind: return isN32Abi(f); case ELF32BEKind: return isN32Abi(f); case ELF64LEKind: return isN32Abi(f); case ELF64BEKind: return isN32Abi(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(); template uint32_t elf::calcMipsEFlags(); template uint32_t elf::calcMipsEFlags(); template uint32_t elf::calcMipsEFlags();