2017-07-21 05:42:04 +08:00
|
|
|
//===-- WindowsManifestMerger.cpp ------------------------------*- C++ -*-===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===---------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This file implements the .manifest merger class.
|
|
|
|
//
|
|
|
|
//===---------------------------------------------------------------------===//
|
|
|
|
|
2017-07-26 09:21:55 +08:00
|
|
|
#include "llvm/WindowsManifest/WindowsManifestMerger.h"
|
2017-07-21 05:42:04 +08:00
|
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
|
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
2017-07-26 08:25:12 +08:00
|
|
|
#define TO_XML_CHAR(X) reinterpret_cast<const unsigned char *>(X)
|
|
|
|
#define FROM_XML_CHAR(X) reinterpret_cast<const char *>(X)
|
|
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
|
2017-07-21 05:42:04 +08:00
|
|
|
namespace llvm {
|
|
|
|
|
|
|
|
char WindowsManifestError::ID = 0;
|
|
|
|
|
|
|
|
WindowsManifestError::WindowsManifestError(const Twine &Msg) : Msg(Msg.str()) {}
|
|
|
|
|
|
|
|
void WindowsManifestError::log(raw_ostream &OS) const { OS << Msg; }
|
|
|
|
|
2017-07-26 08:25:12 +08:00
|
|
|
#if LLVM_LIBXML2_ENABLED
|
|
|
|
static bool xmlStringsEqual(const unsigned char *A, const unsigned char *B) {
|
|
|
|
return strcmp(FROM_XML_CHAR(A), FROM_XML_CHAR(B)) == 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
bool isMergeableElement(const unsigned char *ElementName) {
|
|
|
|
for (StringRef S : {"application", "assembly", "assemblyIdentity",
|
|
|
|
"compatibility", "noInherit", "requestedExecutionLevel",
|
|
|
|
"requestedPrivileges", "security", "trustInfo"}) {
|
|
|
|
if (S == FROM_XML_CHAR(ElementName))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
XMLNodeImpl getChildWithName(XMLNodeImpl Parent,
|
|
|
|
const unsigned char *ElementName) {
|
|
|
|
#if LLVM_LIBXML2_ENABLED
|
|
|
|
for (XMLNodeImpl Child = Parent->children; Child; Child = Child->next)
|
|
|
|
if (xmlStringsEqual(Child->name, ElementName)) {
|
|
|
|
return Child;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const unsigned char *getAttribute(XMLNodeImpl Node,
|
|
|
|
const unsigned char *AttributeName) {
|
|
|
|
#if LLVM_LIBXML2_ENABLED
|
|
|
|
for (xmlAttrPtr Attribute = Node->properties; Attribute != nullptr;
|
|
|
|
Attribute = Attribute->next) {
|
|
|
|
if (xmlStringsEqual(Attribute->name, AttributeName))
|
|
|
|
return Attribute->children->content;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
Error mergeAttributes(XMLNodeImpl OriginalNode, XMLNodeImpl AdditionalNode) {
|
|
|
|
#if LLVM_LIBXML2_ENABLED
|
|
|
|
for (xmlAttrPtr Attribute = AdditionalNode->properties; Attribute != nullptr;
|
|
|
|
Attribute = Attribute->next) {
|
|
|
|
if (const unsigned char *OriginalValue =
|
|
|
|
getAttribute(OriginalNode, Attribute->name)) {
|
|
|
|
// Attributes of the same name must also have the same value. Otherwise
|
|
|
|
// an error is thrown.
|
|
|
|
if (!xmlStringsEqual(OriginalValue, Attribute->children->content))
|
|
|
|
return make_error<WindowsManifestError>(
|
|
|
|
Twine("conflicting attributes for ") +
|
|
|
|
FROM_XML_CHAR(OriginalNode->name));
|
|
|
|
} else {
|
|
|
|
char *NameCopy = strdup(FROM_XML_CHAR(Attribute->name));
|
|
|
|
char *ContentCopy = strdup(FROM_XML_CHAR(Attribute->children->content));
|
|
|
|
xmlNewProp(OriginalNode, TO_XML_CHAR(NameCopy), TO_XML_CHAR(ContentCopy));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
|
|
|
Error treeMerge(XMLNodeImpl OriginalRoot, XMLNodeImpl AdditionalRoot) {
|
|
|
|
#if LLVM_LIBXML2_ENABLED
|
|
|
|
XMLNodeImpl AdditionalFirstChild = AdditionalRoot->children;
|
2017-07-27 02:33:21 +08:00
|
|
|
xmlNode StoreNext;
|
2017-07-26 08:25:12 +08:00
|
|
|
for (XMLNodeImpl Child = AdditionalFirstChild; Child; Child = Child->next) {
|
|
|
|
XMLNodeImpl OriginalChildWithName;
|
|
|
|
if (!isMergeableElement(Child->name) ||
|
|
|
|
!(OriginalChildWithName =
|
|
|
|
getChildWithName(OriginalRoot, Child->name))) {
|
2017-07-27 02:33:21 +08:00
|
|
|
StoreNext.next = Child->next;
|
|
|
|
xmlUnlinkNode(Child);
|
|
|
|
if (!xmlAddChild(OriginalRoot, Child))
|
2017-07-26 08:25:12 +08:00
|
|
|
return make_error<WindowsManifestError>(Twine("could not merge ") +
|
2017-07-27 02:33:21 +08:00
|
|
|
FROM_XML_CHAR(Child->name));
|
|
|
|
Child = &StoreNext;
|
2017-07-26 08:25:12 +08:00
|
|
|
} else if (auto E = treeMerge(OriginalChildWithName, Child)) {
|
|
|
|
return E;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (auto E = mergeAttributes(OriginalRoot, AdditionalRoot))
|
|
|
|
return E;
|
|
|
|
#endif
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
|
|
|
void stripCommentsAndText(XMLNodeImpl Root) {
|
|
|
|
#if LLVM_LIBXML2_ENABLED
|
|
|
|
xmlNode StoreNext;
|
|
|
|
for (XMLNodeImpl Child = Root->children; Child; Child = Child->next) {
|
|
|
|
if (!xmlStringsEqual(Child->name, TO_XML_CHAR("text")) &&
|
|
|
|
!xmlStringsEqual(Child->name, TO_XML_CHAR("comment"))) {
|
|
|
|
stripCommentsAndText(Child);
|
|
|
|
} else {
|
|
|
|
StoreNext.next = Child->next;
|
|
|
|
XMLNodeImpl Remove = Child;
|
|
|
|
Child = &StoreNext;
|
|
|
|
xmlUnlinkNode(Remove);
|
|
|
|
xmlFreeNode(Remove);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
WindowsManifestMerger::~WindowsManifestMerger() {
|
|
|
|
#if LLVM_LIBXML2_ENABLED
|
|
|
|
for (auto &Doc : MergedDocs)
|
|
|
|
xmlFreeDoc(Doc);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2017-07-21 05:42:04 +08:00
|
|
|
Error WindowsManifestMerger::merge(const MemoryBuffer &Manifest) {
|
|
|
|
#if LLVM_LIBXML2_ENABLED
|
2017-07-26 08:25:12 +08:00
|
|
|
if (Manifest.getBufferSize() == 0)
|
|
|
|
return make_error<WindowsManifestError>(
|
|
|
|
"attempted to merge empty manifest");
|
2017-07-21 05:42:04 +08:00
|
|
|
xmlSetGenericErrorFunc((void *)this, WindowsManifestMerger::errorCallback);
|
|
|
|
XMLDocumentImpl ManifestXML =
|
|
|
|
xmlReadMemory(Manifest.getBufferStart(), Manifest.getBufferSize(),
|
2017-07-26 08:25:12 +08:00
|
|
|
"manifest.xml", nullptr, XML_PARSE_NOBLANKS);
|
2017-07-21 05:42:04 +08:00
|
|
|
xmlSetGenericErrorFunc(nullptr, nullptr);
|
|
|
|
if (auto E = getParseError())
|
|
|
|
return E;
|
2017-07-26 08:25:12 +08:00
|
|
|
XMLNodeImpl AdditionalRoot = xmlDocGetRootElement(ManifestXML);
|
|
|
|
stripCommentsAndText(AdditionalRoot);
|
|
|
|
if (CombinedDoc == nullptr) {
|
|
|
|
CombinedDoc = ManifestXML;
|
|
|
|
} else {
|
|
|
|
XMLNodeImpl CombinedRoot = xmlDocGetRootElement(CombinedDoc);
|
|
|
|
if (xmlStringsEqual(CombinedRoot->name, AdditionalRoot->name) &&
|
|
|
|
isMergeableElement(AdditionalRoot->name)) {
|
|
|
|
if (auto E = treeMerge(CombinedRoot, AdditionalRoot)) {
|
|
|
|
return E;
|
|
|
|
}
|
|
|
|
} else {
|
2017-07-27 02:33:21 +08:00
|
|
|
return make_error<WindowsManifestError>("multiple root nodes");
|
2017-07-26 08:25:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
MergedDocs.push_back(ManifestXML);
|
2017-07-21 05:42:04 +08:00
|
|
|
#endif
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<MemoryBuffer> WindowsManifestMerger::getMergedManifest() {
|
|
|
|
#if LLVM_LIBXML2_ENABLED
|
|
|
|
unsigned char *XmlBuff;
|
|
|
|
int BufferSize = 0;
|
2017-07-26 08:25:12 +08:00
|
|
|
if (CombinedDoc) {
|
2017-07-21 05:42:04 +08:00
|
|
|
std::unique_ptr<xmlDoc> OutputDoc(xmlNewDoc((const unsigned char *)"1.0"));
|
2017-07-26 08:25:12 +08:00
|
|
|
xmlDocSetRootElement(OutputDoc.get(), xmlDocGetRootElement(CombinedDoc));
|
|
|
|
xmlKeepBlanksDefault(0);
|
|
|
|
xmlDocDumpFormatMemory(OutputDoc.get(), &XmlBuff, &BufferSize, 1);
|
2017-07-21 05:42:04 +08:00
|
|
|
}
|
|
|
|
if (BufferSize == 0)
|
|
|
|
return nullptr;
|
|
|
|
return MemoryBuffer::getMemBuffer(
|
2017-07-26 08:25:12 +08:00
|
|
|
StringRef(FROM_XML_CHAR(XmlBuff), (size_t)BufferSize));
|
2017-07-21 05:42:04 +08:00
|
|
|
#else
|
|
|
|
return nullptr;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void WindowsManifestMerger::errorCallback(void *Ctx, const char *Format, ...) {
|
|
|
|
auto *Merger = (WindowsManifestMerger *)Ctx;
|
|
|
|
Merger->ParseErrorOccurred = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Error WindowsManifestMerger::getParseError() {
|
|
|
|
if (!ParseErrorOccurred)
|
|
|
|
return Error::success();
|
|
|
|
return make_error<WindowsManifestError>("invalid xml document");
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace llvm
|