Index: lld/trunk/ELF/Writer.cpp =================================================================== --- lld/trunk/ELF/Writer.cpp +++ lld/trunk/ELF/Writer.cpp @@ -1138,21 +1138,201 @@ } } +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() { - // FIXME: ELF flags depends on ELF flags of all input object files and - // selected emulation. For now pick the arch flag from the fisrt input file - // and use hard coded values for other flags. - uint32_t FirstElfFlags = - cast>(Config->FirstElf)->getObj().getHeader()->e_flags; - uint32_t ElfFlags = FirstElfFlags & EF_MIPS_ARCH; - if (ELFT::Is64Bits) - ElfFlags |= EF_MIPS_CPIC | EF_MIPS_PIC; - else { - ElfFlags |= EF_MIPS_CPIC | EF_MIPS_ABI_O32; - if (Config->Shared) - ElfFlags |= EF_MIPS_PIC; + // 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 ElfFlags; + return ResFlags; } template static typename ELFT::uint getEntryAddr() { Index: lld/trunk/test/ELF/mips-elf-flags-err.s =================================================================== --- lld/trunk/test/ELF/mips-elf-flags-err.s +++ lld/trunk/test/ELF/mips-elf-flags-err.s @@ -0,0 +1,52 @@ +# Check MIPS ELF ISA flag calculation if input files have different ISAs. + +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: -mcpu=mips32 %S/Inputs/mips-dynamic.s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: -mcpu=mips32r2 %s -o %t2.o +# RUN: ld.lld %t1.o %t2.o -o %t.exe +# RUN: llvm-readobj -h %t.exe | FileCheck -check-prefix=R1R2 %s + +# Check that lld does not allow to link incompatible ISAs. + +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: -mcpu=mips32 %S/Inputs/mips-dynamic.s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: -mcpu=mips32r6 %s -o %t2.o +# RUN: not ld.lld %t1.o %t2.o -o %t.exe 2>&1 | FileCheck -check-prefix=R1R6 %s + +# Check that lld does not allow to link incompatible ABIs. + +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: -target-abi n32 %S/Inputs/mips-dynamic.s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: -target-abi o32 %s -o %t2.o +# RUN: not ld.lld %t1.o %t2.o -o %t.exe 2>&1 | FileCheck -check-prefix=N32O32 %s + +# Check that lld does not allow to link modules with incompatible NAN flags. + +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: -mattr=+nan2008 %S/Inputs/mips-dynamic.s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: %s -o %t2.o +# RUN: not ld.lld %t1.o %t2.o -o %t.exe 2>&1 | FileCheck -check-prefix=NAN %s + +# REQUIRES: mips + + .option pic0 + .text + .global __start +__start: + nop + +# R1R2: Flags [ +# R1R2-NEXT: EF_MIPS_ABI_O32 +# R1R2-NEXT: EF_MIPS_ARCH_32R2 +# R1R2-NEXT: EF_MIPS_CPIC +# R1R2-NEXT: ] + +# 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 + +# NAN: target -mnan=2008 is incompatible with -mnan=legacy: {{.*}}mips-elf-flags-err.s.tmp2.o Index: lld/trunk/test/ELF/mips-elf-flags.s =================================================================== --- lld/trunk/test/ELF/mips-elf-flags.s +++ lld/trunk/test/ELF/mips-elf-flags.s @@ -1,8 +1,10 @@ # Check generation of MIPS specific ELF header flags. -# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o -# RUN: ld.lld %t.o -shared -o %t.so +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: %S/Inputs/mips-dynamic.s -o %t-so.o +# RUN: ld.lld %t-so.o -shared -o %t.so # RUN: llvm-readobj -h %t.so | FileCheck -check-prefix=SO %s +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o # RUN: ld.lld %t.o -o %t.exe # RUN: llvm-readobj -h %t.exe | FileCheck -check-prefix=EXE %s # RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ @@ -44,4 +46,5 @@ # EXE-R6-NEXT: EF_MIPS_ABI_O32 # EXE-R6-NEXT: EF_MIPS_ARCH_32R6 # EXE-R6-NEXT: EF_MIPS_CPIC +# EXE-R6-NEXT: EF_MIPS_NAN2008 # EXE-R6-NEXT: ]