diff --git a/lld/include/lld/Core/Atom.h b/lld/include/lld/Core/Atom.h index df5efeedbd86..101eb5221bcd 100644 --- a/lld/include/lld/Core/Atom.h +++ b/lld/include/lld/Core/Atom.h @@ -1,4 +1,4 @@ -//===- Core/Atom.h - The Fundimental Unit of Linking ----------------------===// +//===- Core/Atom.h - A node in linking graph ------------------------------===// // // The LLVM Linker // diff --git a/lld/include/lld/Core/DefinedAtom.h b/lld/include/lld/Core/DefinedAtom.h index c151325cdcf6..8db0728875db 100644 --- a/lld/include/lld/Core/DefinedAtom.h +++ b/lld/include/lld/Core/DefinedAtom.h @@ -1,4 +1,4 @@ -//===- Core/DefinedAtom.h - The Fundimental Unit of Linking ---------------===// +//===- Core/DefinedAtom.h - The Fundamental Unit of Linking ---------------===// // // The LLVM Linker // diff --git a/lld/include/lld/Core/File.h b/lld/include/lld/Core/File.h index b2f360149b4a..c6b0e0fec579 100644 --- a/lld/include/lld/Core/File.h +++ b/lld/include/lld/Core/File.h @@ -20,7 +20,7 @@ namespace lld { class File { public: File(llvm::StringRef p) : _path(p) {} - ~File(); + virtual ~File(); class AtomHandler { public: diff --git a/lld/include/lld/Core/NativeReader.h b/lld/include/lld/Core/NativeReader.h new file mode 100644 index 000000000000..023dc2ecface --- /dev/null +++ b/lld/include/lld/Core/NativeReader.h @@ -0,0 +1,39 @@ +//===- Core/NativeReader.h - Reads llvm native object files ---------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_CORE_NATIVE_READER_H_ +#define LLD_CORE_NATIVE_READER_H_ + +#include "lld/Core/File.h" + +#include "llvm/Support/system_error.h" + +#include + +namespace llvm { + class MemoryBuffer; + class StringRef; +} + +namespace lld { + + /// parseNativeObjectFileOrSTDIN - Open the specified native object file (use + /// stdin if the path is "-") and instantiate into an lld::File object. + llvm::error_code parseNativeObjectFileOrSTDIN(llvm::StringRef path + , File*&); + + + /// parseNativeObjectFile - Parse the specified native object file + /// (in a buffer) and instantiate into an lld::File object. + llvm::error_code parseNativeObjectFile(llvm::MemoryBuffer* mb, + llvm::StringRef path, File*& result); + +} // namespace lld + +#endif // LLD_CORE_NATIVE_READER_H_ diff --git a/lld/include/lld/Core/NativeWriter.h b/lld/include/lld/Core/NativeWriter.h new file mode 100644 index 000000000000..24bf671da10a --- /dev/null +++ b/lld/include/lld/Core/NativeWriter.h @@ -0,0 +1,34 @@ +//===- Core/NativeWriter.h - Writes native object file --------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_CORE_NATIVE_WRITER_H_ +#define LLD_CORE_NATIVE_WRITER_H_ + +#include "lld/Core/File.h" + +#include "llvm/Support/raw_ostream.h" + +namespace llvm { + class StringRef; +} + + +namespace lld { + + /// writeNativeObjectFile - writes the lld::File object in native object + /// file format to the specified file path. + int writeNativeObjectFile(const lld::File &, llvm::StringRef path); + + /// writeNativeObjectFile - writes the lld::File object in native object + /// file format to the specified stream. + int writeNativeObjectFile(const lld::File &, llvm::raw_ostream &); + +} // namespace lld + +#endif // LLD_CORE_NATIVE_WRITER_H_ diff --git a/lld/include/lld/Core/YamlWriter.h b/lld/include/lld/Core/YamlWriter.h index b533b98575fd..b4c024731336 100644 --- a/lld/include/lld/Core/YamlWriter.h +++ b/lld/include/lld/Core/YamlWriter.h @@ -1,4 +1,4 @@ -//===- Core/YamlWriter.h - Writes YAML ------------------------------------===// +//===- Core/YamlWriter.h - Writes YAML formatted object files -------------===// // // The LLVM Linker // @@ -11,13 +11,14 @@ #define LLD_CORE_YAML_WRITER_H_ #include "lld/Core/File.h" - #include "llvm/Support/raw_ostream.h" namespace lld { namespace yaml { -void writeObjectText(lld::File &, llvm::raw_ostream &); + /// writeObjectText - writes the lld::File object as in YAML + /// format to the specified stream. + void writeObjectText(const lld::File &, llvm::raw_ostream &); } // namespace yaml } // namespace lld diff --git a/lld/lib/Core/CMakeLists.txt b/lld/lib/Core/CMakeLists.txt index eca024cf231b..c994d8f3f729 100644 --- a/lld/lib/Core/CMakeLists.txt +++ b/lld/lib/Core/CMakeLists.txt @@ -1,8 +1,12 @@ add_lld_library(lldCore File.cpp + NativeFileFormat.h + NativeReader.cpp + NativeWriter.cpp Resolver.cpp SymbolTable.cpp YamlKeyValues.cpp + YamlKeyValues.h YamlReader.cpp YamlWriter.cpp ) diff --git a/lld/lib/Core/NativeFileFormat.h b/lld/lib/Core/NativeFileFormat.h new file mode 100644 index 000000000000..aa8687a2fd59 --- /dev/null +++ b/lld/lib/Core/NativeFileFormat.h @@ -0,0 +1,161 @@ +//===- Core/NativeFileFormat.h - Describes native object file -------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_CORE_NATIVE_FILE_FORMAT_H_ +#define LLD_CORE_NATIVE_FILE_FORMAT_H_ + +#include + +namespace lld { + + +// +// Overview: +// +// The number one design goal of this file format is enable the linker to +// read object files into in-memory Atom objects extremely quickly. +// The second design goal is to enable future modifications to the +// Atom attribute model. +// +// The llvm native object file format is not like traditional object file +// formats (e.g. ELF, COFF, mach-o). There is no symbol table and no +// sections. Instead the file is essentially an array of archived Atoms. +// It is *not* serialized Atoms which would require deserialization into +// in memory objects. Instead it is an array of read-only info about each +// Atom. The NativeReader bulk creates in-memory Atoms which just have +// an ivar which points to the read-only info for that Atom. No additional +// processing is done to construct the in-memory Atoms. All Atom attribute +// getter methods are virtual calls which dig up the info they need from the +// ivar data. +// +// To support the gradual evolution of Atom attributes, the Atom read-only +// data is versioned. The NativeReader chooses which in-memory Atom class +// to use based on the version. What this means is that if new attributes +// are added (or changed) in the Atom model, a new native atom class and +// read-only atom info struct needs to be defined. Then, all the existing +// native reader atom classes need to be modified to do their best effort +// to map their old style read-only data to the new Atom model. At some point +// some classes to support old versions may be dropped. +// +// +// Details: +// +// The native object file format consists of a header that specifies the +// endianness of the file and the architecture along with a list of "chunks" +// in the file. A Chunk is simply a tagged range of the file. There is +// one chunk for the array of atom infos. There is another chunk for the +// string pool, and another for the content pool. +// +// It turns out there most atoms have very similar sets of attributes, only +// the name and content attribute vary. To exploit this fact to reduce the file +// size, the atom read-only info contains just the name and content info plus +// a reference to which attribute set it uses. The attribute sets are stored +// in another chunk. +// + + +// +// An entry in the NativeFileHeader that describes one chunk of the file. +// +struct NativeChunk { + uint32_t signature; + uint32_t fileOffset; + uint32_t fileSize; + uint32_t elementCount; +}; + + +// +// The header in a native object file +// +struct NativeFileHeader { + uint8_t magic[16]; + uint32_t endian; + uint32_t architecture; + uint32_t fileSize; + uint32_t chunkCount; + NativeChunk chunks[]; +}; + +// +// Possible values for NativeChunk.signature field +// +enum NativeChunkSignatures { + NCS_DefinedAtomsV1 = 1, + NCS_AttributesArrayV1 = 2, + NCS_Content = 3, + NCS_Strings = 4, + NCS_ReferencesArray = 5, +}; + +// +// The 16-bytes at the start of a native object file +// +#define NATIVE_FILE_HEADER_MAGIC "llvm nat obj v1 " + +// +// Possible values for the NativeFileHeader.endian field +// +enum { + NFH_BigEndian = 0x42696745, + NFH_LittleEndian = 0x4574696c +}; + + +// +// Possible values for the NativeFileHeader.architecture field +// +enum { + NFA_x86 = 1, + NFA_x86_64 = 2, + NFA_armv6 = 3, + NFA_armv7 = 4, +}; + + +// +// The NCS_DefinedAtomsV1 chunk contains an array of these structs +// +struct NativeDefinedAtomIvarsV1 { + uint32_t nameOffset; + uint32_t attributesOffset; + uint32_t contentOffset; + uint32_t contentSize; +}; + + +// +// The NCS_AttributesArrayV1 chunk contains an array of these structs +// +struct NativeAtomAttributesV1 { + uint32_t sectionNameOffset; + uint16_t align2; + uint16_t alignModulus; + uint8_t internalName; + uint8_t scope; + uint8_t interposable; + uint8_t merge; + uint8_t contentType; + uint8_t sectionChoice; + uint8_t deadStrip; + uint8_t permissions; + uint8_t thumb; + uint8_t alias; + uint8_t pad1; + uint8_t pad2; +}; + + + + + + +} // namespace lld + +#endif // LLD_CORE_NATIVE_FILE_FORMAT_H_ diff --git a/lld/lib/Core/NativeReader.cpp b/lld/lib/Core/NativeReader.cpp new file mode 100644 index 000000000000..cb42a31fa746 --- /dev/null +++ b/lld/lib/Core/NativeReader.cpp @@ -0,0 +1,415 @@ +//===- Core/NativeReader.cpp - reads native object file ------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +#include + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/system_error.h" + +#include "lld/Core/File.h" +#include "lld/Core/Atom.h" + +#include "NativeFileFormat.h" + +namespace lld { + +// forward reference +class NativeFile; + + +enum native_reader_errors { + success = 0, + unknown_file_format, + file_too_short, + file_malformed, + unknown_chunk_type, + memory_error, +}; + +class reader_error_category : public llvm::_do_message { +public: + virtual const char* name() const { + return "lld.native.reader"; + } + virtual std::string message(int ev) const; +}; + +const reader_error_category reader_error_category_singleton; + +std::string reader_error_category::message(int ev) const { + switch (ev) { + case success: + return "Success"; + case unknown_file_format: + return "Unknown file foramt"; + case file_too_short: + return "file truncated"; + case file_malformed: + return "file malformed"; + case memory_error: + return "out of memory"; + case unknown_chunk_type: + return "unknown chunk type"; + default: + llvm_unreachable("An enumerator of native_reader_errors does not have a " + "message defined."); + } +} + +inline llvm::error_code make_error_code(native_reader_errors e) { + return llvm::error_code(static_cast(e), reader_error_category_singleton); +} + + + + +// +// An object of this class is instantied for each NativeDefinedAtomIvarsV1 +// struct in the NCS_DefinedAtomsV1 chunk. +// +class NativeDefinedAtomV1 : public DefinedAtom { +public: + NativeDefinedAtomV1(const NativeFile& f, + const NativeDefinedAtomIvarsV1* ivarData) + : _file(&f), _ivarData(ivarData) { } + + virtual const class File& file() const; + + virtual uint64_t ordinal() const; + + virtual llvm::StringRef name() const; + + virtual bool internalName() const { + return attributes().internalName; + } + + virtual uint64_t size() const { + return _ivarData->contentSize; + } + + virtual DefinedAtom::Scope scope() const { + return (DefinedAtom::Scope)(attributes().scope); + } + + virtual DefinedAtom::Interposable interposable() const { + return (DefinedAtom::Interposable)(attributes().interposable); + } + + virtual DefinedAtom::Merge merge() const { + return (DefinedAtom::Merge)(attributes().merge); + } + + virtual DefinedAtom::ContentType contentType() const { + return (DefinedAtom::ContentType)(attributes().contentType); + } + + virtual DefinedAtom::Alignment alignment() const { + return DefinedAtom::Alignment(attributes().align2, attributes().alignModulus); + } + + virtual DefinedAtom::SectionChoice sectionChoice() const { + return (DefinedAtom::SectionChoice)(attributes().sectionChoice); + } + + virtual llvm::StringRef customSectionName() const; + + virtual DefinedAtom::DeadStripKind deadStrip() const { + return (DefinedAtom::DeadStripKind)(attributes().deadStrip); + } + + virtual DefinedAtom::ContentPermissions permissions() const { + return (DefinedAtom::ContentPermissions)(attributes().permissions); + } + + virtual bool isThumb() const { + return (attributes().thumb != 0); + } + + virtual bool isAlias() const { + return (attributes().alias != 0); + } + + llvm::ArrayRef rawContent() const; + + virtual Reference::iterator referencesBegin() const { + return 0; + } + + virtual Reference::iterator referencesEnd() const { + return 0; + } + +private: + const NativeAtomAttributesV1& attributes() const; + + const NativeFile* _file; + const NativeDefinedAtomIvarsV1* _ivarData; +}; + + + + +// +// lld::File object for native llvm object file +// +class NativeFile : public File { +public: + + /// Instantiates a File object from a native object file. Ownership + /// of the MemoryBuffer is transfered to the resulting File object. + static llvm::error_code make(llvm::MemoryBuffer* mb, llvm::StringRef path, + File*& result) { + const uint8_t* const base = + reinterpret_cast(mb->getBufferStart()); + const NativeFileHeader* const header = + reinterpret_cast(base); + // make sure magic matches + if ( memcmp(header->magic, NATIVE_FILE_HEADER_MAGIC, 16) != 0 ) + return make_error_code(unknown_file_format); + + // make sure mapped file contains all needed data + const size_t fileSize = mb->getBufferSize(); + if ( header->fileSize > fileSize ) + return make_error_code(file_too_short); + + // instantiate NativeFile object and add values to it as found + NativeFile* file = new NativeFile(mb, path); + + // process each chunk + for(uint32_t i=0; i < header->chunkCount; ++i) { + llvm::error_code ec; + const NativeChunk* chunk = &header->chunks[i]; + // sanity check chunk is within file + if ( chunk->fileOffset > fileSize ) + return make_error_code(file_malformed); + if ( (chunk->fileOffset + chunk->fileSize) > fileSize) + return make_error_code(file_malformed); + // process chunk, based on signature + switch ( chunk->signature ) { + case NCS_DefinedAtomsV1: + ec = file->processDefinedAtomsV1(base, chunk); + break; + case NCS_AttributesArrayV1: + ec = file->processAttributesV1(base, chunk); + break; + case NCS_Content: + ec = file->processContent(base, chunk); + break; + case NCS_Strings: + ec = file->processStrings(base, chunk); + break; + default: + return make_error_code(unknown_chunk_type); + } + if ( ec ) { + delete file; + return ec; + } + + // TO DO: validate enough chunks were used + + result = file; + } + + + return make_error_code(success); + } + + virtual ~NativeFile() { + // The NativeFile owns the MemoryBuffer and must not delete it. + delete _buffer; + // All other ivar pointers are pointers into the MemoryBuffer, except + // the _definedAtoms array which was allocated to contain an array + // of Atom objects. The atoms have empty destructors, so it is ok + // to just delete the memory. + delete _definedAtoms.arrayStart; + } + + // visits each atom in the file + virtual bool forEachAtom(AtomHandler& handler) const { + for(const uint8_t* p=_definedAtoms.arrayStart; p != _definedAtoms.arrayEnd; + p += _definedAtoms.elementSize) { + const DefinedAtom* atom = reinterpret_cast(p); + handler.doDefinedAtom(*atom); + } + return (_definedAtoms.arrayStart != _definedAtoms.arrayEnd); + } + + // not used + virtual bool justInTimeforEachAtom(llvm::StringRef name, + AtomHandler &) const { + return false; + } + +private: + friend class NativeDefinedAtomV1; + + // instantiate array of DefinedAtoms from v1 ivar data in file + llvm::error_code processDefinedAtomsV1(const uint8_t* base, + const NativeChunk* chunk) { + const size_t atomSize = sizeof(NativeDefinedAtomV1); + size_t atomsArraySize = chunk->elementCount * atomSize; + uint8_t* atomsStart = reinterpret_cast + (operator new(atomsArraySize, std::nothrow)); + if (atomsStart == NULL ) + return make_error_code(memory_error); + const size_t ivarElementSize = chunk->fileSize + / chunk->elementCount; + if ( ivarElementSize != sizeof(NativeDefinedAtomIvarsV1) ) + return make_error_code(file_malformed); + uint8_t* atomsEnd = atomsStart + atomsArraySize; + const NativeDefinedAtomIvarsV1* ivarData = + reinterpret_cast + (base + chunk->fileOffset); + for(uint8_t* s = atomsStart; s != atomsEnd; s += atomSize) { + NativeDefinedAtomV1* atomAllocSpace = + reinterpret_cast(s); + new (atomAllocSpace) NativeDefinedAtomV1(*this, ivarData); + ++ivarData; + } + this->_definedAtoms.arrayStart = atomsStart; + this->_definedAtoms.arrayEnd = atomsEnd; + this->_definedAtoms.elementSize = atomSize; + return make_error_code(success); + } + + // set up pointers to attributes array + llvm::error_code processAttributesV1(const uint8_t* base, const NativeChunk* chunk) { + this->_attributes = base + chunk->fileOffset; + this->_attributesMaxOffset = chunk->fileSize; + return make_error_code(success); + } + + // set up pointers to string pool in file + llvm::error_code processStrings(const uint8_t* base, + const NativeChunk* chunk) { + this->_strings = reinterpret_cast(base + chunk->fileOffset); + this->_stringsMaxOffset = chunk->fileSize; + return make_error_code(success); + } + + // set up pointers to content area in file + llvm::error_code processContent(const uint8_t* base, + const NativeChunk* chunk) { + this->_contentStart = base + chunk->fileOffset; + this->_contentEnd = base + chunk->fileOffset + chunk->fileSize; + return make_error_code(success); + } + + llvm::StringRef string(uint32_t offset) const { + assert(offset < _stringsMaxOffset); + return llvm::StringRef(&_strings[offset]); + } + + const NativeAtomAttributesV1& attribute(uint32_t offset) const { + assert(offset < _attributesMaxOffset); + return *reinterpret_cast(_attributes + offset); + } + + const uint8_t* content(uint32_t offset, uint32_t size) const { + const uint8_t* result = _contentStart + offset; + assert((result+size) <= _contentEnd); + return result; + } + + + // private constructor, only called by make() + NativeFile(llvm::MemoryBuffer* mb, llvm::StringRef path) : + lld::File(path), _buffer(mb), _header(NULL), + _strings(NULL), _stringsMaxOffset(0), + _contentStart(NULL), _contentEnd(NULL) + { + _header = reinterpret_cast(mb->getBufferStart()); + } + + struct AtomArray { + AtomArray() : arrayStart(NULL), arrayEnd(NULL), + elementSize(0) { } + const uint8_t* arrayStart; + const uint8_t* arrayEnd; + uint32_t elementSize; + }; + + llvm::MemoryBuffer* _buffer; + const NativeFileHeader* _header; + AtomArray _definedAtoms; + const uint8_t* _attributes; + uint32_t _attributesMaxOffset; + const char* _strings; + uint32_t _stringsMaxOffset; + const uint8_t* _contentStart; + const uint8_t* _contentEnd; +}; + + + +inline const class File& NativeDefinedAtomV1::file() const { + return *_file; +} + +inline uint64_t NativeDefinedAtomV1:: ordinal() const { + const uint8_t* p = reinterpret_cast(_ivarData); + return p - _file->_definedAtoms.arrayStart; +} + +inline llvm::StringRef NativeDefinedAtomV1::name() const { + return _file->string(_ivarData->nameOffset); +} + +inline const NativeAtomAttributesV1& NativeDefinedAtomV1::attributes() const { + return _file->attribute(_ivarData->attributesOffset); +} + +inline llvm::ArrayRef NativeDefinedAtomV1::rawContent() const { + if ( this->contentType() == DefinedAtom::typeZeroFill ) + return llvm::ArrayRef(); + const uint8_t* p = _file->content(_ivarData->contentOffset, + _ivarData->contentSize); + return llvm::ArrayRef(p, _ivarData->contentSize); +} + +inline llvm::StringRef NativeDefinedAtomV1::customSectionName() const { + uint32_t offset = attributes().sectionNameOffset; + return _file->string(offset); +} + + + +// +// Instantiate an lld::File from the given native object file buffer +// +llvm::error_code parseNativeObjectFile(llvm::MemoryBuffer* mb, + llvm::StringRef path, File*& result) { + return NativeFile::make(mb, path, result); +} + + + +// +// Instantiate an lld::File from the given native object file path +// +llvm::error_code parseNativeObjectFileOrSTDIN(llvm::StringRef path, + File*& result) { + llvm::OwningPtr mb; + llvm::error_code ec = llvm::MemoryBuffer::getFileOrSTDIN(path, mb); + if ( ec ) + return ec; + + return parseNativeObjectFile(mb.get(), path, result); +} + + + +} // namespace lld diff --git a/lld/lib/Core/NativeWriter.cpp b/lld/lib/Core/NativeWriter.cpp new file mode 100644 index 000000000000..9769cd60eb4c --- /dev/null +++ b/lld/lib/Core/NativeWriter.cpp @@ -0,0 +1,219 @@ +//===- Core/NativeWriter.cpp - Creates a native object file ---------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include +#include + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/ArrayRef.h" + +#include "lld/Core/File.h" +#include "lld/Core/NativeWriter.h" + +#include "NativeFileFormat.h" + + +namespace lld { + + +/// +/// Class for writing native object files. +/// +class NativeWriter : public File::AtomHandler { +public: + /// construct writer for an lld::File object + NativeWriter(const lld::File& file) : _file(file) { + // visit all atoms + _file.forEachAtom(*this); + // construct file header based on atom information accumulated + makeHeader(); + } + + // write the lld::File in native format to the specified stream + void write(llvm::raw_ostream& out) { + out.write((char*)_headerBuffer, _headerBufferSize); + out.write((char*)&_definedAtomIvars[0], + _definedAtomIvars.size()*sizeof(NativeDefinedAtomIvarsV1)); + out.write((char*)&_attributes[0], + _attributes.size()*sizeof(NativeAtomAttributesV1)); + out.write((char*)&_contentPool[0], _contentPool.size()); + out.write(&_stringPool[0], _stringPool.size()); + } + +private: + + // visitor routine called by forEachAtom() + virtual void doDefinedAtom(const class DefinedAtom& atom) { + NativeDefinedAtomIvarsV1 ivar; + ivar.nameOffset = getNameOffset(atom); + ivar.attributesOffset = getAttributeOffset(atom); + ivar.contentOffset = getContentOffset(atom); + ivar.contentSize = atom.size(); + _definedAtomIvars.push_back(ivar); + } + + // visitor routine called by forEachAtom() + virtual void doUndefinedAtom(const class UndefinedAtom& atom) { + } + + // visitor routine called by forEachAtom() + virtual void doFile(const class File &) { + } + + // fill out native file header and chunk directory + void makeHeader() { + _headerBufferSize = sizeof(NativeFileHeader) + 4*sizeof(NativeChunk); + _headerBuffer = reinterpret_cast + (operator new(_headerBufferSize, std::nothrow)); + memcpy(_headerBuffer->magic, NATIVE_FILE_HEADER_MAGIC, 16); + _headerBuffer->endian = NFH_LittleEndian; + _headerBuffer->architecture = 0; + _headerBuffer->fileSize = 0; + _headerBuffer->chunkCount = 4; + + // create chunk for atom ivar array + NativeChunk& ch0 = _headerBuffer->chunks[0]; + ch0.signature = NCS_DefinedAtomsV1; + ch0.fileOffset = _headerBufferSize; + ch0.fileSize = _definedAtomIvars.size()*sizeof(NativeDefinedAtomIvarsV1); + ch0.elementCount = _definedAtomIvars.size(); + // create chunk for attributes + NativeChunk& ch1 = _headerBuffer->chunks[1]; + ch1.signature = NCS_AttributesArrayV1; + ch1.fileOffset = ch0.fileOffset + ch0.fileSize; + ch1.fileSize = _attributes.size()*sizeof(NativeAtomAttributesV1); + ch1.elementCount = _attributes.size(); + // create chunk for content + NativeChunk& ch2 = _headerBuffer->chunks[2]; + ch2.signature = NCS_Content; + ch2.fileOffset = ch1.fileOffset + ch1.fileSize; + ch2.fileSize = _contentPool.size(); + ch2.elementCount = _contentPool.size(); + // create chunk for symbol strings + NativeChunk& ch3 = _headerBuffer->chunks[3]; + ch3.signature = NCS_Strings; + ch3.fileOffset = ch2.fileOffset + ch2.fileSize; + ch3.fileSize = _stringPool.size(); + ch3.elementCount = _stringPool.size(); + + _headerBuffer->fileSize = ch3.fileOffset + ch3.fileSize; + } + + + // append atom name to string pool and return offset + uint32_t getNameOffset(const class DefinedAtom& atom) { + return this->getNameOffset(atom.name()); + } + + // append atom name to string pool and return offset + uint32_t getNameOffset(llvm::StringRef name) { + uint32_t result = _stringPool.size(); + _stringPool.insert(_stringPool.end(), name.size()+1, 0); + strcpy(&_stringPool[result], name.data()); + return result; + } + + // append atom cotent to content pool and return offset + uint32_t getContentOffset(const class DefinedAtom& atom) { + if ( atom.contentType() == DefinedAtom::typeZeroFill ) + return 0; + uint32_t result = _contentPool.size(); + llvm::ArrayRef cont = atom.rawContent(); + _contentPool.insert(_contentPool.end(), cont.size(), 0); + memcpy(&_contentPool[result], cont.data(), cont.size()); + return result; + } + + // reuse existing attributes entry or create a new one and return offet + uint32_t getAttributeOffset(const class DefinedAtom& atom) { + NativeAtomAttributesV1 attrs; + computeAttributesV1(atom, attrs); + for(unsigned int i=0; i < _attributes.size(); ++i) { + if ( !memcmp(&_attributes[i], &attrs, sizeof(NativeAtomAttributesV1)) ) { + // found that this set of attributes already used, so re-use + return i * sizeof(NativeAtomAttributesV1); + } + } + // append new attribute set to end + uint32_t result = _attributes.size() * sizeof(NativeAtomAttributesV1); + _attributes.push_back(attrs); + return result; + } + + uint32_t sectionNameOffset(const class DefinedAtom& atom) { + // if section based on content, then no custom section name available + if ( atom.sectionChoice() == DefinedAtom::sectionBasedOnContent ) + return 0; + llvm::StringRef name = atom.customSectionName(); + assert( ! name.empty() ); + // look to see if this section name was used by another atom + for(NameToOffsetVector::iterator it=_sectionNames.begin(); + it != _sectionNames.end(); ++it) { + if ( name.equals(it->first) ) + return it->second; + } + // first use of this section name + uint32_t result = this->getNameOffset(name); + _sectionNames.push_back( + std::make_pair(name, result)); + return result; + } + + void computeAttributesV1(const class DefinedAtom& atom, + NativeAtomAttributesV1& attrs) { + 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(); + attrs.contentType = atom.contentType(); + attrs.sectionChoice = atom.sectionChoice(); + attrs.deadStrip = atom.deadStrip(); + attrs.permissions = atom.permissions(); + attrs.thumb = atom.isThumb(); + attrs.alias = atom.isAlias(); + } + + typedef std::vector > NameToOffsetVector; + + const lld::File& _file; + NativeFileHeader* _headerBuffer; + size_t _headerBufferSize; + std::vector _stringPool; + std::vector _contentPool; + std::vector _definedAtomIvars; + std::vector _attributes; + NameToOffsetVector _sectionNames; +}; + + + + + +/// writeNativeObjectFile - writes the lld::File object in native object +/// file format to the specified stream. +int writeNativeObjectFile(const lld::File &file, llvm::raw_ostream &out) { + NativeWriter writer(file); + writer.write(out); + return 0; +} + +/// writeNativeObjectFile - writes the lld::File object in native object +/// file format to the specified file path. +int writeNativeObjectFile(const lld::File& file, llvm::StringRef path) { + std::string errorInfo; + llvm::raw_fd_ostream out(path.data(), errorInfo, llvm::raw_fd_ostream::F_Binary); + if ( !errorInfo.empty() ) + return -1; + return writeNativeObjectFile(file, out); +} + +} // namespace lld diff --git a/lld/lib/Core/YamlWriter.cpp b/lld/lib/Core/YamlWriter.cpp index 3e30c0a42603..e5f0b1c44c37 100644 --- a/lld/lib/Core/YamlWriter.cpp +++ b/lld/lib/Core/YamlWriter.cpp @@ -195,7 +195,7 @@ private: bool _firstAtom; }; -void writeObjectText(File &file, llvm::raw_ostream &out) { +void writeObjectText(const File &file, llvm::raw_ostream &out) { Handler h(out); out << "---\n"; out << "atoms:\n"; diff --git a/lld/tools/lld-core/lld-core.cpp b/lld/tools/lld-core/lld-core.cpp index 3bd35c2538fd..77b674957815 100644 --- a/lld/tools/lld-core/lld-core.cpp +++ b/lld/tools/lld-core/lld-core.cpp @@ -14,15 +14,20 @@ #include "lld/Core/Resolver.h" #include "lld/Core/YamlReader.h" #include "lld/Core/YamlWriter.h" +#include "lld/Core/NativeReader.h" +#include "lld/Core/NativeWriter.h" #include "lld/Platform/Platform.h" #include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/DataTypes.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/system_error.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" #include @@ -207,6 +212,28 @@ int main(int argc, const char *argv[]) { // write new atom graph out as YAML doc std::string errorInfo; llvm::raw_fd_ostream out("-", errorInfo); - yaml::writeObjectText(outFile, out); +// yaml::writeObjectText(outFile, out); + + // make unique temp .o file to put generated object file + int fd; + llvm::SmallString<128> tempPath; + llvm::sys::fs::unique_file("temp%%%%%.o", fd, tempPath); + llvm::raw_fd_ostream binaryOut(fd, /*shouldClose=*/true); + + // write native file + writeNativeObjectFile(outFile, binaryOut); + binaryOut.close(); // manually close so that file can be read next + + // read native file + lld::File* natFile; + parseNativeObjectFileOrSTDIN(tempPath, natFile); + + // delete temp .o file + bool existed; + llvm::sys::fs::remove(tempPath.str(), existed); + + // write new atom graph out as YAML doc + yaml::writeObjectText(*natFile, out); + return 0; }