
The difference between this and the previous patch is that now we use ELF physical addresses only for loading objects into the target (and the rest of the module load address logic still uses virtual addresses). Summary: When writing an object file over gdb-remote, use the vFlashErase, vFlashWrite, and vFlashDone commands if the write address is in a flash memory region. A bare metal target may have this kind of setup. - Update ObjectFileELF to set load addresses using physical addresses. A typical case may be a data section with a physical address in ROM and a virtual address in RAM, which should be loaded to the ROM address. - Add support for querying the target's qXfer:memory-map, which contains information about flash memory regions, leveraging MemoryRegionInfo data structures with minor modifications - Update ProcessGDBRemote to use vFlash commands in DoWriteMemory when the target address is in a flash region Original discussion at http://lists.llvm.org/pipermail/lldb-dev/2018-January/013093.html Reviewers: clayborg, labath Reviewed By: labath Subscribers: llvm-commits, arichardson, emaste, mgorny, lldb-commits Differential Revision: https://reviews.llvm.org/D42145 Patch by Owen Shaw <llvm@owenpshaw.net>. llvm-svn: 327970
544 lines
14 KiB
C++
544 lines
14 KiB
C++
//===-- XML.cpp -------------------------------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include <stdlib.h> /* atof */
|
|
|
|
#include "lldb/Host/StringConvert.h"
|
|
#include "lldb/Host/XML.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
#pragma mark-- XMLDocument
|
|
|
|
XMLDocument::XMLDocument() : m_document(nullptr) {}
|
|
|
|
XMLDocument::~XMLDocument() { Clear(); }
|
|
|
|
void XMLDocument::Clear() {
|
|
#if defined(LIBXML2_DEFINED)
|
|
if (m_document) {
|
|
xmlDocPtr doc = m_document;
|
|
m_document = nullptr;
|
|
xmlFreeDoc(doc);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool XMLDocument::IsValid() const { return m_document != nullptr; }
|
|
|
|
void XMLDocument::ErrorCallback(void *ctx, const char *format, ...) {
|
|
XMLDocument *document = (XMLDocument *)ctx;
|
|
va_list args;
|
|
va_start(args, format);
|
|
document->m_errors.PrintfVarArg(format, args);
|
|
document->m_errors.EOL();
|
|
va_end(args);
|
|
}
|
|
|
|
bool XMLDocument::ParseFile(const char *path) {
|
|
#if defined(LIBXML2_DEFINED)
|
|
Clear();
|
|
xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback);
|
|
m_document = xmlParseFile(path);
|
|
xmlSetGenericErrorFunc(nullptr, nullptr);
|
|
#endif
|
|
return IsValid();
|
|
}
|
|
|
|
bool XMLDocument::ParseMemory(const char *xml, size_t xml_length,
|
|
const char *url) {
|
|
#if defined(LIBXML2_DEFINED)
|
|
Clear();
|
|
xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback);
|
|
m_document = xmlReadMemory(xml, (int)xml_length, url, nullptr, 0);
|
|
xmlSetGenericErrorFunc(nullptr, nullptr);
|
|
#endif
|
|
return IsValid();
|
|
}
|
|
|
|
XMLNode XMLDocument::GetRootElement(const char *required_name) {
|
|
#if defined(LIBXML2_DEFINED)
|
|
if (IsValid()) {
|
|
XMLNode root_node(xmlDocGetRootElement(m_document));
|
|
if (required_name) {
|
|
llvm::StringRef actual_name = root_node.GetName();
|
|
if (actual_name == required_name)
|
|
return root_node;
|
|
} else {
|
|
return root_node;
|
|
}
|
|
}
|
|
#endif
|
|
return XMLNode();
|
|
}
|
|
|
|
llvm::StringRef XMLDocument::GetErrors() const { return m_errors.GetString(); }
|
|
|
|
bool XMLDocument::XMLEnabled() {
|
|
#if defined(LIBXML2_DEFINED)
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
#pragma mark-- XMLNode
|
|
|
|
XMLNode::XMLNode() : m_node(nullptr) {}
|
|
|
|
XMLNode::XMLNode(XMLNodeImpl node) : m_node(node) {}
|
|
|
|
XMLNode::~XMLNode() {}
|
|
|
|
void XMLNode::Clear() { m_node = nullptr; }
|
|
|
|
XMLNode XMLNode::GetParent() const {
|
|
#if defined(LIBXML2_DEFINED)
|
|
if (IsValid())
|
|
return XMLNode(m_node->parent);
|
|
else
|
|
return XMLNode();
|
|
#else
|
|
return XMLNode();
|
|
#endif
|
|
}
|
|
|
|
XMLNode XMLNode::GetSibling() const {
|
|
#if defined(LIBXML2_DEFINED)
|
|
if (IsValid())
|
|
return XMLNode(m_node->next);
|
|
else
|
|
return XMLNode();
|
|
#else
|
|
return XMLNode();
|
|
#endif
|
|
}
|
|
|
|
XMLNode XMLNode::GetChild() const {
|
|
#if defined(LIBXML2_DEFINED)
|
|
|
|
if (IsValid())
|
|
return XMLNode(m_node->children);
|
|
else
|
|
return XMLNode();
|
|
#else
|
|
return XMLNode();
|
|
#endif
|
|
}
|
|
|
|
llvm::StringRef XMLNode::GetAttributeValue(const char *name,
|
|
const char *fail_value) const {
|
|
const char *attr_value = NULL;
|
|
#if defined(LIBXML2_DEFINED)
|
|
|
|
if (IsValid())
|
|
attr_value = (const char *)xmlGetProp(m_node, (const xmlChar *)name);
|
|
else
|
|
attr_value = fail_value;
|
|
#else
|
|
attr_value = fail_value;
|
|
#endif
|
|
if (attr_value)
|
|
return llvm::StringRef(attr_value);
|
|
else
|
|
return llvm::StringRef();
|
|
}
|
|
|
|
bool XMLNode::GetAttributeValueAsUnsigned(const char *name, uint64_t &value,
|
|
uint64_t fail_value, int base) const {
|
|
#if defined(LIBXML2_DEFINED)
|
|
llvm::StringRef str_value = GetAttributeValue(name, "");
|
|
#else
|
|
llvm::StringRef str_value;
|
|
#endif
|
|
bool success = false;
|
|
value = StringConvert::ToUInt64(str_value.data(), fail_value, base, &success);
|
|
return success;
|
|
}
|
|
|
|
void XMLNode::ForEachChildNode(NodeCallback const &callback) const {
|
|
#if defined(LIBXML2_DEFINED)
|
|
if (IsValid())
|
|
GetChild().ForEachSiblingNode(callback);
|
|
#endif
|
|
}
|
|
|
|
void XMLNode::ForEachChildElement(NodeCallback const &callback) const {
|
|
#if defined(LIBXML2_DEFINED)
|
|
XMLNode child = GetChild();
|
|
if (child)
|
|
child.ForEachSiblingElement(callback);
|
|
#endif
|
|
}
|
|
|
|
void XMLNode::ForEachChildElementWithName(const char *name,
|
|
NodeCallback const &callback) const {
|
|
#if defined(LIBXML2_DEFINED)
|
|
XMLNode child = GetChild();
|
|
if (child)
|
|
child.ForEachSiblingElementWithName(name, callback);
|
|
#endif
|
|
}
|
|
|
|
void XMLNode::ForEachAttribute(AttributeCallback const &callback) const {
|
|
#if defined(LIBXML2_DEFINED)
|
|
|
|
if (IsValid()) {
|
|
for (xmlAttrPtr attr = m_node->properties; attr != nullptr;
|
|
attr = attr->next) {
|
|
// check if name matches
|
|
if (attr->name) {
|
|
// check child is a text node
|
|
xmlNodePtr child = attr->children;
|
|
if (child->type == XML_TEXT_NODE) {
|
|
llvm::StringRef attr_value;
|
|
if (child->content)
|
|
attr_value = llvm::StringRef((const char *)child->content);
|
|
if (callback(llvm::StringRef((const char *)attr->name), attr_value) ==
|
|
false)
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void XMLNode::ForEachSiblingNode(NodeCallback const &callback) const {
|
|
#if defined(LIBXML2_DEFINED)
|
|
|
|
if (IsValid()) {
|
|
// iterate through all siblings
|
|
for (xmlNodePtr node = m_node; node; node = node->next) {
|
|
if (callback(XMLNode(node)) == false)
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void XMLNode::ForEachSiblingElement(NodeCallback const &callback) const {
|
|
#if defined(LIBXML2_DEFINED)
|
|
|
|
if (IsValid()) {
|
|
// iterate through all siblings
|
|
for (xmlNodePtr node = m_node; node; node = node->next) {
|
|
// we are looking for element nodes only
|
|
if (node->type != XML_ELEMENT_NODE)
|
|
continue;
|
|
|
|
if (callback(XMLNode(node)) == false)
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void XMLNode::ForEachSiblingElementWithName(
|
|
const char *name, NodeCallback const &callback) const {
|
|
#if defined(LIBXML2_DEFINED)
|
|
|
|
if (IsValid()) {
|
|
// iterate through all siblings
|
|
for (xmlNodePtr node = m_node; node; node = node->next) {
|
|
// we are looking for element nodes only
|
|
if (node->type != XML_ELEMENT_NODE)
|
|
continue;
|
|
|
|
// If name is nullptr, we take all nodes of type "t", else
|
|
// just the ones whose name matches
|
|
if (name) {
|
|
if (strcmp((const char *)node->name, name) != 0)
|
|
continue; // Name mismatch, ignore this one
|
|
} else {
|
|
if (node->name)
|
|
continue; // nullptr name specified and this element has a name,
|
|
// ignore this one
|
|
}
|
|
|
|
if (callback(XMLNode(node)) == false)
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
llvm::StringRef XMLNode::GetName() const {
|
|
#if defined(LIBXML2_DEFINED)
|
|
if (IsValid()) {
|
|
if (m_node->name)
|
|
return llvm::StringRef((const char *)m_node->name);
|
|
}
|
|
#endif
|
|
return llvm::StringRef();
|
|
}
|
|
|
|
bool XMLNode::GetElementText(std::string &text) const {
|
|
text.clear();
|
|
#if defined(LIBXML2_DEFINED)
|
|
if (IsValid()) {
|
|
bool success = false;
|
|
if (m_node->type == XML_ELEMENT_NODE) {
|
|
// check child is a text node
|
|
for (xmlNodePtr node = m_node->children; node != nullptr;
|
|
node = node->next) {
|
|
if (node->type == XML_TEXT_NODE) {
|
|
text.append((const char *)node->content);
|
|
success = true;
|
|
}
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
bool XMLNode::GetElementTextAsUnsigned(uint64_t &value, uint64_t fail_value,
|
|
int base) const {
|
|
bool success = false;
|
|
#if defined(LIBXML2_DEFINED)
|
|
if (IsValid()) {
|
|
std::string text;
|
|
if (GetElementText(text))
|
|
value = StringConvert::ToUInt64(text.c_str(), fail_value, base, &success);
|
|
}
|
|
#endif
|
|
if (!success)
|
|
value = fail_value;
|
|
return success;
|
|
}
|
|
|
|
bool XMLNode::GetElementTextAsFloat(double &value, double fail_value) const {
|
|
bool success = false;
|
|
#if defined(LIBXML2_DEFINED)
|
|
if (IsValid()) {
|
|
std::string text;
|
|
if (GetElementText(text)) {
|
|
value = atof(text.c_str());
|
|
success = true;
|
|
}
|
|
}
|
|
#endif
|
|
if (!success)
|
|
value = fail_value;
|
|
return success;
|
|
}
|
|
|
|
bool XMLNode::NameIs(const char *name) const {
|
|
#if defined(LIBXML2_DEFINED)
|
|
|
|
if (IsValid()) {
|
|
// In case we are looking for a nullptr name or an exact pointer match
|
|
if (m_node->name == (const xmlChar *)name)
|
|
return true;
|
|
if (m_node->name)
|
|
return strcmp((const char *)m_node->name, name) == 0;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
XMLNode XMLNode::FindFirstChildElementWithName(const char *name) const {
|
|
XMLNode result_node;
|
|
|
|
#if defined(LIBXML2_DEFINED)
|
|
ForEachChildElementWithName(
|
|
name, [&result_node](const XMLNode &node) -> bool {
|
|
result_node = node;
|
|
// Stop iterating, we found the node we wanted
|
|
return false;
|
|
});
|
|
#endif
|
|
|
|
return result_node;
|
|
}
|
|
|
|
bool XMLNode::IsValid() const { return m_node != nullptr; }
|
|
|
|
bool XMLNode::IsElement() const {
|
|
#if defined(LIBXML2_DEFINED)
|
|
if (IsValid())
|
|
return m_node->type == XML_ELEMENT_NODE;
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
XMLNode XMLNode::GetElementForPath(const NamePath &path) {
|
|
#if defined(LIBXML2_DEFINED)
|
|
|
|
if (IsValid()) {
|
|
if (path.empty())
|
|
return *this;
|
|
else {
|
|
XMLNode node = FindFirstChildElementWithName(path[0].c_str());
|
|
const size_t n = path.size();
|
|
for (size_t i = 1; node && i < n; ++i)
|
|
node = node.FindFirstChildElementWithName(path[i].c_str());
|
|
return node;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return XMLNode();
|
|
}
|
|
|
|
#pragma mark-- ApplePropertyList
|
|
|
|
ApplePropertyList::ApplePropertyList() : m_xml_doc(), m_dict_node() {}
|
|
|
|
ApplePropertyList::ApplePropertyList(const char *path)
|
|
: m_xml_doc(), m_dict_node() {
|
|
ParseFile(path);
|
|
}
|
|
|
|
ApplePropertyList::~ApplePropertyList() {}
|
|
|
|
llvm::StringRef ApplePropertyList::GetErrors() const {
|
|
return m_xml_doc.GetErrors();
|
|
}
|
|
|
|
bool ApplePropertyList::ParseFile(const char *path) {
|
|
if (m_xml_doc.ParseFile(path)) {
|
|
XMLNode plist = m_xml_doc.GetRootElement("plist");
|
|
if (plist) {
|
|
plist.ForEachChildElementWithName("dict",
|
|
[this](const XMLNode &dict) -> bool {
|
|
this->m_dict_node = dict;
|
|
return false; // Stop iterating
|
|
});
|
|
return (bool)m_dict_node;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ApplePropertyList::IsValid() const { return (bool)m_dict_node; }
|
|
|
|
bool ApplePropertyList::GetValueAsString(const char *key,
|
|
std::string &value) const {
|
|
XMLNode value_node = GetValueNode(key);
|
|
if (value_node)
|
|
return ApplePropertyList::ExtractStringFromValueNode(value_node, value);
|
|
return false;
|
|
}
|
|
|
|
XMLNode ApplePropertyList::GetValueNode(const char *key) const {
|
|
XMLNode value_node;
|
|
#if defined(LIBXML2_DEFINED)
|
|
|
|
if (IsValid()) {
|
|
m_dict_node.ForEachChildElementWithName(
|
|
"key", [key, &value_node](const XMLNode &key_node) -> bool {
|
|
std::string key_name;
|
|
if (key_node.GetElementText(key_name)) {
|
|
if (key_name.compare(key) == 0) {
|
|
value_node = key_node.GetSibling();
|
|
while (value_node && !value_node.IsElement())
|
|
value_node = value_node.GetSibling();
|
|
return false; // Stop iterating
|
|
}
|
|
}
|
|
return true; // Keep iterating
|
|
});
|
|
}
|
|
#endif
|
|
return value_node;
|
|
}
|
|
|
|
bool ApplePropertyList::ExtractStringFromValueNode(const XMLNode &node,
|
|
std::string &value) {
|
|
value.clear();
|
|
#if defined(LIBXML2_DEFINED)
|
|
if (node.IsValid()) {
|
|
llvm::StringRef element_name = node.GetName();
|
|
if (element_name == "true" || element_name == "false") {
|
|
// The text value _is_ the element name itself...
|
|
value = element_name.str();
|
|
return true;
|
|
} else if (element_name == "dict" || element_name == "array")
|
|
return false; // dictionaries and arrays have no text value, so we fail
|
|
else
|
|
return node.GetElementText(value);
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
#if defined(LIBXML2_DEFINED)
|
|
|
|
namespace {
|
|
|
|
StructuredData::ObjectSP CreatePlistValue(XMLNode node) {
|
|
llvm::StringRef element_name = node.GetName();
|
|
if (element_name == "array") {
|
|
std::shared_ptr<StructuredData::Array> array_sp(
|
|
new StructuredData::Array());
|
|
node.ForEachChildElement([&array_sp](const XMLNode &node) -> bool {
|
|
array_sp->AddItem(CreatePlistValue(node));
|
|
return true; // Keep iterating through all child elements of the array
|
|
});
|
|
return array_sp;
|
|
} else if (element_name == "dict") {
|
|
XMLNode key_node;
|
|
std::shared_ptr<StructuredData::Dictionary> dict_sp(
|
|
new StructuredData::Dictionary());
|
|
node.ForEachChildElement(
|
|
[&key_node, &dict_sp](const XMLNode &node) -> bool {
|
|
if (node.NameIs("key")) {
|
|
// This is a "key" element node
|
|
key_node = node;
|
|
} else {
|
|
// This is a value node
|
|
if (key_node) {
|
|
std::string key_name;
|
|
key_node.GetElementText(key_name);
|
|
dict_sp->AddItem(key_name, CreatePlistValue(node));
|
|
key_node.Clear();
|
|
}
|
|
}
|
|
return true; // Keep iterating through all child elements of the
|
|
// dictionary
|
|
});
|
|
return dict_sp;
|
|
} else if (element_name == "real") {
|
|
double value = 0.0;
|
|
node.GetElementTextAsFloat(value);
|
|
return StructuredData::ObjectSP(new StructuredData::Float(value));
|
|
} else if (element_name == "integer") {
|
|
uint64_t value = 0;
|
|
node.GetElementTextAsUnsigned(value, 0, 0);
|
|
return StructuredData::ObjectSP(new StructuredData::Integer(value));
|
|
} else if ((element_name == "string") || (element_name == "data") ||
|
|
(element_name == "date")) {
|
|
std::string text;
|
|
node.GetElementText(text);
|
|
return StructuredData::ObjectSP(
|
|
new StructuredData::String(std::move(text)));
|
|
} else if (element_name == "true") {
|
|
return StructuredData::ObjectSP(new StructuredData::Boolean(true));
|
|
} else if (element_name == "false") {
|
|
return StructuredData::ObjectSP(new StructuredData::Boolean(false));
|
|
}
|
|
return StructuredData::ObjectSP(new StructuredData::Null());
|
|
}
|
|
}
|
|
#endif
|
|
|
|
StructuredData::ObjectSP ApplePropertyList::GetStructuredData() {
|
|
StructuredData::ObjectSP root_sp;
|
|
#if defined(LIBXML2_DEFINED)
|
|
if (IsValid()) {
|
|
return CreatePlistValue(m_dict_node);
|
|
}
|
|
#endif
|
|
return root_sp;
|
|
}
|