[ELF][x86-84] Add static TLS support.
authorMichael J. Spencer <bigcheesegs@gmail.com>
Fri, 1 Feb 2013 07:13:49 +0000 (07:13 +0000)
committerMichael J. Spencer <bigcheesegs@gmail.com>
Fri, 1 Feb 2013 07:13:49 +0000 (07:13 +0000)
llvm-svn: 174154

lld/lib/ReaderWriter/ELF/Atoms.h
lld/lib/ReaderWriter/ELF/DefaultLayout.h
lld/lib/ReaderWriter/ELF/File.h
lld/lib/ReaderWriter/ELF/HeaderChunks.h
lld/lib/ReaderWriter/ELF/SectionChunks.h
lld/lib/ReaderWriter/ELF/SegmentChunks.h
lld/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.cpp
lld/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.h
lld/test/elf/Inputs/tls.c [new file with mode: 0644]
lld/test/elf/Inputs/tls.x86-64 [new file with mode: 0644]
lld/test/elf/tls.test [new file with mode: 0644]

index cd6a1c9..a7534e3 100644 (file)
@@ -253,6 +253,12 @@ public:
       return elfAtomHandler.contentType(this);
     }
 
+    if (_section->sh_flags ==
+        (llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE | llvm::ELF::SHF_TLS)) {
+      return _section->sh_type == llvm::ELF::SHT_NOBITS ? typeTLVInitialZeroFill
+                                                        : typeTLVInitialData;
+    }
+
     if (_symbol->getType() == llvm::ELF::STT_GNU_IFUNC)
       return typeResolver;
 
index 326019f..76e55e2 100644 (file)
@@ -291,7 +291,11 @@ Layout::SectionOrder DefaultLayout<ELFT>::getSectionOrder(
     return ORDER_GOT;
   case DefinedAtom::typeStub:
     return ORDER_PLT;
-  
+
+  case DefinedAtom::typeTLVInitialData:
+    return ORDER_TDATA;
+  case DefinedAtom::typeTLVInitialZeroFill:
+    return ORDER_TBSS;
   default:
     // If we get passed in a section push it to OTHER
     if (contentPermissions == DefinedAtom::perm___)
index 15abec8..6d297ed 100644 (file)
@@ -231,7 +231,8 @@ public:
               TargetHandler.targetAtomHandler();
           c = elfAtomHandler.contentType(*si);
 
-          if (c == DefinedAtom::typeZeroFill)
+          if (c == DefinedAtom::typeZeroFill ||
+              c == DefinedAtom::typeTLVInitialZeroFill)
             isCommon = true;
         }
 
index e2512b5..f3660e8 100644 (file)
@@ -156,20 +156,11 @@ public:
   }
 
 private:
-  std::vector<Elf_Phdr *> _ph;
-  PhIterT _phi;
-  llvm::BumpPtrAllocator  _allocator;
-};
-
-template<class ELFT>
-bool
-ProgramHeader<ELFT>::addSegment(Segment<ELFT> *segment) {
-  Elf_Phdr *phdr = nullptr;
-  bool ret = false;
-
-  for (auto slice : segment->slices()) {
+  std::pair<Elf_Phdr *, bool> allocateProgramHeader() {
+    Elf_Phdr *phdr;
+    bool ret = false;
     if (_phi == _ph.end()) {
-      phdr = new(_allocator.Allocate<Elf_Phdr>()) Elf_Phdr;
+      phdr = new (_allocator) Elf_Phdr;
       _ph.push_back(phdr);
       _phi = _ph.end();
       ret = true;
@@ -177,20 +168,50 @@ ProgramHeader<ELFT>::addSegment(Segment<ELFT> *segment) {
       phdr = (*_phi);
       ++_phi;
     }
-    phdr->p_type = segment->segmentType();
-    phdr->p_offset = slice->fileOffset();
-    phdr->p_vaddr = slice->virtualAddr();
-    phdr->p_paddr = slice->virtualAddr();
-    phdr->p_filesz = slice->fileSize();
-    phdr->p_memsz = slice->memSize();
-    phdr->p_flags = segment->flags();
-    phdr->p_align = (phdr->p_type == llvm::ELF::PT_LOAD) ?
-                     segment->pageSize() : slice->align2();
+
+    return std::make_pair(phdr, ret);
+  }
+
+  std::vector<Elf_Phdr *> _ph;
+  PhIterT _phi;
+  llvm::BumpPtrAllocator _allocator;
+};
+
+template <class ELFT>
+bool ProgramHeader<ELFT>::addSegment(Segment<ELFT> *segment) {
+  bool allocatedNew = false;
+  for (auto slice : segment->slices()) {
+    // If we have a TLS segment, emit a LOAD first.
+    if (segment->segmentType() == llvm::ELF::PT_TLS) {
+      auto phdr = allocateProgramHeader();
+      if (phdr.second)
+        allocatedNew = true;
+      phdr.first->p_type = llvm::ELF::PT_LOAD;
+      phdr.first->p_offset = slice->fileOffset();
+      phdr.first->p_vaddr = slice->virtualAddr();
+      phdr.first->p_paddr = slice->virtualAddr();
+      phdr.first->p_filesz = slice->fileSize();
+      phdr.first->p_memsz = slice->memSize();
+      phdr.first->p_flags = segment->flags();
+      phdr.first->p_align = slice->align2();
+    }
+    auto phdr = allocateProgramHeader();
+    if (phdr.second)
+      allocatedNew = true;
+    phdr.first->p_type = segment->segmentType();
+    phdr.first->p_offset = slice->fileOffset();
+    phdr.first->p_vaddr = slice->virtualAddr();
+    phdr.first->p_paddr = slice->virtualAddr();
+    phdr.first->p_filesz = slice->fileSize();
+    phdr.first->p_memsz = slice->memSize();
+    phdr.first->p_flags = segment->flags();
+    phdr.first->p_align = (phdr.first->p_type == llvm::ELF::PT_LOAD) ?
+                          segment->pageSize() : slice->align2();
   }
   this->_fsize = fileSize();
   this->_msize = this->_fsize;
 
-  return ret;
+  return allocatedNew;
 }
 
 template <class ELFT>
index 101089d..6fea1bc 100644 (file)
@@ -240,6 +240,7 @@ const AtomLayout &Section<ELFT>::appendAtom(const Atom *atom) {
     case DefinedAtom::typeGOT:
     case DefinedAtom::typeStub:
     case DefinedAtom::typeResolver:
+    case DefinedAtom::typeTLVInitialData:
       _atoms.push_back(new (_alloc) AtomLayout(atom, fOffset, 0));
       this->_fsize = fOffset + definedAtom->size();
       this->_msize = mOffset + definedAtom->size();
@@ -249,6 +250,7 @@ const AtomLayout &Section<ELFT>::appendAtom(const Atom *atom) {
                                    << fOffset << "\n");
       break;
     case  DefinedAtom::typeZeroFill:
+    case DefinedAtom::typeTLVInitialZeroFill:
       _atoms.push_back(new (_alloc) AtomLayout(atom, mOffset, 0));
       this->_msize = mOffset + definedAtom->size();
       break;
@@ -285,7 +287,10 @@ Section<ELFT>::flags() {
 
   case DefinedAtom::permRW_:
   case DefinedAtom::permRW_L:
-      return llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE;
+    if (_contentType == DefinedAtom::typeTLVInitialData ||
+        _contentType == DefinedAtom::typeTLVInitialZeroFill)
+      return llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE | llvm::ELF::SHF_TLS;
+    return llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE;
 
   case DefinedAtom::permRWX:
       return llvm::ELF::SHF_ALLOC |
@@ -314,9 +319,11 @@ Section<ELFT>::type() {
   case DefinedAtom::typeGOT:
   case DefinedAtom::typeStub:
   case DefinedAtom::typeResolver:
+  case DefinedAtom::typeTLVInitialData:
     return llvm::ELF::SHT_PROGBITS;
 
   case DefinedAtom::typeZeroFill:
+  case DefinedAtom::typeTLVInitialZeroFill:
    return llvm::ELF::SHT_NOBITS;
 
   // Case to handle section types
@@ -638,6 +645,11 @@ SymbolTable<ELFT>::addSymbol(const Atom *atom,
       type = llvm::ELF::STT_OBJECT;
       symbol->st_value = addr;
       break;
+    case DefinedAtom::typeTLVInitialData:
+    case DefinedAtom::typeTLVInitialZeroFill:
+      type = llvm::ELF::STT_TLS;
+      symbol->st_value = addr;
+      break;
     default:
       type = llvm::ELF::STT_NOTYPE;
     }
index efdfc65..7db4913 100644 (file)
@@ -348,6 +348,8 @@ template <class ELFT> void Segment<ELFT>::assignVirtualAddress(uint64_t &addr) {
         else
           s->assignVirtualAddress(addr);
       }
+      if (isTLSSegment)
+        tlsStartAddr += section->memSize();
       addr += section->memSize();
       section->setMemSize(addr - section->virtualAddr());
     }
index 398ff5f..ddd1a65 100644 (file)
@@ -73,6 +73,24 @@ ErrorOr<void> X86_64TargetRelocationHandler::applyRelocation(
   case R_X86_64_32S:
     reloc32S(location, relocVAddress, targetVAddress, ref.addend());
     break;
+  case R_X86_64_TPOFF32: {
+    // Get the start and end of the TLS segment.
+    if (_tlsSize == 0) {
+      auto tdata = _targetInfo.getTargetHandler<X86_64ELFType>().targetLayout()
+          .findOutputSection(".tdata");
+      auto tbss = _targetInfo.getTargetHandler<X86_64ELFType>().targetLayout()
+          .findOutputSection(".tbss");
+      // HACK: The tdata and tbss sections end up together to from the TLS
+      // segment. This should actually use the TLS program header entry.
+      if (tdata)
+        _tlsSize = tdata->memSize();
+      if (tbss)
+        _tlsSize += tbss->memSize();
+    }
+    int32_t result = (int32_t)(targetVAddress - _tlsSize);
+    *reinterpret_cast<llvm::support::little32_t *>(location) = result;
+    break;
+  }
   // Runtime only relocations. Ignore here.
   case R_X86_64_IRELATIVE:
     break;
index 9428478..c18e8f9 100644 (file)
@@ -23,13 +23,16 @@ class X86_64TargetInfo;
 class X86_64TargetRelocationHandler LLVM_FINAL
     : public TargetRelocationHandler<X86_64ELFType> {
 public:
-  X86_64TargetRelocationHandler(const X86_64TargetInfo &ti) : _targetInfo(ti) {}
+  X86_64TargetRelocationHandler(const X86_64TargetInfo &ti)
+      : _tlsSize(0), _targetInfo(ti) {}
 
   virtual ErrorOr<void> applyRelocation(ELFWriter &, llvm::FileOutputBuffer &,
                                         const AtomLayout &,
                                         const Reference &)const;
 
 private:
+  // Cached size of the TLS segment.
+  mutable uint64_t _tlsSize;
   const X86_64TargetInfo &_targetInfo;
 };
 
diff --git a/lld/test/elf/Inputs/tls.c b/lld/test/elf/Inputs/tls.c
new file mode 100644 (file)
index 0000000..6723507
--- /dev/null
@@ -0,0 +1,11 @@
+extern __thread int tls0;
+extern __thread int tls1;
+extern __thread int tls2;
+
+__thread int tls0 = 0;
+__thread int tls1 = 0;
+__thread int tls2 = 1;
+
+int main() {
+  return tls0 + tls1 + tls2;
+}
diff --git a/lld/test/elf/Inputs/tls.x86-64 b/lld/test/elf/Inputs/tls.x86-64
new file mode 100644 (file)
index 0000000..dba3907
Binary files /dev/null and b/lld/test/elf/Inputs/tls.x86-64 differ
diff --git a/lld/test/elf/tls.test b/lld/test/elf/tls.test
new file mode 100644 (file)
index 0000000..7a1281a
--- /dev/null
@@ -0,0 +1,8 @@
+RUN: lld -core -target x86_64-linux %p/Inputs/tls.x86-64 -output=%t \
+RUN:   -noinhibit-exec -entry=main && llvm-objdump -d %t | FileCheck %s
+
+// Verify that the TLS accesses have the correct offsets.
+
+CHECK: movl %fs:-8
+CHECK: movl %fs:-4
+CHECK: movl %fs:-12