Path: blob/main/contrib/llvm-project/llvm/lib/WindowsManifest/WindowsManifestMerger.cpp
35263 views
//===-- WindowsManifestMerger.cpp ------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===---------------------------------------------------------------------===//7//8// This file implements the .manifest merger class.9//10//===---------------------------------------------------------------------===//1112#include "llvm/WindowsManifest/WindowsManifestMerger.h"13#include "llvm/Config/config.h"14#include "llvm/Support/MemoryBuffer.h"1516#if LLVM_ENABLE_LIBXML217#include <libxml/xmlreader.h>18#endif1920#define TO_XML_CHAR(X) reinterpret_cast<const unsigned char *>(X)21#define FROM_XML_CHAR(X) reinterpret_cast<const char *>(X)2223using namespace llvm;24using namespace windows_manifest;2526char WindowsManifestError::ID = 0;2728WindowsManifestError::WindowsManifestError(const Twine &Msg) : Msg(Msg.str()) {}2930void WindowsManifestError::log(raw_ostream &OS) const { OS << Msg; }3132class WindowsManifestMerger::WindowsManifestMergerImpl {33public:34~WindowsManifestMergerImpl();35Error merge(MemoryBufferRef Manifest);36std::unique_ptr<MemoryBuffer> getMergedManifest();3738private:39static void errorCallback(void *Ctx, const char *Format, ...);40Error getParseError();41#if LLVM_ENABLE_LIBXML242xmlDocPtr CombinedDoc = nullptr;43std::vector<xmlDocPtr> MergedDocs;4445bool Merged = false;46struct XmlDeleter {47void operator()(xmlChar *Ptr) { xmlFree(Ptr); }48void operator()(xmlDoc *Ptr) { xmlFreeDoc(Ptr); }49};50int BufferSize = 0;51std::unique_ptr<xmlChar, XmlDeleter> Buffer;52#endif53bool ParseErrorOccurred = false;54};5556#if LLVM_ENABLE_LIBXML25758static constexpr std::pair<StringLiteral, StringLiteral> MtNsHrefsPrefixes[] = {59{"urn:schemas-microsoft-com:asm.v1", "ms_asmv1"},60{"urn:schemas-microsoft-com:asm.v2", "ms_asmv2"},61{"urn:schemas-microsoft-com:asm.v3", "ms_asmv3"},62{"http://schemas.microsoft.com/SMI/2005/WindowsSettings",63"ms_windowsSettings"},64{"urn:schemas-microsoft-com:compatibility.v1", "ms_compatibilityv1"}};6566static bool xmlStringsEqual(const unsigned char *A, const unsigned char *B) {67// Handle null pointers. Comparison of 2 null pointers returns true because68// this indicates the prefix of a default namespace.69if (!A || !B)70return A == B;71return strcmp(FROM_XML_CHAR(A), FROM_XML_CHAR(B)) == 0;72}7374static bool isMergeableElement(const unsigned char *ElementName) {75for (StringRef S : {"application", "assembly", "assemblyIdentity",76"compatibility", "noInherit", "requestedExecutionLevel",77"requestedPrivileges", "security", "trustInfo"}) {78if (S == FROM_XML_CHAR(ElementName)) {79return true;80}81}82return false;83}8485static xmlNodePtr getChildWithName(xmlNodePtr Parent,86const unsigned char *ElementName) {87for (xmlNodePtr Child = Parent->children; Child; Child = Child->next) {88if (xmlStringsEqual(Child->name, ElementName)) {89return Child;90}91}92return nullptr;93}9495static xmlAttrPtr getAttribute(xmlNodePtr Node,96const unsigned char *AttributeName) {97for (xmlAttrPtr Attribute = Node->properties; Attribute != nullptr;98Attribute = Attribute->next) {99if (xmlStringsEqual(Attribute->name, AttributeName)) {100return Attribute;101}102}103return nullptr;104}105106// Check if namespace specified by HRef1 overrides that of HRef2.107static bool namespaceOverrides(const unsigned char *HRef1,108const unsigned char *HRef2) {109auto HRef1Position = llvm::find_if(110MtNsHrefsPrefixes, [=](const std::pair<StringRef, StringRef> &Element) {111return xmlStringsEqual(HRef1, TO_XML_CHAR(Element.first.data()));112});113auto HRef2Position = llvm::find_if(114MtNsHrefsPrefixes, [=](const std::pair<StringRef, StringRef> &Element) {115return xmlStringsEqual(HRef2, TO_XML_CHAR(Element.first.data()));116});117return HRef1Position < HRef2Position;118}119120// Search for prefix-defined namespace specified by HRef, starting on Node and121// continuing recursively upwards. Returns the namespace or nullptr if not122// found.123static xmlNsPtr search(const unsigned char *HRef, xmlNodePtr Node) {124for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) {125if (Def->prefix && xmlStringsEqual(Def->href, HRef)) {126return Def;127}128}129if (Node->parent) {130return search(HRef, Node->parent);131}132return nullptr;133}134135// Return the prefix that corresponds to the HRef. If HRef is not a recognized136// URI, then just return the HRef itself to use as the prefix.137static const unsigned char *getPrefixForHref(const unsigned char *HRef) {138for (auto &Ns : MtNsHrefsPrefixes) {139if (xmlStringsEqual(HRef, TO_XML_CHAR(Ns.first.data()))) {140return TO_XML_CHAR(Ns.second.data());141}142}143return HRef;144}145146// Search for prefix-defined namespace specified by HRef, starting on Node and147// continuing recursively upwards. If it is found, then return it. If it is148// not found, then prefix-define that namespace on the node and return a149// reference to it.150static Expected<xmlNsPtr> searchOrDefine(const unsigned char *HRef,151xmlNodePtr Node) {152if (xmlNsPtr Def = search(HRef, Node))153return Def;154if (xmlNsPtr Def = xmlNewNs(Node, HRef, getPrefixForHref(HRef)))155return Def;156return make_error<WindowsManifestError>("failed to create new namespace");157}158159// Set the namespace of OrigionalAttribute on OriginalNode to be that of160// AdditionalAttribute's.161static Error copyAttributeNamespace(xmlAttrPtr OriginalAttribute,162xmlNodePtr OriginalNode,163xmlAttrPtr AdditionalAttribute) {164165Expected<xmlNsPtr> ExplicitOrError =166searchOrDefine(AdditionalAttribute->ns->href, OriginalNode);167if (!ExplicitOrError)168return ExplicitOrError.takeError();169OriginalAttribute->ns = std::move(ExplicitOrError.get());170return Error::success();171}172173// Return the corresponding namespace definition for the prefix, defined on the174// given Node. Returns nullptr if there is no such definition.175static xmlNsPtr getNamespaceWithPrefix(const unsigned char *Prefix,176xmlNodePtr Node) {177if (Node == nullptr)178return nullptr;179for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) {180if (xmlStringsEqual(Def->prefix, Prefix)) {181return Def;182}183}184return nullptr;185}186187// Search for the closest inheritable default namespace, starting on (and188// including) the Node and traveling upwards through parent nodes. Returns189// nullptr if there are no inheritable default namespaces.190static xmlNsPtr getClosestDefault(xmlNodePtr Node) {191if (xmlNsPtr Ret = getNamespaceWithPrefix(nullptr, Node))192return Ret;193if (Node->parent == nullptr)194return nullptr;195return getClosestDefault(Node->parent);196}197198// Merge the attributes of AdditionalNode into OriginalNode. If attributes199// with identical types are present, they are not duplicated but rather if200// their values are not consistent and error is thrown. In addition, the201// higher priority namespace is used for each attribute, EXCEPT in the case202// of merging two default namespaces and the lower priority namespace203// definition occurs closer than the higher priority one.204static Error mergeAttributes(xmlNodePtr OriginalNode,205xmlNodePtr AdditionalNode) {206xmlNsPtr ClosestDefault = getClosestDefault(OriginalNode);207for (xmlAttrPtr Attribute = AdditionalNode->properties; Attribute;208Attribute = Attribute->next) {209if (xmlAttrPtr OriginalAttribute =210getAttribute(OriginalNode, Attribute->name)) {211if (!xmlStringsEqual(OriginalAttribute->children->content,212Attribute->children->content)) {213return make_error<WindowsManifestError>(214Twine("conflicting attributes for ") +215FROM_XML_CHAR(OriginalNode->name));216}217if (!Attribute->ns) {218continue;219}220if (!OriginalAttribute->ns) {221if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode,222Attribute)) {223return E;224}225continue;226}227if (namespaceOverrides(OriginalAttribute->ns->href,228Attribute->ns->href)) {229// In this case, the original attribute has a higher priority namespace230// than the incomiing attribute, however the namespace definition of231// the lower priority namespace occurs first traveling upwards in the232// tree. Therefore the lower priority namespace is applied.233if (!OriginalAttribute->ns->prefix && !Attribute->ns->prefix &&234ClosestDefault &&235xmlStringsEqual(Attribute->ns->href, ClosestDefault->href)) {236if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode,237Attribute)) {238return E;239}240continue;241}242continue;243// This covers the case where the incoming attribute has the higher244// priority. The higher priority namespace is applied in all cases245// EXCEPT when both of the namespaces are default inherited, and the246// closest inherited default is the lower priority one.247}248if (Attribute->ns->prefix || OriginalAttribute->ns->prefix ||249(ClosestDefault && !xmlStringsEqual(OriginalAttribute->ns->href,250ClosestDefault->href))) {251if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode,252Attribute)) {253return E;254}255continue;256}257continue;258}259// If the incoming attribute is not already found on the node, append it260// to the end of the properties list. Also explicitly apply its261// namespace as a prefix because it might be contained in a separate262// namespace that doesn't use the attribute.263xmlAttrPtr NewProp =264xmlNewProp(OriginalNode, Attribute->name, Attribute->children->content);265Expected<xmlNsPtr> ExplicitOrError =266searchOrDefine(Attribute->ns->href, OriginalNode);267if (!ExplicitOrError)268return ExplicitOrError.takeError();269NewProp->ns = std::move(ExplicitOrError.get());270}271return Error::success();272}273274// Given two nodes, return the one with the higher priority namespace.275static xmlNodePtr getDominantNode(xmlNodePtr Node1, xmlNodePtr Node2) {276277if (!Node1 || !Node1->ns)278return Node2;279if (!Node2 || !Node2->ns)280return Node1;281if (namespaceOverrides(Node1->ns->href, Node2->ns->href))282return Node1;283return Node2;284}285286// Checks if this Node's namespace is inherited or one it defined itself.287static bool hasInheritedNs(xmlNodePtr Node) {288return Node->ns && Node->ns != getNamespaceWithPrefix(Node->ns->prefix, Node);289}290291// Check if this Node's namespace is a default namespace that it inherited, as292// opposed to defining itself.293static bool hasInheritedDefaultNs(xmlNodePtr Node) {294return hasInheritedNs(Node) && Node->ns->prefix == nullptr;295}296297// Check if this Node's namespace is a default namespace it defined itself.298static bool hasDefinedDefaultNamespace(xmlNodePtr Node) {299return Node->ns && (Node->ns == getNamespaceWithPrefix(nullptr, Node));300}301302// For the given explicit prefix-definition of a namespace, travel downwards303// from a node recursively, and for every implicit, inherited default usage of304// that namespace replace it with that explicit prefix use. This is important305// when namespace overriding occurs when merging, so that elements unique to a306// namespace will still stay in that namespace.307static void explicateNamespace(xmlNsPtr PrefixDef, xmlNodePtr Node) {308// If a node as its own default namespace definition it clearly cannot have309// inherited the given default namespace, and neither will any of its310// children.311if (hasDefinedDefaultNamespace(Node))312return;313if (Node->ns && xmlStringsEqual(Node->ns->href, PrefixDef->href) &&314hasInheritedDefaultNs(Node))315Node->ns = PrefixDef;316for (xmlAttrPtr Attribute = Node->properties; Attribute;317Attribute = Attribute->next) {318if (Attribute->ns &&319xmlStringsEqual(Attribute->ns->href, PrefixDef->href)) {320Attribute->ns = PrefixDef;321}322}323for (xmlNodePtr Child = Node->children; Child; Child = Child->next) {324explicateNamespace(PrefixDef, Child);325}326}327328// Perform the namespace merge between two nodes.329static Error mergeNamespaces(xmlNodePtr OriginalNode,330xmlNodePtr AdditionalNode) {331// Save the original default namespace definition in case the incoming node332// overrides it.333const unsigned char *OriginalDefinedDefaultHref = nullptr;334if (xmlNsPtr OriginalDefinedDefaultNs =335getNamespaceWithPrefix(nullptr, OriginalNode)) {336OriginalDefinedDefaultHref = xmlStrdup(OriginalDefinedDefaultNs->href);337}338const unsigned char *NewDefinedDefaultHref = nullptr;339// Copy all namespace definitions. There can only be one default namespace340// definition per node, so the higher priority one takes precedence in the341// case of collision.342for (xmlNsPtr Def = AdditionalNode->nsDef; Def; Def = Def->next) {343if (xmlNsPtr OriginalNsDef =344getNamespaceWithPrefix(Def->prefix, OriginalNode)) {345if (!Def->prefix) {346if (namespaceOverrides(Def->href, OriginalNsDef->href)) {347NewDefinedDefaultHref = TO_XML_CHAR(strdup(FROM_XML_CHAR(Def->href)));348}349} else if (!xmlStringsEqual(OriginalNsDef->href, Def->href)) {350return make_error<WindowsManifestError>(351Twine("conflicting namespace definitions for ") +352FROM_XML_CHAR(Def->prefix));353}354} else {355xmlNsPtr NewDef = xmlCopyNamespace(Def);356NewDef->next = OriginalNode->nsDef;357OriginalNode->nsDef = NewDef;358}359}360361// Check whether the original node or the incoming node has the higher362// priority namespace. Depending on which one is dominant, we will have363// to recursively apply namespace changes down to children of the original364// node.365xmlNodePtr DominantNode = getDominantNode(OriginalNode, AdditionalNode);366xmlNodePtr NonDominantNode =367DominantNode == OriginalNode ? AdditionalNode : OriginalNode;368if (DominantNode == OriginalNode) {369if (OriginalDefinedDefaultHref) {370xmlNsPtr NonDominantDefinedDefault =371getNamespaceWithPrefix(nullptr, NonDominantNode);372// In this case, both the nodes defined a default namespace. However373// the lower priority node ended up having a higher priority default374// definition. This can occur if the higher priority node is prefix375// namespace defined. In this case we have to define an explicit376// prefix for the overridden definition and apply it to all children377// who relied on that definition.378if (NonDominantDefinedDefault &&379namespaceOverrides(NonDominantDefinedDefault->href,380OriginalDefinedDefaultHref)) {381Expected<xmlNsPtr> EC =382searchOrDefine(OriginalDefinedDefaultHref, DominantNode);383if (!EC) {384return EC.takeError();385}386xmlNsPtr PrefixDominantDefinedDefault = std::move(EC.get());387explicateNamespace(PrefixDominantDefinedDefault, DominantNode);388}389// In this case the node with a higher priority namespace did not have a390// default namespace definition, but the lower priority node did. In this391// case the new default namespace definition is copied. A side effect of392// this is that all children will suddenly find themselves in a different393// default namespace. To maintain correctness we need to ensure that all394// children now explicitly refer to the namespace that they had previously395// implicitly inherited.396} else if (getNamespaceWithPrefix(nullptr, NonDominantNode)) {397if (DominantNode->parent) {398xmlNsPtr ClosestDefault = getClosestDefault(DominantNode->parent);399Expected<xmlNsPtr> EC =400searchOrDefine(ClosestDefault->href, DominantNode);401if (!EC) {402return EC.takeError();403}404xmlNsPtr ExplicitDefault = std::move(EC.get());405explicateNamespace(ExplicitDefault, DominantNode);406}407}408} else {409// Covers case where the incoming node has a default namespace definition410// that overrides the original node's namespace. This always leads to411// the original node receiving that new default namespace.412if (hasDefinedDefaultNamespace(DominantNode)) {413NonDominantNode->ns = getNamespaceWithPrefix(nullptr, NonDominantNode);414} else {415// This covers the case where the incoming node either has a prefix416// namespace, or an inherited default namespace. Since the namespace417// may not yet be defined in the original tree we do a searchOrDefine418// for it, and then set the namespace equal to it.419Expected<xmlNsPtr> EC =420searchOrDefine(DominantNode->ns->href, NonDominantNode);421if (!EC) {422return EC.takeError();423}424xmlNsPtr Explicit = std::move(EC.get());425NonDominantNode->ns = Explicit;426}427// This covers cases where the incoming dominant node HAS a default428// namespace definition, but MIGHT NOT NECESSARILY be in that namespace.429if (xmlNsPtr DominantDefaultDefined =430getNamespaceWithPrefix(nullptr, DominantNode)) {431if (OriginalDefinedDefaultHref) {432if (namespaceOverrides(DominantDefaultDefined->href,433OriginalDefinedDefaultHref)) {434// In this case, the incoming node's default definition overrides435// the original default definition, all children who relied on that436// definition must be updated accordingly.437Expected<xmlNsPtr> EC =438searchOrDefine(OriginalDefinedDefaultHref, NonDominantNode);439if (!EC) {440return EC.takeError();441}442xmlNsPtr ExplicitDefault = std::move(EC.get());443explicateNamespace(ExplicitDefault, NonDominantNode);444}445} else {446// The original did not define a default definition, however the new447// default definition still applies to all children, so they must be448// updated to explicitly refer to the namespace they had previously449// been inheriting implicitly.450xmlNsPtr ClosestDefault = getClosestDefault(NonDominantNode);451Expected<xmlNsPtr> EC =452searchOrDefine(ClosestDefault->href, NonDominantNode);453if (!EC) {454return EC.takeError();455}456xmlNsPtr ExplicitDefault = std::move(EC.get());457explicateNamespace(ExplicitDefault, NonDominantNode);458}459}460}461if (NewDefinedDefaultHref) {462xmlNsPtr OriginalNsDef = getNamespaceWithPrefix(nullptr, OriginalNode);463xmlFree(const_cast<unsigned char *>(OriginalNsDef->href));464OriginalNsDef->href = NewDefinedDefaultHref;465}466xmlFree(const_cast<unsigned char *>(OriginalDefinedDefaultHref));467return Error::success();468}469470static bool isRecognizedNamespace(const unsigned char *NsHref) {471for (auto &Ns : MtNsHrefsPrefixes) {472if (xmlStringsEqual(NsHref, TO_XML_CHAR(Ns.first.data()))) {473return true;474}475}476return false;477}478479static bool hasRecognizedNamespace(xmlNodePtr Node) {480return isRecognizedNamespace(Node->ns->href);481}482483// Ensure a node's inherited namespace is actually defined in the tree it484// resides in.485static Error reconcileNamespaces(xmlNodePtr Node) {486if (!Node) {487return Error::success();488}489if (hasInheritedNs(Node)) {490Expected<xmlNsPtr> ExplicitOrError = searchOrDefine(Node->ns->href, Node);491if (!ExplicitOrError) {492return ExplicitOrError.takeError();493}494xmlNsPtr Explicit = std::move(ExplicitOrError.get());495Node->ns = Explicit;496}497for (xmlNodePtr Child = Node->children; Child; Child = Child->next) {498if (auto E = reconcileNamespaces(Child)) {499return E;500}501}502return Error::success();503}504505// Recursively merge the two given manifest trees, depending on which elements506// are of a mergeable type, and choose namespaces according to which have507// higher priority.508static Error treeMerge(xmlNodePtr OriginalRoot, xmlNodePtr AdditionalRoot) {509if (auto E = mergeAttributes(OriginalRoot, AdditionalRoot))510return E;511if (auto E = mergeNamespaces(OriginalRoot, AdditionalRoot))512return E;513xmlNodePtr AdditionalFirstChild = AdditionalRoot->children;514xmlNode StoreNext;515for (xmlNodePtr Child = AdditionalFirstChild; Child; Child = Child->next) {516xmlNodePtr OriginalChildWithName;517if (!isMergeableElement(Child->name) ||518!(OriginalChildWithName =519getChildWithName(OriginalRoot, Child->name)) ||520!hasRecognizedNamespace(Child)) {521StoreNext.next = Child->next;522xmlUnlinkNode(Child);523if (!xmlAddChild(OriginalRoot, Child)) {524return make_error<WindowsManifestError>(Twine("could not merge ") +525FROM_XML_CHAR(Child->name));526}527if (auto E = reconcileNamespaces(Child)) {528return E;529}530Child = &StoreNext;531} else if (auto E = treeMerge(OriginalChildWithName, Child)) {532return E;533}534}535return Error::success();536}537538static void stripComments(xmlNodePtr Root) {539xmlNode StoreNext;540for (xmlNodePtr Child = Root->children; Child; Child = Child->next) {541if (!xmlStringsEqual(Child->name, TO_XML_CHAR("comment"))) {542stripComments(Child);543continue;544}545StoreNext.next = Child->next;546xmlNodePtr Remove = Child;547Child = &StoreNext;548xmlUnlinkNode(Remove);549xmlFreeNode(Remove);550}551}552553// libxml2 assumes that attributes do not inherit default namespaces, whereas554// the original mt.exe does make this assumption. This function reconciles555// this by setting all attributes to have the inherited default namespace.556static void setAttributeNamespaces(xmlNodePtr Node) {557for (xmlAttrPtr Attribute = Node->properties; Attribute;558Attribute = Attribute->next) {559if (!Attribute->ns) {560Attribute->ns = getClosestDefault(Node);561}562}563for (xmlNodePtr Child = Node->children; Child; Child = Child->next) {564setAttributeNamespaces(Child);565}566}567568// The merging process may create too many prefix defined namespaces. This569// function removes all unnecessary ones from the tree.570static void checkAndStripPrefixes(xmlNodePtr Node,571std::vector<xmlNsPtr> &RequiredPrefixes) {572for (xmlNodePtr Child = Node->children; Child; Child = Child->next) {573checkAndStripPrefixes(Child, RequiredPrefixes);574}575if (Node->ns && Node->ns->prefix != nullptr) {576xmlNsPtr ClosestDefault = getClosestDefault(Node);577if (ClosestDefault &&578xmlStringsEqual(ClosestDefault->href, Node->ns->href)) {579Node->ns = ClosestDefault;580} else if (!llvm::is_contained(RequiredPrefixes, Node->ns)) {581RequiredPrefixes.push_back(Node->ns);582}583}584for (xmlAttrPtr Attribute = Node->properties; Attribute;585Attribute = Attribute->next) {586if (Attribute->ns && Attribute->ns->prefix != nullptr) {587xmlNsPtr ClosestDefault = getClosestDefault(Node);588if (ClosestDefault &&589xmlStringsEqual(ClosestDefault->href, Attribute->ns->href)) {590Attribute->ns = ClosestDefault;591} else if (!llvm::is_contained(RequiredPrefixes, Node->ns)) {592RequiredPrefixes.push_back(Attribute->ns);593}594}595}596xmlNsPtr Prev;597xmlNs Temp;598for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) {599if (!Def->prefix || llvm::is_contained(RequiredPrefixes, Def)) {600Prev = Def;601continue;602}603if (Def == Node->nsDef) {604Node->nsDef = Def->next;605} else {606Prev->next = Def->next;607}608Temp.next = Def->next;609xmlFreeNs(Def);610Def = &Temp;611}612}613614WindowsManifestMerger::WindowsManifestMergerImpl::~WindowsManifestMergerImpl() {615for (auto &Doc : MergedDocs)616xmlFreeDoc(Doc);617}618619Error WindowsManifestMerger::WindowsManifestMergerImpl::merge(620MemoryBufferRef Manifest) {621if (Merged)622return make_error<WindowsManifestError>(623"merge after getMergedManifest is not supported");624if (Manifest.getBufferSize() == 0)625return make_error<WindowsManifestError>(626"attempted to merge empty manifest");627xmlSetGenericErrorFunc((void *)this,628WindowsManifestMergerImpl::errorCallback);629xmlDocPtr ManifestXML = xmlReadMemory(630Manifest.getBufferStart(), Manifest.getBufferSize(), "manifest.xml",631nullptr, XML_PARSE_NOBLANKS | XML_PARSE_NODICT);632xmlSetGenericErrorFunc(nullptr, nullptr);633if (auto E = getParseError())634return E;635xmlNodePtr AdditionalRoot = xmlDocGetRootElement(ManifestXML);636stripComments(AdditionalRoot);637setAttributeNamespaces(AdditionalRoot);638if (CombinedDoc == nullptr) {639CombinedDoc = ManifestXML;640} else {641xmlNodePtr CombinedRoot = xmlDocGetRootElement(CombinedDoc);642if (!xmlStringsEqual(CombinedRoot->name, AdditionalRoot->name) ||643!isMergeableElement(AdditionalRoot->name) ||644!hasRecognizedNamespace(AdditionalRoot)) {645return make_error<WindowsManifestError>("multiple root nodes");646}647if (auto E = treeMerge(CombinedRoot, AdditionalRoot)) {648return E;649}650}651MergedDocs.push_back(ManifestXML);652return Error::success();653}654655std::unique_ptr<MemoryBuffer>656WindowsManifestMerger::WindowsManifestMergerImpl::getMergedManifest() {657if (!Merged) {658Merged = true;659660if (!CombinedDoc)661return nullptr;662663xmlNodePtr CombinedRoot = xmlDocGetRootElement(CombinedDoc);664std::vector<xmlNsPtr> RequiredPrefixes;665checkAndStripPrefixes(CombinedRoot, RequiredPrefixes);666std::unique_ptr<xmlDoc, XmlDeleter> OutputDoc(667xmlNewDoc((const unsigned char *)"1.0"));668xmlDocSetRootElement(OutputDoc.get(), CombinedRoot);669assert(nullptr == xmlDocGetRootElement(CombinedDoc));670671xmlChar *Buff = nullptr;672xmlDocDumpFormatMemoryEnc(OutputDoc.get(), &Buff, &BufferSize, "UTF-8", 1);673Buffer.reset(Buff);674}675676return BufferSize ? MemoryBuffer::getMemBufferCopy(StringRef(677FROM_XML_CHAR(Buffer.get()), (size_t)BufferSize))678: nullptr;679}680681bool windows_manifest::isAvailable() { return true; }682683#else684685WindowsManifestMerger::WindowsManifestMergerImpl::~WindowsManifestMergerImpl() {686}687688Error WindowsManifestMerger::WindowsManifestMergerImpl::merge(689MemoryBufferRef Manifest) {690return make_error<WindowsManifestError>("no libxml2");691}692693std::unique_ptr<MemoryBuffer>694WindowsManifestMerger::WindowsManifestMergerImpl::getMergedManifest() {695return nullptr;696}697698bool windows_manifest::isAvailable() { return false; }699700#endif701702WindowsManifestMerger::WindowsManifestMerger()703: Impl(std::make_unique<WindowsManifestMergerImpl>()) {}704705WindowsManifestMerger::~WindowsManifestMerger() = default;706707Error WindowsManifestMerger::merge(MemoryBufferRef Manifest) {708return Impl->merge(Manifest);709}710711std::unique_ptr<MemoryBuffer> WindowsManifestMerger::getMergedManifest() {712return Impl->getMergedManifest();713}714715void WindowsManifestMerger::WindowsManifestMergerImpl::errorCallback(716void *Ctx, const char *Format, ...) {717auto *Merger = (WindowsManifestMergerImpl *)Ctx;718Merger->ParseErrorOccurred = true;719}720721Error WindowsManifestMerger::WindowsManifestMergerImpl::getParseError() {722if (!ParseErrorOccurred)723return Error::success();724return make_error<WindowsManifestError>("invalid xml document");725}726727728