| //===- MipsAbiFlags.cpp ---------------------------------------------------===// |
| // |
| // The MCLinker Project |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| #include "MipsAbiFlags.h" |
| |
| #include "mcld/Fragment/RegionFragment.h" |
| #include "mcld/LD/LDSection.h" |
| #include "mcld/LD/SectionData.h" |
| #include "mcld/MC/Input.h" |
| #include "mcld/Support/MsgHandling.h" |
| |
| #include <llvm/Support/Casting.h> |
| #include <llvm/Support/MipsABIFlags.h> |
| |
| namespace mcld { |
| |
| // SHT_MIPS_ABIFLAGS has the same format for both 32/64-bit targets. |
| // We do not support linking of big-endian code now so 32-bit LE |
| // combination is Okay. |
| typedef llvm::object::ELFType<llvm::support::little, false> ELF32LE; |
| typedef llvm::object::Elf_Mips_ABIFlags<ELF32LE> ElfMipsAbiFlags; |
| |
| uint64_t MipsAbiFlags::size() { |
| return sizeof(ElfMipsAbiFlags); |
| } |
| |
| uint64_t MipsAbiFlags::emit(const MipsAbiFlags& pInfo, MemoryRegion& pRegion) { |
| auto* buf = reinterpret_cast<ElfMipsAbiFlags*>(pRegion.begin()); |
| buf->version = 0; |
| buf->isa_level = pInfo.m_IsaLevel; |
| buf->isa_rev = pInfo.m_IsaRev; |
| buf->gpr_size = pInfo.m_GprSize; |
| buf->cpr1_size = pInfo.m_Cpr1Size; |
| buf->cpr2_size = pInfo.m_Cpr2Size; |
| buf->fp_abi = pInfo.m_FpAbi; |
| buf->isa_ext = pInfo.m_IsaExt; |
| buf->ases = pInfo.m_Ases; |
| buf->flags1 = pInfo.m_Flags1; |
| buf->flags2 = 0; |
| return size(); |
| } |
| |
| bool MipsAbiFlags::fillBySection(const Input& pInput, const LDSection& pSection, |
| MipsAbiFlags& mipsAbi) { |
| assert(pSection.type() == llvm::ELF::SHT_MIPS_ABIFLAGS && |
| "Unexpected section type"); |
| |
| if (pSection.size() != size()) { |
| error(diag::error_Mips_abiflags_invalid_size) << pInput.name(); |
| return false; |
| } |
| |
| const SectionData* secData = pSection.getSectionData(); |
| if (secData->size() != 2 || !llvm::isa<RegionFragment>(secData->front())) { |
| error(diag::error_Mips_abiflags_invalid_size) << pInput.name(); |
| return false; |
| } |
| |
| const auto& frag = llvm::cast<RegionFragment>(secData->front()); |
| auto* data = |
| reinterpret_cast<const ElfMipsAbiFlags*>(frag.getRegion().data()); |
| if (data->version != 0) { |
| error(diag::error_Mips_abiflags_invalid_version) << int(data->version) |
| << pInput.name(); |
| return false; |
| } |
| |
| mipsAbi.m_IsaLevel = data->isa_level; |
| mipsAbi.m_IsaRev = data->isa_rev; |
| mipsAbi.m_GprSize = data->gpr_size; |
| mipsAbi.m_Cpr1Size = data->cpr1_size; |
| mipsAbi.m_Cpr2Size = data->cpr2_size; |
| mipsAbi.m_FpAbi = data->fp_abi; |
| mipsAbi.m_IsaExt = data->isa_ext; |
| mipsAbi.m_Ases = data->ases; |
| mipsAbi.m_Flags1 = data->flags1; |
| return true; |
| } |
| |
| static unsigned getIsaLevel(uint64_t flags) { |
| switch (flags & llvm::ELF::EF_MIPS_ARCH) { |
| case llvm::ELF::EF_MIPS_ARCH_1: |
| return 1; |
| case llvm::ELF::EF_MIPS_ARCH_2: |
| return 2; |
| case llvm::ELF::EF_MIPS_ARCH_3: |
| return 3; |
| case llvm::ELF::EF_MIPS_ARCH_4: |
| return 4; |
| case llvm::ELF::EF_MIPS_ARCH_5: |
| return 5; |
| case llvm::ELF::EF_MIPS_ARCH_32: |
| case llvm::ELF::EF_MIPS_ARCH_32R2: |
| case llvm::ELF::EF_MIPS_ARCH_32R6: |
| return 32; |
| case llvm::ELF::EF_MIPS_ARCH_64: |
| case llvm::ELF::EF_MIPS_ARCH_64R2: |
| case llvm::ELF::EF_MIPS_ARCH_64R6: |
| return 64; |
| default: |
| // We check ELF flags and show error in case |
| // of unknown value in other place. |
| llvm_unreachable("Unknown MIPS architecture flag"); |
| } |
| } |
| |
| static unsigned getIsaRev(uint64_t flags) { |
| switch (flags & llvm::ELF::EF_MIPS_ARCH) { |
| case llvm::ELF::EF_MIPS_ARCH_1: |
| case llvm::ELF::EF_MIPS_ARCH_2: |
| case llvm::ELF::EF_MIPS_ARCH_3: |
| case llvm::ELF::EF_MIPS_ARCH_4: |
| case llvm::ELF::EF_MIPS_ARCH_5: |
| return 0; |
| case llvm::ELF::EF_MIPS_ARCH_32: |
| case llvm::ELF::EF_MIPS_ARCH_64: |
| return 1; |
| case llvm::ELF::EF_MIPS_ARCH_32R2: |
| case llvm::ELF::EF_MIPS_ARCH_64R2: |
| return 2; |
| case llvm::ELF::EF_MIPS_ARCH_32R6: |
| case llvm::ELF::EF_MIPS_ARCH_64R6: |
| return 6; |
| default: |
| // We check ELF flags and show error in case |
| // of unknown value in other place. |
| llvm_unreachable("Unknown MIPS architecture flag"); |
| } |
| } |
| |
| static unsigned getIsaExt(uint64_t flags) { |
| switch (flags & llvm::ELF::EF_MIPS_MACH) { |
| case 0: |
| return llvm::Mips::AFL_EXT_NONE; |
| case llvm::ELF::EF_MIPS_MACH_3900: return llvm::Mips::AFL_EXT_3900; |
| case llvm::ELF::EF_MIPS_MACH_4010: return llvm::Mips::AFL_EXT_4010; |
| case llvm::ELF::EF_MIPS_MACH_4100: return llvm::Mips::AFL_EXT_4010; |
| case llvm::ELF::EF_MIPS_MACH_4111: return llvm::Mips::AFL_EXT_4111; |
| case llvm::ELF::EF_MIPS_MACH_4120: return llvm::Mips::AFL_EXT_4120; |
| case llvm::ELF::EF_MIPS_MACH_4650: return llvm::Mips::AFL_EXT_4650; |
| case llvm::ELF::EF_MIPS_MACH_5400: return llvm::Mips::AFL_EXT_5400; |
| case llvm::ELF::EF_MIPS_MACH_5500: return llvm::Mips::AFL_EXT_5500; |
| case llvm::ELF::EF_MIPS_MACH_5900: return llvm::Mips::AFL_EXT_5900; |
| case llvm::ELF::EF_MIPS_MACH_SB1: return llvm::Mips::AFL_EXT_SB1; |
| case llvm::ELF::EF_MIPS_MACH_LS2E: return llvm::Mips::AFL_EXT_LOONGSON_2E; |
| case llvm::ELF::EF_MIPS_MACH_LS2F: return llvm::Mips::AFL_EXT_LOONGSON_2F; |
| case llvm::ELF::EF_MIPS_MACH_LS3A: return llvm::Mips::AFL_EXT_LOONGSON_3A; |
| case llvm::ELF::EF_MIPS_MACH_OCTEON3: return llvm::Mips::AFL_EXT_OCTEON3; |
| case llvm::ELF::EF_MIPS_MACH_OCTEON2: return llvm::Mips::AFL_EXT_OCTEON2; |
| case llvm::ELF::EF_MIPS_MACH_OCTEON: return llvm::Mips::AFL_EXT_OCTEON; |
| case llvm::ELF::EF_MIPS_MACH_XLR: return llvm::Mips::AFL_EXT_XLR; |
| default: |
| // We check ELF flags and show error in case |
| // of unknown value in other place. |
| llvm_unreachable("Unknown MIPS extension flag"); |
| } |
| } |
| |
| static bool is32BitElfFlags(uint64_t flags) { |
| if (flags & llvm::ELF::EF_MIPS_32BITMODE) |
| return true; |
| |
| uint64_t arch = flags & llvm::ELF::EF_MIPS_ARCH; |
| if (arch == llvm::ELF::EF_MIPS_ARCH_1 || |
| arch == llvm::ELF::EF_MIPS_ARCH_2 || |
| arch == llvm::ELF::EF_MIPS_ARCH_32 || |
| arch == llvm::ELF::EF_MIPS_ARCH_32R2 || |
| arch == llvm::ELF::EF_MIPS_ARCH_32R6) |
| return true; |
| |
| uint64_t abi = flags & llvm::ELF::EF_MIPS_ABI; |
| if (abi == llvm::ELF::EF_MIPS_ABI_O32 || abi == llvm::ELF::EF_MIPS_ABI_EABI32) |
| return true; |
| |
| return false; |
| } |
| |
| bool MipsAbiFlags::fillByElfFlags(const Input& pInput, uint64_t elfFlags, |
| MipsAbiFlags& mipsAbi) { |
| mipsAbi.m_IsaLevel = getIsaLevel(elfFlags); |
| mipsAbi.m_IsaRev = getIsaRev(elfFlags); |
| mipsAbi.m_IsaExt = getIsaExt(elfFlags); |
| |
| mipsAbi.m_GprSize = is32BitElfFlags(elfFlags) ? |
| llvm::Mips::AFL_REG_32 : llvm::Mips::AFL_REG_64; |
| |
| mipsAbi.m_Cpr1Size = llvm::Mips::AFL_REG_NONE; |
| mipsAbi.m_Cpr2Size = llvm::Mips::AFL_REG_NONE; |
| mipsAbi.m_FpAbi = llvm::Mips::Val_GNU_MIPS_ABI_FP_ANY; |
| |
| mipsAbi.m_Ases = 0; |
| if (elfFlags & llvm::ELF::EF_MIPS_MICROMIPS) |
| mipsAbi.m_Ases |= llvm::Mips::AFL_ASE_MICROMIPS; |
| if (elfFlags & llvm::ELF::EF_MIPS_ARCH_ASE_M16) |
| mipsAbi.m_Ases |= llvm::Mips::AFL_ASE_MIPS16; |
| if (elfFlags & llvm::ELF::EF_MIPS_ARCH_ASE_MDMX) |
| mipsAbi.m_Ases |= llvm::Mips::AFL_ASE_MDMX; |
| |
| mipsAbi.m_Flags1 = 0; |
| return true; |
| } |
| |
| bool MipsAbiFlags::isCompatible(const Input& pInput, const MipsAbiFlags& elf, |
| const MipsAbiFlags& abi) { |
| unsigned isaRev = abi.m_IsaRev; |
| if (isaRev == 3 || isaRev == 5) |
| isaRev = 2; |
| if (abi.m_IsaLevel != elf.m_IsaLevel || isaRev != elf.m_IsaRev) { |
| warning(diag::warn_Mips_isa_incompatible) << pInput.name(); |
| return false; |
| } |
| if (abi.m_IsaExt != elf.m_IsaExt) { |
| warning(diag::warn_Mips_isa_ext_incompatible) << pInput.name(); |
| return false; |
| } |
| if ((abi.m_Ases & elf.m_Ases) != elf.m_Ases) { |
| warning(diag::warn_Mips_ases_incompatible) << pInput.name(); |
| return false; |
| } |
| return true; |
| } |
| |
| static bool isFpGreater(uint64_t fpA, uint64_t fpB) { |
| if (fpB == llvm::Mips::Val_GNU_MIPS_ABI_FP_ANY) |
| return true; |
| if (fpB == llvm::Mips::Val_GNU_MIPS_ABI_FP_64A && |
| fpA == llvm::Mips::Val_GNU_MIPS_ABI_FP_64) |
| return true; |
| if (fpB != llvm::Mips::Val_GNU_MIPS_ABI_FP_XX) |
| return false; |
| return fpA == llvm::Mips::Val_GNU_MIPS_ABI_FP_DOUBLE || |
| fpA == llvm::Mips::Val_GNU_MIPS_ABI_FP_64 || |
| fpA == llvm::Mips::Val_GNU_MIPS_ABI_FP_64A; |
| } |
| |
| static llvm::StringRef getFpAbiName(uint64_t abi) { |
| switch (abi) { |
| case llvm::Mips::Val_GNU_MIPS_ABI_FP_ANY: |
| return "<any>"; |
| case llvm::Mips::Val_GNU_MIPS_ABI_FP_DOUBLE: |
| return "-mdouble-float"; |
| case llvm::Mips::Val_GNU_MIPS_ABI_FP_SINGLE: |
| return "-msingle-float"; |
| case llvm::Mips::Val_GNU_MIPS_ABI_FP_SOFT: |
| return "-msoft-float"; |
| case llvm::Mips::Val_GNU_MIPS_ABI_FP_OLD_64: |
| return "-mips32r2 -mfp64 (old)"; |
| case llvm::Mips::Val_GNU_MIPS_ABI_FP_XX: |
| return "-mfpxx"; |
| case llvm::Mips::Val_GNU_MIPS_ABI_FP_64: |
| return "-mgp32 -mfp64"; |
| case llvm::Mips::Val_GNU_MIPS_ABI_FP_64A: |
| return "-mgp32 -mfp64 -mno-odd-spreg"; |
| default: |
| return "<unknown>"; |
| } |
| } |
| |
| bool MipsAbiFlags::merge(const Input& pInput, MipsAbiFlags& oldFlags, |
| const MipsAbiFlags& newFlags) { |
| if (oldFlags.m_IsaLevel == 0) { |
| oldFlags = newFlags; |
| return true; |
| } |
| |
| if (newFlags.m_IsaLevel > oldFlags.m_IsaLevel) |
| oldFlags.m_IsaLevel = newFlags.m_IsaLevel; |
| |
| oldFlags.m_IsaRev = std::max(oldFlags.m_IsaRev, newFlags.m_IsaRev); |
| oldFlags.m_GprSize = std::max(oldFlags.m_GprSize, newFlags.m_GprSize); |
| oldFlags.m_Cpr1Size = std::max(oldFlags.m_Cpr1Size, newFlags.m_Cpr1Size); |
| oldFlags.m_Cpr2Size = std::max(oldFlags.m_Cpr2Size, newFlags.m_Cpr2Size); |
| oldFlags.m_Ases |= newFlags.m_Ases; |
| oldFlags.m_Flags1 |= newFlags.m_Flags1; |
| |
| if (oldFlags.m_FpAbi == newFlags.m_FpAbi) |
| return true; |
| |
| if (isFpGreater(newFlags.m_FpAbi, oldFlags.m_FpAbi)) { |
| oldFlags.m_FpAbi = newFlags.m_FpAbi; |
| return true; |
| } |
| |
| if (isFpGreater(oldFlags.m_FpAbi, newFlags.m_FpAbi)) |
| return true; |
| |
| llvm::StringRef oldAbiName = getFpAbiName(oldFlags.m_FpAbi); |
| llvm::StringRef newAbiName = getFpAbiName(newFlags.m_FpAbi); |
| warning(diag::warn_Mips_fp_abi_incompatible) << oldAbiName << newAbiName |
| << pInput.name(); |
| return false; |
| } |
| } // namespace mcld |