[lld] r286052 - [ELF][MIPS] N32 ABI support

Simon Atanasyan via llvm-commits llvm-commits at lists.llvm.org
Sat Nov 5 15:58:01 PDT 2016


Author: atanasyan
Date: Sat Nov  5 17:58:01 2016
New Revision: 286052

URL: http://llvm.org/viewvc/llvm-project?rev=286052&view=rev
Log:
[ELF][MIPS] N32 ABI support

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

Added:
    lld/trunk/test/ELF/Inputs/mips-n32-rels.o
    lld/trunk/test/ELF/mips-n32-emul.s
    lld/trunk/test/ELF/mips-n32-rels.s
Modified:
    lld/trunk/ELF/Config.h
    lld/trunk/ELF/Driver.cpp
    lld/trunk/ELF/Mips.cpp
    lld/trunk/ELF/Relocations.cpp
    lld/trunk/ELF/SymbolTable.cpp
    lld/trunk/ELF/Target.cpp
    lld/trunk/ELF/Writer.h
    lld/trunk/test/ELF/mips-elf-flags-err.s

Modified: lld/trunk/ELF/Config.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Config.h?rev=286052&r1=286051&r2=286052&view=diff
==============================================================================
--- lld/trunk/ELF/Config.h (original)
+++ lld/trunk/ELF/Config.h Sat Nov  5 17:58:01 2016
@@ -105,6 +105,7 @@ struct Configuration {
   bool GnuHash = false;
   bool ICF;
   bool Mips64EL = false;
+  bool MipsN32Abi = false;
   bool NoGnuUnique;
   bool NoUndefinedVersion;
   bool Nostdlib;

Modified: lld/trunk/ELF/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Driver.cpp?rev=286052&r1=286051&r2=286052&view=diff
==============================================================================
--- lld/trunk/ELF/Driver.cpp (original)
+++ lld/trunk/ELF/Driver.cpp Sat Nov  5 17:58:01 2016
@@ -59,7 +59,8 @@ bool elf::link(ArrayRef<const char *> Ar
 }
 
 // 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, uin
           .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, uin
     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::Inpu
   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:
   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);

Modified: lld/trunk/ELF/Mips.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Mips.cpp?rev=286052&r1=286051&r2=286052&view=diff
==============================================================================
--- lld/trunk/ELF/Mips.cpp (original)
+++ lld/trunk/ELF/Mips.cpp Sat Nov  5 17:58:01 2016
@@ -342,6 +342,27 @@ uint8_t elf::getMipsFpAbiFlag(uint8_t Ol
   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>();

Modified: lld/trunk/ELF/Relocations.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Relocations.cpp?rev=286052&r1=286051&r2=286052&view=diff
==============================================================================
--- lld/trunk/ELF/Relocations.cpp (original)
+++ lld/trunk/ELF/Relocations.cpp Sat Nov  5 17:58:01 2016
@@ -585,6 +585,22 @@ static void reportUndefined(SymbolBody &
     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<
     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())

Modified: lld/trunk/ELF/SymbolTable.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/SymbolTable.cpp?rev=286052&r1=286051&r2=286052&view=diff
==============================================================================
--- lld/trunk/ELF/SymbolTable.cpp (original)
+++ lld/trunk/ELF/SymbolTable.cpp Sat Nov  5 17:58:01 2016
@@ -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())

Modified: lld/trunk/ELF/Target.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Target.cpp?rev=286052&r1=286051&r2=286052&view=diff
==============================================================================
--- lld/trunk/ELF/Target.cpp (original)
+++ lld/trunk/ELF/Target.cpp Sat Nov  5 17:58:01 2016
@@ -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<ELF
 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 isMips
 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>::getImplic
   }
 }
 
-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(u
   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:

Modified: lld/trunk/ELF/Writer.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Writer.h?rev=286052&r1=286051&r2=286052&view=diff
==============================================================================
--- lld/trunk/ELF/Writer.h (original)
+++ lld/trunk/ELF/Writer.h Sat Nov  5 17:58:01 2016
@@ -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 getMipsEF
 
 uint8_t getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag,
                          llvm::StringRef FileName);
+
+bool isMipsN32Abi(const InputFile *F);
 }
 }
 

Added: lld/trunk/test/ELF/Inputs/mips-n32-rels.o
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/Inputs/mips-n32-rels.o?rev=286052&view=auto
==============================================================================
Binary files lld/trunk/test/ELF/Inputs/mips-n32-rels.o (added) and lld/trunk/test/ELF/Inputs/mips-n32-rels.o Sat Nov  5 17:58:01 2016 differ

Modified: lld/trunk/test/ELF/mips-elf-flags-err.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/mips-elf-flags-err.s?rev=286052&r1=286051&r2=286052&view=diff
==============================================================================
--- lld/trunk/test/ELF/mips-elf-flags-err.s (original)
+++ lld/trunk/test/ELF/mips-elf-flags-err.s Sat Nov  5 17:58:01 2016
@@ -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

Added: lld/trunk/test/ELF/mips-n32-emul.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/mips-n32-emul.s?rev=286052&view=auto
==============================================================================
--- lld/trunk/test/ELF/mips-n32-emul.s (added)
+++ lld/trunk/test/ELF/mips-n32-emul.s Sat Nov  5 17:58:01 2016
@@ -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

Added: lld/trunk/test/ELF/mips-n32-rels.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/mips-n32-rels.s?rev=286052&view=auto
==============================================================================
--- lld/trunk/test/ELF/mips-n32-rels.s (added)
+++ lld/trunk/test/ELF/mips-n32-rels.s Sat Nov  5 17:58:01 2016
@@ -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", at 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:   ]




More information about the llvm-commits mailing list