diff options
Diffstat (limited to 'lib/ReaderWriter/ELF/Mips/MipsAbiInfoHandler.cpp')
-rw-r--r-- | lib/ReaderWriter/ELF/Mips/MipsAbiInfoHandler.cpp | 675 |
1 files changed, 675 insertions, 0 deletions
diff --git a/lib/ReaderWriter/ELF/Mips/MipsAbiInfoHandler.cpp b/lib/ReaderWriter/ELF/Mips/MipsAbiInfoHandler.cpp new file mode 100644 index 000000000000..ad4e62e64680 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsAbiInfoHandler.cpp @@ -0,0 +1,675 @@ +//===- lib/ReaderWriter/ELF/MipsAbiInfoHandler.cpp ------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsAbiInfoHandler.h" +#include "lld/Core/Error.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/MipsABIFlags.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lld; +using namespace lld::elf; +using namespace llvm; +using namespace llvm::ELF; +using namespace llvm::Mips; + +namespace { + +// The joined set of MIPS ISAs and MIPS ISA extensions. +enum MipsISAs { + ArchNone, + + // General ISAs + Arch1, + Arch2, + Arch3, + Arch4, + Arch5, + Arch32, + Arch32r2, + Arch32r3, + Arch32r5, + Arch32r6, + Arch64, + Arch64r2, + Arch64r3, + Arch64r5, + Arch64r6, + + // CPU specific ISAs + Arch3900, + Arch4010, + Arch4100, + Arch4111, + Arch4120, + Arch4650, + Arch5400, + Arch5500, + Arch5900, + Arch9000, + Arch10000, + ArchLs2e, + ArchLs2f, + ArchLs3a, + ArchOcteon, + ArchOcteonP, + ArchOcteon2, + ArchOcteon3, + ArchSB1, + ArchXLR +}; + +struct MipsISATreeEdge { + MipsISAs child; + MipsISAs parent; +}; + +struct ElfArchPair { + uint32_t _elfFlag; + MipsISAs _arch; +}; + +struct AbiIsaArchPair { + uint8_t _isaLevel; + uint8_t _isaRev; + uint8_t _isaExt; + MipsISAs _arch; +}; +} + +static const MipsISATreeEdge isaTree[] = { + // MIPS32R6 and MIPS64R6 are not compatible with other extensions + + // MIPS64R2 extensions. + {ArchOcteon3, ArchOcteon2}, + {ArchOcteon2, ArchOcteonP}, + {ArchOcteonP, ArchOcteon}, + {ArchOcteon, Arch64r2}, + {ArchLs3a, Arch64r2}, + + // MIPS64 extensions. + {Arch64r2, Arch64}, + {ArchSB1, Arch64}, + {ArchXLR, Arch64}, + + // MIPS V extensions. + {Arch64, Arch5}, + + // R5000 extensions. + {Arch5500, Arch5400}, + + // MIPS IV extensions. + {Arch5, Arch4}, + {Arch5400, Arch4}, + {Arch9000, Arch4}, + + // VR4100 extensions. + {Arch4120, Arch4100}, + {Arch4111, Arch4100}, + + // MIPS III extensions. + {ArchLs2e, Arch3}, + {ArchLs2f, Arch3}, + {Arch4650, Arch3}, + {Arch4100, Arch3}, + {Arch4010, Arch3}, + {Arch5900, Arch3}, + {Arch4, Arch3}, + + // MIPS32 extensions. + {Arch32r2, Arch32}, + + // MIPS II extensions. + {Arch3, Arch2}, + {Arch32, Arch2}, + + // MIPS I extensions. + {Arch3900, Arch1}, + {Arch2, Arch1}, +}; + +// Conversion ELF arch flags => MipsISAs +static const ElfArchPair elfArchPairs[] = { + {EF_MIPS_ARCH_1 | EF_MIPS_MACH_3900, Arch3900}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4010, Arch4010}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100, Arch4100}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4111, Arch4111}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4120, Arch4120}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4650, Arch4650}, + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400, Arch5400}, + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5500, Arch5500}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_5900, Arch5900}, + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_9000, Arch9000}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2E, ArchLs2e}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2F, ArchLs2f}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_LS3A, ArchLs3a}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON, ArchOcteon}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON2, ArchOcteon2}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON3, ArchOcteon3}, + {EF_MIPS_ARCH_64 | EF_MIPS_MACH_SB1, ArchSB1}, + {EF_MIPS_ARCH_64 | EF_MIPS_MACH_XLR, ArchXLR}, + {EF_MIPS_ARCH_1, Arch1}, + {EF_MIPS_ARCH_2, Arch2}, + {EF_MIPS_ARCH_3, Arch3}, + {EF_MIPS_ARCH_4, Arch4}, + {EF_MIPS_ARCH_5, Arch5}, + {EF_MIPS_ARCH_32, Arch32}, + {EF_MIPS_ARCH_32R2, Arch32r2}, + {EF_MIPS_ARCH_32R6, Arch32r6}, + {EF_MIPS_ARCH_64, Arch64}, + {EF_MIPS_ARCH_64R2, Arch64r2}, + {EF_MIPS_ARCH_64R6, Arch64r6} +}; + +// Conversion MipsISAs => ELF arch flags +static const ElfArchPair archElfPairs[] = { + {EF_MIPS_ARCH_1, Arch1}, + {EF_MIPS_ARCH_2, Arch2}, + {EF_MIPS_ARCH_3, Arch3}, + {EF_MIPS_ARCH_4, Arch4}, + {EF_MIPS_ARCH_5, Arch5}, + {EF_MIPS_ARCH_32, Arch32}, + {EF_MIPS_ARCH_32R2, Arch32r2}, + {EF_MIPS_ARCH_32R2, Arch32r3}, + {EF_MIPS_ARCH_32R2, Arch32r5}, + {EF_MIPS_ARCH_32R6, Arch32r6}, + {EF_MIPS_ARCH_64, Arch64}, + {EF_MIPS_ARCH_64R2, Arch64r2}, + {EF_MIPS_ARCH_64R2, Arch64r3}, + {EF_MIPS_ARCH_64R2, Arch64r5}, + {EF_MIPS_ARCH_64R6, Arch64r6}, + {EF_MIPS_ARCH_1 | EF_MIPS_MACH_3900, Arch3900}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4010, Arch4010}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100, Arch4100}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4111, Arch4111}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4120, Arch4120}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4650, Arch4650}, + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400, Arch5400}, + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5500, Arch5500}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_5900, Arch5900}, + {EF_MIPS_ARCH_4 | EF_MIPS_MACH_9000, Arch9000}, + {EF_MIPS_ARCH_4, Arch10000}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2E, ArchLs2e}, + {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2F, ArchLs2f}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_LS3A, ArchLs3a}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON, ArchOcteon}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON, ArchOcteonP}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON2, ArchOcteon2}, + {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON3, ArchOcteon3}, + {EF_MIPS_ARCH_64 | EF_MIPS_MACH_SB1, ArchSB1}, + {EF_MIPS_ARCH_64 | EF_MIPS_MACH_SB1, ArchXLR} +}; + +// Conversion .MIPS.abiflags isa/level/extension <=> MipsISAs +static const AbiIsaArchPair abiIsaArchPair[] = { + { 0, 0, 0, ArchNone}, + { 1, 0, 0, Arch1}, + { 2, 0, 0, Arch2}, + { 3, 0, 0, Arch3}, + { 4, 0, 0, Arch4}, + { 5, 0, 0, Arch5}, + {32, 1, 0, Arch32}, + {32, 2, 0, Arch32r2}, + {32, 3, 0, Arch32r3}, + {32, 5, 0, Arch32r5}, + {32, 6, 0, Arch32r6}, + {64, 1, 0, Arch64}, + {64, 2, 0, Arch64r2}, + {64, 3, 0, Arch64r3}, + {64, 5, 0, Arch64r5}, + {64, 6, 0, Arch64r6}, + { 1, 0, AFL_EXT_3900, Arch3900}, + { 3, 0, AFL_EXT_4010, Arch4010}, + { 3, 0, AFL_EXT_4100, Arch4100}, + { 3, 0, AFL_EXT_4111, Arch4111}, + { 3, 0, AFL_EXT_4120, Arch4120}, + { 3, 0, AFL_EXT_4650, Arch4650}, + { 4, 0, AFL_EXT_5400, Arch5400}, + { 4, 0, AFL_EXT_5500, Arch5500}, + { 3, 0, AFL_EXT_5900, Arch5900}, + { 4, 0, AFL_EXT_10000, Arch10000}, + { 3, 0, AFL_EXT_LOONGSON_2E, ArchLs2e}, + { 3, 0, AFL_EXT_LOONGSON_2F, ArchLs2f}, + {64, 2, AFL_EXT_LOONGSON_3A, ArchLs3a}, + {64, 2, AFL_EXT_OCTEON, ArchOcteon}, + {64, 2, AFL_EXT_OCTEON2, ArchOcteon2}, + {64, 2, AFL_EXT_OCTEON3, ArchOcteon3}, + {64, 1, AFL_EXT_SB1, ArchSB1}, + {64, 1, AFL_EXT_XLR, ArchXLR} +}; + +static bool matchMipsISA(MipsISAs base, MipsISAs ext) { + if (base == ext) + return true; + if (base == Arch32 && matchMipsISA(Arch64, ext)) + return true; + if (base == Arch32r2 && matchMipsISA(Arch64r2, ext)) + return true; + for (const auto &edge : isaTree) { + if (ext == edge.child) { + ext = edge.parent; + if (ext == base) + return true; + } + } + return false; +} + +static bool is32BitElfFlags(unsigned flags) { + if (flags & EF_MIPS_32BITMODE) + return true; + + unsigned arch = flags & EF_MIPS_ARCH; + if (arch == EF_MIPS_ARCH_1 || arch == EF_MIPS_ARCH_2 || + arch == EF_MIPS_ARCH_32 || arch == EF_MIPS_ARCH_32R2 || + arch == EF_MIPS_ARCH_32R6) + return true; + + unsigned abi = flags & EF_MIPS_ABI; + if (abi == EF_MIPS_ABI_O32 || abi == EF_MIPS_ABI_EABI32) + return true; + + return false; +} + +static ErrorOr<MipsISAs> headerFlagsToIsa(uint32_t flags) { + uint32_t arch = flags & (EF_MIPS_ARCH | EF_MIPS_MACH); + for (const auto &p : elfArchPairs) + if (p._elfFlag == arch) + return p._arch; + return make_dynamic_error_code( + StringRef("Unknown EF_MIPS_ARCH | EF_MIPS_MACH flags (0x") + + Twine::utohexstr(arch) + ")"); +} + +static uint32_t isaToHeaderFlags(unsigned isa) { + for (const auto &p : archElfPairs) + if (p._arch == isa) + return p._elfFlag; + llvm_unreachable("Unknown MIPS ISA"); +} + +static ErrorOr<uint32_t> flagsToAses(uint32_t flags) { + uint32_t ases = flags & EF_MIPS_ARCH_ASE; + switch (ases) { + case 0: + return 0; + case EF_MIPS_MICROMIPS: + return AFL_ASE_MICROMIPS; + case EF_MIPS_ARCH_ASE_M16: + return AFL_ASE_MIPS16; + case EF_MIPS_ARCH_ASE_MDMX: + return AFL_ASE_MDMX; + default: + return make_dynamic_error_code( + StringRef("Unknown EF_MIPS_ARCH_ASE flag (0x") + + Twine::utohexstr(ases) + ")"); + } +} + +static uint32_t asesToFlags(uint32_t ases) { + switch (ases) { + case AFL_ASE_MICROMIPS: + return EF_MIPS_MICROMIPS; + case AFL_ASE_MIPS16: + return EF_MIPS_ARCH_ASE_M16; + case AFL_ASE_MDMX: + return EF_MIPS_ARCH_ASE_MDMX; + default: + return 0; + } +} + +static ErrorOr<MipsISAs> sectionFlagsToIsa(uint8_t isaLevel, uint8_t isaRev, + uint8_t isaExt) { + for (const auto &p : abiIsaArchPair) + if (p._isaLevel == isaLevel && p._isaRev == isaRev && p._isaExt == isaExt) + return p._arch; + return make_dynamic_error_code( + StringRef("Unknown ISA level/revision/extension ") + Twine(isaLevel) + + "/" + Twine(isaRev) + "/" + Twine(isaExt)); +} + +static std::tuple<uint8_t, uint8_t, uint32_t> isaToSectionFlags(unsigned isa) { + for (const auto &p : abiIsaArchPair) + if (p._arch == isa) + return std::make_tuple(p._isaLevel, p._isaRev, p._isaExt); + llvm_unreachable("Unknown MIPS ISA"); +} + +static bool checkCompatibility(const MipsAbiFlags &hdr, + const MipsAbiFlags &sec) { + uint32_t secIsa = ArchNone; + switch (sec._isa) { + case Arch32r3: + case Arch32r5: + secIsa = Arch32r2; + break; + case Arch64r3: + case Arch64r5: + secIsa = Arch64r2; + break; + default: + secIsa = sec._isa; + break; + } + if (secIsa != hdr._isa) { + llvm::errs() << "inconsistent ISA between .MIPS.abiflags " + "and ELF header e_flags field\n"; + return false; + } + if ((sec._ases & hdr._ases) != hdr._ases) { + llvm::errs() << "inconsistent ASEs between .MIPS.abiflags " + "and ELF header e_flags field\n"; + return false; + } + return true; +} + +static int compareFpAbi(uint32_t fpA, uint32_t fpB) { + if (fpA == fpB) + return 0; + if (fpB == Val_GNU_MIPS_ABI_FP_ANY) + return 1; + if (fpB == Val_GNU_MIPS_ABI_FP_64A && fpA == Val_GNU_MIPS_ABI_FP_64) + return 1; + if (fpB != Val_GNU_MIPS_ABI_FP_XX) + return -1; + if (fpA == Val_GNU_MIPS_ABI_FP_DOUBLE || fpA == Val_GNU_MIPS_ABI_FP_64 || + fpA == Val_GNU_MIPS_ABI_FP_64A) + return 1; + return -1; +} + +static StringRef getFpAbiName(uint32_t fpAbi) { + switch (fpAbi) { + case Val_GNU_MIPS_ABI_FP_ANY: + return "<any>"; + case Val_GNU_MIPS_ABI_FP_DOUBLE: + return "-mdouble-float"; + case Val_GNU_MIPS_ABI_FP_SINGLE: + return "-msingle-float"; + case Val_GNU_MIPS_ABI_FP_SOFT: + return "-msoft-float"; + case Val_GNU_MIPS_ABI_FP_OLD_64: + return "-mips32r2 -mfp64 (old)"; + case Val_GNU_MIPS_ABI_FP_XX: + return "-mfpxx"; + case Val_GNU_MIPS_ABI_FP_64: + return "-mgp32 -mfp64"; + case Val_GNU_MIPS_ABI_FP_64A: + return "-mgp32 -mfp64 -mno-odd-spreg"; + default: + return "<unknown>"; + } +} + +static uint32_t selectFpAbiFlag(uint32_t oldFp, uint32_t newFp) { + if (compareFpAbi(newFp, oldFp) >= 0) + return newFp; + if (compareFpAbi(oldFp, newFp) < 0) + llvm::errs() << "FP ABI " << getFpAbiName(oldFp) << " is incompatible with " + << getFpAbiName(newFp) << "\n"; + return oldFp; +} + +namespace lld { +namespace elf { + +template <class ELFT> bool MipsAbiInfoHandler<ELFT>::isMicroMips() const { + assert(_abiFlags.hasValue()); + return _abiFlags->_ases & AFL_ASE_MICROMIPS; +} + +template <class ELFT> bool MipsAbiInfoHandler<ELFT>::isMipsR6() const { + assert(_abiFlags.hasValue()); + return _abiFlags->_isa == Arch32r6 || _abiFlags->_isa == Arch64r6; +} + +template <class ELFT> bool MipsAbiInfoHandler<ELFT>::isFp64() const { + assert(_abiFlags.hasValue()); + return _abiFlags->_fpAbi == Val_GNU_MIPS_ABI_FP_64 || + _abiFlags->_fpAbi == Val_GNU_MIPS_ABI_FP_64A; +} + +template <class ELFT> bool MipsAbiInfoHandler<ELFT>::isCPicOnly() const { + assert(_abiFlags.hasValue()); + return _abiFlags->_isCPic && !_abiFlags->_isPic; +} + +template <class ELFT> uint32_t MipsAbiInfoHandler<ELFT>::getFlags() const { + std::lock_guard<std::mutex> lock(_mutex); + uint32_t flags = 0; + if (_abiFlags.hasValue()) { + flags |= isaToHeaderFlags(_abiFlags->_isa); + flags |= asesToFlags(_abiFlags->_ases); + flags |= _abiFlags->_abi; + flags |= _abiFlags->_isPic ? EF_MIPS_PIC : 0u; + flags |= _abiFlags->_isCPic ? EF_MIPS_CPIC : 0u; + flags |= _abiFlags->_isNoReorder ? EF_MIPS_NOREORDER : 0u; + flags |= _abiFlags->_is32BitMode ? EF_MIPS_32BITMODE : 0u; + flags |= _abiFlags->_isNan2008 ? EF_MIPS_NAN2008 : 0u; + } + return flags; +} + +template <class ELFT> +llvm::Optional<typename MipsAbiInfoHandler<ELFT>::Elf_Mips_RegInfo> +MipsAbiInfoHandler<ELFT>::getRegistersMask() const { + std::lock_guard<std::mutex> lock(_mutex); + return _regMask; +} + +template <class ELFT> +llvm::Optional<typename MipsAbiInfoHandler<ELFT>::Elf_Mips_ABIFlags> +MipsAbiInfoHandler<ELFT>::getAbiFlags() const { + std::lock_guard<std::mutex> lock(_mutex); + if (!_hasAbiSection) + return llvm::Optional<Elf_Mips_ABIFlags>(); + + Elf_Mips_ABIFlags sec; + sec.version = 0; + std::tie(sec.isa_level, sec.isa_rev, sec.isa_ext) = + isaToSectionFlags(_abiFlags->_isa); + sec.gpr_size = _abiFlags->_gprSize; + sec.cpr1_size = _abiFlags->_cpr1Size; + sec.cpr2_size = _abiFlags->_cpr2Size; + sec.fp_abi = _abiFlags->_fpAbi; + sec.ases = _abiFlags->_ases; + sec.flags1 = _abiFlags->_flags1; + sec.flags2 = 0; + return sec; +} + +template <class ELFT> MipsAbi MipsAbiInfoHandler<ELFT>::getAbi() const { + if (!_abiFlags.hasValue()) + return ELFT::Is64Bits ? MipsAbi::N64 : MipsAbi::O32; + switch (_abiFlags->_abi & (EF_MIPS_ABI_O32 | EF_MIPS_ABI2)) { + case EF_MIPS_ABI_O32: + return MipsAbi::O32; + case EF_MIPS_ABI2: + return MipsAbi::N32; + case 0: + return MipsAbi::N64; + default: + llvm_unreachable("Unknown ABI flag"); + } +} + +template <class ELFT> +std::error_code +MipsAbiInfoHandler<ELFT>::mergeFlags(uint32_t newFlags, + const Elf_Mips_ABIFlags *newSec) { + std::lock_guard<std::mutex> lock(_mutex); + + ErrorOr<MipsAbiFlags> abiFlags = createAbiFlags(newFlags, newSec); + if (auto ec = abiFlags.getError()) + return ec; + + // We support three ABI: O32, N32, and N64. The last one does not have + // the corresponding ELF flag. + if (ELFT::Is64Bits) { + if (abiFlags->_abi) + return make_dynamic_error_code("Unsupported ABI"); + } else { + if (!(abiFlags->_abi & (EF_MIPS_ABI_O32 | EF_MIPS_ABI2))) + return make_dynamic_error_code("Unsupported ABI"); + } + + // ... and still do not support MIPS-16 extension. + if (abiFlags->_ases & AFL_ASE_MIPS16) + return make_dynamic_error_code("Unsupported extension: MIPS16"); + + // PIC code is inherently CPIC and may not set CPIC flag explicitly. + // Ensure that this flag will exist in the linked file. + if (abiFlags->_isPic) + abiFlags->_isCPic = true; + + // If the old set of flags is empty, use the new one as a result. + if (!_abiFlags.hasValue()) { + _abiFlags = *abiFlags; + return std::error_code(); + } + + // Check ABI compatibility. + if (abiFlags->_abi != _abiFlags->_abi) + return make_dynamic_error_code("Linking modules with incompatible ABI"); + + // Check PIC / CPIC flags compatibility. + if (abiFlags->_isCPic != _abiFlags->_isCPic) + llvm::errs() << "lld warning: linking abicalls and non-abicalls files\n"; + + if (!abiFlags->_isPic) + _abiFlags->_isPic = false; + if (abiFlags->_isCPic) + _abiFlags->_isCPic = true; + + // Check mixing -mnan=2008 / -mnan=legacy modules. + if (abiFlags->_isNan2008 != _abiFlags->_isNan2008) + return make_dynamic_error_code( + "Linking -mnan=2008 and -mnan=legacy modules"); + + // Check ISA compatibility and update the extension flag. + if (!matchMipsISA(MipsISAs(abiFlags->_isa), MipsISAs(_abiFlags->_isa))) { + if (!matchMipsISA(MipsISAs(_abiFlags->_isa), MipsISAs(abiFlags->_isa))) + return make_dynamic_error_code("Linking modules with incompatible ISA"); + _abiFlags->_isa = abiFlags->_isa; + } + + _abiFlags->_ases |= abiFlags->_ases; + _abiFlags->_isNoReorder = _abiFlags->_isNoReorder || abiFlags->_isNoReorder; + _abiFlags->_is32BitMode = _abiFlags->_is32BitMode || abiFlags->_is32BitMode; + + _abiFlags->_fpAbi = selectFpAbiFlag(_abiFlags->_fpAbi, abiFlags->_fpAbi); + _abiFlags->_gprSize = std::max(_abiFlags->_gprSize, abiFlags->_gprSize); + _abiFlags->_cpr1Size = std::max(_abiFlags->_cpr1Size, abiFlags->_cpr1Size); + _abiFlags->_cpr2Size = std::max(_abiFlags->_cpr2Size, abiFlags->_cpr2Size); + _abiFlags->_flags1 |= abiFlags->_flags1; + + return std::error_code(); +} + +template <class ELFT> +void MipsAbiInfoHandler<ELFT>::mergeRegistersMask( + const Elf_Mips_RegInfo &info) { + std::lock_guard<std::mutex> lock(_mutex); + if (!_regMask.hasValue()) { + _regMask = info; + return; + } + _regMask->ri_gprmask = _regMask->ri_gprmask | info.ri_gprmask; + _regMask->ri_cprmask[0] = _regMask->ri_cprmask[0] | info.ri_cprmask[0]; + _regMask->ri_cprmask[1] = _regMask->ri_cprmask[1] | info.ri_cprmask[1]; + _regMask->ri_cprmask[2] = _regMask->ri_cprmask[2] | info.ri_cprmask[2]; + _regMask->ri_cprmask[3] = _regMask->ri_cprmask[3] | info.ri_cprmask[3]; +} + +template <class ELFT> +ErrorOr<MipsAbiFlags> +MipsAbiInfoHandler<ELFT>::createAbiFlags(uint32_t flags, + const Elf_Mips_ABIFlags *sec) { + ErrorOr<MipsAbiFlags> hdrFlags = createAbiFromHeaderFlags(flags); + if (auto ec = hdrFlags.getError()) + return ec; + if (!sec) + return *hdrFlags; + ErrorOr<MipsAbiFlags> secFlags = createAbiFromSection(*sec); + if (auto ec = secFlags.getError()) + return ec; + if (!checkCompatibility(*hdrFlags, *secFlags)) + return *hdrFlags; + + _hasAbiSection = true; + + secFlags->_abi = hdrFlags->_abi; + secFlags->_isPic = hdrFlags->_isPic; + secFlags->_isCPic = hdrFlags->_isCPic; + secFlags->_isNoReorder = hdrFlags->_isNoReorder; + secFlags->_is32BitMode = hdrFlags->_is32BitMode; + secFlags->_isNan2008 = hdrFlags->_isNan2008; + return *secFlags; +} + +template <class ELFT> +ErrorOr<MipsAbiFlags> +MipsAbiInfoHandler<ELFT>::createAbiFromHeaderFlags(uint32_t flags) { + MipsAbiFlags abi; + ErrorOr<MipsISAs> isa = headerFlagsToIsa(flags); + if (auto ec = isa.getError()) + return ec; + abi._isa = *isa; + + abi._fpAbi = Val_GNU_MIPS_ABI_FP_ANY; + abi._cpr1Size = AFL_REG_NONE; + abi._cpr2Size = AFL_REG_NONE; + abi._gprSize = is32BitElfFlags(flags) ? AFL_REG_32 : AFL_REG_64; + + ErrorOr<uint32_t> ases = flagsToAses(flags); + if (auto ec = ases.getError()) + return ec; + abi._ases = *ases; + abi._flags1 = 0; + abi._abi = flags & (EF_MIPS_ABI | EF_MIPS_ABI2); + abi._isPic = flags & EF_MIPS_PIC; + abi._isCPic = flags & EF_MIPS_CPIC; + abi._isNoReorder = flags & EF_MIPS_NOREORDER; + abi._is32BitMode = flags & EF_MIPS_32BITMODE; + abi._isNan2008 = flags & EF_MIPS_NAN2008; + return abi; +} + +template <class ELFT> +ErrorOr<MipsAbiFlags> +MipsAbiInfoHandler<ELFT>::createAbiFromSection(const Elf_Mips_ABIFlags &sec) { + MipsAbiFlags abi; + ErrorOr<MipsISAs> isa = + sectionFlagsToIsa(sec.isa_level, sec.isa_rev, sec.isa_ext); + if (auto ec = isa.getError()) + return ec; + abi._isa = *isa; + abi._fpAbi = sec.fp_abi; + abi._cpr1Size = sec.cpr1_size; + abi._cpr2Size = sec.cpr2_size; + abi._gprSize = sec.gpr_size; + abi._ases = sec.ases; + abi._flags1 = sec.flags1; + if (sec.flags2 != 0) + return make_dynamic_error_code("unexpected non-zero 'flags2' value"); + return abi; +} + +template class MipsAbiInfoHandler<ELF32BE>; +template class MipsAbiInfoHandler<ELF32LE>; +template class MipsAbiInfoHandler<ELF64BE>; +template class MipsAbiInfoHandler<ELF64LE>; + +} +} |