forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			390 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			390 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- 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())
 | |
|     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>(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>();
 |