|  | //===- MipsArchTree.cpp --------------------------------------------------===// | 
|  | // | 
|  | //                             The LLVM Linker | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===---------------------------------------------------------------------===// | 
|  | // | 
|  | // 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; | 
|  | } | 
|  |  | 
|  | 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 0; | 
|  | 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<ELFT>>(F)) | 
|  | return EF->getObj().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>(); |