[lld] r265059 - [ELF] Implement infrastructure for thunk code creation

Simon Atanasyan via llvm-commits llvm-commits at lists.llvm.org
Thu Mar 31 14:26:24 PDT 2016


Author: atanasyan
Date: Thu Mar 31 16:26:23 2016
New Revision: 265059

URL: http://llvm.org/viewvc/llvm-project?rev=265059&view=rev
Log:
[ELF] Implement infrastructure for thunk code creation

Some targets might require creation of thunks. For example, MIPS targets
require stubs to call PIC code from non-PIC one. The patch implements
infrastructure for thunk code creation and provides support for MIPS
LA25 stubs. Any MIPS PIC code function is invoked with its address
in register $t9. So if we have a branch instruction from non-PIC code
to the PIC one we cannot make the jump directly and need to create a small
stub to save the target function address.
See page 3-38 ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf

- In relocation scanning phase we ask target about thunk creation necessity
by calling `TagetInfo::needsThunk` method. The `InputSection` class
maintains list of Symbols requires thunk creation.

- Reassigning offsets performed for each input sections after relocation
scanning complete because position of each section might change due
thunk creation.

- The patch introduces new dedicated value for DefinedSynthetic symbols
DefinedSynthetic::SectionEnd. Synthetic symbol with that value always
points to the end of the corresponding output section. That allows to
escape updating synthetic symbols if output sections sizes changes after
relocation scanning due thunk creation.

- In the `InputSection::writeTo` method we write thunks after corresponding
input section. Each thunk is written by calling `TargetInfo::writeThunk` method.

- The patch supports the only type of thunk code for each target. For now,
it is enough.

Differential Revision: http://reviews.llvm.org/D17934

Added:
    lld/trunk/test/ELF/Inputs/mips-pic.s
    lld/trunk/test/ELF/mips-npic-call-pic.s
Modified:
    lld/trunk/ELF/InputSection.cpp
    lld/trunk/ELF/InputSection.h
    lld/trunk/ELF/OutputSections.cpp
    lld/trunk/ELF/OutputSections.h
    lld/trunk/ELF/Symbols.cpp
    lld/trunk/ELF/Symbols.h
    lld/trunk/ELF/Target.cpp
    lld/trunk/ELF/Target.h
    lld/trunk/ELF/Writer.cpp

Modified: lld/trunk/ELF/InputSection.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/InputSection.cpp?rev=265059&r1=265058&r2=265059&view=diff
==============================================================================
--- lld/trunk/ELF/InputSection.cpp (original)
+++ lld/trunk/ELF/InputSection.cpp Thu Mar 31 16:26:23 2016
@@ -38,6 +38,13 @@ InputSectionBase<ELFT>::InputSectionBase
   Align = std::max<uintX_t>(Header->sh_addralign, 1);
 }
 
+template <class ELFT> size_t InputSectionBase<ELFT>::getSize() const {
+  if (auto *D = dyn_cast<InputSection<ELFT>>(this))
+    if (D->getThunksSize() > 0)
+      return D->getThunkOff() + D->getThunksSize();
+  return Header->sh_size;
+}
+
 template <class ELFT> StringRef InputSectionBase<ELFT>::getSectionName() const {
   return check(File->getObj().getSectionName(this->Header));
 }
@@ -105,6 +112,19 @@ InputSectionBase<ELFT> *InputSection<ELF
   return Sections[this->Header->sh_info];
 }
 
+template <class ELFT> void InputSection<ELFT>::addThunk(SymbolBody &Body) {
+  Body.ThunkIndex = Thunks.size();
+  Thunks.push_back(&Body);
+}
+
+template <class ELFT> uint64_t InputSection<ELFT>::getThunkOff() const {
+  return this->Header->sh_size;
+}
+
+template <class ELFT> uint64_t InputSection<ELFT>::getThunksSize() const {
+  return Thunks.size() * Target->ThunkSize;
+}
+
 // This is used for -r. We can't use memcpy to copy relocations because we need
 // to update symbol table offset and section index for each relocation. So we
 // copy relocations one by one.
@@ -293,6 +313,9 @@ void InputSectionBase<ELFT>::relocate(ui
       // If that's the case, we leave the field alone rather than filling it
       // with a possibly incorrect value.
       continue;
+    } else if (Target->needsThunk(Type, *this->getFile(), Body)) {
+      // Get address of a thunk code related to the symbol.
+      SymVA = Body.getThunkVA<ELFT>();
     } else if (Config->EMachine == EM_MIPS) {
       SymVA = adjustMipsSymVA<ELFT>(Type, *File, Body, AddrLoc, SymVA);
     } else if (!Target->needsCopyRel<ELFT>(Type, Body) &&
@@ -333,6 +356,19 @@ template <class ELFT> void InputSection<
     else
       this->relocate(Buf, BufEnd, EObj.rels(RelSec));
   }
+
+  // The section might have a data/code generated by the linker and need
+  // to be written after the section. Usually these are thunks - small piece
+  // of code used to jump between "incompatible" functions like PIC and non-PIC
+  // or if the jump target too far and its address does not fit to the short
+  // jump istruction.
+  if (!Thunks.empty()) {
+    Buf += OutSecOff + getThunkOff();
+    for (const SymbolBody *S : Thunks) {
+      Target->writeThunk(Buf, S->getVA<ELFT>());
+      Buf += Target->ThunkSize;
+    }
+  }
 }
 
 template <class ELFT>

Modified: lld/trunk/ELF/InputSection.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/InputSection.h?rev=265059&r1=265058&r2=265059&view=diff
==============================================================================
--- lld/trunk/ELF/InputSection.h (original)
+++ lld/trunk/ELF/InputSection.h Thu Mar 31 16:26:23 2016
@@ -56,7 +56,7 @@ public:
   InputSectionBase<ELFT> *Repl;
 
   // Returns the size of this section (even if this is a common or BSS.)
-  size_t getSize() const { return Header->sh_size; }
+  size_t getSize() const;
 
   static InputSectionBase<ELFT> *Discarded;
 
@@ -167,6 +167,17 @@ public:
 
   InputSectionBase<ELFT> *getRelocatedSection();
 
+  // Register thunk related to the symbol. When the section is written
+  // to a mmap'ed file, target is requested to write an actual thunk code.
+  // Now thunks is supported for MIPS target only.
+  void addThunk(SymbolBody &Body);
+
+  // The offset of synthetic thunk code from beginning of this section.
+  uint64_t getThunkOff() const;
+
+  // Size of chunk with thunks code.
+  uint64_t getThunksSize() const;
+
 private:
   template <class RelTy>
   void copyRelocations(uint8_t *Buf, llvm::iterator_range<const RelTy *> Rels);
@@ -176,6 +187,8 @@ private:
 
   // Used by ICF.
   uint64_t GroupId = 0;
+
+  llvm::TinyPtrVector<const SymbolBody *> Thunks;
 };
 
 // MIPS .reginfo section provides information on the registers used by the code

Modified: lld/trunk/ELF/OutputSections.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/OutputSections.cpp?rev=265059&r1=265058&r2=265059&view=diff
==============================================================================
--- lld/trunk/ELF/OutputSections.cpp (original)
+++ lld/trunk/ELF/OutputSections.cpp Thu Mar 31 16:26:23 2016
@@ -821,12 +821,6 @@ void OutputSection<ELFT>::addSection(Inp
   Sections.push_back(S);
   S->OutSec = this;
   this->updateAlign(S->Align);
-
-  uintX_t Off = this->Header.sh_size;
-  Off = alignTo(Off, S->Align);
-  S->OutSecOff = Off;
-  Off += S->getSize();
-  this->Header.sh_size = Off;
 }
 
 // If an input string is in the form of "foo.N" where N is a number,
@@ -843,8 +837,8 @@ static int getPriority(StringRef S) {
 }
 
 // This function is called after we sort input sections
-// to update their offsets.
-template <class ELFT> void OutputSection<ELFT>::reassignOffsets() {
+// and scan relocations to setup sections' offsets.
+template <class ELFT> void OutputSection<ELFT>::assignOffsets() {
   uintX_t Off = 0;
   for (InputSection<ELFT> *S : Sections) {
     Off = alignTo(Off, S->Align);
@@ -872,7 +866,6 @@ template <class ELFT> void OutputSection
   Sections.clear();
   for (Pair &P : V)
     Sections.push_back(P.second);
-  reassignOffsets();
 }
 
 // Returns true if S matches /Filename.?\.o$/.
@@ -933,7 +926,6 @@ static bool compCtors(const InputSection
 // Read the comment above.
 template <class ELFT> void OutputSection<ELFT>::sortCtorsDtors() {
   std::stable_sort(Sections.begin(), Sections.end(), compCtors<ELFT>);
-  reassignOffsets();
 }
 
 static void fill(uint8_t *Buf, size_t Size, ArrayRef<uint8_t> A) {

Modified: lld/trunk/ELF/OutputSections.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/OutputSections.h?rev=265059&r1=265058&r2=265059&view=diff
==============================================================================
--- lld/trunk/ELF/OutputSections.h (original)
+++ lld/trunk/ELF/OutputSections.h Thu Mar 31 16:26:23 2016
@@ -87,6 +87,7 @@ public:
   // Typically the first section of each PT_LOAD segment has this flag.
   bool PageAlign = false;
 
+  virtual void assignOffsets() {}
   virtual void finalize() {}
   virtual void writeTo(uint8_t *Buf) {}
   virtual ~OutputSectionBase() = default;
@@ -271,10 +272,10 @@ public:
   void sortInitFini();
   void sortCtorsDtors();
   void writeTo(uint8_t *Buf) override;
+  void assignOffsets() override;
   void finalize() override;
 
 private:
-  void reassignOffsets();
   std::vector<InputSection<ELFT> *> Sections;
 };
 

Modified: lld/trunk/ELF/Symbols.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Symbols.cpp?rev=265059&r1=265058&r2=265059&view=diff
==============================================================================
--- lld/trunk/ELF/Symbols.cpp (original)
+++ lld/trunk/ELF/Symbols.cpp Thu Mar 31 16:26:23 2016
@@ -37,6 +37,8 @@ static typename ELFT::uint getSymVA(cons
   switch (Body.kind()) {
   case SymbolBody::DefinedSyntheticKind: {
     auto &D = cast<DefinedSynthetic<ELFT>>(Body);
+    if (D.Value == DefinedSynthetic<ELFT>::SectionEnd)
+      return D.Section.getVA() + D.Section.getSize();
     return D.Section.getVA() + D.Value;
   }
   case SymbolBody::DefinedRegularKind: {
@@ -133,6 +135,13 @@ template <class ELFT> typename ELFT::uin
          PltIndex * Target->PltEntrySize;
 }
 
+template <class ELFT> typename ELFT::uint SymbolBody::getThunkVA() const {
+  auto *D = cast<DefinedRegular<ELFT>>(this);
+  auto *S = cast<InputSection<ELFT>>(D->Section);
+  return S->OutSec->getVA() + S->OutSecOff + S->getThunkOff() +
+         ThunkIndex * Target->ThunkSize;
+}
+
 template <class ELFT> typename ELFT::uint SymbolBody::getSize() const {
   if (auto *B = dyn_cast<DefinedElf<ELFT>>(this))
     return B->Sym.st_size;
@@ -298,6 +307,11 @@ template uint32_t SymbolBody::template g
 template uint64_t SymbolBody::template getSize<ELF64LE>() const;
 template uint64_t SymbolBody::template getSize<ELF64BE>() const;
 
+template uint32_t SymbolBody::template getThunkVA<ELF32LE>() const;
+template uint32_t SymbolBody::template getThunkVA<ELF32BE>() const;
+template uint64_t SymbolBody::template getThunkVA<ELF64LE>() const;
+template uint64_t SymbolBody::template getThunkVA<ELF64BE>() const;
+
 template int SymbolBody::compare<ELF32LE>(SymbolBody *Other);
 template int SymbolBody::compare<ELF32BE>(SymbolBody *Other);
 template int SymbolBody::compare<ELF64LE>(SymbolBody *Other);

Modified: lld/trunk/ELF/Symbols.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Symbols.h?rev=265059&r1=265058&r2=265059&view=diff
==============================================================================
--- lld/trunk/ELF/Symbols.h (original)
+++ lld/trunk/ELF/Symbols.h Thu Mar 31 16:26:23 2016
@@ -85,9 +85,11 @@ public:
   uint32_t GotIndex = -1;
   uint32_t GotPltIndex = -1;
   uint32_t PltIndex = -1;
+  uint32_t ThunkIndex = -1;
   bool hasGlobalDynIndex() { return GlobalDynIndex != uint32_t(-1); }
   bool isInGot() const { return GotIndex != -1U; }
   bool isInPlt() const { return PltIndex != -1U; }
+  bool hasThunk() const { return ThunkIndex != -1U; }
 
   void setUsedInRegularObj() { IsUsedInRegularObj = true; }
 
@@ -97,6 +99,7 @@ public:
   template <class ELFT> typename ELFT::uint getGotVA() const;
   template <class ELFT> typename ELFT::uint getGotPltVA() const;
   template <class ELFT> typename ELFT::uint getPltVA() const;
+  template <class ELFT> typename ELFT::uint getThunkVA() const;
   template <class ELFT> typename ELFT::uint getSize() const;
 
   // A SymbolBody has a backreference to a Symbol. Originally they are
@@ -249,6 +252,10 @@ public:
     return S->kind() == SymbolBody::DefinedSyntheticKind;
   }
 
+  // Special value designates that the symbol 'points'
+  // to the end of the section.
+  static const uintX_t SectionEnd = uintX_t(-1);
+
   uintX_t Value;
   const OutputSectionBase<ELFT> &Section;
 };

Modified: lld/trunk/ELF/Target.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Target.cpp?rev=265059&r1=265058&r2=265059&view=diff
==============================================================================
--- lld/trunk/ELF/Target.cpp (original)
+++ lld/trunk/ELF/Target.cpp Thu Mar 31 16:26:23 2016
@@ -17,6 +17,7 @@
 
 #include "Target.h"
 #include "Error.h"
+#include "InputFiles.h"
 #include "OutputSections.h"
 #include "Symbols.h"
 
@@ -198,9 +199,12 @@ public:
   void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
                 int32_t Index, unsigned RelOff) const override;
   void writeGotHeader(uint8_t *Buf) const override;
+  void writeThunk(uint8_t *Buf, uint64_t S) const override;
   bool needsCopyRelImpl(uint32_t Type) const override;
   bool needsGot(uint32_t Type, const SymbolBody &S) const override;
   bool needsPltImpl(uint32_t Type) const override;
+  bool needsThunk(uint32_t Type, const InputFile &File,
+                  const SymbolBody &S) const override;
   void relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P,
                    uint64_t SA) const override;
   bool isHintRel(uint32_t Type) const override;
@@ -331,6 +335,11 @@ TargetInfo::PltNeed TargetInfo::needsPlt
   return Plt_No;
 }
 
+bool TargetInfo::needsThunk(uint32_t Type, const InputFile &File,
+                            const SymbolBody &S) const {
+  return false;
+}
+
 bool TargetInfo::isTlsInitialExecRel(uint32_t Type) const { return false; }
 
 bool TargetInfo::pointsToLocalDynamicGotEntry(uint32_t Type) const {
@@ -1581,6 +1590,7 @@ template <class ELFT> MipsTargetInfo<ELF
   PageSize = 65536;
   PltEntrySize = 16;
   PltZeroSize = 32;
+  ThunkSize = 16;
   UseLazyBinding = true;
   CopyRel = R_MIPS_COPY;
   PltRel = R_MIPS_JUMP_SLOT;
@@ -1695,6 +1705,20 @@ void MipsTargetInfo<ELFT>::writePlt(uint
 }
 
 template <class ELFT>
+void MipsTargetInfo<ELFT>::writeThunk(uint8_t *Buf, uint64_t S) const {
+  // Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
+  // See MipsTargetInfo::writeThunk for details.
+  const endianness E = ELFT::TargetEndianness;
+  write32<E>(Buf, 0x3c190000);      // lui   $25, %hi(func)
+  write32<E>(Buf + 4, 0x08000000);  // j     func
+  write32<E>(Buf + 8, 0x27390000);  // addiu $25, $25, %lo(func)
+  write32<E>(Buf + 12, 0x00000000); // nop
+  writeMipsHi16<E>(Buf, S);
+  write32<E>(Buf + 4, 0x08000000 | (S >> 2));
+  writeMipsLo16<E>(Buf + 8, S);
+}
+
+template <class ELFT>
 bool MipsTargetInfo<ELFT>::needsCopyRelImpl(uint32_t Type) const {
   return !isRelRelative(Type);
 }
@@ -1715,6 +1739,31 @@ bool MipsTargetInfo<ELFT>::needsPltImpl(
 }
 
 template <class ELFT>
+bool MipsTargetInfo<ELFT>::needsThunk(uint32_t Type, const InputFile &File,
+                                      const SymbolBody &S) const {
+  // Any MIPS PIC code function is invoked with its address in register $t9.
+  // So if we have a branch instruction from non-PIC code to the PIC one
+  // we cannot make the jump directly and need to create a small stubs
+  // to save the target function address.
+  // See page 3-38 ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
+  if (Type != R_MIPS_26)
+    return false;
+  auto *F = dyn_cast<ELFFileBase<ELFT>>(&File);
+  if (!F)
+    return false;
+  // If current file has PIC code, LA25 stub is not required.
+  if (F->getObj().getHeader()->e_flags & EF_MIPS_PIC)
+    return false;
+  auto *D = dyn_cast<DefinedRegular<ELFT>>(&S);
+  if (!D || !D->Section)
+    return false;
+  // LA25 is required if target file has PIC code
+  // or target symbol is a PIC symbol.
+  return (D->Section->getFile()->getObj().getHeader()->e_flags & EF_MIPS_PIC) ||
+         (D->Sym.st_other & STO_MIPS_MIPS16) == STO_MIPS_PIC;
+}
+
+template <class ELFT>
 uint64_t MipsTargetInfo<ELFT>::getImplicitAddend(uint8_t *Buf,
                                                  uint32_t Type) const {
   const endianness E = ELFT::TargetEndianness;

Modified: lld/trunk/ELF/Target.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Target.h?rev=265059&r1=265058&r2=265059&view=diff
==============================================================================
--- lld/trunk/ELF/Target.h (original)
+++ lld/trunk/ELF/Target.h Thu Mar 31 16:26:23 2016
@@ -17,6 +17,7 @@
 
 namespace lld {
 namespace elf {
+class InputFile;
 class SymbolBody;
 
 class TargetInfo {
@@ -62,6 +63,11 @@ public:
   enum PltNeed { Plt_No, Plt_Explicit, Plt_Implicit };
   PltNeed needsPlt(uint32_t Type, const SymbolBody &S) const;
 
+  virtual bool needsThunk(uint32_t Type, const InputFile &File,
+                          const SymbolBody &S) const;
+
+  virtual void writeThunk(uint8_t *Buf, uint64_t S) const {}
+
   virtual void relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type,
                            uint64_t P, uint64_t SA) const = 0;
   virtual bool isGotRelative(uint32_t Type) const;
@@ -94,6 +100,7 @@ public:
   unsigned PltZeroSize = 0;
   unsigned GotHeaderEntriesNum = 0;
   unsigned GotPltHeaderEntriesNum = 3;
+  uint32_t ThunkSize = 0;
   bool UseLazyBinding = false;
 
 private:

Modified: lld/trunk/ELF/Writer.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Writer.cpp?rev=265059&r1=265058&r2=265059&view=diff
==============================================================================
--- lld/trunk/ELF/Writer.cpp (original)
+++ lld/trunk/ELF/Writer.cpp Thu Mar 31 16:26:23 2016
@@ -85,6 +85,9 @@ private:
   bool isOutputDynamic() const {
     return !Symtab.getSharedFiles().empty() || Config->Pic;
   }
+  template <class RelTy>
+  void scanRelocsForThunks(const elf::ObjectFile<ELFT> &File,
+                           iterator_range<const RelTy *> Rels);
 
   void ensureBss();
   void addCommonSymbols(std::vector<DefinedCommon *> &Syms);
@@ -298,6 +301,25 @@ static unsigned handleTlsRelocation(uint
   return 0;
 }
 
+// Some targets might require creation of thunks for relocations. Now we
+// support only MIPS which requires LA25 thunk to call PIC code from non-PIC
+// one. Scan relocations to find each one requires thunk.
+template <class ELFT>
+template <class RelTy>
+void Writer<ELFT>::scanRelocsForThunks(const elf::ObjectFile<ELFT> &File,
+                                       iterator_range<const RelTy *> Rels) {
+  for (const RelTy &RI : Rels) {
+    uint32_t Type = RI.getType(Config->Mips64EL);
+    uint32_t SymIndex = RI.getSymbol(Config->Mips64EL);
+    SymbolBody &Body = File.getSymbolBody(SymIndex).repl();
+    if (Body.hasThunk() || !Target->needsThunk(Type, File, Body))
+      continue;
+    auto *D = cast<DefinedRegular<ELFT>>(&Body);
+    auto *S = cast<InputSection<ELFT>>(D->Section);
+    S->addThunk(Body);
+  }
+}
+
 // 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.
@@ -479,6 +501,10 @@ void Writer<ELFT>::scanRelocs(InputSecti
     Out<ELFT>::RelaDyn->addReloc(
         {Target->RelativeRel, &C, RI.r_offset, true, &Body, Addend});
   }
+
+  // Scan relocations for necessary thunks.
+  if (Config->EMachine == EM_MIPS)
+    scanRelocsForThunks(File, Rels);
 }
 
 template <class ELFT> void Writer<ELFT>::scanRelocs(InputSection<ELFT> &C) {
@@ -1042,6 +1068,9 @@ template <class ELFT> bool Writer<ELFT>:
     }
   }
 
+  for (OutputSectionBase<ELFT> *Sec : getSections())
+    Sec->assignOffsets();
+
   // Now that we have defined all possible symbols including linker-
   // synthesized ones. Visit all symbols to give the finishing touches.
   std::vector<DefinedCommon *> CommonSymbols;
@@ -1167,7 +1196,8 @@ template <class ELFT> void Writer<ELFT>:
                     OutputSectionBase<ELFT> *OS) {
     if (OS) {
       Symtab.addSynthetic(Start, *OS, 0, STV_DEFAULT);
-      Symtab.addSynthetic(End, *OS, OS->getSize(), STV_DEFAULT);
+      Symtab.addSynthetic(End, *OS, DefinedSynthetic<ELFT>::SectionEnd,
+                          STV_DEFAULT);
     } else {
       Symtab.addIgnored(Start);
       Symtab.addIgnored(End);
@@ -1200,7 +1230,8 @@ void Writer<ELFT>::addStartStopSymbols(O
       Symtab.addSynthetic(Start, *Sec, 0, STV_DEFAULT);
   if (SymbolBody *B = Symtab.find(Stop))
     if (B->isUndefined())
-      Symtab.addSynthetic(Stop, *Sec, Sec->getSize(), STV_DEFAULT);
+      Symtab.addSynthetic(Stop, *Sec, DefinedSynthetic<ELFT>::SectionEnd,
+                          STV_DEFAULT);
 }
 
 template <class ELFT> static bool needsPtLoad(OutputSectionBase<ELFT> *Sec) {

Added: lld/trunk/test/ELF/Inputs/mips-pic.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/Inputs/mips-pic.s?rev=265059&view=auto
==============================================================================
--- lld/trunk/test/ELF/Inputs/mips-pic.s (added)
+++ lld/trunk/test/ELF/Inputs/mips-pic.s Thu Mar 31 16:26:23 2016
@@ -0,0 +1,19 @@
+  .option pic2
+
+  .section .text.1,"ax", at progbits
+  .align 4
+  .globl foo1a
+  .type foo1a, @function
+foo1a:
+  nop
+  .globl foo1b
+  .type foo1b, @function
+foo1b:
+  nop
+
+  .section .text.2,"ax", at progbits
+  .align 4
+  .globl foo2
+  .type foo2, @function
+foo2:
+  nop

Added: lld/trunk/test/ELF/mips-npic-call-pic.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/mips-npic-call-pic.s?rev=265059&view=auto
==============================================================================
--- lld/trunk/test/ELF/mips-npic-call-pic.s (added)
+++ lld/trunk/test/ELF/mips-npic-call-pic.s Thu Mar 31 16:26:23 2016
@@ -0,0 +1,58 @@
+# Check LA25 stubs creation. This stub code is necessary when
+# non-PIC code calls PIC function.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN:   %p/Inputs/mips-pic.s -o %t-pic.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t-npic.o
+# RUN: ld.lld %t-npic.o %t-pic.o -o %t.exe
+# RUN: llvm-objdump -d %t.exe | FileCheck %s
+
+# REQUIRES: mips
+
+# CHECK:     Disassembly of section .text:
+# CHECK-NEXT: __start:
+# CHECK-NEXT:    20000:       0c 00 80 0a     jal     131112
+#                                                     ^-- 0x20030 .pic.foo1a
+# CHECK-NEXT:    20004:       00 00 00 00     nop
+# CHECK-NEXT:    20008:       0c 00 80 15     jal     131156
+#                                                     ^-- 0x20060 .pic.foo2
+# CHECK-NEXT:    2000c:       00 00 00 00     nop
+# CHECK-NEXT:    20010:       0c 00 80 0e     jal     131128
+#                                                     ^-- 0x20040 .pic.foo1b
+# CHECK-NEXT:    20014:       00 00 00 00     nop
+# CHECK-NEXT:    20018:       0c 00 80 15     jal     131156
+#                                                     ^-- 0x20060 .pic.foo2
+# CHECK-NEXT:    2001c:       00 00 00 00     nop
+#
+# CHECK:      foo1a:
+# CHECK-NEXT:    20020:       00 00 00 00     nop
+#
+# CHECK:      foo1b:
+# CHECK-NEXT:    20024:       00 00 00 00     nop
+#
+# CHECK-NEXT:    20028:       3c 19 00 02     lui     $25, 2
+# CHECK-NEXT:    2002c:       08 00 80 08     j       131104 <foo1a>
+# CHECK-NEXT:    20030:       27 39 00 20     addiu   $25, $25, 32
+# CHECK-NEXT:    20034:       00 00 00 00     nop
+# CHECK-NEXT:    20038:       3c 19 00 02     lui     $25, 2
+# CHECK-NEXT:    2003c:       08 00 80 09     j       131108 <foo1b>
+# CHECK-NEXT:    20040:       27 39 00 24     addiu   $25, $25, 36
+# CHECK-NEXT:    20044:       00 00 00 00     nop
+# CHECK-NEXT:    20048:       00 00 00 00     nop
+# CHECK-NEXT:    2004c:       00 00 00 00     nop
+#
+# CHECK:      foo2:
+# CHECK-NEXT:    20050:       00 00 00 00     nop
+#
+# CHECK-NEXT:    20054:       3c 19 00 02     lui     $25, 2
+# CHECK-NEXT:    20058:       08 00 80 14     j       131152 <foo2>
+# CHECK-NEXT:    2005c:       27 39 00 50     addiu   $25, $25, 80
+# CHECK-NEXT:    20060:       00 00 00 00     nop
+
+  .text
+  .globl __start
+__start:
+  jal foo1a
+  jal foo2
+  jal foo1b
+  jal foo2




More information about the llvm-commits mailing list