From 49d6cc8457edaf98f199eaf936103d010883c8d7 Mon Sep 17 00:00:00 2001 From: Nick Kledzik Date: Wed, 15 Feb 2012 00:38:09 +0000 Subject: [PATCH] (no commit message) llvm-svn: 150539 --- lld/include/lld/Core/DefinedAtom.h | 32 +-- lld/include/lld/Core/Reference.h | 56 ++++- lld/include/lld/Core/Resolver.h | 16 ++ lld/lib/Core/NativeFileFormat.h | 59 ++++- lld/lib/Core/NativeReader.cpp | 216 +++++++++++++++--- lld/lib/Core/NativeWriter.cpp | 206 +++++++++++++++++- lld/lib/Core/Resolver.cpp | 28 ++- lld/lib/Core/SymbolTable.cpp | 2 +- lld/lib/Core/YamlKeyValues.cpp | 25 +-- lld/lib/Core/YamlKeyValues.h | 15 +- lld/lib/Core/YamlReader.cpp | 265 ++++++++++++++++------- lld/lib/Core/YamlWriter.cpp | 205 +++++++++++++++--- lld/test/cstring-coalesce.objtxt | 12 +- lld/test/fixups-addend.objtxt | 50 +++++ lld/test/fixups-dup-named.objtxt | 31 +++ lld/test/fixups-named.objtxt | 35 +++ lld/test/fixups-unnamed.objtxt | 40 ++++ lld/test/internal-name-attributes.objtxt | 27 --- lld/tools/lld-core/lld-core.cpp | 5 +- 19 files changed, 1080 insertions(+), 245 deletions(-) create mode 100644 lld/test/fixups-addend.objtxt create mode 100644 lld/test/fixups-dup-named.objtxt create mode 100644 lld/test/fixups-named.objtxt create mode 100644 lld/test/fixups-unnamed.objtxt delete mode 100644 lld/test/internal-name-attributes.objtxt diff --git a/lld/include/lld/Core/DefinedAtom.h b/lld/include/lld/Core/DefinedAtom.h index 8db0728875db..e7dfe3abe3af 100644 --- a/lld/include/lld/Core/DefinedAtom.h +++ b/lld/include/lld/Core/DefinedAtom.h @@ -34,7 +34,7 @@ class File; /// Here are some example attribute sets for common atoms. If a particular /// attribute is not listed, the default values are: definition=regular, /// sectionChoice=basedOnContent, scope=translationUnit, merge=no, -/// internalName=false, deadStrip=normal, interposable=no +/// deadStrip=normal, interposable=no /// /// C function: void foo() {}
/// name=foo, type=code, perm=r_x, scope=global @@ -71,16 +71,16 @@ class File; /// mergeDupes=asAddressedWeak /// /// literal c-string: "hello"
-/// name=L0, internalName=true, type=cstring, perm=r__, scope=linkageUnit +/// name="" type=cstring, perm=r__, scope=linkageUnit /// /// literal double: 1.234
-/// name=L0, internalName=true, type=literal8, perm=r__, scope=linkageUnit +/// name="" type=literal8, perm=r__, scope=linkageUnit /// /// constant: { 1,2,3 }
-/// name=L0, internalName=true, type=constant, perm=r__, scope=linkageUnit +/// name="" type=constant, perm=r__, scope=linkageUnit /// /// Pointer to initializer function:
-/// name=_init, internalName=true, type=initializer, perm=rw_l, +/// name="" type=initializer, perm=rw_l, /// sectionChoice=customRequired /// /// C function place in custom section: __attribute__((section("__foo"))) @@ -195,15 +195,18 @@ public: uint16_t modulus; }; + /// for use iterating over this Atom's References + class ReferenceHandler { + public: + virtual ~ReferenceHandler() {} + virtual void doReference(const Reference &) = 0; + }; + /// ordinal - returns a value for the order of this Atom within its file. /// This is used by the linker to order the layout of Atoms so that /// the resulting image is stable and reproducible. virtual uint64_t ordinal() const = 0; - - /// internalName - If the name is just a temporary label that should - /// not show up in the final linked image. - virtual bool internalName() const = 0; - + /// size - the number of bytes of space this atom's content will occupy /// in the final linked image. For a function atom, it is the number /// of bytes of code in the function. @@ -264,12 +267,9 @@ public: /// this Atom's content. virtual llvm::ArrayRef rawContent() const = 0; - /// referencesBegin - used to start iterating this Atom's References - virtual Reference::iterator referencesBegin() const = 0; - - /// referencesEnd - used to end iterating this Atom's References - virtual Reference::iterator referencesEnd() const = 0; - + /// iterator over this Atom's References + virtual void forEachReference(ReferenceHandler&) const = 0; + protected: /// DefinedAtom is an abstract base class. /// Only subclasses can access constructor. diff --git a/lld/include/lld/Core/Reference.h b/lld/include/lld/Core/Reference.h index 36588d18396b..f20e43852353 100644 --- a/lld/include/lld/Core/Reference.h +++ b/lld/include/lld/Core/Reference.h @@ -12,22 +12,60 @@ #include - namespace lld { -class Atom; - +/// +/// The linker has a Graph Theory model of linking. An object file is seen +/// as a set of Atoms with References to other Atoms. Each Atom is a node +/// and each Reference is an edge. +/// +/// For example if a function contains a call site to "malloc" 40 bytes into +/// the Atom, then the function Atom will have a Reference of: offsetInAtom=40, +/// kind=callsite, target=malloc, addend=0. +/// +/// Besides supporting traditional "relocations", References are also used +/// grouping atoms (group comdat), forcing layout (one atom must follow +/// another), marking data-in-code (jump tables or ARM constants), etc. +/// class Reference { public: - typedef Reference *iterator; + /// The meaning of positive kind values is architecture specific. + /// Negative kind values are architecture independent. + typedef int32_t Kind; - const Atom *target; - uint64_t addend; - uint32_t offsetInAtom; - uint16_t kind; - uint16_t flags; + // A value to be added to the value of a target + typedef int64_t Addend; + + /// What sort of reference this is. + virtual Kind kind() const = 0; + + /// If the reference is a fixup in the Atom, then this returns the + /// byte offset into the Atom's content to do the fix up. + virtual uint64_t offsetInAtom() const = 0; + + /// If the reference is an edge to another Atom, then this returns the + /// other Atom. Otherwise, it returns NULL. + virtual const class Atom * target() const = 0; + + /// During linking, the linker may merge graphs which coalesces some nodes + /// (i.e. Atoms). To switch the target of a reference, this method is called. + virtual void setTarget(const class Atom *) = 0; + + /// Some relocations require a symbol and a value (e.g. foo + 4). + virtual Addend addend() const = 0; + +protected: + /// Atom is an abstract base class. Only subclasses can access constructor. + Reference() {} + + /// The memory for Reference objects is always managed by the owning File + /// object. Therefore, no one but the owning File object should call + /// delete on an Reference. In fact, some File objects may bulk allocate + /// an array of References, so they cannot be individually deleted by anyone. + virtual ~Reference() {} }; + } // namespace lld #endif // LLD_CORE_REFERENCES_H_ diff --git a/lld/include/lld/Core/Resolver.h b/lld/include/lld/Core/Resolver.h index 1bb2abea5223..d90b02f1da94 100644 --- a/lld/include/lld/Core/Resolver.h +++ b/lld/include/lld/Core/Resolver.h @@ -70,6 +70,22 @@ private: void markLive(const Atom &atom, WhyLiveBackChain *previous); void addAtoms(const std::vector&); + + // helper to update targets for use with forEachReference() + class MarkLiveReferences : public DefinedAtom::ReferenceHandler { + public: + MarkLiveReferences(Resolver& resolver, WhyLiveBackChain* chain) + : _resolver(resolver), _chain(chain) { } + + virtual void doReference(const Reference& ref) { + _resolver.markLive(*ref.target(), _chain); + } + + private: + Resolver& _resolver; + WhyLiveBackChain* _chain; + }; + Platform &_platform; const InputFiles &_inputFiles; SymbolTable _symbolTable; diff --git a/lld/lib/Core/NativeFileFormat.h b/lld/lib/Core/NativeFileFormat.h index 9fb6d5fe62c2..91996983c6e3 100644 --- a/lld/lib/Core/NativeFileFormat.h +++ b/lld/lib/Core/NativeFileFormat.h @@ -80,6 +80,7 @@ struct NativeFileHeader { uint32_t architecture; uint32_t fileSize; uint32_t chunkCount; + // NativeChunk chunks[] }; // @@ -90,8 +91,11 @@ enum NativeChunkSignatures { NCS_AttributesArrayV1 = 2, NCS_UndefinedAtomsV1 = 3, NCS_Strings = 4, - NCS_Content = 5, - NCS_ReferencesArray = 6, + NCS_ReferencesArrayV1 = 5, + NCS_ReferencesArrayV2 = 6, + NCS_TargetsTable = 7, + NCS_AddendsTable = 8, + NCS_Content = 9, }; // @@ -125,6 +129,8 @@ enum { struct NativeDefinedAtomIvarsV1 { uint32_t nameOffset; uint32_t attributesOffset; + uint32_t referencesStartIndex; + uint32_t referencesCount; uint32_t contentOffset; uint32_t contentSize; }; @@ -137,7 +143,6 @@ struct NativeAtomAttributesV1 { uint32_t sectionNameOffset; uint16_t align2; uint16_t alignModulus; - uint8_t internalName; uint8_t scope; uint8_t interposable; uint8_t merge; @@ -145,10 +150,7 @@ struct NativeAtomAttributesV1 { uint8_t sectionChoice; uint8_t deadStrip; uint8_t permissions; - uint8_t thumb; uint8_t alias; - uint8_t pad1; - uint8_t pad2; }; @@ -164,6 +166,51 @@ struct NativeUndefinedAtomIvarsV1 { +// +// The NCS_ReferencesArrayV1 chunk contains an array of these structs +// +struct NativeReferenceIvarsV1 { + uint16_t offsetInAtom; + int16_t kind; + uint16_t targetIndex; + uint16_t addendIndex; +}; + + + +// +// The NCS_ReferencesArrayV2 chunk contains an array of these structs +// +struct NativeReferenceIvarsV2 { + uint64_t offsetInAtom; + int64_t addend; + int32_t kind; + uint32_t targetIndex; +}; + + +// +// The NCS_TargetsTable chunk contains an array of uint32_t entries. +// The C++ class Reference has a target() method that returns a +// pointer to another Atom. We can't have pointers in object files, +// so instead NativeReferenceIvarsV1 contains an index to the target. +// The index is into this NCS_TargetsTable of uint32_t entries. +// The values in this table are the index of the (target) atom in this file. +// For DefinedAtoms the value is from 0 to NCS_DefinedAtomsV1.elementCount. +// For UndefinedAtoms the value is from NCS_DefinedAtomsV1.elementCount to +// NCS_DefinedAtomsV1.elementCount+NCS_UndefinedAtomsV1.elementCount. +// + + +// +// The NCS_AddendsTable chunk contains an array of int64_t entries. +// If we allocated space for addends directly in NativeReferenceIvarsV1 +// it would double the size of that struct. But since addends are rare, +// we instead just keep a pool of addends and have NativeReferenceIvarsV1 +// (if it needs an addend) just store the index (into the pool) of the +// addend it needs. +// + } // namespace lld diff --git a/lld/lib/Core/NativeReader.cpp b/lld/lib/Core/NativeReader.cpp index 033cf9890f6e..bc86d19c13b1 100644 --- a/lld/lib/Core/NativeReader.cpp +++ b/lld/lib/Core/NativeReader.cpp @@ -44,10 +44,6 @@ public: virtual llvm::StringRef name() const; - virtual bool internalName() const { - return attributes().internalName; - } - virtual uint64_t size() const { return _ivarData->contentSize; } @@ -88,23 +84,17 @@ public: } virtual bool isThumb() const { - return (attributes().thumb != 0); + return false; //(attributes().thumb != 0); } virtual bool isAlias() const { return (attributes().alias != 0); } - llvm::ArrayRef rawContent() const; + virtual llvm::ArrayRef rawContent() const; - virtual Reference::iterator referencesBegin() const { - return 0; - } - - virtual Reference::iterator referencesEnd() const { - return 0; - } - + virtual void forEachReference(ReferenceHandler&) const; + private: const NativeAtomAttributesV1& attributes() const; @@ -138,6 +128,35 @@ private: +// +// An object of this class is instantied for each NativeReferenceIvarsV1 +// struct in the NCS_ReferencesArrayV1 chunk. +// +class NativeReferenceV1 : public Reference { +public: + NativeReferenceV1(const NativeFile& f, + const NativeReferenceIvarsV1* ivarData) + : _file(&f), _ivarData(ivarData) { } + + virtual uint64_t offsetInAtom() const { + return _ivarData->offsetInAtom; + } + + virtual Kind kind() const { + return _ivarData->kind; + } + + virtual const Atom* target() const; + virtual Addend addend() const; + virtual void setTarget(const Atom* newAtom); + +private: + const NativeFile* _file; + const NativeReferenceIvarsV1* _ivarData; +}; + + + // // lld::File object for native llvm object file // @@ -187,6 +206,15 @@ public: case NCS_UndefinedAtomsV1: ec = file->processUndefinedAtomsV1(base, chunk); break; + case NCS_ReferencesArrayV1: + ec = file->processReferencesV1(base, chunk); + break; + case NCS_TargetsTable: + ec = file->processTargetsTable(base, chunk); + break; + case NCS_AddendsTable: + ec = file->processAddendsTable(base, chunk); + break; case NCS_Content: ec = file->processContent(base, chunk); break; @@ -219,6 +247,8 @@ public: // to just delete the memory. delete _definedAtoms.arrayStart; delete _undefinedAtoms.arrayStart; + delete _references.arrayStart; + delete _targetsTable; } // visits each atom in the file @@ -245,6 +275,7 @@ public: private: friend class NativeDefinedAtomV1; friend class NativeUndefinedAtomV1; + friend class NativeReferenceV1; // instantiate array of DefinedAtoms from v1 ivar data in file llvm::error_code processDefinedAtomsV1(const uint8_t* base, @@ -272,6 +303,7 @@ private: this->_definedAtoms.arrayStart = atomsStart; this->_definedAtoms.arrayEnd = atomsEnd; this->_definedAtoms.elementSize = atomSize; + this->_definedAtoms.elementCount = chunk->elementCount; return make_error_code(native_reader_error::success); } @@ -307,6 +339,77 @@ private: this->_undefinedAtoms.arrayStart = atomsStart; this->_undefinedAtoms.arrayEnd = atomsEnd; this->_undefinedAtoms.elementSize = atomSize; + this->_undefinedAtoms.elementCount = chunk->elementCount; + return make_error_code(native_reader_error::success); + } + + + // instantiate array of Referemces from v1 ivar data in file + llvm::error_code processReferencesV1(const uint8_t* base, + const NativeChunk* chunk) { + if ( chunk->elementCount == 0 ) + return make_error_code(native_reader_error::success); + const size_t refSize = sizeof(NativeReferenceV1); + size_t refsArraySize = chunk->elementCount * refSize; + uint8_t* refsStart = reinterpret_cast + (operator new(refsArraySize, std::nothrow)); + if (refsStart == NULL ) + return make_error_code(native_reader_error::memory_error); + const size_t ivarElementSize = chunk->fileSize + / chunk->elementCount; + if ( ivarElementSize != sizeof(NativeReferenceIvarsV1) ) + return make_error_code(native_reader_error::file_malformed); + uint8_t* refsEnd = refsStart + refsArraySize; + const NativeReferenceIvarsV1* ivarData = + reinterpret_cast + (base + chunk->fileOffset); + for(uint8_t* s = refsStart; s != refsEnd; s += refSize) { + NativeReferenceV1* atomAllocSpace = + reinterpret_cast(s); + new (atomAllocSpace) NativeReferenceV1(*this, ivarData); + ++ivarData; + } + this->_references.arrayStart = refsStart; + this->_references.arrayEnd = refsEnd; + this->_references.elementSize = refSize; + this->_references.elementCount = chunk->elementCount; + return make_error_code(native_reader_error::success); + } + + // set up pointers to target table + llvm::error_code processTargetsTable(const uint8_t* base, + const NativeChunk* chunk) { + const uint32_t* targetIndexes = reinterpret_cast + (base + chunk->fileOffset); + this->_targetsTableCount = chunk->elementCount; + this->_targetsTable = new const Atom*[chunk->elementCount]; + for (uint32_t i=0; i < chunk->elementCount; ++i) { + const uint32_t index = targetIndexes[i]; + if ( index < _definedAtoms.elementCount ) { + const uint8_t* p = _definedAtoms.arrayStart + + index * _definedAtoms.elementSize; + this->_targetsTable[i] = reinterpret_cast(p); + continue; + } + const uint32_t undefIndex = index - _definedAtoms.elementCount; + if ( undefIndex < _undefinedAtoms.elementCount ) { + const uint8_t* p = _undefinedAtoms.arrayStart + + undefIndex * _undefinedAtoms.elementSize; + this->_targetsTable[i] = reinterpret_cast(p); + continue; + } + return make_error_code(native_reader_error::file_malformed); + } + return make_error_code(native_reader_error::success); + } + + + // set up pointers to addend pool in file + llvm::error_code processAddendsTable(const uint8_t* base, + const NativeChunk* chunk) { + this->_addends = reinterpret_cast + (base + chunk->fileOffset); + this->_addendsMaxIndex = chunk->elementCount; return make_error_code(native_reader_error::success); } @@ -331,9 +434,16 @@ private: return llvm::StringRef(&_strings[offset]); } - const NativeAtomAttributesV1& attribute(uint32_t offset) const { - assert(offset < _attributesMaxOffset); - return *reinterpret_cast(_attributes + offset); + Reference::Addend addend(uint32_t index) const { + if ( index == 0 ) + return 0; // addend index zero is used to mean "no addend" + assert(index <= _addendsMaxIndex); + return _addends[index-1]; // one-based indexing + } + + const NativeAtomAttributesV1& attribute(uint32_t off) const { + assert(off < _attributesMaxOffset); + return *reinterpret_cast(_attributes + off); } const uint8_t* content(uint32_t offset, uint32_t size) const { @@ -342,44 +452,75 @@ private: return result; } - + void forEachReference(DefinedAtom::ReferenceHandler& handler, + uint32_t start, uint32_t count) const { + assert(start < _references.elementCount); + assert(start+count <= _references.elementCount); + const uint8_t* arrStart = _references.arrayStart + + start * _references.elementSize; + const uint8_t* arrEnd = arrStart + count * _references.elementSize; + for(const uint8_t* p=arrStart; p != arrEnd; p += _references.elementSize) { + const NativeReferenceV1* ref + = reinterpret_cast(p); + handler.doReference(*ref); + } + } + + const Atom* target(uint32_t index) const { + assert(index < _targetsTableCount); + return _targetsTable[index]; + } + + void setTarget(uint32_t index, const Atom* newAtom) const { + assert(index > _targetsTableCount); + _targetsTable[index] = newAtom; + } + + // private constructor, only called by make() NativeFile(llvm::OwningPtr& mb, llvm::StringRef path) : lld::File(path), _buffer(mb.take()), // NativeFile now takes ownership of buffer _header(NULL), + _targetsTable(NULL), + _targetsTableCount(0), _strings(NULL), _stringsMaxOffset(0), + _addends(NULL), + _addendsMaxIndex(0), _contentStart(NULL), _contentEnd(NULL) { _header = reinterpret_cast(_buffer->getBufferStart()); - _definedAtoms.arrayStart = NULL; - _undefinedAtoms.arrayStart = NULL; } - struct AtomArray { - AtomArray() : arrayStart(NULL), arrayEnd(NULL), - elementSize(0) { } + struct IvarArray { + IvarArray() : arrayStart(NULL), arrayEnd(NULL), + elementSize(0), elementCount(0) { } const uint8_t* arrayStart; const uint8_t* arrayEnd; uint32_t elementSize; - }; + uint32_t elementCount; + }; llvm::OwningPtr _buffer; const NativeFileHeader* _header; - AtomArray _definedAtoms; - AtomArray _undefinedAtoms; + IvarArray _definedAtoms; + IvarArray _undefinedAtoms; const uint8_t* _attributes; uint32_t _attributesMaxOffset; + IvarArray _references; + const Atom** _targetsTable; + uint32_t _targetsTableCount; const char* _strings; uint32_t _stringsMaxOffset; + const Reference::Addend* _addends; + uint32_t _addendsMaxIndex; const uint8_t* _contentStart; const uint8_t* _contentEnd; }; - - + inline const class File& NativeDefinedAtomV1::file() const { return *_file; } @@ -410,8 +551,12 @@ inline llvm::StringRef NativeDefinedAtomV1::customSectionName() const { return _file->string(offset); } - - +inline void NativeDefinedAtomV1::forEachReference(ReferenceHandler& hnd) const { + if ( _ivarData->referencesCount == 0 ) + return; + _file->forEachReference(hnd, _ivarData->referencesStartIndex, + _ivarData->referencesCount); +} inline const class File& NativeUndefinedAtomV1::file() const { return *_file; @@ -422,6 +567,17 @@ inline llvm::StringRef NativeUndefinedAtomV1::name() const { } +inline const Atom* NativeReferenceV1::target() const { + return _file->target(_ivarData->targetIndex); +} + +inline Reference::Addend NativeReferenceV1::addend() const { + return _file->addend(_ivarData->addendIndex); +} + +inline void NativeReferenceV1::setTarget(const Atom* newAtom) { + return _file->setTarget(_ivarData->targetIndex, newAtom); +} // diff --git a/lld/lib/Core/NativeWriter.cpp b/lld/lib/Core/NativeWriter.cpp index 09fdd9cdd17a..da877fd652cf 100644 --- a/lld/lib/Core/NativeWriter.cpp +++ b/lld/lib/Core/NativeWriter.cpp @@ -12,6 +12,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" #include "lld/Core/File.h" #include "lld/Core/NativeWriter.h" @@ -25,10 +26,13 @@ namespace lld { /// /// Class for writing native object files. /// -class NativeWriter : public File::AtomHandler { +class NativeWriter : public File::AtomHandler, + public DefinedAtom::ReferenceHandler { public: /// construct writer for an lld::File object NativeWriter(const lld::File& file) : _file(file) { + // reserve first byte for unnamed atoms + _stringPool.push_back('\0'); // visit all atoms _file.forEachAtom(*this); // construct file header based on atom information accumulated @@ -37,36 +41,73 @@ public: // write the lld::File in native format to the specified stream void write(llvm::raw_ostream& out) { + assert( out.tell() == 0 ); out.write((char*)_headerBuffer, _headerBufferSize); - if (!_definedAtomIvars.empty()) + + if (!_definedAtomIvars.empty()) { + assert( out.tell() == findChunk(NCS_DefinedAtomsV1).fileOffset ); out.write((char*)&_definedAtomIvars[0], _definedAtomIvars.size()*sizeof(NativeDefinedAtomIvarsV1)); - if (!_attributes.empty()) + } + + if (!_attributes.empty()) { + assert( out.tell() == findChunk(NCS_AttributesArrayV1).fileOffset ); out.write((char*)&_attributes[0], _attributes.size()*sizeof(NativeAtomAttributesV1)); - if ( !_undefinedAtomIvars.empty() ) + } + + if ( !_undefinedAtomIvars.empty() ) { + assert( out.tell() == findChunk(NCS_UndefinedAtomsV1).fileOffset ); out.write((char*)&_undefinedAtomIvars[0], _undefinedAtomIvars.size()*sizeof(NativeUndefinedAtomIvarsV1)); - if (!_stringPool.empty()) + } + + if (!_stringPool.empty()) { + assert( out.tell() == findChunk(NCS_Strings).fileOffset ); out.write(&_stringPool[0], _stringPool.size()); - if (!_contentPool.empty()) + } + + if ( !_references.empty() ) { + assert( out.tell() == findChunk(NCS_ReferencesArrayV1).fileOffset ); + out.write((char*)&_references[0], + _references.size()*sizeof(NativeReferenceIvarsV1)); + } + + if ( !_targetsTableIndex.empty() ) { + assert( out.tell() == findChunk(NCS_TargetsTable).fileOffset ); + writeTargetTable(out); + } + + if ( !_addendsTableIndex.empty() ) { + assert( out.tell() == findChunk(NCS_AddendsTable).fileOffset ); + writeAddendTable(out); + } + + if (!_contentPool.empty()) { + assert( out.tell() == findChunk(NCS_Content).fileOffset ); out.write((char*)&_contentPool[0], _contentPool.size()); + } } private: // visitor routine called by forEachAtom() - virtual void doDefinedAtom(const class DefinedAtom& atom) { + virtual void doDefinedAtom(const DefinedAtom& atom) { + _definedAtomIndex[&atom] = _definedAtomIvars.size(); NativeDefinedAtomIvarsV1 ivar; + unsigned refsCount; ivar.nameOffset = getNameOffset(atom); ivar.attributesOffset = getAttributeOffset(atom); + ivar.referencesStartIndex = getReferencesIndex(atom, refsCount); + ivar.referencesCount = refsCount; ivar.contentOffset = getContentOffset(atom); ivar.contentSize = atom.size(); _definedAtomIvars.push_back(ivar); } // visitor routine called by forEachAtom() - virtual void doUndefinedAtom(const class UndefinedAtom& atom) { + virtual void doUndefinedAtom(const UndefinedAtom& atom) { + _undefinedAtomIndex[&atom] = _undefinedAtomIvars.size(); NativeUndefinedAtomIvarsV1 ivar; ivar.nameOffset = getNameOffset(atom); ivar.flags = (atom.weakImport() ? 1 : 0); @@ -74,13 +115,18 @@ private: } // visitor routine called by forEachAtom() - virtual void doFile(const class File &) { + virtual void doFile(const File &) { } // fill out native file header and chunk directory void makeHeader() { const bool hasUndefines = !_undefinedAtomIvars.empty(); - const int chunkCount = hasUndefines ? 5 : 4; + const bool hasTargetsTable = !_targetsTableIndex.empty(); + const bool hasAddendTable = !_addendsTableIndex.empty(); + int chunkCount = 5; + if ( hasUndefines ) ++chunkCount; + if ( hasTargetsTable ) ++chunkCount; + if ( hasAddendTable ) ++chunkCount; _headerBufferSize = sizeof(NativeFileHeader) + chunkCount*sizeof(NativeChunk); _headerBuffer = reinterpret_cast @@ -124,6 +170,9 @@ private: } // create chunk for symbol strings + // pad end of string pool to 4-bytes + while ( (_stringPool.size() % 4) != 0 ) + _stringPool.push_back('\0'); NativeChunk& chs = chunks[nextIndex++]; chs.signature = NCS_Strings; chs.fileOffset = nextFileOffset; @@ -131,6 +180,34 @@ private: chs.elementCount = _stringPool.size(); nextFileOffset = chs.fileOffset + chs.fileSize; + // create chunk for references + NativeChunk& chr = chunks[nextIndex++]; + chr.signature = NCS_ReferencesArrayV1; + chr.fileOffset = nextFileOffset; + chr.fileSize = _references.size() * sizeof(NativeReferenceIvarsV1); + chr.elementCount = _references.size(); + nextFileOffset = chr.fileOffset + chr.fileSize; + + // create chunk for target table + if ( hasTargetsTable ) { + NativeChunk& cht = chunks[nextIndex++]; + cht.signature = NCS_TargetsTable; + cht.fileOffset = nextFileOffset; + cht.fileSize = _targetsTableIndex.size() * sizeof(uint32_t); + cht.elementCount = _targetsTableIndex.size(); + nextFileOffset = cht.fileOffset + cht.fileSize; + } + + // create chunk for addend table + if ( hasAddendTable ) { + NativeChunk& chad = chunks[nextIndex++]; + chad.signature = NCS_AddendsTable; + chad.fileOffset = nextFileOffset; + chad.fileSize = _addendsTableIndex.size() * sizeof(Reference::Addend); + chad.elementCount = _addendsTableIndex.size(); + nextFileOffset = chad.fileOffset + chad.fileSize; + } + // create chunk for content NativeChunk& chc = chunks[nextIndex++]; chc.signature = NCS_Content; @@ -142,6 +219,18 @@ private: _headerBuffer->fileSize = nextFileOffset; } + // scan header to find particular chunk + NativeChunk& findChunk(uint32_t signature) { + const uint32_t chunkCount = _headerBuffer->chunkCount; + NativeChunk* chunks = + reinterpret_cast(reinterpret_cast(_headerBuffer) + + sizeof(NativeFileHeader)); + for (uint32_t i=0; i < chunkCount; ++i) { + if ( chunks[i].signature == signature ) + return chunks[i]; + } + assert(0 && "findChunk() signature not found"); + } // append atom name to string pool and return offset uint32_t getNameOffset(const Atom& atom) { @@ -150,6 +239,8 @@ private: // append atom name to string pool and return offset uint32_t getNameOffset(llvm::StringRef name) { + if ( name.empty() ) + return 0; uint32_t result = _stringPool.size(); _stringPool.insert(_stringPool.end(), name.size()+1, 0); strcpy(&_stringPool[result], name.data()); @@ -206,7 +297,6 @@ private: attrs.sectionNameOffset = sectionNameOffset(atom); attrs.align2 = atom.alignment().powerOf2; attrs.alignModulus = atom.alignment().modulus; - attrs.internalName = atom.internalName(); attrs.scope = atom.scope(); attrs.interposable = atom.interposable(); attrs.merge = atom.merge(); @@ -214,12 +304,99 @@ private: attrs.sectionChoice = atom.sectionChoice(); attrs.deadStrip = atom.deadStrip(); attrs.permissions = atom.permissions(); - attrs.thumb = atom.isThumb(); + //attrs.thumb = atom.isThumb(); attrs.alias = atom.isAlias(); } + // add references for this atom in a contiguous block in NCS_ReferencesArrayV1 + uint32_t getReferencesIndex(const DefinedAtom& atom, unsigned& count) { + count = 0; + size_t startRefSize = _references.size(); + uint32_t result = startRefSize; + atom.forEachReference(*this); + count = _references.size() - startRefSize; + if ( count == 0 ) + return 0; + else + return result; + } + + void doReference(const Reference& ref) { + NativeReferenceIvarsV1 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); + } + + uint32_t getTargetIndex(const Atom* target) { + TargetToIndex::const_iterator pos = _targetsTableIndex.find(target); + if ( pos != _targetsTableIndex.end() ) { + return pos->second; + } + uint32_t result = _targetsTableIndex.size(); + _targetsTableIndex[target] = result; + return result; + } + + void writeTargetTable(llvm::raw_ostream& out) { + // Build table of target indexes + uint32_t maxTargetIndex = _targetsTableIndex.size(); + uint32_t targetIndexes[maxTargetIndex]; + for (TargetToIndex::iterator it = _targetsTableIndex.begin(); + it != _targetsTableIndex.end(); ++it) { + const Atom* atom = it->first; + uint32_t targetIndex = it->second; + assert(targetIndex < maxTargetIndex); + uint32_t atomIndex = 0; + TargetToIndex::iterator pos = _definedAtomIndex.find(atom); + if ( pos != _definedAtomIndex.end() ) { + atomIndex = pos->second; + } + else { + pos = _undefinedAtomIndex.find(atom); + assert(pos != _undefinedAtomIndex.end()); + atomIndex = pos->second + _definedAtomIvars.size(); + } + targetIndexes[targetIndex] = atomIndex; + } + // write table + out.write((char*)&targetIndexes[0], maxTargetIndex*sizeof(uint32_t)); + } + + uint32_t getAddendIndex(Reference::Addend addend) { + if ( addend == 0 ) + return 0; // addend index zero is used to mean "no addend" + AddendToIndex::const_iterator pos = _addendsTableIndex.find(addend); + if ( pos != _addendsTableIndex.end() ) { + return pos->second; + } + uint32_t result = _addendsTableIndex.size() + 1; // one-based index + _addendsTableIndex[addend] = result; + return result; + } + + void writeAddendTable(llvm::raw_ostream& out) { + // Build table of addends + uint32_t maxAddendIndex = _addendsTableIndex.size(); + Reference::Addend addends[maxAddendIndex]; + for (AddendToIndex::iterator it = _addendsTableIndex.begin(); + it != _addendsTableIndex.end(); ++it) { + Reference::Addend addend = it->first; + uint32_t index = it->second; + assert(index <= maxAddendIndex); + addends[index-1] = addend; + } + // write table + out.write((char*)&addends[0], maxAddendIndex*sizeof(Reference::Addend)); + } + typedef std::vector > NameToOffsetVector; + typedef llvm::DenseMap TargetToIndex; + typedef llvm::DenseMap AddendToIndex; + const lld::File& _file; NativeFileHeader* _headerBuffer; size_t _headerBufferSize; @@ -228,6 +405,11 @@ private: std::vector _definedAtomIvars; std::vector _attributes; std::vector _undefinedAtomIvars; + std::vector _references; + TargetToIndex _targetsTableIndex; + TargetToIndex _definedAtomIndex; + TargetToIndex _undefinedAtomIndex; + AddendToIndex _addendsTableIndex; NameToOffsetVector _sectionNames; }; diff --git a/lld/lib/Core/Resolver.cpp b/lld/lib/Core/Resolver.cpp index cebf3f7d10c3..68f4ba5b4f82 100644 --- a/lld/lib/Core/Resolver.cpp +++ b/lld/lib/Core/Resolver.cpp @@ -199,20 +199,34 @@ void Resolver::resolveUndefines() { } } +// helper to update targets for use with forEachReference() +class ReferenceUpdater : public DefinedAtom::ReferenceHandler { +public: + ReferenceUpdater(SymbolTable& sym) : _symbolTable(sym) { } + + virtual void doReference(const Reference& ref) { + const Atom* newTarget = _symbolTable.replacement(ref.target()); + (const_cast(&ref))->setTarget(newTarget); + } + +private: + SymbolTable& _symbolTable; +}; + + // switch all references to undefined or coalesced away atoms // to the new defined atom void Resolver::updateReferences() { + ReferenceUpdater updater(_symbolTable); for (std::vector::iterator it = _atoms.begin(); it != _atoms.end(); ++it) { if ( const DefinedAtom* defAtom = (*it)->definedAtom() ) { - for (Reference::iterator rit = defAtom->referencesBegin(), - end = defAtom->referencesEnd(); rit != end; ++rit) { - rit->target = _symbolTable.replacement(rit->target); - } + defAtom->forEachReference(updater); } } } + // for dead code stripping, recursively mark atom "live" void Resolver::markLive(const Atom &atom, WhyLiveBackChain *previous) { // if -why_live cares about this symbol, then dump chain @@ -240,10 +254,8 @@ void Resolver::markLive(const Atom &atom, WhyLiveBackChain *previous) { thisChain.previous = previous; thisChain.referer = &atom; if ( const DefinedAtom* defAtom = atom.definedAtom() ) { - for (Reference::iterator rit = defAtom->referencesBegin(), - end = defAtom->referencesEnd(); rit != end; ++rit) { - this->markLive(*(rit->target), &thisChain); - } + MarkLiveReferences markRefs(*this, &thisChain); + defAtom->forEachReference(markRefs); } } diff --git a/lld/lib/Core/SymbolTable.cpp b/lld/lib/Core/SymbolTable.cpp index f964fd8acf94..1b940cc88ef5 100644 --- a/lld/lib/Core/SymbolTable.cpp +++ b/lld/lib/Core/SymbolTable.cpp @@ -38,7 +38,7 @@ void SymbolTable::add(const UndefinedAtom &atom) { void SymbolTable::add(const DefinedAtom &atom) { assert(atom.scope() != DefinedAtom::scopeTranslationUnit); - if ( !atom.internalName() ) { + if ( !atom.name().empty() ) { this->addByName(atom); } else { diff --git a/lld/lib/Core/YamlKeyValues.cpp b/lld/lib/Core/YamlKeyValues.cpp index 9401aae949ad..bb32aa884896 100644 --- a/lld/lib/Core/YamlKeyValues.cpp +++ b/lld/lib/Core/YamlKeyValues.cpp @@ -18,12 +18,12 @@ namespace yaml { const char* const KeyValues::nameKeyword = "name"; +const char* const KeyValues::refNameKeyword = "ref-name"; const char* const KeyValues::definitionKeyword = "definition"; const char* const KeyValues::scopeKeyword = "scope"; const char* const KeyValues::contentTypeKeyword = "type"; const char* const KeyValues::deadStripKindKeyword = "dead-strip"; const char* const KeyValues::sectionChoiceKeyword = "section-choice"; -const char* const KeyValues::internalNameKeyword = "internal-name"; const char* const KeyValues::interposableKeyword = "interposable"; const char* const KeyValues::mergeKeyword = "merge"; const char* const KeyValues::isThumbKeyword = "is-thumb"; @@ -31,8 +31,14 @@ const char* const KeyValues::isAliasKeyword = "is-alias"; const char* const KeyValues::sectionNameKeyword = "section-name"; const char* const KeyValues::contentKeyword = "content"; const char* const KeyValues::sizeKeyword = "size"; +const char* const KeyValues::fixupsKeyword = "fixups"; const char* const KeyValues::permissionsKeyword = "permissions"; const char* const KeyValues::weakImportKeyword = "weak-import"; +const char* const KeyValues::fixupsKindKeyword = "kind"; +const char* const KeyValues::fixupsOffsetKeyword = "offset"; +const char* const KeyValues::fixupsTargetKeyword = "target"; +const char* const KeyValues::fixupsAddendKeyword = "addend"; + const DefinedAtom::Definition KeyValues::definitionDefault = Atom::definitionRegular; @@ -43,7 +49,6 @@ const DefinedAtom::SectionChoice KeyValues::sectionChoiceDefault = DefinedA const DefinedAtom::Interposable KeyValues::interposableDefault = DefinedAtom::interposeNo; const DefinedAtom::Merge KeyValues::mergeDefault = DefinedAtom::mergeNo; const DefinedAtom::ContentPermissions KeyValues::permissionsDefault = DefinedAtom::permR__; -const bool KeyValues::internalNameDefault = false; const bool KeyValues::isThumbDefault = false; const bool KeyValues::isAliasDefault = false; const bool KeyValues::weakImportDefault = false; @@ -352,22 +357,6 @@ const char* KeyValues::permissions(DefinedAtom::ContentPermissions s) { -bool KeyValues::internalName(const char* s) -{ - if ( strcmp(s, "true") == 0 ) - return true; - else if ( strcmp(s, "false") == 0 ) - return false; - llvm::report_fatal_error("bad internal-name value"); -} - -const char* KeyValues::internalName(bool b) { - return b ? "true" : "false"; -} - - - - bool KeyValues::isThumb(const char* s) { if ( strcmp(s, "true") == 0 ) diff --git a/lld/lib/Core/YamlKeyValues.h b/lld/lib/Core/YamlKeyValues.h index 2a15ed256399..6cd0908c8552 100644 --- a/lld/lib/Core/YamlKeyValues.h +++ b/lld/lib/Core/YamlKeyValues.h @@ -20,10 +20,12 @@ namespace yaml { class KeyValues { public: static const char* const nameKeyword; + static const char* const refNameKeyword; static const char* const sectionNameKeyword; static const char* const contentKeyword; static const char* const sizeKeyword; - + static const char* const fixupsKeyword; + static const char* const definitionKeyword; static const Atom::Definition definitionDefault; static Atom::Definition definition(const char*); @@ -64,11 +66,6 @@ public: static DefinedAtom::ContentPermissions permissions(const char*); static const char* permissions(DefinedAtom::ContentPermissions); - static const char* const internalNameKeyword; - static const bool internalNameDefault; - static bool internalName(const char*); - static const char* internalName(bool); - static const char* const isThumbKeyword; static const bool isThumbDefault; static bool isThumb(const char*); @@ -84,6 +81,12 @@ public: static bool weakImport(const char*); static const char* weakImport(bool); + + static const char* const fixupsKindKeyword; + static const char* const fixupsOffsetKeyword; + static const char* const fixupsTargetKeyword; + static const char* const fixupsAddendKeyword; + }; } // namespace yaml diff --git a/lld/lib/Core/YamlReader.cpp b/lld/lib/Core/YamlReader.cpp index 37c6382fc52b..d5e60eb21310 100644 --- a/lld/lib/Core/YamlReader.cpp +++ b/lld/lib/Core/YamlReader.cpp @@ -7,6 +7,8 @@ // //===----------------------------------------------------------------------===// +#include + #include "YamlKeyValues.h" #include "lld/Core/YamlReader.h" @@ -30,6 +32,8 @@ namespace lld { namespace yaml { +namespace { + class YAML { public: struct Entry { @@ -214,7 +218,7 @@ void YAML::parse(llvm::MemoryBuffer *mb, std::vector &entries) { sequenceBytes->push_back(contentByte); state = inValueSequenceEnd; } - else if (c == ' ') { + else if ( (c == ' ') || (c == '\n') ) { // eat white space } else if (c == ',') { @@ -245,6 +249,44 @@ void YAML::parse(llvm::MemoryBuffer *mb, std::vector &entries) { } } + + +class YAMLReference : public Reference { +public: + YAMLReference() : _target(NULL), _targetName(NULL), + _offsetInAtom(0), _addend(0), _kind(0) { } + + virtual uint64_t offsetInAtom() const { + return _offsetInAtom; + } + + virtual Kind kind() const { + return _kind; + } + + virtual const Atom* target() const { + return _target; + } + + virtual Addend addend() const { + return _addend; + } + + virtual void setTarget(const Atom* newAtom) { + _target = newAtom; + } + + const Atom* _target; + const char* _targetName; + uint64_t _offsetInAtom; + Addend _addend; + Kind _kind; +}; + + + +class YAMLDefinedAtom; + class YAMLFile : public File { public: YAMLFile() @@ -255,29 +297,24 @@ public: virtual bool justInTimeforEachAtom(llvm::StringRef name, File::AtomHandler &) const; - std::vector _definedAtoms; - std::vector _undefinedAtoms; - std::vector _references; - unsigned int _lastRefIndex; + void bindTargetReferences(); + void addDefinedAtom(YAMLDefinedAtom* atom, const char* refName); + void addUndefinedAtom(UndefinedAtom* atom); + Atom* findAtom(const char* name); + + struct NameAtomPair { + NameAtomPair(const char* n, Atom* a) : name(n), atom(a) {} + const char* name; + Atom* atom; + }; + + std::vector _definedAtoms; + std::vector _undefinedAtoms; + std::vector _references; + std::vector _nameToAtomMapping; + unsigned int _lastRefIndex; }; -bool YAMLFile::forEachAtom(File::AtomHandler &handler) const { - handler.doFile(*this); - for (std::vector::const_iterator it = _definedAtoms.begin(); - it != _definedAtoms.end(); ++it) { - handler.doDefinedAtom(**it); - } - for (std::vector::const_iterator it = _undefinedAtoms.begin(); - it != _undefinedAtoms.end(); ++it) { - handler.doUndefinedAtom(**it); - } - return true; -} - -bool YAMLFile::justInTimeforEachAtom(llvm::StringRef name, - File::AtomHandler &handler) const { - return false; -} class YAMLDefinedAtom : public DefinedAtom { @@ -291,7 +328,6 @@ public: , DefinedAtom::Merge merge , DefinedAtom::DeadStripKind deadStrip , DefinedAtom::ContentPermissions perms - , bool internalName , bool isThumb , bool isAlias , DefinedAtom::Alignment alignment @@ -313,7 +349,6 @@ public: , _merge(merge) , _deadStrip(deadStrip) , _permissions(perms) - , _internalName(internalName) , _isThumb(isThumb) , _isAlias(isAlias) , _refStartIndex(file._lastRefIndex) @@ -326,13 +361,12 @@ public: } virtual llvm::StringRef name() const { - return _name; + if ( _name == NULL ) + return llvm::StringRef(); + else + return _name; } - virtual bool internalName() const { - return _internalName; - } - virtual uint64_t size() const { return (_content ? _content->size() : _size); } @@ -393,16 +427,18 @@ public: } - virtual Reference::iterator referencesBegin() const { - if (_file._references.size() < _refStartIndex) - return (Reference::iterator)&_file._references[_refStartIndex]; - return 0; + virtual void forEachReference(ReferenceHandler& handler) const { + for (uint32_t i=_refStartIndex; i < _refEndIndex; ++i) { + handler.doReference(_file._references[i]); + } } - - virtual Reference::iterator referencesEnd() const { - if (_file._references.size() < _refEndIndex) - return (Reference::iterator)&_file._references[_refEndIndex]; - return 0; + + void bindTargetReferences() { + for (unsigned int i=_refStartIndex; i < _refEndIndex; ++i) { + const char* targetName = _file._references[i]._targetName; + Atom* targetAtom = _file.findAtom(targetName); + _file._references[i]._target = targetAtom; + } } private: @@ -420,7 +456,6 @@ private: DefinedAtom::Merge _merge; DefinedAtom::DeadStripKind _deadStrip; DefinedAtom::ContentPermissions _permissions; - bool _internalName; bool _isThumb; bool _isAlias; unsigned int _refStartIndex; @@ -453,22 +488,71 @@ private: }; +bool YAMLFile::forEachAtom(File::AtomHandler &handler) const { + handler.doFile(*this); + for (std::vector::const_iterator it = _definedAtoms.begin(); + it != _definedAtoms.end(); ++it) { + handler.doDefinedAtom(**it); + } + for (std::vector::const_iterator it = _undefinedAtoms.begin(); + it != _undefinedAtoms.end(); ++it) { + handler.doUndefinedAtom(**it); + } + return true; +} + +bool YAMLFile::justInTimeforEachAtom(llvm::StringRef name, + File::AtomHandler &handler) const { + return false; +} + +void YAMLFile::bindTargetReferences() { + for (std::vector::const_iterator + it = _definedAtoms.begin(); it != _definedAtoms.end(); ++it) { + YAMLDefinedAtom* atom = *it; + atom->bindTargetReferences(); + } +} + +Atom* YAMLFile::findAtom(const char* name) { + for (std::vector::const_iterator it = _nameToAtomMapping.begin(); + it != _nameToAtomMapping.end(); ++it) { + if ( strcmp(name, it->name) == 0 ) + return it->atom; + } + llvm::report_fatal_error("reference to atom that does not exist"); +} + +void YAMLFile::addDefinedAtom(YAMLDefinedAtom* atom, const char* refName) { + _definedAtoms.push_back(atom); + assert(refName != NULL); + _nameToAtomMapping.push_back(NameAtomPair(refName, atom)); +} + +void YAMLFile::addUndefinedAtom(UndefinedAtom* atom) { + _undefinedAtoms.push_back(atom); + _nameToAtomMapping.push_back(NameAtomPair(atom->name().data(), atom)); +} + class YAMLAtomState { public: YAMLAtomState(); void setName(const char *n); + void setRefName(const char *n); void setAlign2(const char *n); void setFixupKind(const char *n); void setFixupOffset(const char *n); void setFixupTarget(const char *n); + void setFixupAddend(const char *n); void addFixup(YAMLFile *f); void makeAtom(YAMLFile&); const char * _name; + const char * _refName; const char * _sectionName; unsigned long long _size; uint32_t _ordinal; @@ -482,16 +566,16 @@ public: DefinedAtom::Merge _merge; DefinedAtom::DeadStripKind _deadStrip; DefinedAtom::ContentPermissions _permissions; - bool _internalName; bool _isThumb; bool _isAlias; bool _weakImport; - Reference _ref; + YAMLReference _ref; }; YAMLAtomState::YAMLAtomState() : _name(NULL) + , _refName(NULL) , _sectionName(NULL) , _size(0) , _ordinal(0) @@ -505,38 +589,31 @@ YAMLAtomState::YAMLAtomState() , _merge(KeyValues::mergeDefault) , _deadStrip(KeyValues::deadStripKindDefault) , _permissions(KeyValues::permissionsDefault) - , _internalName(KeyValues::internalNameDefault) , _isThumb(KeyValues::isThumbDefault) , _isAlias(KeyValues::isAliasDefault) , _weakImport(false) { - _ref.target = NULL; - _ref.addend = 0; - _ref.offsetInAtom = 0; - _ref.kind = 0; - _ref.flags = 0; -} + } void YAMLAtomState::makeAtom(YAMLFile& f) { if ( _definition == Atom::definitionRegular ) { - DefinedAtom *a = new YAMLDefinedAtom(_ordinal, f, _scope, _type, + YAMLDefinedAtom *a = new YAMLDefinedAtom(_ordinal, f, _scope, _type, _sectionChoice, _interpose, _merge, _deadStrip, - _permissions, _internalName, _isThumb, _isAlias, + _permissions, _isThumb, _isAlias, _alignment, _name, _sectionName, _size, _content); - - f._definedAtoms.push_back(a); + f.addDefinedAtom(a, _refName ? _refName : _name); ++_ordinal; } else if ( _definition == Atom::definitionUndefined ) { UndefinedAtom *a = new YAMLUndefinedAtom(f, _ordinal, _name, _weakImport); - - f._undefinedAtoms.push_back(a); + f.addUndefinedAtom(a); ++_ordinal; } // reset state for next atom _name = NULL; + _refName = NULL; _sectionName = NULL; _size = 0; _ordinal = 0; @@ -554,17 +631,20 @@ void YAMLAtomState::makeAtom(YAMLFile& f) { _isThumb = KeyValues::isThumbDefault; _isAlias = KeyValues::isAliasDefault; _weakImport = KeyValues::weakImportDefault; - _ref.target = NULL; - _ref.addend = 0; - _ref.offsetInAtom = 0; - _ref.kind = 0; - _ref.flags = 0; + _ref._target = NULL; + _ref._targetName = NULL; + _ref._addend = 0; + _ref._offsetInAtom = 0; + _ref._kind = 0; } void YAMLAtomState::setName(const char *n) { _name = n; } +void YAMLAtomState::setRefName(const char *n) { + _refName = n; +} void YAMLAtomState::setAlign2(const char *s) { llvm::StringRef str(s); @@ -576,33 +656,54 @@ void YAMLAtomState::setAlign2(const char *s) { void YAMLAtomState::setFixupKind(const char *s) { if (strcmp(s, "pcrel32") == 0) - _ref.kind = 1; + _ref._kind = 1; else if (strcmp(s, "call32") == 0) - _ref.kind = 2; - else - llvm::report_fatal_error("bad fixup kind value"); + _ref._kind = 2; + else { + int k; + llvm::StringRef(s).getAsInteger(10, k); + _ref._kind = k; + } } void YAMLAtomState::setFixupOffset(const char *s) { if ((s[0] == '0') && (s[1] == 'x')) - llvm::StringRef(s).getAsInteger(16, _ref.offsetInAtom); + llvm::StringRef(s).getAsInteger(16, _ref._offsetInAtom); else - llvm::StringRef(s).getAsInteger(10, _ref.offsetInAtom); + llvm::StringRef(s).getAsInteger(10, _ref._offsetInAtom); } void YAMLAtomState::setFixupTarget(const char *s) { + _ref._targetName = s; } +void YAMLAtomState::setFixupAddend(const char *s) { + if ((s[0] == '0') && (s[1] == 'x')) + llvm::StringRef(s).getAsInteger(16, _ref._addend); + else + llvm::StringRef(s).getAsInteger(10, _ref._addend); +} + + void YAMLAtomState::addFixup(YAMLFile *f) { f->_references.push_back(_ref); // clear for next ref - _ref.target = NULL; - _ref.addend = 0; - _ref.offsetInAtom = 0; - _ref.kind = 0; - _ref.flags = 0; + _ref._target = NULL; + _ref._targetName = NULL; + _ref._addend = 0; + _ref._offsetInAtom = 0; + _ref._kind = 0; } + +} // anonymous namespace + + + + + +/// parseObjectText - Parse the specified YAML formatted MemoryBuffer +/// into lld::File object(s) and append each to the specified vector. llvm::error_code parseObjectText( llvm::MemoryBuffer *mb , std::vector &result) { std::vector entries; @@ -628,6 +729,7 @@ llvm::error_code parseObjectText( llvm::MemoryBuffer *mb atomState.makeAtom(*file); haveAtom = false; } + file->bindTargetReferences(); result.push_back(file); } file = new YAMLFile(); @@ -663,10 +765,10 @@ llvm::error_code parseObjectText( llvm::MemoryBuffer *mb atomState.setName(entry->value); haveAtom = true; } - else if (strcmp(entry->key, KeyValues::internalNameKeyword) == 0) { - atomState._internalName = KeyValues::internalName(entry->value); + else if (strcmp(entry->key, KeyValues::refNameKeyword) == 0) { + atomState.setRefName(entry->value); haveAtom = true; - } + } else if (strcmp(entry->key, KeyValues::definitionKeyword) == 0) { atomState._definition = KeyValues::definition(entry->value); haveAtom = true; @@ -725,8 +827,9 @@ llvm::error_code parseObjectText( llvm::MemoryBuffer *mb atomState.setAlign2(entry->value); haveAtom = true; } - else if (strcmp(entry->key, "fixups") == 0) { + else if (strcmp(entry->key, KeyValues::fixupsKeyword) == 0) { inFixups = true; + } else { return make_error_code(yaml_reader_error::unknown_keyword); @@ -739,16 +842,22 @@ llvm::error_code parseObjectText( llvm::MemoryBuffer *mb haveFixup = false; } } - if (strcmp(entry->key, "kind") == 0) { + if (strcmp(entry->key, KeyValues::fixupsKindKeyword) == 0) { atomState.setFixupKind(entry->value); haveFixup = true; - } else if (strcmp(entry->key, "offset") == 0) { + } + else if (strcmp(entry->key, KeyValues::fixupsOffsetKeyword) == 0) { atomState.setFixupOffset(entry->value); haveFixup = true; - } else if (strcmp(entry->key, "target") == 0) { + } + else if (strcmp(entry->key, KeyValues::fixupsTargetKeyword) == 0) { atomState.setFixupTarget(entry->value); haveFixup = true; } + else if (strcmp(entry->key, KeyValues::fixupsAddendKeyword) == 0) { + atomState.setFixupAddend(entry->value); + haveFixup = true; + } } } lastDepth = entry->depth; @@ -757,10 +866,12 @@ llvm::error_code parseObjectText( llvm::MemoryBuffer *mb atomState.makeAtom(*file); } + file->bindTargetReferences(); result.push_back(file); return make_error_code(yaml_reader_error::success); } + // // Fill in vector from path to input text file. // diff --git a/lld/lib/Core/YamlWriter.cpp b/lld/lib/Core/YamlWriter.cpp index db46b5a12d74..c38e735b4643 100644 --- a/lld/lib/Core/YamlWriter.cpp +++ b/lld/lib/Core/YamlWriter.cpp @@ -15,6 +15,8 @@ #include "lld/Core/Reference.h" #include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/DataTypes.h" @@ -26,50 +28,154 @@ namespace lld { namespace yaml { -class Handler : public File::AtomHandler { +namespace { +/// +/// In most cases, atoms names are unambiguous, so references can just +/// use the atom name as the target (e.g. target: foo). But in a few +/// cases that does not work, so ref-names are added. These are labels +/// used only in yaml. The labels do not exist in the Atom model. +/// +/// One need for ref-names are when atoms have no user supplied name +/// (e.g. c-string literal). Another case is when two object files with +/// identically named static functions are merged (ld -r) into one object file. +/// In that case referencing the function by name is ambiguous, so a unique +/// ref-name is added. +/// +class RefNameBuilder : public File::AtomHandler, + public DefinedAtom::ReferenceHandler { public: - Handler(llvm::raw_ostream &out) : _out(out), _firstAtom(true) { } + RefNameBuilder() { } + + virtual void doReference(const Reference& ref) { + // create refname for any unnamed reference target + if ( ref.target()->name().empty() ) { + char* buffer; + asprintf(&buffer, "L%03d", _unnamedCounter++); + _refNames[ref.target()] = buffer; + } + } + + virtual void doFile(const File &) { } + + virtual void doDefinedAtom(const DefinedAtom& atom) { + // Build map of atoms names to detect duplicates + if ( ! atom.name().empty() ) + buildDuplicateNameMap(atom); + + // Find references to unnamed atoms and create ref-names for them. + _unnamedCounter = 0; + atom.forEachReference(*this); + } + + virtual void doUndefinedAtom(const UndefinedAtom& atom) { + buildDuplicateNameMap(atom); + } + + void buildDuplicateNameMap(const Atom& atom) { + assert(!atom.name().empty()); + NameToAtom::iterator pos = _nameMap.find(atom.name()); + if ( pos != _nameMap.end() ) { + // Found name collision, give each a unique ref-name. + char* buffer; + asprintf(&buffer, "%s.%03d", atom.name().data(), ++_collisionCount); + _refNames[&atom] = buffer; + const Atom* prevAtom = pos->second; + AtomToRefName::iterator pos2 = _refNames.find(prevAtom); + if ( pos2 == _refNames.end() ) { + // only create ref-name for previous if none already created + asprintf(&buffer, "%s.%03d", prevAtom->name().data(), ++_collisionCount); + _refNames[prevAtom] = buffer; + } + } + else { + // First time we've seen this name, just add it to map. + _nameMap[atom.name()] = &atom; + } + } + + bool hasRefName(const Atom* atom) { + return _refNames.count(atom); + } + + const char* refName(const Atom* atom) { + return _refNames.find(atom)->second; + } + +private: + struct MyMappingInfo { + static llvm::StringRef getEmptyKey() { return llvm::StringRef(); } + static llvm::StringRef getTombstoneKey() { return llvm::StringRef(" ", 0); } + static unsigned getHashValue(llvm::StringRef const val) { + return llvm::HashString(val); } + static bool isEqual(llvm::StringRef const lhs, + llvm::StringRef const rhs) { return lhs.equals(rhs); } + }; + typedef llvm::DenseMap NameToAtom; + typedef llvm::DenseMap AtomToRefName; + + unsigned int _collisionCount; + unsigned int _unnamedCounter; + NameToAtom _nameMap; + AtomToRefName _refNames; +}; + + +/// +/// Helper class for writeObjectText() to write out atoms in yaml format. +/// +class AtomWriter : public File::AtomHandler, + public DefinedAtom::ReferenceHandler { +public: + AtomWriter(RefNameBuilder& rnb, llvm::raw_ostream &out) + : _out(out), _rnb(rnb), _firstAtom(true) { } virtual void doFile(const class File &) { _firstAtom = true; } virtual void doDefinedAtom(const class DefinedAtom &atom) { - // add blank line between atoms for readability - if ( !_firstAtom ) - _out << "\n"; - _firstAtom = false; - + // add blank line between atoms for readability + if ( !_firstAtom ) + _out << "\n"; + _firstAtom = false; + + bool hasDash = false; + if ( !atom.name().empty() ) { _out << " - " << KeyValues::nameKeyword << ":" << spacePadding(KeyValues::nameKeyword) << atom.name() << "\n"; - - if ( atom.internalName() != KeyValues::internalNameDefault ) { - _out << " " - << KeyValues::internalNameKeyword + hasDash = true; + } + + if ( _rnb.hasRefName(&atom) ) { + _out << (hasDash ? " " : " - ") + << KeyValues::refNameKeyword << ":" - << spacePadding(KeyValues::internalNameKeyword) - << KeyValues::internalName(atom.internalName()) + << spacePadding(KeyValues::refNameKeyword) + << _rnb.refName(&atom) << "\n"; + hasDash = true; } if ( atom.definition() != KeyValues::definitionDefault ) { - _out << " " + _out << (hasDash ? " " : " - ") << KeyValues::definitionKeyword << ":" << spacePadding(KeyValues::definitionKeyword) << KeyValues::definition(atom.definition()) << "\n"; + hasDash = true; } if ( atom.scope() != KeyValues::scopeDefault ) { - _out << " " + _out << (hasDash ? " " : " - ") << KeyValues::scopeKeyword << ":" << spacePadding(KeyValues::scopeKeyword) << KeyValues::scope(atom.scope()) << "\n"; + hasDash = true; } if ( atom.interposable() != KeyValues::interposableDefault ) { @@ -161,16 +267,50 @@ public: _out << " ]\n"; } - if (atom.referencesBegin() != atom.referencesEnd()) { - _out << " fixups:\n"; - for (Reference::iterator it = atom.referencesBegin(), - end = atom.referencesEnd(); it != end; ++it) { - _out << " - kind: " << it->kind << "\n"; - _out << " offset: " << it->offsetInAtom << "\n"; - } - } - + _wroteFirstFixup = false; + atom.forEachReference(*this); } + + virtual void doReference(const Reference& ref) { + if ( !_wroteFirstFixup ) { + _out << " fixups:\n"; + _wroteFirstFixup = true; + } + _out << " - " + << KeyValues::fixupsOffsetKeyword + << ":" + << spacePadding(KeyValues::fixupsOffsetKeyword) + << ref.offsetInAtom() + << "\n"; + _out << " " + << KeyValues::fixupsKindKeyword + << ":" + << spacePadding(KeyValues::fixupsKindKeyword) + << ref.kind() + << "\n"; + const Atom* target = ref.target(); + if ( target != NULL ) { + llvm::StringRef refName = target->name(); + if ( _rnb.hasRefName(target) ) + refName = _rnb.refName(target); + assert(!refName.empty()); + _out << " " + << KeyValues::fixupsTargetKeyword + << ":" + << spacePadding(KeyValues::fixupsTargetKeyword) + << refName + << "\n"; + } + if ( ref.addend() != 0 ) { + _out << " " + << KeyValues::fixupsAddendKeyword + << ":" + << spacePadding(KeyValues::fixupsAddendKeyword) + << ref.addend() + << "\n"; + } + } + virtual void doUndefinedAtom(const class UndefinedAtom &atom) { // add blank line between atoms for readability @@ -219,11 +359,26 @@ private: } llvm::raw_ostream& _out; + RefNameBuilder _rnb; bool _firstAtom; + bool _wroteFirstFixup; }; +} // anonymous namespace + + + +/// +/// writeObjectText - writes the lld::File object as in YAML +/// format to the specified stream. +/// void writeObjectText(const File &file, llvm::raw_ostream &out) { - Handler h(out); + // Figure what ref-name labels are needed + RefNameBuilder rnb; + file.forEachAtom(rnb); + + // Write out all atoms + AtomWriter h(rnb, out); out << "---\n"; out << "atoms:\n"; file.forEachAtom(h); diff --git a/lld/test/cstring-coalesce.objtxt b/lld/test/cstring-coalesce.objtxt index 205af3721e9f..faf35486e6bb 100644 --- a/lld/test/cstring-coalesce.objtxt +++ b/lld/test/cstring-coalesce.objtxt @@ -6,28 +6,24 @@ --- atoms: - - name: L0 - internal-name: true + - ref-name: L0 scope: hidden type: c-string content: [ 68, 65, 6c, 6c, 6f, 00 ] - - name: L1 - internal-name: true + - ref-name: L1 scope: hidden type: c-string content: [ 74, 68, 65, 72, 65, 00 ] --- atoms: - - name: L2 - internal-name: true + - ref-name: L2 scope: hidden type: c-string content: [ 68, 65, 6c, 6c, 6f, 00 ] --- atoms: - - name: L2 - internal-name: true + - ref-name: L2 scope: hidden type: c-string content: [ 74, 68, 65, 72, 65, 00 ] diff --git a/lld/test/fixups-addend.objtxt b/lld/test/fixups-addend.objtxt new file mode 100644 index 000000000000..70bf92fd429e --- /dev/null +++ b/lld/test/fixups-addend.objtxt @@ -0,0 +1,50 @@ +# RUN: lld-core %s | FileCheck %s + +# +# Test addends in fixups +# + +--- +atoms: + - name: foo + type: code + content: [ 48, 8D, 3D, 00, 00, 00, 00, + 48, 8D, 3D, 00, 00, 00, 00 ] + fixups: + - offset: 3 + kind: 3 + target: bar + addend: 100 + - offset: 10 + kind: 3 + target: bar + addend: -50 + + - name: func + type: code + content: [ 48, 8D, 3D, 00, 00, 00, 00, + 48, 8D, 3D, 00, 00, 00, 00 ] + fixups: + - offset: 3 + kind: 3 + target: bar + addend: 8000000000 + - offset: 10 + kind: 3 + target: bar + addend: -50 + + - name: bar + definition: undefined + + +... + +# CHECK: name: foo +# CHECK: fixups: +# CHECK: addend: 100 +# CHECK: addend: -50 +# CHECK: name: func +# CHECK: fixups: +# CHECK: addend: 8000000000 +# CHECK: addend: -50 diff --git a/lld/test/fixups-dup-named.objtxt b/lld/test/fixups-dup-named.objtxt new file mode 100644 index 000000000000..d1b1284e8f3b --- /dev/null +++ b/lld/test/fixups-dup-named.objtxt @@ -0,0 +1,31 @@ +# RUN: lld-core %s | FileCheck %s + +# +# Test fixups referencing multiple atoms that have the same name +# + +--- +atoms: + - name: foo + type: code + content: [ E8, 00, 00, 00, 00, E8, 00, 00, 00, 00 ] + fixups: + - offset: 1 + kind: 3 + target: bar_1 + - offset: 6 + kind: 3 + target: bar_2 + + - name: bar + ref-name: bar_1 + scope: static + + - name: bar + ref-name: bar_2 + scope: static + + +... + +# CHECK: ... diff --git a/lld/test/fixups-named.objtxt b/lld/test/fixups-named.objtxt new file mode 100644 index 000000000000..1bf38c06e561 --- /dev/null +++ b/lld/test/fixups-named.objtxt @@ -0,0 +1,35 @@ +# RUN: lld-core %s | FileCheck %s + +# +# Test fixups to simple named atoms +# + +--- +atoms: + - name: foo + type: code + content: [ E8, 00, 00, 00, 00, E8, 00, 00, 00, 00 ] + fixups: + - offset: 1 + kind: 3 + target: bar + - offset: 6 + kind: 3 + target: baz + + - name: baz + scope: static + type: code + + - name: bar + definition: undefined + + +... + +# CHECK: name: foo +# CHECK: fixups: +# CHECK: target: bar +# CHECK: target: baz +# CHECK: ... + diff --git a/lld/test/fixups-unnamed.objtxt b/lld/test/fixups-unnamed.objtxt new file mode 100644 index 000000000000..9dbab8124453 --- /dev/null +++ b/lld/test/fixups-unnamed.objtxt @@ -0,0 +1,40 @@ +# RUN: lld-core %s | FileCheck %s + +# +# Test fixups to unnamed atoms +# + +--- +atoms: + - name: foo + type: code + content: [ 48, 8D, 3D, 00, 00, 00, 00, + 48, 8D, 3D, 00, 00, 00, 00 ] + fixups: + - offset: 3 + kind: 3 + target: LC1 + - offset: 10 + kind: 3 + target: LC2 + + + - ref-name: LC1 + scope: hidden + type: c-string + content: [ 68, 65, 6c, 6c, 6f, 00 ] + + - ref-name: LC2 + scope: hidden + type: c-string + content: [ 74, 68, 65, 72, 65, 00 ] + + +... + +# CHECK: name: foo +# CHECK: fixups: +# CHECK: offset: 3 +# CHECK: offset: 10 +# CHECK: ref-name: +# CHECK: ref-name: diff --git a/lld/test/internal-name-attributes.objtxt b/lld/test/internal-name-attributes.objtxt deleted file mode 100644 index 8d37a0060eba..000000000000 --- a/lld/test/internal-name-attributes.objtxt +++ /dev/null @@ -1,27 +0,0 @@ -# RUN: lld-core %s | FileCheck %s - -# -# Test that internal-name attributes are preserved -# - ---- -atoms: - - name: foo - internal-name: false - - name: L0 - internal-name: true - - name: L1 - internal-name: true - - name: bar -... - - -# CHECK: name: foo -# CHECK-NOT: internal-name: false -# CHECK: name: L0 -# CHECK: internal-name: true -# CHECK: name: L1 -# CHECK: internal-name: true -# CHECK: name: bar -# CHECK-NOT: internal-name: false -# CHECK: ... diff --git a/lld/tools/lld-core/lld-core.cpp b/lld/tools/lld-core/lld-core.cpp index 152eb9529e97..385a1465b577 100644 --- a/lld/tools/lld-core/lld-core.cpp +++ b/lld/tools/lld-core/lld-core.cpp @@ -234,8 +234,9 @@ int main(int argc, const char *argv[]) { // read native file llvm::OwningPtr natFile; - parseNativeObjectFileOrSTDIN(tempPath, natFile); - + if ( error(parseNativeObjectFileOrSTDIN(tempPath, natFile)) ) + return 1; + // write new atom graph out as YAML doc yaml::writeObjectText(*natFile, out);