diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt index cabdd9a09663..4ab303b76b34 100644 --- a/lld/ELF/CMakeLists.txt +++ b/lld/ELF/CMakeLists.txt @@ -13,6 +13,7 @@ add_lld_library(lldELF LTO.cpp LinkerScript.cpp MarkLive.cpp + Mips.cpp OutputSections.cpp Relocations.cpp ScriptParser.cpp diff --git a/lld/ELF/Mips.cpp b/lld/ELF/Mips.cpp new file mode 100644 index 000000000000..fbd1b007f8b5 --- /dev/null +++ b/lld/ELF/Mips.cpp @@ -0,0 +1,217 @@ +//===- Mips.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 "Error.h" +#include "InputFiles.h" +#include "SymbolTable.h" +#include "Writer.h" + +#include "llvm/Object/ELF.h" +#include "llvm/Support/ELF.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 { + StringRef Filename; + uint32_t Flags; +}; +} + +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) { + 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.slice(1)) { + uint32_t ABI2 = F.Flags & (EF_MIPS_ABI | EF_MIPS_ABI2); + if (ABI != ABI2) + error("target ABI '" + getAbiName(ABI) + "' is incompatible with '" + + getAbiName(ABI2) + "': " + F.Filename); + + bool Nan2 = F.Flags & EF_MIPS_NAN2008; + if (Nan != Nan2) + error("target -mnan=" + getNanName(Nan) + " is incompatible with -mnan=" + + getNanName(Nan2) + ": " + F.Filename); + + bool Fp2 = F.Flags & EF_MIPS_FP64; + if (Fp != Fp2) + error("target -mfp" + getFpName(Fp) + " is incompatible with -mfp" + + getFpName(Fp2) + ": " + F.Filename); + } +} + +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) + warning("linking abicalls code with non-abicalls file: " + F.Filename); + if (!IsPic && IsPic2) + warning("linking non-abicalls code with abicalls file: " + F.Filename); + } + + // 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 + // MIPS64 extensions. + {EF_MIPS_ARCH_64R2, EF_MIPS_ARCH_64}, + // MIPS V extensions. + {EF_MIPS_ARCH_64, EF_MIPS_ARCH_5}, + // MIPS IV extensions. + {EF_MIPS_ARCH_5, EF_MIPS_ARCH_4}, + // MIPS III extensions. + {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_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 getArchName(uint32_t Flags) { + switch (Flags) { + 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"; + } +} + +static uint32_t getArchFlags(ArrayRef Files) { + uint32_t Ret = Files[0].Flags & EF_MIPS_ARCH; + + for (const FileFlags &F : Files.slice(1)) { + uint32_t New = F.Flags & EF_MIPS_ARCH; + + // Check ISA compatibility. + if (isArchMatched(New, Ret)) + continue; + if (!isArchMatched(Ret, New)) { + error("target ISA '" + getArchName(Ret) + "' is incompatible with '" + + getArchName(New) + "': " + F.Filename); + return 0; + } + Ret = New; + } + return Ret; +} + +template uint32_t elf::getMipsEFlags() { + std::vector V; + for (const std::unique_ptr> &F : + Symtab::X->getObjectFiles()) + V.push_back({F->getName(), F->getObj().getHeader()->e_flags}); + + checkFlags(V); + return getMiscFlags(V) | getPicFlags(V) | getArchFlags(V); +} + +template uint32_t elf::getMipsEFlags(); +template uint32_t elf::getMipsEFlags(); +template uint32_t elf::getMipsEFlags(); +template uint32_t elf::getMipsEFlags(); diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 8ab906dfd139..7392480a2b87 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1142,203 +1142,6 @@ template void Writer::setPhdrs() { } } -namespace { -struct MipsIsaTreeEdge { - uint32_t Child; - uint32_t Parent; -}; -} - -static MipsIsaTreeEdge MipsIsaTree[] = { - // MIPS32R6 and MIPS64R6 are not compatible with other extensions - // MIPS64 extensions. - {EF_MIPS_ARCH_64R2, EF_MIPS_ARCH_64}, - // MIPS V extensions. - {EF_MIPS_ARCH_64, EF_MIPS_ARCH_5}, - // MIPS IV extensions. - {EF_MIPS_ARCH_5, EF_MIPS_ARCH_4}, - // MIPS III extensions. - {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_2, EF_MIPS_ARCH_1}, -}; - -static bool isMipsIsaMatched(uint32_t New, uint32_t Res) { - if (New == Res) - return true; - if (New == EF_MIPS_ARCH_32 && isMipsIsaMatched(EF_MIPS_ARCH_64, Res)) - return true; - if (New == EF_MIPS_ARCH_32R2 && isMipsIsaMatched(EF_MIPS_ARCH_64R2, Res)) - return true; - for (const auto &Edge : MipsIsaTree) { - if (Res == Edge.Child) { - Res = Edge.Parent; - if (Res == New) - return true; - } - } - return false; -} - -static StringRef getMipsIsaName(uint32_t Flags) { - switch (Flags) { - 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"; - } -} - -static StringRef getMipsAbiName(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 getMipsNanName(bool IsNan2008) { - return IsNan2008 ? "2008" : "legacy"; -} - -static StringRef getMipsFpName(bool IsFp64) { return IsFp64 ? "64" : "32"; } - -static uint32_t updateMipsPicFlags(uint32_t ResFlags, uint32_t NewFlags, - StringRef FName) { - uint32_t NewPic = NewFlags & (EF_MIPS_PIC | EF_MIPS_CPIC); - uint32_t ResPic = ResFlags & (EF_MIPS_PIC | EF_MIPS_CPIC); - - // Check PIC / CPIC flags compatibility. - if (NewPic && !ResPic) - warning("linking non-abicalls code with abicalls file: " + FName); - if (!NewPic && ResPic) - warning("linking abicalls code with non-abicalls file: " + FName); - - if (!(NewPic & EF_MIPS_PIC)) - ResFlags &= ~EF_MIPS_PIC; - if (NewPic) - ResFlags |= EF_MIPS_CPIC; - return ResFlags; -} - -static uint32_t updateMipsIsaFlags(uint32_t ResFlags, uint32_t NewFlags, - StringRef FName) { - uint32_t NewIsa = NewFlags & EF_MIPS_ARCH; - uint32_t ResIsa = ResFlags & EF_MIPS_ARCH; - - // Check ISA compatibility. - if (isMipsIsaMatched(NewIsa, ResIsa)) - return ResFlags; - if (!isMipsIsaMatched(ResIsa, NewIsa)) { - error("target isa '" + getMipsIsaName(ResIsa) + "' is incompatible with '" + - getMipsIsaName(NewIsa) + "': " + FName); - return ResFlags; - } - ResFlags &= ~EF_MIPS_ARCH; - ResFlags |= NewIsa; - return ResFlags; -} - -static void checkMipsAbiFlags(uint32_t ResFlags, uint32_t NewFlags, - StringRef FName) { - uint32_t NewAbi = NewFlags & (EF_MIPS_ABI | EF_MIPS_ABI2); - uint32_t ResAbi = ResFlags & (EF_MIPS_ABI | EF_MIPS_ABI2); - // Check ABI compatibility. - if (NewAbi != ResAbi) - error("target ABI '" + getMipsAbiName(ResAbi) + "' is incompatible with '" + - getMipsAbiName(NewAbi) + "': " + FName); -} - -static void checkMipsNanFlags(uint32_t ResFlags, uint32_t NewFlags, - StringRef FName) { - bool NewNan2008 = NewFlags & EF_MIPS_NAN2008; - bool ResNan2008 = ResFlags & EF_MIPS_NAN2008; - // Check -mnan flags compatibility. - if (NewNan2008 != ResNan2008) - error("target -mnan=" + getMipsNanName(ResNan2008) + - " is incompatible with -mnan=" + getMipsNanName(NewNan2008) + ": " + - FName); -} - -static void checkMipsFpFlags(uint32_t ResFlags, uint32_t NewFlags, - StringRef FName) { - bool NewFp64 = NewFlags & EF_MIPS_FP64; - bool ResFp64 = ResFlags & EF_MIPS_FP64; - // Check FP64 compatibility. - if (NewFp64 != ResFp64) - error("target -mfp" + getMipsFpName(ResFp64) + - " is incompatible with -mfp" + getMipsFpName(NewFp64) + ": " + FName); -} - -template static uint32_t getMipsEFlags() { - // Iterates over all object files andretrieve ELF header flags - // to check that ISA, ABI and features declared by these flags - // are compatible with each other. - uint32_t ResFlags = 0; - for (const std::unique_ptr> &F : - Symtab::X->getObjectFiles()) { - uint32_t NewFlags = F->getObj().getHeader()->e_flags; - if (ResFlags == 0) { - if (NewFlags & EF_MIPS_PIC) - // PIC code is inherently CPIC - // and may not set CPIC flag explicitly. - NewFlags |= EF_MIPS_CPIC; - ResFlags = NewFlags; - continue; - } - - ResFlags = updateMipsPicFlags(ResFlags, NewFlags, F->getName()); - ResFlags = updateMipsIsaFlags(ResFlags, NewFlags, F->getName()); - - checkMipsAbiFlags(ResFlags, NewFlags, F->getName()); - checkMipsNanFlags(ResFlags, NewFlags, F->getName()); - checkMipsFpFlags(ResFlags, NewFlags, F->getName()); - - ResFlags |= NewFlags & EF_MIPS_ARCH_ASE; - ResFlags |= NewFlags & EF_MIPS_NOREORDER; - ResFlags |= NewFlags & EF_MIPS_MICROMIPS; - ResFlags |= NewFlags & EF_MIPS_NAN2008; - ResFlags |= NewFlags & EF_MIPS_32BITMODE; - } - return ResFlags; -} - template static typename ELFT::uint getEntryAddr() { if (Symbol *S = Config->EntrySym) return S->body()->getVA(); diff --git a/lld/ELF/Writer.h b/lld/ELF/Writer.h index 9e6e3671c40d..9caf7a7a10e6 100644 --- a/lld/ELF/Writer.h +++ b/lld/ELF/Writer.h @@ -45,6 +45,8 @@ template llvm::StringRef getOutputSectionName(InputSectionBase *S); template void reportDiscarded(InputSectionBase *IS); + +template uint32_t getMipsEFlags(); } } diff --git a/lld/test/ELF/mips-elf-flags-err.s b/lld/test/ELF/mips-elf-flags-err.s index 358608fbda49..bab6ed137b7d 100644 --- a/lld/test/ELF/mips-elf-flags-err.s +++ b/lld/test/ELF/mips-elf-flags-err.s @@ -45,7 +45,7 @@ __start: # R1R2-NEXT: EF_MIPS_CPIC # R1R2-NEXT: ] -# R1R6: target isa 'mips32' is incompatible with 'mips32r6': {{.*}}mips-elf-flags-err.s.tmp2.o +# R1R6: target ISA 'mips32' is incompatible with 'mips32r6': {{.*}}mips-elf-flags-err.s.tmp2.o # N32O32: target ABI 'n32' is incompatible with 'o32': {{.*}}mips-elf-flags-err.s.tmp2.o