hanchenye-llvm-project/lld/lib/Core/YamlReader.cpp

771 lines
21 KiB
C++

//===- Core/YamlReader.cpp - Reads YAML -----------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "YamlKeyValues.h"
#include "lld/Core/YamlReader.h"
#include "lld/Core/Atom.h"
#include "lld/Core/File.h"
#include "lld/Core/Reference.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/system_error.h"
#include <vector>
namespace lld {
namespace yaml {
enum yaml_reader_errors {
success = 0,
unknown_keyword,
illegal_value
};
class reader_error_category : public llvm::_do_message {
public:
virtual const char* name() const {
return "lld.yaml.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_keyword:
return "Unknown keyword found in yaml file";
case illegal_value:
return "Bad value found in yaml file";
default:
llvm_unreachable("An enumerator of yaml_reader_errors does not have a "
"message defined.");
}
}
inline llvm::error_code make_error_code(yaml_reader_errors e) {
return llvm::error_code(static_cast<int>(e), reader_error_category_singleton);
}
class YAML {
public:
struct Entry {
Entry(const char *k, const char *v, std::vector<uint8_t>* vs,
int d, bool bd, bool bs)
: key(strdup(k))
, value(v ? strdup(v) : NULL)
, valueSequenceBytes(vs)
, depth(d)
, beginSequence(bs)
, beginDocument(bd) {}
const char * key;
const char * value;
std::vector<uint8_t>* valueSequenceBytes;
int depth;
bool beginSequence;
bool beginDocument;
};
static void parse(llvm::MemoryBuffer *mb, std::vector<const Entry *>&);
private:
enum State {
start,
inHeaderComment,
inTripleDash,
inTriplePeriod,
inDocument,
inKey,
inSpaceBeforeValue,
inValue,
inValueSequence,
inValueSequenceEnd
};
};
void YAML::parse(llvm::MemoryBuffer *mb, std::vector<const Entry *> &entries) {
State state = start;
char key[64];
char value[64];
char *p = NULL;
unsigned int lineNumber = 1;
int depth = 0;
bool nextKeyIsStartOfDocument = false;
bool nextKeyIsStartOfSequence = false;
std::vector<uint8_t>* sequenceBytes = NULL;
unsigned contentByte = 0;
for (const char *s = mb->getBufferStart(); s < mb->getBufferEnd(); ++s) {
char c = *s;
if (c == '\n')
++lineNumber;
switch (state) {
case start:
if (c == '#')
state = inHeaderComment;
else if (c == '-') {
p = &key[0];
*p++ = c;
state = inTripleDash;
}
break;
case inHeaderComment:
if (c == '\n') {
state = start;
}
break;
case inTripleDash:
if (c == '-') {
*p++ = c;
} else if (c == '\n') {
*p = '\0';
if (strcmp(key, "---") != 0)
return;
depth = 0;
state = inDocument;
nextKeyIsStartOfDocument = true;
} else {
return;
}
break;
case inTriplePeriod:
if (c == '.') {
*p++ = c;
} else if (c == '\n') {
*p = '\0';
if (strcmp(key, "...") != 0)
return;
depth = 0;
state = inHeaderComment;
} else {
return;
}
break;
case inDocument:
if (isalnum(c)) {
state = inKey;
p = &key[0];
*p++ = c;
} else if (c == '-') {
if (depth == 0) {
p = &key[0];
*p++ = c;
state = inTripleDash;
} else {
nextKeyIsStartOfSequence = true;
++depth;
}
} else if (c == ' ') {
++depth;
} else if (c == '.') {
p = &key[0];
*p++ = c;
state = inTriplePeriod;
} else if (c == '\n') {
// ignore empty lines
depth = 0;
} else if (c == '\t') {
llvm::report_fatal_error("TAB character found in yaml file");
} else {
return;
}
break;
case inKey:
if (isalnum(c) || (c == '-')) {
*p++ = c;
} else if (c == ':') {
*p = '\0';
state = inSpaceBeforeValue;
} else if (c == '\n') {
*p = '\0';
if (strcmp(key, "---") == 0)
state = inDocument;
else
return;
} else {
return;
}
break;
case inSpaceBeforeValue:
if (isalnum(c) || (c == '-') || (c == '_')) {
p = &value[0];
*p++ = c;
state = inValue;
} else if (c == '\n') {
entries.push_back(new Entry(key, "", NULL, depth,
nextKeyIsStartOfDocument,
nextKeyIsStartOfSequence));
nextKeyIsStartOfSequence = false;
nextKeyIsStartOfDocument = false;
state = inDocument;
depth = 0;
} else if (c == '[') {
contentByte = 0;
sequenceBytes = new std::vector<uint8_t>();
state = inValueSequence;
} else if (c == ' ') {
// eat space
} else if (c == '\t') {
llvm::report_fatal_error("TAB character found in yaml file");
} else {
return;
}
break;
case inValue:
if (isalnum(c) || (c == '-') || (c == '_')) {
*p++ = c;
} else if (c == '\n') {
*p = '\0';
entries.push_back(new Entry(key, value, NULL, depth,
nextKeyIsStartOfDocument,
nextKeyIsStartOfSequence));
nextKeyIsStartOfSequence = false;
nextKeyIsStartOfDocument = false;
state = inDocument;
depth = 0;
}
break;
case inValueSequence:
if (c == ']') {
sequenceBytes->push_back(contentByte);
state = inValueSequenceEnd;
}
else if (c == ' ') {
// eat white space
}
else if (c == ',') {
sequenceBytes->push_back(contentByte);
}
else if ( isdigit(c) ) {
contentByte = (contentByte << 4) | (c-'0');
}
else if ( ('a' <= tolower(c)) && (tolower(c) <= 'f') ) {
contentByte = (contentByte << 4) | (tolower(c)-'a'+10);
}
else {
llvm::report_fatal_error("non-hex digit found in content [ ]");
}
break;
case inValueSequenceEnd:
if (c == '\n') {
entries.push_back(new Entry(key, NULL, sequenceBytes, depth,
nextKeyIsStartOfDocument,
nextKeyIsStartOfSequence));
nextKeyIsStartOfSequence = false;
nextKeyIsStartOfDocument = false;
state = inDocument;
depth = 0;
}
break;
}
}
}
class YAMLFile : public File {
public:
YAMLFile()
: File("path")
, _lastRefIndex(0) {}
virtual bool forEachAtom(File::AtomHandler &) const;
virtual bool justInTimeforEachAtom(llvm::StringRef name,
File::AtomHandler &) const;
std::vector<DefinedAtom*> _definedAtoms;
std::vector<UndefinedAtom*> _undefinedAtoms;
std::vector<Reference> _references;
unsigned int _lastRefIndex;
};
bool YAMLFile::forEachAtom(File::AtomHandler &handler) const {
handler.doFile(*this);
for (std::vector<DefinedAtom *>::const_iterator it = _definedAtoms.begin();
it != _definedAtoms.end(); ++it) {
handler.doDefinedAtom(**it);
}
return true;
}
bool YAMLFile::justInTimeforEachAtom(llvm::StringRef name,
File::AtomHandler &handler) const {
return false;
}
class YAMLDefinedAtom : public DefinedAtom {
public:
YAMLDefinedAtom( uint32_t ord
, YAMLFile& file
, DefinedAtom::Scope scope
, DefinedAtom::ContentType type
, DefinedAtom::SectionChoice sectionChoice
, DefinedAtom::Interposable interpose
, DefinedAtom::Merge merge
, DefinedAtom::DeadStripKind deadStrip
, DefinedAtom::ContentPermissions perms
, bool internalName
, bool isThumb
, bool isAlias
, DefinedAtom::Alignment alignment
, const char* name
, const char* sectionName
, uint64_t size
, std::vector<uint8_t>* content)
: _file(file)
, _name(name)
, _sectionName(sectionName)
, _size(size)
, _ord(ord)
, _content(content)
, _alignment(alignment)
, _scope(scope)
, _type(type)
, _sectionChoice(sectionChoice)
, _interpose(interpose)
, _merge(merge)
, _deadStrip(deadStrip)
, _permissions(perms)
, _internalName(internalName)
, _isThumb(isThumb)
, _isAlias(isAlias)
, _refStartIndex(file._lastRefIndex)
, _refEndIndex(file._references.size()) {
file._lastRefIndex = _refEndIndex;
}
virtual const class File& file() const {
return _file;
}
virtual llvm::StringRef name() const {
return _name;
}
virtual bool internalName() const {
return _internalName;
}
virtual uint64_t size() const {
return (_content ? _content->size() : _size);
}
virtual DefinedAtom::Scope scope() const {
return _scope;
}
virtual DefinedAtom::Interposable interposable() const {
return _interpose;
}
virtual DefinedAtom::Merge merge() const {
return _merge;
}
virtual DefinedAtom::ContentType contentType() const {
return _type;
}
virtual DefinedAtom::Alignment alignment() const {
return _alignment;
}
virtual DefinedAtom::SectionChoice sectionChoice() const {
return _sectionChoice;
}
virtual llvm::StringRef customSectionName() const {
return _sectionName;
}
virtual DefinedAtom::DeadStripKind deadStrip() const {
return _deadStrip;
}
virtual DefinedAtom::ContentPermissions permissions() const {
return _permissions;
}
virtual bool isThumb() const {
return _isThumb;
}
virtual bool isAlias() const {
return _isAlias;
}
llvm::ArrayRef<uint8_t> rawContent() const {
if ( _content != NULL )
return llvm::ArrayRef<uint8_t>(*_content);
else
return llvm::ArrayRef<uint8_t>();
}
virtual uint64_t ordinal() const {
return _ord;
}
virtual Reference::iterator referencesBegin() const {
if (_file._references.size() < _refStartIndex)
return (Reference::iterator)&_file._references[_refStartIndex];
return 0;
}
virtual Reference::iterator referencesEnd() const {
if (_file._references.size() < _refEndIndex)
return (Reference::iterator)&_file._references[_refEndIndex];
return 0;
}
private:
YAMLFile& _file;
const char * _name;
const char * _sectionName;
unsigned long _size;
uint32_t _ord;
std::vector<uint8_t>* _content;
DefinedAtom::Alignment _alignment;
DefinedAtom::Scope _scope;
DefinedAtom::ContentType _type;
DefinedAtom::SectionChoice _sectionChoice;
DefinedAtom::Interposable _interpose;
DefinedAtom::Merge _merge;
DefinedAtom::DeadStripKind _deadStrip;
DefinedAtom::ContentPermissions _permissions;
bool _internalName;
bool _isThumb;
bool _isAlias;
unsigned int _refStartIndex;
unsigned int _refEndIndex;
};
class YAMLAtomState {
public:
YAMLAtomState();
void setName(const char *n);
void setAlign2(const char *n);
void setFixupKind(const char *n);
void setFixupOffset(const char *n);
void setFixupTarget(const char *n);
void addFixup(YAMLFile *f);
void makeAtom(YAMLFile&);
const char * _name;
const char * _sectionName;
unsigned long long _size;
uint32_t _ordinal;
std::vector<uint8_t>* _content;
DefinedAtom::Alignment _alignment;
Atom::Definition _definition;
DefinedAtom::Scope _scope;
DefinedAtom::ContentType _type;
DefinedAtom::SectionChoice _sectionChoice;
DefinedAtom::Interposable _interpose;
DefinedAtom::Merge _merge;
DefinedAtom::DeadStripKind _deadStrip;
DefinedAtom::ContentPermissions _permissions;
bool _internalName;
bool _isThumb;
bool _isAlias;
Reference _ref;
};
YAMLAtomState::YAMLAtomState()
: _name(NULL)
, _sectionName(NULL)
, _size(0)
, _ordinal(0)
, _content(NULL)
, _alignment(0, 0)
, _definition(KeyValues::definitionDefault)
, _scope(KeyValues::scopeDefault)
, _type(KeyValues::contentTypeDefault)
, _sectionChoice(KeyValues::sectionChoiceDefault)
, _interpose(KeyValues::interposableDefault)
, _merge(KeyValues::mergeDefault)
, _deadStrip(KeyValues::deadStripKindDefault)
, _permissions(KeyValues::permissionsDefault)
, _internalName(KeyValues::internalNameDefault)
, _isThumb(KeyValues::isThumbDefault)
, _isAlias(KeyValues::isAliasDefault)
{
_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,
_sectionChoice, _interpose, _merge, _deadStrip,
_permissions, _internalName, _isThumb, _isAlias,
_alignment, _name, _sectionName, _size, _content);
f._definedAtoms.push_back(a);
++_ordinal;
}
// reset state for next atom
_name = NULL;
_sectionName = NULL;
_size = 0;
_ordinal = 0;
_content = NULL;
_alignment.powerOf2= 0;
_alignment.modulus = 0;
_definition = KeyValues::definitionDefault;
_scope = KeyValues::scopeDefault;
_type = KeyValues::contentTypeDefault;
_sectionChoice = KeyValues::sectionChoiceDefault;
_interpose = KeyValues::interposableDefault;
_merge = KeyValues::mergeDefault;
_deadStrip = KeyValues::deadStripKindDefault;
_permissions = KeyValues::permissionsDefault;
_isThumb = KeyValues::isThumbDefault;
_isAlias = KeyValues::isAliasDefault;
_ref.target = NULL;
_ref.addend = 0;
_ref.offsetInAtom = 0;
_ref.kind = 0;
_ref.flags = 0;
}
void YAMLAtomState::setName(const char *n) {
_name = n;
}
void YAMLAtomState::setAlign2(const char *s) {
llvm::StringRef str(s);
uint32_t res;
str.getAsInteger(10, res);
_alignment.powerOf2 = static_cast<uint16_t>(res);
}
void YAMLAtomState::setFixupKind(const char *s) {
if (strcmp(s, "pcrel32") == 0)
_ref.kind = 1;
else if (strcmp(s, "call32") == 0)
_ref.kind = 2;
else
llvm::report_fatal_error("bad fixup kind value");
}
void YAMLAtomState::setFixupOffset(const char *s) {
if ((s[0] == '0') && (s[1] == 'x'))
llvm::StringRef(s).getAsInteger(16, _ref.offsetInAtom);
else
llvm::StringRef(s).getAsInteger(10, _ref.offsetInAtom);
}
void YAMLAtomState::setFixupTarget(const char *s) {
}
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;
}
llvm::error_code parseObjectText( llvm::MemoryBuffer *mb
, std::vector<File *> &result) {
std::vector<const YAML::Entry *> entries;
YAML::parse(mb, entries);
YAMLFile *file = NULL;
YAMLAtomState atomState;
bool inAtoms = false;
bool inFixups = false;
int depthForAtoms = -1;
int depthForFixups = -1;
int lastDepth = -1;
bool haveAtom = false;
bool haveFixup = false;
for (std::vector<const YAML::Entry *>::iterator it = entries.begin();
it != entries.end(); ++it) {
const YAML::Entry *entry = *it;
if (entry->beginDocument) {
if (file != NULL) {
if (haveAtom) {
atomState.makeAtom(*file);
haveAtom = false;
}
result.push_back(file);
}
file = new YAMLFile();
inAtoms = false;
depthForAtoms = -1;
}
if (lastDepth > entry->depth) {
// end of fixup sequence
if (haveFixup) {
atomState.addFixup(file);
haveFixup = false;
}
}
if (inAtoms && (depthForAtoms == -1)) {
depthForAtoms = entry->depth;
}
if (inFixups && (depthForFixups == -1)) {
depthForFixups = entry->depth;
}
if (strcmp(entry->key, "atoms") == 0) {
inAtoms = true;
}
if (inAtoms) {
if (depthForAtoms == entry->depth) {
if (entry->beginSequence) {
if (haveAtom) {
atomState.makeAtom(*file);
haveAtom = false;
}
}
if (strcmp(entry->key, KeyValues::nameKeyword) == 0) {
atomState.setName(entry->value);
haveAtom = true;
}
else if (strcmp(entry->key, KeyValues::internalNameKeyword) == 0) {
atomState._internalName = KeyValues::internalName(entry->value);
haveAtom = true;
}
else if (strcmp(entry->key, KeyValues::definitionKeyword) == 0) {
atomState._definition = KeyValues::definition(entry->value);
haveAtom = true;
}
else if (strcmp(entry->key, KeyValues::scopeKeyword) == 0) {
atomState._scope = KeyValues::scope(entry->value);
haveAtom = true;
}
else if (strcmp(entry->key, KeyValues::contentTypeKeyword) == 0) {
atomState._type = KeyValues::contentType(entry->value);
haveAtom = true;
}
else if (strcmp(entry->key, KeyValues::deadStripKindKeyword) == 0) {
atomState._deadStrip = KeyValues::deadStripKind(entry->value);
haveAtom = true;
}
else if (strcmp(entry->key, KeyValues::sectionChoiceKeyword) == 0) {
atomState._sectionChoice = KeyValues::sectionChoice(entry->value);
haveAtom = true;
}
else if (strcmp(entry->key, KeyValues::mergeKeyword) == 0) {
atomState._merge = KeyValues::merge(entry->value);
haveAtom = true;
}
else if (strcmp(entry->key, KeyValues::interposableKeyword) == 0) {
atomState._interpose = KeyValues::interposable(entry->value);
haveAtom = true;
}
else if (strcmp(entry->key, KeyValues::isThumbKeyword) == 0) {
atomState._isThumb = KeyValues::isThumb(entry->value);
haveAtom = true;
}
else if (strcmp(entry->key, KeyValues::isAliasKeyword) == 0) {
atomState._isAlias = KeyValues::isAlias(entry->value);
haveAtom = true;
}
else if (strcmp(entry->key, KeyValues::sectionNameKeyword) == 0) {
atomState._sectionName = entry->value;
haveAtom = true;
}
else if (strcmp(entry->key, KeyValues::sizeKeyword) == 0) {
llvm::StringRef val = entry->value;
if ( val.getAsInteger(0, atomState._size) )
return make_error_code(illegal_value);
haveAtom = true;
}
else if (strcmp(entry->key, KeyValues::contentKeyword) == 0) {
atomState._content = entry->valueSequenceBytes;
haveAtom = true;
}
else if (strcmp(entry->key, "align2") == 0) {
atomState.setAlign2(entry->value);
haveAtom = true;
}
else if (strcmp(entry->key, "fixups") == 0) {
inFixups = true;
}
else {
return make_error_code(unknown_keyword);
}
}
else if (depthForFixups == entry->depth) {
if (entry->beginSequence) {
if (haveFixup) {
atomState.addFixup(file);
haveFixup = false;
}
}
if (strcmp(entry->key, "kind") == 0) {
atomState.setFixupKind(entry->value);
haveFixup = true;
} else if (strcmp(entry->key, "offset") == 0) {
atomState.setFixupOffset(entry->value);
haveFixup = true;
} else if (strcmp(entry->key, "target") == 0) {
atomState.setFixupTarget(entry->value);
haveFixup = true;
}
}
}
lastDepth = entry->depth;
}
if (haveAtom) {
atomState.makeAtom(*file);
}
result.push_back(file);
return make_error_code(success);
}
//
// Fill in vector<File*> from path to input text file.
//
llvm::error_code parseObjectTextFileOrSTDIN(llvm::StringRef path
, std::vector<File*>& result) {
llvm::OwningPtr<llvm::MemoryBuffer> mb;
llvm::error_code ec = llvm::MemoryBuffer::getFileOrSTDIN(path, mb);
if ( ec )
return ec;
return parseObjectText(mb.get(), result);
}
} // namespace yaml
} // namespace lld