forked from OSchip/llvm-project
First chunk of native object file reader/writer. The lld-core tool now reads YAML file, links, writes that out as native object format, then reads that native file, then writes the YAML to stdout. Thus the test suite tests both YAML reading/writing as well as native object file reading/writing.
llvm-svn: 148256
This commit is contained in:
parent
81ce08c983
commit
55fd6beb00
|
@ -1,4 +1,4 @@
|
|||
//===- Core/Atom.h - The Fundimental Unit of Linking ----------------------===//
|
||||
//===- Core/Atom.h - A node in linking graph ------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//===- Core/DefinedAtom.h - The Fundimental Unit of Linking ---------------===//
|
||||
//===- Core/DefinedAtom.h - The Fundamental Unit of Linking ---------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace lld {
|
|||
class File {
|
||||
public:
|
||||
File(llvm::StringRef p) : _path(p) {}
|
||||
~File();
|
||||
virtual ~File();
|
||||
|
||||
class AtomHandler {
|
||||
public:
|
||||
|
|
|
@ -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 <vector>
|
||||
|
||||
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_
|
|
@ -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_
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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 <stdint.h>
|
||||
|
||||
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_
|
|
@ -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 <vector>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#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<int>(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<uint8_t> 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<const uint8_t*>(mb->getBufferStart());
|
||||
const NativeFileHeader* const header =
|
||||
reinterpret_cast<const NativeFileHeader*>(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<const DefinedAtom*>(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<uint8_t*>
|
||||
(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<const NativeDefinedAtomIvarsV1*>
|
||||
(base + chunk->fileOffset);
|
||||
for(uint8_t* s = atomsStart; s != atomsEnd; s += atomSize) {
|
||||
NativeDefinedAtomV1* atomAllocSpace =
|
||||
reinterpret_cast<NativeDefinedAtomV1*>(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<const char*>(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<const NativeAtomAttributesV1*>(_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<const NativeFileHeader*>(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<const uint8_t*>(_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<uint8_t> NativeDefinedAtomV1::rawContent() const {
|
||||
if ( this->contentType() == DefinedAtom::typeZeroFill )
|
||||
return llvm::ArrayRef<uint8_t>();
|
||||
const uint8_t* p = _file->content(_ivarData->contentOffset,
|
||||
_ivarData->contentSize);
|
||||
return llvm::ArrayRef<uint8_t>(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<llvm::MemoryBuffer> mb;
|
||||
llvm::error_code ec = llvm::MemoryBuffer::getFileOrSTDIN(path, mb);
|
||||
if ( ec )
|
||||
return ec;
|
||||
|
||||
return parseNativeObjectFile(mb.get(), path, result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace lld
|
|
@ -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 <vector>
|
||||
#include <map>
|
||||
|
||||
#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<NativeFileHeader*>
|
||||
(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<uint8_t> 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<llvm::StringRef, uint32_t>(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<std::pair<llvm::StringRef, uint32_t> > NameToOffsetVector;
|
||||
|
||||
const lld::File& _file;
|
||||
NativeFileHeader* _headerBuffer;
|
||||
size_t _headerBufferSize;
|
||||
std::vector<char> _stringPool;
|
||||
std::vector<uint8_t> _contentPool;
|
||||
std::vector<NativeDefinedAtomIvarsV1> _definedAtomIvars;
|
||||
std::vector<NativeAtomAttributesV1> _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
|
|
@ -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";
|
||||
|
|
|
@ -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 <vector>
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue