Index: lld/trunk/ELF/Config.h =================================================================== --- lld/trunk/ELF/Config.h +++ lld/trunk/ELF/Config.h @@ -105,6 +105,7 @@ bool GnuHash = false; bool ICF; bool Mips64EL = false; + bool MipsN32Abi = false; bool NoGnuUnique; bool NoUndefinedVersion; bool Nostdlib; Index: lld/trunk/ELF/Driver.cpp =================================================================== --- lld/trunk/ELF/Driver.cpp +++ lld/trunk/ELF/Driver.cpp @@ -59,7 +59,8 @@ } // Parses a linker -m option. -static std::tuple parseEmulation(StringRef Emul) { +static std::tuple +parseEmulation(StringRef Emul) { uint8_t OSABI = 0; StringRef S = Emul; if (S.endswith("_fbsd")) { @@ -74,6 +75,8 @@ .Case("elf32_x86_64", {ELF32LEKind, EM_X86_64}) .Case("elf32btsmip", {ELF32BEKind, EM_MIPS}) .Case("elf32ltsmip", {ELF32LEKind, EM_MIPS}) + .Case("elf32btsmipn32", {ELF32BEKind, EM_MIPS}) + .Case("elf32ltsmipn32", {ELF32LEKind, EM_MIPS}) .Case("elf32ppc", {ELF32BEKind, EM_PPC}) .Case("elf64btsmip", {ELF64BEKind, EM_MIPS}) .Case("elf64ltsmip", {ELF64LEKind, EM_MIPS}) @@ -89,7 +92,8 @@ else error("unknown emulation: " + Emul); } - return std::make_tuple(Ret.first, Ret.second, OSABI); + bool IsMipsN32ABI = S == "elf32btsmipn32" || S == "elf32ltsmipn32"; + return std::make_tuple(Ret.first, Ret.second, OSABI, IsMipsN32ABI); } // Returns slices of MB by parsing MB as an archive file. @@ -459,8 +463,8 @@ if (auto *Arg = Args.getLastArg(OPT_m)) { // Parse ELF{32,64}{LE,BE} and CPU type. StringRef S = Arg->getValue(); - std::tie(Config->EKind, Config->EMachine, Config->OSABI) = - parseEmulation(S); + std::tie(Config->EKind, Config->EMachine, Config->OSABI, + Config->MipsN32Abi) = parseEmulation(S); Config->Emulation = S; } @@ -655,6 +659,7 @@ Config->EKind = F->EKind; Config->EMachine = F->EMachine; Config->OSABI = F->OSABI; + Config->MipsN32Abi = Config->EMachine == EM_MIPS && isMipsN32Abi(F); return; } error("target emulation unknown: -m or at least one .o file required"); @@ -691,7 +696,8 @@ LinkerScript LS; ScriptBase = Script::X = &LS; - Config->Rela = ELFT::Is64Bits || Config->EMachine == EM_X86_64; + Config->Rela = + ELFT::Is64Bits || Config->EMachine == EM_X86_64 || Config->MipsN32Abi; Config->Mips64EL = (Config->EMachine == EM_MIPS && Config->EKind == ELF64LEKind); Config->ImageBase = getImageBase(Args); Index: lld/trunk/ELF/Mips.cpp =================================================================== --- lld/trunk/ELF/Mips.cpp +++ lld/trunk/ELF/Mips.cpp @@ -342,6 +342,27 @@ return OldFlag; } +template static bool isN32Abi(const InputFile *F) { + if (auto *EF = dyn_cast>(F)) + return EF->getObj().getHeader()->e_flags & EF_MIPS_ABI2; + return false; +} + +bool elf::isMipsN32Abi(const InputFile *F) { + switch (Config->EKind) { + case ELF32LEKind: + return isN32Abi(F); + case ELF32BEKind: + return isN32Abi(F); + case ELF64LEKind: + return isN32Abi(F); + case ELF64BEKind: + return isN32Abi(F); + default: + llvm_unreachable("unknown Config->EKind"); + } +} + template uint32_t elf::getMipsEFlags(); template uint32_t elf::getMipsEFlags(); template uint32_t elf::getMipsEFlags(); Index: lld/trunk/ELF/Relocations.cpp =================================================================== --- lld/trunk/ELF/Relocations.cpp +++ lld/trunk/ELF/Relocations.cpp @@ -585,6 +585,22 @@ error(Msg); } +template +static std::pair +mergeMipsN32RelTypes(uint32_t Type, uint32_t Offset, RelTy *I, RelTy *E) { + // MIPS N32 ABI treats series of successive relocations with the same offset + // as a single relocation. The similar approach used by N64 ABI, but this ABI + // packs all relocations into the single relocation record. Here we emulate + // this for the N32 ABI. Iterate over relocation with the same offset and put + // theirs types into the single bit-set. + uint32_t Processed = 0; + for (; I != E && Offset == I->r_offset; ++I) { + ++Processed; + Type |= I->getType(Config->Mips64EL) << (8 * Processed); + } + return std::make_pair(Type, Processed); +} + // The reason we have to do this early scan is as follows // * To mmap the output file, we need to know the size // * For that, we need to know how many dynamic relocs we will have. @@ -624,6 +640,13 @@ SymbolBody &Body = File.getRelocTargetSym(RI); uint32_t Type = RI.getType(Config->Mips64EL); + if (Config->MipsN32Abi) { + uint32_t Processed; + std::tie(Type, Processed) = + mergeMipsN32RelTypes(Type, RI.r_offset, I + 1, E); + I += Processed; + } + // We only report undefined symbols if they are referenced somewhere in the // code. if (!Body.isLocal() && Body.isUndefined() && !Body.symbol()->isWeak()) Index: lld/trunk/ELF/SymbolTable.cpp =================================================================== --- lld/trunk/ELF/SymbolTable.cpp +++ lld/trunk/ELF/SymbolTable.cpp @@ -36,8 +36,12 @@ template static bool isCompatible(InputFile *F) { if (!isa>(F) && !isa(F)) return true; - if (F->EKind == Config->EKind && F->EMachine == Config->EMachine) - return true; + if (F->EKind == Config->EKind && F->EMachine == Config->EMachine) { + if (Config->EMachine != EM_MIPS) + return true; + if (isMipsN32Abi(F) == Config->MipsN32Abi) + return true; + } StringRef A = F->getName(); StringRef B = Config->Emulation; if (B.empty()) Index: lld/trunk/ELF/Target.cpp =================================================================== --- lld/trunk/ELF/Target.cpp +++ lld/trunk/ELF/Target.cpp @@ -30,6 +30,7 @@ #include "OutputSections.h" #include "Symbols.h" #include "Thunks.h" +#include "Writer.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Object/ELF.h" @@ -1921,8 +1922,8 @@ template RelExpr MipsTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const { - if (ELFT::Is64Bits) - // See comment in the calculateMips64RelChain. + // See comment in the calculateMipsRelChain. + if (ELFT::Is64Bits || Config->MipsN32Abi) Type &= 0xff; switch (Type) { default: @@ -2047,10 +2048,17 @@ template void MipsTargetInfo::writePltHeader(uint8_t *Buf) const { const endianness E = ELFT::TargetEndianness; - write32(Buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0]) - write32(Buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28) - write32(Buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0]) - write32(Buf + 12, 0x031cc023); // subu $24, $24, $28 + if (Config->MipsN32Abi) { + write32(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) + write32(Buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14) + write32(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) + write32(Buf + 12, 0x030ec023); // subu $24, $24, $14 + } else { + write32(Buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0]) + write32(Buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28) + write32(Buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0]) + write32(Buf + 12, 0x031cc023); // subu $24, $24, $28 + } write32(Buf + 16, 0x03e07825); // move $15, $31 write32(Buf + 20, 0x0018c082); // srl $24, $24, 2 write32(Buf + 24, 0x0320f809); // jalr $25 @@ -2137,8 +2145,8 @@ } } -static std::pair calculateMips64RelChain(uint32_t Type, - uint64_t Val) { +static std::pair calculateMipsRelChain(uint32_t Type, + uint64_t Val) { // MIPS N64 ABI packs multiple relocations into the single relocation // record. In general, all up to three relocations can have arbitrary // types. In fact, Clang and GCC uses only a few combinations. For now, @@ -2175,8 +2183,8 @@ else if (Type == R_MIPS_TLS_TPREL_HI16 || Type == R_MIPS_TLS_TPREL_LO16 || Type == R_MIPS_TLS_TPREL32 || Type == R_MIPS_TLS_TPREL64) Val -= 0x7000; - if (ELFT::Is64Bits) - std::tie(Type, Val) = calculateMips64RelChain(Type, Val); + if (ELFT::Is64Bits || Config->MipsN32Abi) + std::tie(Type, Val) = calculateMipsRelChain(Type, Val); switch (Type) { case R_MIPS_32: case R_MIPS_GPREL32: Index: lld/trunk/ELF/Writer.h =================================================================== --- lld/trunk/ELF/Writer.h +++ lld/trunk/ELF/Writer.h @@ -16,6 +16,7 @@ namespace lld { namespace elf { +class InputFile; template class OutputSectionBase; template class InputSectionBase; template class ObjectFile; @@ -45,6 +46,8 @@ uint8_t getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag, llvm::StringRef FileName); + +bool isMipsN32Abi(const InputFile *F); } } 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 @@ -82,6 +82,6 @@ # OCTEON-NEXT: EF_MIPS_PIC # OCTEON-NEXT: ] -# N32O32: target ABI 'n32' is incompatible with 'o32': {{.*}}mips-elf-flags-err.s.tmp2.o +# N32O32: error: {{.*}}mips-elf-flags-err.s.tmp2.o is incompatible with {{.*}}mips-elf-flags-err.s.tmp1.o # NAN: target -mnan=2008 is incompatible with -mnan=legacy: {{.*}}mips-elf-flags-err.s.tmp2.o Index: lld/trunk/test/ELF/mips-n32-emul.s =================================================================== --- lld/trunk/test/ELF/mips-n32-emul.s +++ lld/trunk/test/ELF/mips-n32-emul.s @@ -0,0 +1,14 @@ +# Check that LLD shows an error when N32 ABI emulation argument +# is combined with non-N32 ABI object files. + +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o +# RUN: not ld.lld -m elf32btsmipn32 %t.o -o %t.exe 2>&1 | FileCheck %s + +# REQUIRES: mips + + .text + .global __start +__start: + nop + +# CHECK: error: {{.*}}mips-n32-emul.s.tmp.o is incompatible with elf32btsmipn32 Index: lld/trunk/test/ELF/mips-n32-rels.s =================================================================== --- lld/trunk/test/ELF/mips-n32-rels.s +++ lld/trunk/test/ELF/mips-n32-rels.s @@ -0,0 +1,71 @@ +# Check handling of N32 ABI relocation records. + +# For now llvm-mc generates incorrect object files for N32 ABI. +# We use the binary input file generated by GNU tool. +# llvm-mc -filetype=obj -triple=mips64-unknown-linux \ +# -target-abi n32 %s -o %t.o +# RUN: ld.lld %S/Inputs/mips-n32-rels.o -o %t.exe +# RUN: llvm-objdump -t -d -s %t.exe | FileCheck %s +# RUN: llvm-readobj -h %t.exe | FileCheck -check-prefix=ELF %s + +# REQUIRES: mips + +# .text +# .type __start, @function +# .global __start +# __start: +# lui $gp,%hi(%neg(%gp_rel(__start))) # R_MIPS_GPREL16 +# # R_MIPS_SUB +# # R_MIPS_HI16 +# loc: +# daddiu $gp,$gp,%lo(%neg(%gp_rel(__start))) # R_MIPS_GPREL16 +# # R_MIPS_SUB +# # R_MIPS_LO16 +# +# .section .rodata,"a",@progbits +# .gpword(loc) # R_MIPS_32 + +# CHECK: Disassembly of section .text: +# CHECK-NEXT: __start: +# CHECK-NEXT: 20000: 3c 1c 00 01 lui $gp, 1 +# ^-- 0x20000 - 0x37ff0 +# ^-- 0 - 0xfffe8010 +# ^-- %hi(0x17ff0) +# CHECK: loc: +# CHECK-NEXT: 20004: 67 9c 7f f0 daddiu $gp, $gp, 32752 +# ^-- 0x20000 - 0x37ff0 +# ^-- 0 - 0xfffe8010 +# ^-- %lo(0x17ff0) + +# CHECK: Contents of section .rodata: +# CHECK-NEXT: 10128 00020004 +# ^-- loc + +# CHECK: 00020004 .text 00000000 loc +# CHECK: 00037ff0 .got 00000000 .hidden _gp +# CHECK: 00020000 g F .text 00000000 __start + +# ELF: Format: ELF32-mips +# ELF-NEXT: Arch: mips +# ELF-NEXT: AddressSize: 32bit +# ELF-NEXT: LoadName: +# ELF-NEXT: ElfHeader { +# ELF-NEXT: Ident { +# ELF-NEXT: Magic: (7F 45 4C 46) +# ELF-NEXT: Class: 32-bit (0x1) +# ELF-NEXT: DataEncoding: BigEndian (0x2) +# ELF-NEXT: FileVersion: 1 +# ELF-NEXT: OS/ABI: SystemV (0x0) +# ELF-NEXT: ABIVersion: 0 +# ELF-NEXT: Unused: (00 00 00 00 00 00 00) +# ELF-NEXT: } +# ELF-NEXT: Type: Executable (0x2) +# ELF-NEXT: Machine: EM_MIPS (0x8) +# ELF-NEXT: Version: 1 +# ELF-NEXT: Entry: 0x20000 +# ELF-NEXT: ProgramHeaderOffset: +# ELF-NEXT: SectionHeaderOffset: +# ELF-NEXT: Flags [ +# ELF-NEXT: EF_MIPS_ABI2 +# ELF-NEXT: EF_MIPS_ARCH_64R2 +# ELF-NEXT: ]