[lld] r195270 - Use NativeReferenceIvarsV2 if necessary.

Rui Ueyama ruiu at google.com
Wed Nov 20 12:54:18 PST 2013


Author: ruiu
Date: Wed Nov 20 14:54:18 2013
New Revision: 195270

URL: http://llvm.org/viewvc/llvm-project?rev=195270&view=rev
Log:
Use NativeReferenceIvarsV2 if necessary.

NativeReferenceIvarsV1 cannot handle more than 65535 relocation targets
because its field to point to the target table is of type uint16_t. Because
of that limitation, the LLD couldn't link a file containing more than 65535
relocations. 65535 is not a big number - the LLD couldn't even link itself
with V1.

This patch solves the issue by adding NativeReferenceIvarsV2 support. The
new structure has more bits for the target table, so it can handle a large
number of relocatinos.

V2 structure is larger than V1. In order to prevent file bloating, V2 format
is used only when the resulting file cannot be represented in V1 format. The
writer and the reader support both V1 and V2 formats.

Differential Revision: http://llvm-reviews.chandlerc.com/D2217

Modified:
    lld/trunk/lib/ReaderWriter/Native/NativeFileFormat.h
    lld/trunk/lib/ReaderWriter/Native/ReaderNative.cpp
    lld/trunk/lib/ReaderWriter/Native/WriterNative.cpp

Modified: lld/trunk/lib/ReaderWriter/Native/NativeFileFormat.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/Native/NativeFileFormat.h?rev=195270&r1=195269&r2=195270&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/Native/NativeFileFormat.h (original)
+++ lld/trunk/lib/ReaderWriter/Native/NativeFileFormat.h Wed Nov 20 14:54:18 2013
@@ -16,7 +16,6 @@
 
 namespace lld {
 
-
 //
 // Overview:
 //
@@ -214,6 +213,9 @@ struct NativeReferenceIvarsV1 {
 // The NCS_ReferencesArrayV2 chunk contains an array of these structs
 //
 struct NativeReferenceIvarsV2 {
+  enum {
+    noTarget = UINT32_MAX
+  };
   uint64_t  offsetInAtom;
   int64_t   addend;
   int32_t   kind;

Modified: lld/trunk/lib/ReaderWriter/Native/ReaderNative.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/Native/ReaderNative.cpp?rev=195270&r1=195269&r2=195270&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/Native/ReaderNative.cpp (original)
+++ lld/trunk/lib/ReaderWriter/Native/ReaderNative.cpp Wed Nov 20 14:54:18 2013
@@ -207,7 +207,6 @@ private:
 };
 
 
-
 //
 // An object of this class is instantied for each NativeReferenceIvarsV1
 // struct in the NCS_ReferencesArrayV1 chunk.
@@ -229,21 +228,36 @@ public:
   virtual void setAddend(Addend a);
 
 private:
-  // Used in rare cases when Reference is modified,
-  // since ivar data is mapped read-only.
-  void cloneIvarData() {
-    // TODO: do nothing on second call
-   NativeReferenceIvarsV1* niv = reinterpret_cast<NativeReferenceIvarsV1*>
-                                (operator new(sizeof(NativeReferenceIvarsV1),
-                                                                std::nothrow));
-    memcpy(niv, _ivarData, sizeof(NativeReferenceIvarsV1));
-  }
-
   const File                    *_file;
   const NativeReferenceIvarsV1  *_ivarData;
 };
 
 
+//
+// An object of this class is instantied for each NativeReferenceIvarsV1
+// struct in the NCS_ReferencesArrayV1 chunk.
+//
+class NativeReferenceV2 : public Reference {
+public:
+  NativeReferenceV2(const File& f, const NativeReferenceIvarsV2* ivarData)
+      : _file(&f), _ivarData(ivarData) {
+    setKind(ivarData->kind);
+  }
+
+  virtual uint64_t offsetInAtom() const {
+    return _ivarData->offsetInAtom;
+  }
+
+  virtual const Atom* target() const;
+  virtual Addend addend() const;
+  virtual void setTarget(const Atom* newAtom);
+  virtual void setAddend(Addend a);
+
+private:
+  const File                    *_file;
+  const NativeReferenceIvarsV2  *_ivarData;
+};
+
 
 //
 // lld::File object for native llvm object file
@@ -313,6 +327,9 @@ public:
         case NCS_ReferencesArrayV1:
           ec = file->processReferencesV1(base, chunk);
           break;
+        case NCS_ReferencesArrayV2:
+          ec = file->processReferencesV2(base, chunk);
+          break;
         case NCS_TargetsTable:
           ec = file->processTargetsTable(base, chunk);
           break;
@@ -363,7 +380,8 @@ public:
     delete _undefinedAtoms._arrayStart;
     delete _sharedLibraryAtoms._arrayStart;
     delete _absoluteAtoms._arrayStart;
-    delete _references.arrayStart;
+    delete _referencesV1.arrayStart;
+    delete _referencesV2.arrayStart;
     delete [] _targetsTable;
   }
 
@@ -387,6 +405,7 @@ private:
   friend NativeSharedLibraryAtomV1;
   friend NativeAbsoluteAtomV1;
   friend NativeReferenceV1;
+  friend NativeReferenceV2;
 
   // instantiate array of DefinedAtoms from v1 ivar data in file
   error_code processDefinedAtomsV1(const uint8_t *base,
@@ -558,43 +577,65 @@ private:
     return make_error_code(NativeReaderError::success);
   }
 
-
-
-
-  // instantiate array of Referemces from v1 ivar data in file
-  error_code processReferencesV1(const uint8_t *base,
-                                 const NativeChunk *chunk) {
-    if ( chunk->elementCount == 0 )
+  template<class T, class U>
+  error_code processReferences(const uint8_t *base, const NativeChunk *chunk,
+                               uint8_t *&refsStart, uint8_t *&refsEnd) const {
+    if (chunk->elementCount == 0)
       return make_error_code(NativeReaderError::success);
-    const size_t refSize = sizeof(NativeReferenceV1);
-    size_t refsArraySize = chunk->elementCount * refSize;
-    uint8_t* refsStart = reinterpret_cast<uint8_t*>
-                                (operator new(refsArraySize, std::nothrow));
+    size_t refsArraySize = chunk->elementCount * sizeof(T);
+    refsStart = reinterpret_cast<uint8_t *>(
+        operator new(refsArraySize, std::nothrow));
     if (refsStart == nullptr)
       return make_error_code(NativeReaderError::memory_error);
-    const size_t ivarElementSize = chunk->fileSize
-                                          / chunk->elementCount;
-    if ( ivarElementSize != sizeof(NativeReferenceIvarsV1) )
+    const size_t ivarElementSize = chunk->fileSize / chunk->elementCount;
+    if (ivarElementSize != sizeof(U))
       return make_error_code(NativeReaderError::file_malformed);
-    uint8_t* refsEnd = refsStart + refsArraySize;
-    const NativeReferenceIvarsV1* ivarData =
-                             reinterpret_cast<const NativeReferenceIvarsV1*>
-                                                  (base + chunk->fileOffset);
-    for(uint8_t* s = refsStart; s != refsEnd; s += refSize) {
-      NativeReferenceV1* atomAllocSpace =
-                  reinterpret_cast<NativeReferenceV1*>(s);
-      new (atomAllocSpace) NativeReferenceV1(*this, ivarData);
-      ++ivarData;
+    refsEnd = refsStart + refsArraySize;
+    const U* ivarData = reinterpret_cast<const U *>(base + chunk->fileOffset);
+    for (uint8_t *s = refsStart; s != refsEnd; s += sizeof(T), ++ivarData) {
+      T *atomAllocSpace = reinterpret_cast<T *>(s);
+      new (atomAllocSpace) T(*this, ivarData);
     }
-    this->_references.arrayStart = refsStart;
-    this->_references.arrayEnd = refsEnd;
-    this->_references.elementSize = refSize;
-    this->_references.elementCount = chunk->elementCount;
-    DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs()
-                    << " chunk ReferencesV1:        "
-                    << " count=" << chunk->elementCount
-                    << " chunkSize=" << chunk->fileSize
-                    << "\n");
+    return make_error_code(NativeReaderError::success);
+  }
+
+  // instantiate array of Referemces from v1 ivar data in file
+  error_code processReferencesV1(const uint8_t *base,
+                                 const NativeChunk *chunk) {
+    uint8_t *refsStart, *refsEnd;
+    if (error_code ec
+            = processReferences<NativeReferenceV1, NativeReferenceIvarsV1>(
+                base, chunk, refsStart, refsEnd))
+      return ec;
+    this->_referencesV1.arrayStart = refsStart;
+    this->_referencesV1.arrayEnd = refsEnd;
+    this->_referencesV1.elementSize = sizeof(NativeReferenceV1);
+    this->_referencesV1.elementCount = chunk->elementCount;
+    DEBUG_WITH_TYPE("ReaderNative", {
+      llvm::dbgs() << " chunk ReferencesV1:        "
+                   << " count=" << chunk->elementCount
+                   << " chunkSize=" << chunk->fileSize << "\n";
+    });
+    return make_error_code(NativeReaderError::success);
+  }
+
+  // instantiate array of Referemces from v2 ivar data in file
+  error_code processReferencesV2(const uint8_t *base,
+                                 const NativeChunk *chunk) {
+    uint8_t *refsStart, *refsEnd;
+    if (error_code ec
+            = processReferences<NativeReferenceV2, NativeReferenceIvarsV2>(
+                base, chunk, refsStart, refsEnd))
+      return ec;
+    this->_referencesV2.arrayStart = refsStart;
+    this->_referencesV2.arrayEnd = refsEnd;
+    this->_referencesV2.elementSize = sizeof(NativeReferenceV2);
+    this->_referencesV2.elementCount = chunk->elementCount;
+    DEBUG_WITH_TYPE("ReaderNative", {
+      llvm::dbgs() << " chunk ReferencesV2:        "
+                   << " count=" << chunk->elementCount
+                   << " chunkSize=" << chunk->fileSize << "\n";
+    });
     return make_error_code(NativeReaderError::success);
   }
 
@@ -715,24 +756,41 @@ private:
   }
 
   const Reference* referenceByIndex(uintptr_t index) const {
-    assert(index < _references.elementCount);
-    const uint8_t* p = _references.arrayStart + index * _references.elementSize;
-    return reinterpret_cast<const NativeReferenceV1*>(p);
+    if (index < _referencesV1.elementCount) {
+      return reinterpret_cast<const NativeReferenceV1*>(
+          _referencesV1.arrayStart + index * _referencesV1.elementSize);
+    }
+    assert(index < _referencesV2.elementCount);
+    return reinterpret_cast<const NativeReferenceV2*>(
+        _referencesV2.arrayStart + index * _referencesV2.elementSize);
   }
 
-  const Atom* target(uint16_t index) const {
+  const Atom* targetV1(uint16_t index) const {
     if ( index == NativeReferenceIvarsV1::noTarget )
       return nullptr;
     assert(index < _targetsTableCount);
     return _targetsTable[index];
   }
 
-  void setTarget(uint16_t index, const Atom* newAtom) const {
+  void setTargetV1(uint16_t index, const Atom* newAtom) const {
     assert(index != NativeReferenceIvarsV1::noTarget);
     assert(index > _targetsTableCount);
     _targetsTable[index] = newAtom;
   }
 
+  const Atom* targetV2(uint32_t index) const {
+    if (index == NativeReferenceIvarsV2::noTarget)
+      return nullptr;
+    assert(index < _targetsTableCount);
+    return _targetsTable[index];
+  }
+
+  void setTargetV2(uint32_t index, const Atom* newAtom) const {
+    assert(index != NativeReferenceIvarsV2::noTarget);
+    assert(index > _targetsTableCount);
+    _targetsTable[index] = newAtom;
+  }
+
   // private constructor, only called by make()
   File(const LinkingContext &context, std::unique_ptr<MemoryBuffer> mb,
        StringRef path)
@@ -797,7 +855,8 @@ private:
   uint32_t                        _absAbsoluteMaxOffset;
   const uint8_t*                  _attributes;
   uint32_t                        _attributesMaxOffset;
-  IvarArray                       _references;
+  IvarArray                       _referencesV1;
+  IvarArray                       _referencesV2;
   const Atom**                    _targetsTable;
   uint32_t                        _targetsTableCount;
   const char*                     _strings;
@@ -906,7 +965,7 @@ inline const NativeAtomAttributesV1& Nat
 }
 
 inline const Atom* NativeReferenceV1::target() const {
-  return _file->target(_ivarData->targetIndex);
+  return _file->targetV1(_ivarData->targetIndex);
 }
 
 inline Reference::Addend NativeReferenceV1::addend() const {
@@ -914,13 +973,32 @@ inline Reference::Addend NativeReference
 }
 
 inline void NativeReferenceV1::setTarget(const Atom* newAtom) {
-  return _file->setTarget(_ivarData->targetIndex, newAtom);
+  return _file->setTargetV1(_ivarData->targetIndex, newAtom);
 }
 
 inline void NativeReferenceV1::setAddend(Addend a) {
   // Do nothing if addend value is not being changed.
   if (addend() == a)
     return;
+  llvm_unreachable("setAddend() not supported");
+}
+
+inline const Atom* NativeReferenceV2::target() const {
+  return _file->targetV2(_ivarData->targetIndex);
+}
+
+inline Reference::Addend NativeReferenceV2::addend() const {
+  return _ivarData->addend;
+}
+
+inline void NativeReferenceV2::setTarget(const Atom* newAtom) {
+  return _file->setTargetV2(_ivarData->targetIndex, newAtom);
+}
+
+inline void NativeReferenceV2::setAddend(Addend a) {
+  // Do nothing if addend value is not being changed.
+  if (addend() == a)
+    return;
   llvm_unreachable("setAddend() not supported");
 }
 

Modified: lld/trunk/lib/ReaderWriter/Native/WriterNative.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/Native/WriterNative.cpp?rev=195270&r1=195269&r2=195270&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/Native/WriterNative.cpp (original)
+++ lld/trunk/lib/ReaderWriter/Native/WriterNative.cpp Wed Nov 20 14:54:18 2013
@@ -18,7 +18,8 @@
 
 #include "NativeFileFormat.h"
 
-#include <limits>
+#include <cstdint>
+#include <set>
 #include <vector>
 
 namespace lld {
@@ -48,6 +49,8 @@ public:
       this->addIVarsForAbsoluteAtom(*absAtom);
     }
 
+    maybeConvertReferencesToV1();
+
     // construct file header based on atom information accumulated
     this->makeHeader();
 
@@ -79,7 +82,8 @@ private:
     writeChunk(out, _absoluteAtomIvars, NCS_AbsoluteAtomsV1);
     writeChunk(out, _absAttributes, NCS_AbsoluteAttributesV1);
     writeChunk(out, _stringPool, NCS_Strings);
-    writeChunk(out, _references, NCS_ReferencesArrayV1);
+    writeChunk(out, _referencesV1, NCS_ReferencesArrayV1);
+    writeChunk(out, _referencesV2, NCS_ReferencesArrayV2);
 
     if (!_targetsTableIndex.empty()) {
       assert(out.tell() == findChunk(NCS_TargetsTable).fileOffset);
@@ -147,13 +151,47 @@ private:
     _absoluteAtomIvars.push_back(ivar);
   }
 
+  void convertReferencesToV1() {
+    for (const NativeReferenceIvarsV2 &v2 : _referencesV2) {
+      NativeReferenceIvarsV1 v1;
+      v1.offsetInAtom = v2.offsetInAtom;
+      v1.kind = v2.kind;
+      v1.targetIndex = (v2.targetIndex == NativeReferenceIvarsV2::noTarget) ?
+          NativeReferenceIvarsV1::noTarget : v2.targetIndex;
+      v1.addendIndex = this->getAddendIndex(v2.addend);
+      _referencesV1.push_back(v1);
+    }
+    _referencesV2.clear();
+  }
+
+  bool canConvertReferenceToV1(const NativeReferenceIvarsV2 &ref) {
+    bool validOffset = (ref.offsetInAtom == NativeReferenceIvarsV2::noTarget) ||
+        ref.offsetInAtom < NativeReferenceIvarsV1::noTarget;
+    return validOffset && ref.targetIndex < UINT16_MAX;
+  }
+
+  // Convert vector of NativeReferenceIvarsV2 to NativeReferenceIvarsV1 if
+  // possible.
+  void maybeConvertReferencesToV1() {
+    std::set<int64_t> addends;
+    for (const NativeReferenceIvarsV2 &ref : _referencesV2) {
+      if (!canConvertReferenceToV1(ref))
+        return;
+      addends.insert(ref.addend);
+      if (addends.size() >= UINT16_MAX)
+        return;
+    }
+    convertReferencesToV1();
+  }
+
   // fill out native file header and chunk directory
   void makeHeader() {
     const bool hasDefines = !_definedAtomIvars.empty();
     const bool hasUndefines = !_undefinedAtomIvars.empty();
     const bool hasSharedLibraries = !_sharedLibraryAtomIvars.empty();
     const bool hasAbsolutes = !_absoluteAtomIvars.empty();
-    const bool hasReferences = !_references.empty();
+    const bool hasReferencesV1 = !_referencesV1.empty();
+    const bool hasReferencesV2 = !_referencesV2.empty();
     const bool hasTargetsTable = !_targetsTableIndex.empty();
     const bool hasAddendTable = !_addendsTableIndex.empty();
     const bool hasContent = !_contentPool.empty();
@@ -163,7 +201,8 @@ private:
     if ( hasUndefines ) ++chunkCount;
     if ( hasSharedLibraries ) ++chunkCount;
     if ( hasAbsolutes ) chunkCount += 2;
-    if ( hasReferences ) ++chunkCount;
+    if ( hasReferencesV1 ) ++chunkCount;
+    if ( hasReferencesV2 ) ++chunkCount;
     if ( hasTargetsTable ) ++chunkCount;
     if ( hasAddendTable ) ++chunkCount;
     if ( hasContent ) ++chunkCount;
@@ -221,11 +260,16 @@ private:
     fillChunkHeader(chunks[nextIndex++], nextFileOffset, _stringPool,
                     NCS_Strings);
 
-    // create chunk for references
-    if (hasReferences)
-      fillChunkHeader(chunks[nextIndex++], nextFileOffset, _references,
+    // create chunk for referencesV2
+    if (hasReferencesV1)
+      fillChunkHeader(chunks[nextIndex++], nextFileOffset, _referencesV1,
                       NCS_ReferencesArrayV1);
 
+    // create chunk for referencesV2
+    if (hasReferencesV2)
+      fillChunkHeader(chunks[nextIndex++], nextFileOffset, _referencesV2,
+                      NCS_ReferencesArrayV2);
+
     // create chunk for target table
     if (hasTargetsTable) {
       NativeChunk& cht = chunks[nextIndex++];
@@ -380,32 +424,31 @@ private:
     return attrs;
   }
 
-  // add references for this atom in a contiguous block in NCS_ReferencesArrayV1
+  // add references for this atom in a contiguous block in NCS_ReferencesArrayV2
   uint32_t getReferencesIndex(const DefinedAtom& atom, unsigned& refsCount) {
-    size_t startRefSize = _references.size();
+    size_t startRefSize = _referencesV2.size();
     uint32_t result = startRefSize;
     for (const Reference *ref : atom) {
-      NativeReferenceIvarsV1 nref;
+      NativeReferenceIvarsV2 nref;
       nref.offsetInAtom = ref->offsetInAtom();
       nref.kind = ref->kind();
       nref.targetIndex = this->getTargetIndex(ref->target());
-      nref.addendIndex = this->getAddendIndex(ref->addend());
-      _references.push_back(nref);
+      nref.addend = ref->addend();
+      _referencesV2.push_back(nref);
     }
-    refsCount = _references.size() - startRefSize;
+    refsCount = _referencesV2.size() - startRefSize;
     return (refsCount == 0) ? 0 : result;
   }
 
   uint32_t getTargetIndex(const Atom* target) {
     if ( target == nullptr )
-      return NativeReferenceIvarsV1::noTarget;
+      return NativeReferenceIvarsV2::noTarget;
     TargetToIndex::const_iterator pos = _targetsTableIndex.find(target);
     if ( pos != _targetsTableIndex.end() ) {
       return pos->second;
     }
     uint32_t result = _targetsTableIndex.size();
     _targetsTableIndex[target] = result;
-    assert(result < NativeReferenceIvarsV1::noTarget);
     return result;
   }
 
@@ -489,7 +532,8 @@ private:
   std::vector<NativeUndefinedAtomIvarsV1> _undefinedAtomIvars;
   std::vector<NativeSharedLibraryAtomIvarsV1> _sharedLibraryAtomIvars;
   std::vector<NativeAbsoluteAtomIvarsV1>  _absoluteAtomIvars;
-  std::vector<NativeReferenceIvarsV1>     _references;
+  std::vector<NativeReferenceIvarsV1>     _referencesV1;
+  std::vector<NativeReferenceIvarsV2>     _referencesV2;
   TargetToIndex                           _targetsTableIndex;
   TargetToIndex                           _definedAtomIndex;
   TargetToIndex                           _undefinedAtomIndex;





More information about the llvm-commits mailing list