[ELF][MIPS] N32 ABI support
authorSimon Atanasyan <simon@atanasyan.com>
Sat, 5 Nov 2016 22:58:01 +0000 (22:58 +0000)
committerSimon Atanasyan <simon@atanasyan.com>
Sat, 5 Nov 2016 22:58:01 +0000 (22:58 +0000)
In short the patch introduces support for linking object file conform
MIPS N32 ABI [1]. This ABI is similar to N64 ABI but uses 32-bit
pointer size.

The most non-trivial requirement of this ABI is one more relocation
packing format. N64 ABI puts multiple relocation type into the single
relocation record. The N32 ABI uses series of successive relocations
with the same offset for this purpose. In this patch, new function
`mergeMipsN32RelTypes` handle this case and "convert" N32 relocation to
the N64 relocation so the rest of the code keep unchanged.

For now, linker does not support series of relocations applied to sections
without SHF_ALLOC bit. Probably later I will add the support or insert
some sort of assert into the `relocateNonAlloc` routine to catch this
case.

[1] ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/MIPS-N32-ABI-Handbook.pdf

Differential revision: https://reviews.llvm.org/D26298

llvm-svn: 286052

lld/ELF/Config.h
lld/ELF/Driver.cpp
lld/ELF/Mips.cpp
lld/ELF/Relocations.cpp
lld/ELF/SymbolTable.cpp
lld/ELF/Target.cpp
lld/ELF/Writer.h
lld/test/ELF/Inputs/mips-n32-rels.o [new file with mode: 0644]
lld/test/ELF/mips-elf-flags-err.s
lld/test/ELF/mips-n32-emul.s [new file with mode: 0644]
lld/test/ELF/mips-n32-rels.s [new file with mode: 0644]

index 3b9f716..52b4efc 100644 (file)
@@ -105,6 +105,7 @@ struct Configuration {
   bool GnuHash = false;
   bool ICF;
   bool Mips64EL = false;
+  bool MipsN32Abi = false;
   bool NoGnuUnique;
   bool NoUndefinedVersion;
   bool Nostdlib;
index 85006a2..5b2eb69 100644 (file)
@@ -59,7 +59,8 @@ bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
 }
 
 // Parses a linker -m option.
-static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
+static std::tuple<ELFKind, uint16_t, uint8_t, bool>
+parseEmulation(StringRef Emul) {
   uint8_t OSABI = 0;
   StringRef S = Emul;
   if (S.endswith("_fbsd")) {
@@ -74,6 +75,8 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
           .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 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
     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 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
   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 @@ void LinkerDriver::inferMachineType() {
     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 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
   LinkerScript<ELFT> LS;
   ScriptBase = Script<ELFT>::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 2e7753c..ac65672 100644 (file)
@@ -342,6 +342,27 @@ uint8_t elf::getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag,
   return OldFlag;
 }
 
+template <class ELFT> static bool isN32Abi(const InputFile *F) {
+  if (auto *EF = dyn_cast<ELFFileBase<ELFT>>(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<ELF32LE>(F);
+  case ELF32BEKind:
+    return isN32Abi<ELF32BE>(F);
+  case ELF64LEKind:
+    return isN32Abi<ELF64LE>(F);
+  case ELF64BEKind:
+    return isN32Abi<ELF64BE>(F);
+  default:
+    llvm_unreachable("unknown Config->EKind");
+  }
+}
+
 template uint32_t elf::getMipsEFlags<ELF32LE>();
 template uint32_t elf::getMipsEFlags<ELF32BE>();
 template uint32_t elf::getMipsEFlags<ELF64LE>();
index bf9eac2..d111c75 100644 (file)
@@ -585,6 +585,22 @@ static void reportUndefined(SymbolBody &Sym, InputSectionBase<ELFT> &S,
     error(Msg);
 }
 
+template <class RelTy>
+static std::pair<uint32_t, uint32_t>
+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 @@ static void scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
     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 5c6c8e6..c3e9f2e 100644 (file)
@@ -36,8 +36,12 @@ using namespace lld::elf;
 template <class ELFT> static bool isCompatible(InputFile *F) {
   if (!isa<ELFFileBase<ELFT>>(F) && !isa<BitcodeFile>(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 d2e26d8..12a3ab0 100644 (file)
@@ -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 <class ELFT> MipsTargetInfo<ELFT>::MipsTargetInfo() {
 template <class ELFT>
 RelExpr MipsTargetInfo<ELFT>::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 <class ELFT> static bool isMipsR6() {
 template <class ELFT>
 void MipsTargetInfo<ELFT>::writePltHeader(uint8_t *Buf) const {
   const endianness E = ELFT::TargetEndianness;
-  write32<E>(Buf, 0x3c1c0000);      // lui   $28, %hi(&GOTPLT[0])
-  write32<E>(Buf + 4, 0x8f990000);  // lw    $25, %lo(&GOTPLT[0])($28)
-  write32<E>(Buf + 8, 0x279c0000);  // addiu $28, $28, %lo(&GOTPLT[0])
-  write32<E>(Buf + 12, 0x031cc023); // subu  $24, $24, $28
+  if (Config->MipsN32Abi) {
+    write32<E>(Buf, 0x3c0e0000);      // lui   $14, %hi(&GOTPLT[0])
+    write32<E>(Buf + 4, 0x8dd90000);  // lw    $25, %lo(&GOTPLT[0])($14)
+    write32<E>(Buf + 8, 0x25ce0000);  // addiu $14, $14, %lo(&GOTPLT[0])
+    write32<E>(Buf + 12, 0x030ec023); // subu  $24, $24, $14
+  } else {
+    write32<E>(Buf, 0x3c1c0000);      // lui   $28, %hi(&GOTPLT[0])
+    write32<E>(Buf + 4, 0x8f990000);  // lw    $25, %lo(&GOTPLT[0])($28)
+    write32<E>(Buf + 8, 0x279c0000);  // addiu $28, $28, %lo(&GOTPLT[0])
+    write32<E>(Buf + 12, 0x031cc023); // subu  $24, $24, $28
+  }
   write32<E>(Buf + 16, 0x03e07825); // move  $15, $31
   write32<E>(Buf + 20, 0x0018c082); // srl   $24, $24, 2
   write32<E>(Buf + 24, 0x0320f809); // jalr  $25
@@ -2137,8 +2145,8 @@ uint64_t MipsTargetInfo<ELFT>::getImplicitAddend(const uint8_t *Buf,
   }
 }
 
-static std::pair<uint32_t, uint64_t> calculateMips64RelChain(uint32_t Type,
-                                                             uint64_t Val) {
+static std::pair<uint32_t, uint64_t> 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 @@ void MipsTargetInfo<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type,
   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 b0d60b4..3b6c6e5 100644 (file)
@@ -16,6 +16,7 @@
 
 namespace lld {
 namespace elf {
+class InputFile;
 template <class ELFT> class OutputSectionBase;
 template <class ELFT> class InputSectionBase;
 template <class ELFT> class ObjectFile;
@@ -45,6 +46,8 @@ template <class ELFT> uint32_t getMipsEFlags();
 
 uint8_t getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag,
                          llvm::StringRef FileName);
+
+bool isMipsN32Abi(const InputFile *F);
 }
 }
 
diff --git a/lld/test/ELF/Inputs/mips-n32-rels.o b/lld/test/ELF/Inputs/mips-n32-rels.o
new file mode 100644 (file)
index 0000000..88cbce6
Binary files /dev/null and b/lld/test/ELF/Inputs/mips-n32-rels.o differ
index 45188cc..eab8377 100644 (file)
@@ -82,6 +82,6 @@ __start:
 # 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
diff --git a/lld/test/ELF/mips-n32-emul.s b/lld/test/ELF/mips-n32-emul.s
new file mode 100644 (file)
index 0000000..d0d81cc
--- /dev/null
@@ -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
diff --git a/lld/test/ELF/mips-n32-rels.s b/lld/test/ELF/mips-n32-rels.s
new file mode 100644 (file)
index 0000000..6bd171b
--- /dev/null
@@ -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:   ]