Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ 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: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -74,6 +74,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}) @@ -680,6 +682,24 @@ return V; } +template +static bool checkMipsN32Abi(const std::vector &Files) { + bool HasN32AbiEmulation = Config->Emulation == "elf32btsmipn32" || + Config->Emulation == "elf32ltsmipn32"; + for (const InputFile *F : Files) { + const auto *OF = dyn_cast>(F); + if (!OF) + continue; + bool HasN32AbiFlag = + (OF->getObj().getHeader()->e_flags & EF_MIPS_ABI2) != 0; + if (!Config->Emulation.empty() && HasN32AbiFlag != HasN32AbiEmulation) + error("emulation " + Config->Emulation + " is incompatible with " + + F->getName()); + return HasN32AbiFlag; + } + return HasN32AbiEmulation; +} + // Do actual linking. Note that when this function is called, // all linker scripts have already been parsed. template void LinkerDriver::link(opt::InputArgList &Args) { @@ -691,7 +711,10 @@ LinkerScript LS; ScriptBase = Script::X = &LS; - Config->Rela = ELFT::Is64Bits || Config->EMachine == EM_X86_64; + Config->MipsN32Abi = checkMipsN32Abi(Files); + + 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: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ ELF/Relocations.cpp @@ -585,6 +585,23 @@ error(Msg); } +template +static unsigned mergeMipsN32RelTypes(uint32_t &Type, + typename RelTy::Elf_Addr 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. + unsigned Processed = 0; + for (; I != E && Offset == I->r_offset; ++I) { + ++Processed; + Type |= I->getType(Config->Mips64EL) << (8 * Processed); + } + return 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 +641,9 @@ SymbolBody &Body = File.getRelocTargetSym(RI); uint32_t Type = RI.getType(Config->Mips64EL); + if (Config->MipsN32Abi) + I += mergeMipsN32RelTypes(Type, RI.r_offset, std::next(I), E); + // We only report undefined symbols if they are referenced somewhere in the // code. if (!Body.isLocal() && Body.isUndefined() && !Body.symbol()->isWeak()) Index: ELF/Target.cpp =================================================================== --- ELF/Target.cpp +++ 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: test/ELF/mips-n32-emul.s =================================================================== --- /dev/null +++ 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: emulation elf32btsmipn32 is incompatible with {{.*}}mips-n32-emul.s.tmp.o Index: test/ELF/mips-n32-rels.s =================================================================== --- /dev/null +++ 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: ]