[MIPS] Fix PLT entries generation in case of linking regular and microMIPS code
authorSimon Atanasyan <simon@atanasyan.com>
Mon, 2 Oct 2017 14:56:41 +0000 (14:56 +0000)
committerSimon Atanasyan <simon@atanasyan.com>
Mon, 2 Oct 2017 14:56:41 +0000 (14:56 +0000)
Currently LLD calls the `isMicroMips` routine to determine type of PLT entries
needs to be generated: regular or microMIPS. This routine checks ELF
header flags in the `FirstObj` to retrieve type of linked object files.
So if the first file does not contain microMIPS code, LLD will generate
PLT entries with regular (non-microMIPS) code only.

Ideally, if a PLT entry is referenced by microMIPS code only this entry
should contain microMIPS code, if a PLT entry is referenced by regular
code this entry should contain regular code. In a "mixed" case the PLT
entry can be either microMIPS or regular, but each "cross-mode-call" has
additional cost.

It's rather difficult to implement this ideal solution. But we can
assume that if there is an input object file with microMIPS code, the
most part of the code is microMIPS too. So we need to deduce type of PLT
entries based on finally calculated ELF header flags and do not check
only the first input object file.

This change implements this.
  - The `getMipsEFlags` renamed to the `calcMipsEFlags`. The function
    called from the `LinkerDriver::link`. Result is stored in
    the Configuration::MipsEFlags field.
  - The `isMicroMips` and `isMipsR6` routines access the `MipsEFlags`
    field to get and check calculated ELF flags.
  - New types of PLT records created when necessary.

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

llvm-svn: 314675

lld/ELF/Arch/Mips.cpp
lld/ELF/Arch/MipsArchTree.cpp
lld/ELF/Config.h
lld/ELF/Driver.cpp
lld/ELF/SyntheticSections.cpp
lld/ELF/Writer.cpp
lld/ELF/Writer.h
lld/test/ELF/mips-elf-flags.s
lld/test/ELF/mips-micro-jal.s

index 06618ec..4665ca1 100644 (file)
@@ -238,37 +238,29 @@ static void writeMicroRelocation16(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
   write16<E>(Loc, Data);
 }
 
-template <class ELFT> static bool isMicroMips() {
-  // FIXME (simon): This code does not support the case when both
-  // microMIPS and MIPS object files are linked together.
-  const auto &FirstObj = cast<ELFFileBase<ELFT>>(*Config->FirstElf);
-  uint32_t Arch = FirstObj.getObj().getHeader()->e_flags & EF_MIPS_ARCH_ASE;
-  return Arch == EF_MIPS_MICROMIPS;
-}
+static bool isMicroMips() { return Config->MipsEFlags & EF_MIPS_MICROMIPS; }
 
-template <class ELFT> static bool isMipsR6() {
-  const auto &FirstObj = cast<ELFFileBase<ELFT>>(*Config->FirstElf);
-  uint32_t Arch = FirstObj.getObj().getHeader()->e_flags & EF_MIPS_ARCH;
+static bool isMipsR6() {
+  uint32_t Arch = Config->MipsEFlags & EF_MIPS_ARCH;
   return Arch == EF_MIPS_ARCH_32R6 || Arch == EF_MIPS_ARCH_64R6;
 }
 
 template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *Buf) const {
   const endianness E = ELFT::TargetEndianness;
-  if (isMicroMips<ELFT>()) {
+  if (isMicroMips()) {
     uint64_t GotPlt = In<ELFT>::GotPlt->getVA();
     uint64_t Plt = In<ELFT>::Plt->getVA();
     // Overwrite trap instructions written by Writer::writeTrapInstr.
     memset(Buf, 0, PltHeaderSize);
 
-    write16<E>(Buf, isMipsR6<ELFT>() ? 0x7860 : 0x7980);
-                                    // addiupc v1, (GOTPLT) - .
+    write16<E>(Buf, isMipsR6() ? 0x7860 : 0x7980);  // addiupc v1, (GOTPLT) - .
     write16<E>(Buf + 4, 0xff23);    // lw      $25, 0($3)
     write16<E>(Buf + 8, 0x0535);    // subu16  $2,  $2, $3
     write16<E>(Buf + 10, 0x2525);   // srl16   $2,  $2, 2
     write16<E>(Buf + 12, 0x3302);   // addiu   $24, $2, -2
     write16<E>(Buf + 14, 0xfffe);
     write16<E>(Buf + 16, 0x0dff);   // move    $15, $31
-    if (isMipsR6<ELFT>()) {
+    if (isMipsR6()) {
       write16<E>(Buf + 18, 0x0f83); // move    $28, $3
       write16<E>(Buf + 20, 0x472b); // jalrc   $25
       write16<E>(Buf + 22, 0x0c00); // nop
@@ -310,11 +302,11 @@ void MIPS<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
                           uint64_t PltEntryAddr, int32_t Index,
                           unsigned RelOff) const {
   const endianness E = ELFT::TargetEndianness;
-  if (isMicroMips<ELFT>()) {
+  if (isMicroMips()) {
     // Overwrite trap instructions written by Writer::writeTrapInstr.
     memset(Buf, 0, PltEntrySize);
 
-    if (isMipsR6<ELFT>()) {
+    if (isMipsR6()) {
       write16<E>(Buf, 0x7840);      // addiupc $2, (GOTPLT) - .
       write16<E>(Buf + 4, 0xff22);  // lw $25, 0($2)
       write16<E>(Buf + 8, 0x0f02);  // move $24, $2
@@ -332,8 +324,7 @@ void MIPS<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
 
   write32<E>(Buf, 0x3c0f0000);     // lui   $15, %hi(.got.plt entry)
   write32<E>(Buf + 4, 0x8df90000); // l[wd] $25, %lo(.got.plt entry)($15)
-                                   // jr    $25
-  write32<E>(Buf + 8, isMipsR6<ELFT>() ? 0x03200009 : 0x03200008);
+  write32<E>(Buf + 8, isMipsR6() ? 0x03200009 : 0x03200008);  // jr  $25
   write32<E>(Buf + 12, 0x25f80000); // addiu $24, $15, %lo(.got.plt entry)
   writeRelocation<E>(Buf, GotPltEntryAddr + 0x8000, 16, 16);
   writeRelocation<E>(Buf + 4, GotPltEntryAddr, 16, 0);
index ff3aa9d..f78b6ba 100644 (file)
@@ -281,7 +281,7 @@ static uint32_t getArchFlags(ArrayRef<FileFlags> Files) {
   return Ret;
 }
 
-template <class ELFT> uint32_t elf::getMipsEFlags() {
+template <class ELFT> uint32_t elf::calcMipsEFlags() {
   std::vector<FileFlags> V;
   for (InputFile *F : ObjectFiles)
     V.push_back(
@@ -364,7 +364,7 @@ bool elf::isMipsN32Abi(const InputFile *F) {
   }
 }
 
-template uint32_t elf::getMipsEFlags<ELF32LE>();
-template uint32_t elf::getMipsEFlags<ELF32BE>();
-template uint32_t elf::getMipsEFlags<ELF64LE>();
-template uint32_t elf::getMipsEFlags<ELF64BE>();
+template uint32_t elf::calcMipsEFlags<ELF32LE>();
+template uint32_t elf::calcMipsEFlags<ELF32BE>();
+template uint32_t elf::calcMipsEFlags<ELF64LE>();
+template uint32_t elf::calcMipsEFlags<ELF64BE>();
index ecc3399..83afb84 100644 (file)
@@ -211,6 +211,12 @@ struct Configuration {
   // if that's true.)
   bool IsMips64EL;
 
+  // Holds set of ELF header flags for MIPS targets. The set calculated
+  // by the `elf::calcMipsEFlags` function and cached in this field. For
+  // the calculation we iterate over all input object files and combine
+  // their ELF flags.
+  uint32_t MipsEFlags = 0;
+
   // The ELF spec defines two types of relocation table entries, RELA and
   // REL. RELA is a triplet of (offset, info, addend) while REL is a
   // tuple of (offset, info). Addends for REL are implicit and read from
index e272f00..b53d88b 100644 (file)
@@ -1080,6 +1080,9 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
     for (InputSectionBase *S : F->getSections())
       InputSections.push_back(cast<InputSection>(S));
 
+  if (Config->EMachine == EM_MIPS)
+    Config->MipsEFlags = calcMipsEFlags<ELFT>();
+
   // This adds a .comment section containing a version string. We have to add it
   // before decompressAndMergeSections because the .comment section is a
   // mergeable section.
index 835b4ca..5910395 100644 (file)
@@ -151,7 +151,7 @@ MipsAbiFlagsSection<ELFT> *MipsAbiFlagsSection<ELFT>::create() {
       return nullptr;
     }
 
-    // LLD checks ISA compatibility in getMipsEFlags(). Here we just
+    // LLD checks ISA compatibility in calcMipsEFlags(). Here we just
     // select the highest number of ISA/Rev/Ext.
     Flags.isa_level = std::max(Flags.isa_level, S->isa_level);
     Flags.isa_rev = std::max(Flags.isa_rev, S->isa_rev);
index 6274808..aee315e 100644 (file)
@@ -1798,7 +1798,7 @@ template <class ELFT> void Writer<ELFT>::writeHeader() {
     // kernels (as of 2016) require an EABI version to be set.
     EHdr->e_flags = EF_ARM_EABI_VER5;
   else if (Config->EMachine == EM_MIPS)
-    EHdr->e_flags = getMipsEFlags<ELFT>();
+    EHdr->e_flags = Config->MipsEFlags;
 
   if (!Config->Relocatable) {
     EHdr->e_phoff = sizeof(Elf_Ehdr);
index 8307000..4db5fe8 100644 (file)
@@ -48,7 +48,7 @@ struct PhdrEntry {
 
 llvm::StringRef getOutputSectionName(llvm::StringRef Name);
 
-template <class ELFT> uint32_t getMipsEFlags();
+template <class ELFT> uint32_t calcMipsEFlags();
 
 uint8_t getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag,
                          llvm::StringRef FileName);
index f8f916c..d2b3d92 100644 (file)
 # RUN: llvm-readobj -h -mips-abi-flags %t.exe \
 # RUN:   | FileCheck -check-prefix=OCTEON %s
 
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN:         -mattr=micromips %S/Inputs/mips-fpic.s -o %t-mm.o
+# RUN: ld.lld %t.o %t-mm.o -o %t.exe
+# RUN: llvm-readobj -h -mips-abi-flags %t.exe | FileCheck -check-prefix=MICRO %s
+
 # REQUIRES: mips
 
   .text
@@ -170,3 +176,26 @@ __start:
 # OCTEON-NEXT:   ]
 # OCTEON-NEXT:   Flags 2: 0x0
 # OCTEON-NEXT: }
+
+# MICRO:      Flags [
+# MICRO-NEXT:   EF_MIPS_ABI_O32
+# MICRO-NEXT:   EF_MIPS_ARCH_32
+# MICRO-NEXT:   EF_MIPS_CPIC
+# MICRO-NEXT:   EF_MIPS_MICROMIPS
+# MICRO-NEXT: ]
+# MICRO:      MIPS ABI Flags {
+# MICRO-NEXT:   Version: 0
+# MICRO-NEXT:   ISA: MIPS32
+# MICRO-NEXT:   ISA Extension: None
+# MICRO-NEXT:   ASEs [
+# MICRO-NEXT:     microMIPS
+# MICRO-NEXT:   ]
+# MICRO-NEXT:   FP ABI: Hard float (double precision)
+# MICRO-NEXT:   GPR size: 32
+# MICRO-NEXT:   CPR1 size: 32
+# MICRO-NEXT:   CPR2 size: 0
+# MICRO-NEXT:   Flags 1 [
+# MICRO-NEXT:     ODDSPREG
+# MICRO-NEXT:   ]
+# MICRO-NEXT:   Flags 2: 0x0
+# MICRO-NEXT: }
index 39193ae..8382612 100644 (file)
 # RUN: ld.lld -o %tel.exe %t2el.o %tel.so
 # RUN: llvm-objdump -d -mattr=micromips %tel.exe | FileCheck --check-prefix=ELR6 %s
 
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN:         -mattr=micromips %S/Inputs/mips-micro.s -o %t1eb.o
+# RUN: ld.lld -shared -o %teb.so %t1eb.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN:         %S/Inputs/mips-fpic.s -o %t-reg.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN:         -mattr=micromips %s -o %t2eb.o
+# RUN: ld.lld --no-threads -o %teb.exe %t-reg.o %t2eb.o %teb.so
+# RUN: llvm-objdump -d -mattr=micromips %teb.exe \
+# RUN:   | FileCheck --check-prefix=MIXED %s
+
 # REQUIRES: mips
 
 # EB:      Disassembly of section .plt:
 # ELR6-NEXT:    20038:       02 0f           move16  $24, $2
 # ELR6-NEXT:    2003a:       23 47           jrc16   $25
 
+# MIXED:      Disassembly of section .plt:
+# MIXED-NEXT: .plt:
+# MIXED-NEXT:    20020:       79 80 3f f9     addiupc $3, 65508
+# MIXED-NEXT:    20024:       ff 23 00 00     lw      $25, 0($3)
+# MIXED-NEXT:    20028:       05 35           subu16  $2, $2, $3
+# MIXED-NEXT:    2002a:       25 25           srl16   $2, $2, 2
+# MIXED-NEXT:    2002c:       33 02 ff fe     addiu   $24, $2, -2
+# MIXED-NEXT:    20030:       0d ff           move    $15, $ra
+# MIXED-NEXT:    20032:       45 f9           jalrs16 $25
+# MIXED-NEXT:    20034:       0f 83           move    $gp, $3
+# MIXED-NEXT:    20036:       0c 00           nop
+# MIXED-NEXT:    20038:       00 00 00 00     nop
+# MIXED-NEXT:    2003c:       00 00 00 00     nop
+
+# MIXED-NEXT:    20040:       79 00 3f f3     addiupc $2, 65484
+# MIXED-NEXT:    20044:       ff 22 00 00     lw      $25, 0($2)
+# MIXED-NEXT:    20048:       45 99           jr16    $25
+# MIXED-NEXT:    2004a:       0f 02           move    $24, $2
+
 # PLT:      Entries [
 # PLT-NEXT:   Entry {
 # PLT-NEXT:     Address: 0x3000C