| File: | build/source/llvm/lib/WindowsManifest/WindowsManifestMerger.cpp |
| Warning: | line 607, column 18 Access to field 'next' results in a dereference of an undefined pointer value (loaded from variable 'Prev') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | //===-- WindowsManifestMerger.cpp ------------------------------*- C++ -*-===// | |||
| 2 | // | |||
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |||
| 4 | // See https://llvm.org/LICENSE.txt for license information. | |||
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |||
| 6 | // | |||
| 7 | //===---------------------------------------------------------------------===// | |||
| 8 | // | |||
| 9 | // This file implements the .manifest merger class. | |||
| 10 | // | |||
| 11 | //===---------------------------------------------------------------------===// | |||
| 12 | ||||
| 13 | #include "llvm/WindowsManifest/WindowsManifestMerger.h" | |||
| 14 | #include "llvm/Config/config.h" | |||
| 15 | #include "llvm/Support/MemoryBuffer.h" | |||
| 16 | ||||
| 17 | #if LLVM_ENABLE_LIBXML21 | |||
| 18 | #include <libxml/xmlreader.h> | |||
| 19 | #endif | |||
| 20 | ||||
| 21 | #define TO_XML_CHAR(X)reinterpret_cast<const unsigned char *>(X) reinterpret_cast<const unsigned char *>(X) | |||
| 22 | #define FROM_XML_CHAR(X)reinterpret_cast<const char *>(X) reinterpret_cast<const char *>(X) | |||
| 23 | ||||
| 24 | using namespace llvm; | |||
| 25 | using namespace windows_manifest; | |||
| 26 | ||||
| 27 | char WindowsManifestError::ID = 0; | |||
| 28 | ||||
| 29 | WindowsManifestError::WindowsManifestError(const Twine &Msg) : Msg(Msg.str()) {} | |||
| 30 | ||||
| 31 | void WindowsManifestError::log(raw_ostream &OS) const { OS << Msg; } | |||
| 32 | ||||
| 33 | class WindowsManifestMerger::WindowsManifestMergerImpl { | |||
| 34 | public: | |||
| 35 | ~WindowsManifestMergerImpl(); | |||
| 36 | Error merge(MemoryBufferRef Manifest); | |||
| 37 | std::unique_ptr<MemoryBuffer> getMergedManifest(); | |||
| 38 | ||||
| 39 | private: | |||
| 40 | static void errorCallback(void *Ctx, const char *Format, ...); | |||
| 41 | Error getParseError(); | |||
| 42 | #if LLVM_ENABLE_LIBXML21 | |||
| 43 | xmlDocPtr CombinedDoc = nullptr; | |||
| 44 | std::vector<xmlDocPtr> MergedDocs; | |||
| 45 | ||||
| 46 | bool Merged = false; | |||
| 47 | struct XmlDeleter { | |||
| 48 | void operator()(xmlChar *Ptr) { xmlFree(Ptr); } | |||
| 49 | void operator()(xmlDoc *Ptr) { xmlFreeDoc(Ptr); } | |||
| 50 | }; | |||
| 51 | int BufferSize = 0; | |||
| 52 | std::unique_ptr<xmlChar, XmlDeleter> Buffer; | |||
| 53 | #endif | |||
| 54 | bool ParseErrorOccurred = false; | |||
| 55 | }; | |||
| 56 | ||||
| 57 | #if LLVM_ENABLE_LIBXML21 | |||
| 58 | ||||
| 59 | static constexpr std::pair<StringLiteral, StringLiteral> MtNsHrefsPrefixes[] = { | |||
| 60 | {"urn:schemas-microsoft-com:asm.v1", "ms_asmv1"}, | |||
| 61 | {"urn:schemas-microsoft-com:asm.v2", "ms_asmv2"}, | |||
| 62 | {"urn:schemas-microsoft-com:asm.v3", "ms_asmv3"}, | |||
| 63 | {"http://schemas.microsoft.com/SMI/2005/WindowsSettings", | |||
| 64 | "ms_windowsSettings"}, | |||
| 65 | {"urn:schemas-microsoft-com:compatibility.v1", "ms_compatibilityv1"}}; | |||
| 66 | ||||
| 67 | static bool xmlStringsEqual(const unsigned char *A, const unsigned char *B) { | |||
| 68 | // Handle null pointers. Comparison of 2 null pointers returns true because | |||
| 69 | // this indicates the prefix of a default namespace. | |||
| 70 | if (!A || !B) | |||
| 71 | return A == B; | |||
| 72 | return strcmp(FROM_XML_CHAR(A)reinterpret_cast<const char *>(A), FROM_XML_CHAR(B)reinterpret_cast<const char *>(B)) == 0; | |||
| 73 | } | |||
| 74 | ||||
| 75 | static bool isMergeableElement(const unsigned char *ElementName) { | |||
| 76 | for (StringRef S : {"application", "assembly", "assemblyIdentity", | |||
| 77 | "compatibility", "noInherit", "requestedExecutionLevel", | |||
| 78 | "requestedPrivileges", "security", "trustInfo"}) { | |||
| 79 | if (S == FROM_XML_CHAR(ElementName)reinterpret_cast<const char *>(ElementName)) { | |||
| 80 | return true; | |||
| 81 | } | |||
| 82 | } | |||
| 83 | return false; | |||
| 84 | } | |||
| 85 | ||||
| 86 | static xmlNodePtr getChildWithName(xmlNodePtr Parent, | |||
| 87 | const unsigned char *ElementName) { | |||
| 88 | for (xmlNodePtr Child = Parent->children; Child; Child = Child->next) { | |||
| 89 | if (xmlStringsEqual(Child->name, ElementName)) { | |||
| 90 | return Child; | |||
| 91 | } | |||
| 92 | } | |||
| 93 | return nullptr; | |||
| 94 | } | |||
| 95 | ||||
| 96 | static xmlAttrPtr getAttribute(xmlNodePtr Node, | |||
| 97 | const unsigned char *AttributeName) { | |||
| 98 | for (xmlAttrPtr Attribute = Node->properties; Attribute != nullptr; | |||
| 99 | Attribute = Attribute->next) { | |||
| 100 | if (xmlStringsEqual(Attribute->name, AttributeName)) { | |||
| 101 | return Attribute; | |||
| 102 | } | |||
| 103 | } | |||
| 104 | return nullptr; | |||
| 105 | } | |||
| 106 | ||||
| 107 | // Check if namespace specified by HRef1 overrides that of HRef2. | |||
| 108 | static bool namespaceOverrides(const unsigned char *HRef1, | |||
| 109 | const unsigned char *HRef2) { | |||
| 110 | auto HRef1Position = llvm::find_if( | |||
| 111 | MtNsHrefsPrefixes, [=](const std::pair<StringRef, StringRef> &Element) { | |||
| 112 | return xmlStringsEqual(HRef1, TO_XML_CHAR(Element.first.data())reinterpret_cast<const unsigned char *>(Element.first.data ())); | |||
| 113 | }); | |||
| 114 | auto HRef2Position = llvm::find_if( | |||
| 115 | MtNsHrefsPrefixes, [=](const std::pair<StringRef, StringRef> &Element) { | |||
| 116 | return xmlStringsEqual(HRef2, TO_XML_CHAR(Element.first.data())reinterpret_cast<const unsigned char *>(Element.first.data ())); | |||
| 117 | }); | |||
| 118 | return HRef1Position < HRef2Position; | |||
| 119 | } | |||
| 120 | ||||
| 121 | // Search for prefix-defined namespace specified by HRef, starting on Node and | |||
| 122 | // continuing recursively upwards. Returns the namespace or nullptr if not | |||
| 123 | // found. | |||
| 124 | static xmlNsPtr search(const unsigned char *HRef, xmlNodePtr Node) { | |||
| 125 | for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) { | |||
| 126 | if (Def->prefix && xmlStringsEqual(Def->href, HRef)) { | |||
| 127 | return Def; | |||
| 128 | } | |||
| 129 | } | |||
| 130 | if (Node->parent) { | |||
| 131 | return search(HRef, Node->parent); | |||
| 132 | } | |||
| 133 | return nullptr; | |||
| 134 | } | |||
| 135 | ||||
| 136 | // Return the prefix that corresponds to the HRef. If HRef is not a recognized | |||
| 137 | // URI, then just return the HRef itself to use as the prefix. | |||
| 138 | static const unsigned char *getPrefixForHref(const unsigned char *HRef) { | |||
| 139 | for (auto &Ns : MtNsHrefsPrefixes) { | |||
| 140 | if (xmlStringsEqual(HRef, TO_XML_CHAR(Ns.first.data())reinterpret_cast<const unsigned char *>(Ns.first.data() ))) { | |||
| 141 | return TO_XML_CHAR(Ns.second.data())reinterpret_cast<const unsigned char *>(Ns.second.data( )); | |||
| 142 | } | |||
| 143 | } | |||
| 144 | return HRef; | |||
| 145 | } | |||
| 146 | ||||
| 147 | // Search for prefix-defined namespace specified by HRef, starting on Node and | |||
| 148 | // continuing recursively upwards. If it is found, then return it. If it is | |||
| 149 | // not found, then prefix-define that namespace on the node and return a | |||
| 150 | // reference to it. | |||
| 151 | static Expected<xmlNsPtr> searchOrDefine(const unsigned char *HRef, | |||
| 152 | xmlNodePtr Node) { | |||
| 153 | if (xmlNsPtr Def = search(HRef, Node)) | |||
| 154 | return Def; | |||
| 155 | if (xmlNsPtr Def = xmlNewNs(Node, HRef, getPrefixForHref(HRef))) | |||
| 156 | return Def; | |||
| 157 | return make_error<WindowsManifestError>("failed to create new namespace"); | |||
| 158 | } | |||
| 159 | ||||
| 160 | // Set the namespace of OrigionalAttribute on OriginalNode to be that of | |||
| 161 | // AdditionalAttribute's. | |||
| 162 | static Error copyAttributeNamespace(xmlAttrPtr OriginalAttribute, | |||
| 163 | xmlNodePtr OriginalNode, | |||
| 164 | xmlAttrPtr AdditionalAttribute) { | |||
| 165 | ||||
| 166 | Expected<xmlNsPtr> ExplicitOrError = | |||
| 167 | searchOrDefine(AdditionalAttribute->ns->href, OriginalNode); | |||
| 168 | if (!ExplicitOrError) | |||
| 169 | return ExplicitOrError.takeError(); | |||
| 170 | OriginalAttribute->ns = std::move(ExplicitOrError.get()); | |||
| 171 | return Error::success(); | |||
| 172 | } | |||
| 173 | ||||
| 174 | // Return the corresponding namespace definition for the prefix, defined on the | |||
| 175 | // given Node. Returns nullptr if there is no such definition. | |||
| 176 | static xmlNsPtr getNamespaceWithPrefix(const unsigned char *Prefix, | |||
| 177 | xmlNodePtr Node) { | |||
| 178 | if (Node == nullptr) | |||
| 179 | return nullptr; | |||
| 180 | for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) { | |||
| 181 | if (xmlStringsEqual(Def->prefix, Prefix)) { | |||
| 182 | return Def; | |||
| 183 | } | |||
| 184 | } | |||
| 185 | return nullptr; | |||
| 186 | } | |||
| 187 | ||||
| 188 | // Search for the closest inheritable default namespace, starting on (and | |||
| 189 | // including) the Node and traveling upwards through parent nodes. Returns | |||
| 190 | // nullptr if there are no inheritable default namespaces. | |||
| 191 | static xmlNsPtr getClosestDefault(xmlNodePtr Node) { | |||
| 192 | if (xmlNsPtr Ret = getNamespaceWithPrefix(nullptr, Node)) | |||
| 193 | return Ret; | |||
| 194 | if (Node->parent == nullptr) | |||
| 195 | return nullptr; | |||
| 196 | return getClosestDefault(Node->parent); | |||
| 197 | } | |||
| 198 | ||||
| 199 | // Merge the attributes of AdditionalNode into OriginalNode. If attributes | |||
| 200 | // with identical types are present, they are not duplicated but rather if | |||
| 201 | // their values are not consistent and error is thrown. In addition, the | |||
| 202 | // higher priority namespace is used for each attribute, EXCEPT in the case | |||
| 203 | // of merging two default namespaces and the lower priority namespace | |||
| 204 | // definition occurs closer than the higher priority one. | |||
| 205 | static Error mergeAttributes(xmlNodePtr OriginalNode, | |||
| 206 | xmlNodePtr AdditionalNode) { | |||
| 207 | xmlNsPtr ClosestDefault = getClosestDefault(OriginalNode); | |||
| 208 | for (xmlAttrPtr Attribute = AdditionalNode->properties; Attribute; | |||
| 209 | Attribute = Attribute->next) { | |||
| 210 | if (xmlAttrPtr OriginalAttribute = | |||
| 211 | getAttribute(OriginalNode, Attribute->name)) { | |||
| 212 | if (!xmlStringsEqual(OriginalAttribute->children->content, | |||
| 213 | Attribute->children->content)) { | |||
| 214 | return make_error<WindowsManifestError>( | |||
| 215 | Twine("conflicting attributes for ") + | |||
| 216 | FROM_XML_CHAR(OriginalNode->name)reinterpret_cast<const char *>(OriginalNode->name)); | |||
| 217 | } | |||
| 218 | if (!Attribute->ns) { | |||
| 219 | continue; | |||
| 220 | } | |||
| 221 | if (!OriginalAttribute->ns) { | |||
| 222 | if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode, | |||
| 223 | Attribute)) { | |||
| 224 | return E; | |||
| 225 | } | |||
| 226 | continue; | |||
| 227 | } | |||
| 228 | if (namespaceOverrides(OriginalAttribute->ns->href, | |||
| 229 | Attribute->ns->href)) { | |||
| 230 | // In this case, the original attribute has a higher priority namespace | |||
| 231 | // than the incomiing attribute, however the namespace definition of | |||
| 232 | // the lower priority namespace occurs first traveling upwards in the | |||
| 233 | // tree. Therefore the lower priority namespace is applied. | |||
| 234 | if (!OriginalAttribute->ns->prefix && !Attribute->ns->prefix && | |||
| 235 | ClosestDefault && | |||
| 236 | xmlStringsEqual(Attribute->ns->href, ClosestDefault->href)) { | |||
| 237 | if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode, | |||
| 238 | Attribute)) { | |||
| 239 | return E; | |||
| 240 | } | |||
| 241 | continue; | |||
| 242 | } | |||
| 243 | continue; | |||
| 244 | // This covers the case where the incoming attribute has the higher | |||
| 245 | // priority. The higher priority namespace is applied in all cases | |||
| 246 | // EXCEPT when both of the namespaces are default inherited, and the | |||
| 247 | // closest inherited default is the lower priority one. | |||
| 248 | } | |||
| 249 | if (Attribute->ns->prefix || OriginalAttribute->ns->prefix || | |||
| 250 | (ClosestDefault && !xmlStringsEqual(OriginalAttribute->ns->href, | |||
| 251 | ClosestDefault->href))) { | |||
| 252 | if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode, | |||
| 253 | Attribute)) { | |||
| 254 | return E; | |||
| 255 | } | |||
| 256 | continue; | |||
| 257 | } | |||
| 258 | continue; | |||
| 259 | } | |||
| 260 | // If the incoming attribute is not already found on the node, append it | |||
| 261 | // to the end of the properties list. Also explicitly apply its | |||
| 262 | // namespace as a prefix because it might be contained in a separate | |||
| 263 | // namespace that doesn't use the attribute. | |||
| 264 | xmlAttrPtr NewProp = | |||
| 265 | xmlNewProp(OriginalNode, Attribute->name, Attribute->children->content); | |||
| 266 | Expected<xmlNsPtr> ExplicitOrError = | |||
| 267 | searchOrDefine(Attribute->ns->href, OriginalNode); | |||
| 268 | if (!ExplicitOrError) | |||
| 269 | return ExplicitOrError.takeError(); | |||
| 270 | NewProp->ns = std::move(ExplicitOrError.get()); | |||
| 271 | } | |||
| 272 | return Error::success(); | |||
| 273 | } | |||
| 274 | ||||
| 275 | // Given two nodes, return the one with the higher priority namespace. | |||
| 276 | static xmlNodePtr getDominantNode(xmlNodePtr Node1, xmlNodePtr Node2) { | |||
| 277 | ||||
| 278 | if (!Node1 || !Node1->ns) | |||
| 279 | return Node2; | |||
| 280 | if (!Node2 || !Node2->ns) | |||
| 281 | return Node1; | |||
| 282 | if (namespaceOverrides(Node1->ns->href, Node2->ns->href)) | |||
| 283 | return Node1; | |||
| 284 | return Node2; | |||
| 285 | } | |||
| 286 | ||||
| 287 | // Checks if this Node's namespace is inherited or one it defined itself. | |||
| 288 | static bool hasInheritedNs(xmlNodePtr Node) { | |||
| 289 | return Node->ns && Node->ns != getNamespaceWithPrefix(Node->ns->prefix, Node); | |||
| 290 | } | |||
| 291 | ||||
| 292 | // Check if this Node's namespace is a default namespace that it inherited, as | |||
| 293 | // opposed to defining itself. | |||
| 294 | static bool hasInheritedDefaultNs(xmlNodePtr Node) { | |||
| 295 | return hasInheritedNs(Node) && Node->ns->prefix == nullptr; | |||
| 296 | } | |||
| 297 | ||||
| 298 | // Check if this Node's namespace is a default namespace it defined itself. | |||
| 299 | static bool hasDefinedDefaultNamespace(xmlNodePtr Node) { | |||
| 300 | return Node->ns && (Node->ns == getNamespaceWithPrefix(nullptr, Node)); | |||
| 301 | } | |||
| 302 | ||||
| 303 | // For the given explicit prefix-definition of a namespace, travel downwards | |||
| 304 | // from a node recursively, and for every implicit, inherited default usage of | |||
| 305 | // that namespace replace it with that explicit prefix use. This is important | |||
| 306 | // when namespace overriding occurs when merging, so that elements unique to a | |||
| 307 | // namespace will still stay in that namespace. | |||
| 308 | static void explicateNamespace(xmlNsPtr PrefixDef, xmlNodePtr Node) { | |||
| 309 | // If a node as its own default namespace definition it clearly cannot have | |||
| 310 | // inherited the given default namespace, and neither will any of its | |||
| 311 | // children. | |||
| 312 | if (hasDefinedDefaultNamespace(Node)) | |||
| 313 | return; | |||
| 314 | if (Node->ns && xmlStringsEqual(Node->ns->href, PrefixDef->href) && | |||
| 315 | hasInheritedDefaultNs(Node)) | |||
| 316 | Node->ns = PrefixDef; | |||
| 317 | for (xmlAttrPtr Attribute = Node->properties; Attribute; | |||
| 318 | Attribute = Attribute->next) { | |||
| 319 | if (Attribute->ns && | |||
| 320 | xmlStringsEqual(Attribute->ns->href, PrefixDef->href)) { | |||
| 321 | Attribute->ns = PrefixDef; | |||
| 322 | } | |||
| 323 | } | |||
| 324 | for (xmlNodePtr Child = Node->children; Child; Child = Child->next) { | |||
| 325 | explicateNamespace(PrefixDef, Child); | |||
| 326 | } | |||
| 327 | } | |||
| 328 | ||||
| 329 | // Perform the namespace merge between two nodes. | |||
| 330 | static Error mergeNamespaces(xmlNodePtr OriginalNode, | |||
| 331 | xmlNodePtr AdditionalNode) { | |||
| 332 | // Save the original default namespace definition in case the incoming node | |||
| 333 | // overrides it. | |||
| 334 | const unsigned char *OriginalDefinedDefaultHref = nullptr; | |||
| 335 | if (xmlNsPtr OriginalDefinedDefaultNs = | |||
| 336 | getNamespaceWithPrefix(nullptr, OriginalNode)) { | |||
| 337 | OriginalDefinedDefaultHref = xmlStrdup(OriginalDefinedDefaultNs->href); | |||
| 338 | } | |||
| 339 | const unsigned char *NewDefinedDefaultHref = nullptr; | |||
| 340 | // Copy all namespace definitions. There can only be one default namespace | |||
| 341 | // definition per node, so the higher priority one takes precedence in the | |||
| 342 | // case of collision. | |||
| 343 | for (xmlNsPtr Def = AdditionalNode->nsDef; Def; Def = Def->next) { | |||
| 344 | if (xmlNsPtr OriginalNsDef = | |||
| 345 | getNamespaceWithPrefix(Def->prefix, OriginalNode)) { | |||
| 346 | if (!Def->prefix) { | |||
| 347 | if (namespaceOverrides(Def->href, OriginalNsDef->href)) { | |||
| 348 | NewDefinedDefaultHref = TO_XML_CHAR(strdup(FROM_XML_CHAR(Def->href)))reinterpret_cast<const unsigned char *>(strdup(reinterpret_cast <const char *>(Def->href))); | |||
| 349 | } | |||
| 350 | } else if (!xmlStringsEqual(OriginalNsDef->href, Def->href)) { | |||
| 351 | return make_error<WindowsManifestError>( | |||
| 352 | Twine("conflicting namespace definitions for ") + | |||
| 353 | FROM_XML_CHAR(Def->prefix)reinterpret_cast<const char *>(Def->prefix)); | |||
| 354 | } | |||
| 355 | } else { | |||
| 356 | xmlNsPtr NewDef = xmlCopyNamespace(Def); | |||
| 357 | NewDef->next = OriginalNode->nsDef; | |||
| 358 | OriginalNode->nsDef = NewDef; | |||
| 359 | } | |||
| 360 | } | |||
| 361 | ||||
| 362 | // Check whether the original node or the incoming node has the higher | |||
| 363 | // priority namespace. Depending on which one is dominant, we will have | |||
| 364 | // to recursively apply namespace changes down to children of the original | |||
| 365 | // node. | |||
| 366 | xmlNodePtr DominantNode = getDominantNode(OriginalNode, AdditionalNode); | |||
| 367 | xmlNodePtr NonDominantNode = | |||
| 368 | DominantNode == OriginalNode ? AdditionalNode : OriginalNode; | |||
| 369 | if (DominantNode == OriginalNode) { | |||
| 370 | if (OriginalDefinedDefaultHref) { | |||
| 371 | xmlNsPtr NonDominantDefinedDefault = | |||
| 372 | getNamespaceWithPrefix(nullptr, NonDominantNode); | |||
| 373 | // In this case, both the nodes defined a default namespace. However | |||
| 374 | // the lower priority node ended up having a higher priority default | |||
| 375 | // definition. This can occur if the higher priority node is prefix | |||
| 376 | // namespace defined. In this case we have to define an explicit | |||
| 377 | // prefix for the overridden definition and apply it to all children | |||
| 378 | // who relied on that definition. | |||
| 379 | if (NonDominantDefinedDefault && | |||
| 380 | namespaceOverrides(NonDominantDefinedDefault->href, | |||
| 381 | OriginalDefinedDefaultHref)) { | |||
| 382 | Expected<xmlNsPtr> EC = | |||
| 383 | searchOrDefine(OriginalDefinedDefaultHref, DominantNode); | |||
| 384 | if (!EC) { | |||
| 385 | return EC.takeError(); | |||
| 386 | } | |||
| 387 | xmlNsPtr PrefixDominantDefinedDefault = std::move(EC.get()); | |||
| 388 | explicateNamespace(PrefixDominantDefinedDefault, DominantNode); | |||
| 389 | } | |||
| 390 | // In this case the node with a higher priority namespace did not have a | |||
| 391 | // default namespace definition, but the lower priority node did. In this | |||
| 392 | // case the new default namespace definition is copied. A side effect of | |||
| 393 | // this is that all children will suddenly find themselves in a different | |||
| 394 | // default namespace. To maintain correctness we need to ensure that all | |||
| 395 | // children now explicitly refer to the namespace that they had previously | |||
| 396 | // implicitly inherited. | |||
| 397 | } else if (getNamespaceWithPrefix(nullptr, NonDominantNode)) { | |||
| 398 | if (DominantNode->parent) { | |||
| 399 | xmlNsPtr ClosestDefault = getClosestDefault(DominantNode->parent); | |||
| 400 | Expected<xmlNsPtr> EC = | |||
| 401 | searchOrDefine(ClosestDefault->href, DominantNode); | |||
| 402 | if (!EC) { | |||
| 403 | return EC.takeError(); | |||
| 404 | } | |||
| 405 | xmlNsPtr ExplicitDefault = std::move(EC.get()); | |||
| 406 | explicateNamespace(ExplicitDefault, DominantNode); | |||
| 407 | } | |||
| 408 | } | |||
| 409 | } else { | |||
| 410 | // Covers case where the incoming node has a default namespace definition | |||
| 411 | // that overrides the original node's namespace. This always leads to | |||
| 412 | // the original node receiving that new default namespace. | |||
| 413 | if (hasDefinedDefaultNamespace(DominantNode)) { | |||
| 414 | NonDominantNode->ns = getNamespaceWithPrefix(nullptr, NonDominantNode); | |||
| 415 | } else { | |||
| 416 | // This covers the case where the incoming node either has a prefix | |||
| 417 | // namespace, or an inherited default namespace. Since the namespace | |||
| 418 | // may not yet be defined in the original tree we do a searchOrDefine | |||
| 419 | // for it, and then set the namespace equal to it. | |||
| 420 | Expected<xmlNsPtr> EC = | |||
| 421 | searchOrDefine(DominantNode->ns->href, NonDominantNode); | |||
| 422 | if (!EC) { | |||
| 423 | return EC.takeError(); | |||
| 424 | } | |||
| 425 | xmlNsPtr Explicit = std::move(EC.get()); | |||
| 426 | NonDominantNode->ns = Explicit; | |||
| 427 | } | |||
| 428 | // This covers cases where the incoming dominant node HAS a default | |||
| 429 | // namespace definition, but MIGHT NOT NECESSARILY be in that namespace. | |||
| 430 | if (xmlNsPtr DominantDefaultDefined = | |||
| 431 | getNamespaceWithPrefix(nullptr, DominantNode)) { | |||
| 432 | if (OriginalDefinedDefaultHref) { | |||
| 433 | if (namespaceOverrides(DominantDefaultDefined->href, | |||
| 434 | OriginalDefinedDefaultHref)) { | |||
| 435 | // In this case, the incoming node's default definition overrides | |||
| 436 | // the original default definition, all children who relied on that | |||
| 437 | // definition must be updated accordingly. | |||
| 438 | Expected<xmlNsPtr> EC = | |||
| 439 | searchOrDefine(OriginalDefinedDefaultHref, NonDominantNode); | |||
| 440 | if (!EC) { | |||
| 441 | return EC.takeError(); | |||
| 442 | } | |||
| 443 | xmlNsPtr ExplicitDefault = std::move(EC.get()); | |||
| 444 | explicateNamespace(ExplicitDefault, NonDominantNode); | |||
| 445 | } | |||
| 446 | } else { | |||
| 447 | // The original did not define a default definition, however the new | |||
| 448 | // default definition still applies to all children, so they must be | |||
| 449 | // updated to explicitly refer to the namespace they had previously | |||
| 450 | // been inheriting implicitly. | |||
| 451 | xmlNsPtr ClosestDefault = getClosestDefault(NonDominantNode); | |||
| 452 | Expected<xmlNsPtr> EC = | |||
| 453 | searchOrDefine(ClosestDefault->href, NonDominantNode); | |||
| 454 | if (!EC) { | |||
| 455 | return EC.takeError(); | |||
| 456 | } | |||
| 457 | xmlNsPtr ExplicitDefault = std::move(EC.get()); | |||
| 458 | explicateNamespace(ExplicitDefault, NonDominantNode); | |||
| 459 | } | |||
| 460 | } | |||
| 461 | } | |||
| 462 | if (NewDefinedDefaultHref) { | |||
| 463 | xmlNsPtr OriginalNsDef = getNamespaceWithPrefix(nullptr, OriginalNode); | |||
| 464 | xmlFree(const_cast<unsigned char *>(OriginalNsDef->href)); | |||
| 465 | OriginalNsDef->href = NewDefinedDefaultHref; | |||
| 466 | } | |||
| 467 | xmlFree(const_cast<unsigned char *>(OriginalDefinedDefaultHref)); | |||
| 468 | return Error::success(); | |||
| 469 | } | |||
| 470 | ||||
| 471 | static bool isRecognizedNamespace(const unsigned char *NsHref) { | |||
| 472 | for (auto &Ns : MtNsHrefsPrefixes) { | |||
| 473 | if (xmlStringsEqual(NsHref, TO_XML_CHAR(Ns.first.data())reinterpret_cast<const unsigned char *>(Ns.first.data() ))) { | |||
| 474 | return true; | |||
| 475 | } | |||
| 476 | } | |||
| 477 | return false; | |||
| 478 | } | |||
| 479 | ||||
| 480 | static bool hasRecognizedNamespace(xmlNodePtr Node) { | |||
| 481 | return isRecognizedNamespace(Node->ns->href); | |||
| 482 | } | |||
| 483 | ||||
| 484 | // Ensure a node's inherited namespace is actually defined in the tree it | |||
| 485 | // resides in. | |||
| 486 | static Error reconcileNamespaces(xmlNodePtr Node) { | |||
| 487 | if (!Node) { | |||
| 488 | return Error::success(); | |||
| 489 | } | |||
| 490 | if (hasInheritedNs(Node)) { | |||
| 491 | Expected<xmlNsPtr> ExplicitOrError = searchOrDefine(Node->ns->href, Node); | |||
| 492 | if (!ExplicitOrError) { | |||
| 493 | return ExplicitOrError.takeError(); | |||
| 494 | } | |||
| 495 | xmlNsPtr Explicit = std::move(ExplicitOrError.get()); | |||
| 496 | Node->ns = Explicit; | |||
| 497 | } | |||
| 498 | for (xmlNodePtr Child = Node->children; Child; Child = Child->next) { | |||
| 499 | if (auto E = reconcileNamespaces(Child)) { | |||
| 500 | return E; | |||
| 501 | } | |||
| 502 | } | |||
| 503 | return Error::success(); | |||
| 504 | } | |||
| 505 | ||||
| 506 | // Recursively merge the two given manifest trees, depending on which elements | |||
| 507 | // are of a mergeable type, and choose namespaces according to which have | |||
| 508 | // higher priority. | |||
| 509 | static Error treeMerge(xmlNodePtr OriginalRoot, xmlNodePtr AdditionalRoot) { | |||
| 510 | if (auto E = mergeAttributes(OriginalRoot, AdditionalRoot)) | |||
| 511 | return E; | |||
| 512 | if (auto E = mergeNamespaces(OriginalRoot, AdditionalRoot)) | |||
| 513 | return E; | |||
| 514 | xmlNodePtr AdditionalFirstChild = AdditionalRoot->children; | |||
| 515 | xmlNode StoreNext; | |||
| 516 | for (xmlNodePtr Child = AdditionalFirstChild; Child; Child = Child->next) { | |||
| 517 | xmlNodePtr OriginalChildWithName; | |||
| 518 | if (!isMergeableElement(Child->name) || | |||
| 519 | !(OriginalChildWithName = | |||
| 520 | getChildWithName(OriginalRoot, Child->name)) || | |||
| 521 | !hasRecognizedNamespace(Child)) { | |||
| 522 | StoreNext.next = Child->next; | |||
| 523 | xmlUnlinkNode(Child); | |||
| 524 | if (!xmlAddChild(OriginalRoot, Child)) { | |||
| 525 | return make_error<WindowsManifestError>(Twine("could not merge ") + | |||
| 526 | FROM_XML_CHAR(Child->name)reinterpret_cast<const char *>(Child->name)); | |||
| 527 | } | |||
| 528 | if (auto E = reconcileNamespaces(Child)) { | |||
| 529 | return E; | |||
| 530 | } | |||
| 531 | Child = &StoreNext; | |||
| 532 | } else if (auto E = treeMerge(OriginalChildWithName, Child)) { | |||
| 533 | return E; | |||
| 534 | } | |||
| 535 | } | |||
| 536 | return Error::success(); | |||
| 537 | } | |||
| 538 | ||||
| 539 | static void stripComments(xmlNodePtr Root) { | |||
| 540 | xmlNode StoreNext; | |||
| 541 | for (xmlNodePtr Child = Root->children; Child; Child = Child->next) { | |||
| 542 | if (!xmlStringsEqual(Child->name, TO_XML_CHAR("comment")reinterpret_cast<const unsigned char *>("comment"))) { | |||
| 543 | stripComments(Child); | |||
| 544 | continue; | |||
| 545 | } | |||
| 546 | StoreNext.next = Child->next; | |||
| 547 | xmlNodePtr Remove = Child; | |||
| 548 | Child = &StoreNext; | |||
| 549 | xmlUnlinkNode(Remove); | |||
| 550 | xmlFreeNode(Remove); | |||
| 551 | } | |||
| 552 | } | |||
| 553 | ||||
| 554 | // libxml2 assumes that attributes do not inherit default namespaces, whereas | |||
| 555 | // the original mt.exe does make this assumption. This function reconciles | |||
| 556 | // this by setting all attributes to have the inherited default namespace. | |||
| 557 | static void setAttributeNamespaces(xmlNodePtr Node) { | |||
| 558 | for (xmlAttrPtr Attribute = Node->properties; Attribute; | |||
| 559 | Attribute = Attribute->next) { | |||
| 560 | if (!Attribute->ns) { | |||
| 561 | Attribute->ns = getClosestDefault(Node); | |||
| 562 | } | |||
| 563 | } | |||
| 564 | for (xmlNodePtr Child = Node->children; Child; Child = Child->next) { | |||
| 565 | setAttributeNamespaces(Child); | |||
| 566 | } | |||
| 567 | } | |||
| 568 | ||||
| 569 | // The merging process may create too many prefix defined namespaces. This | |||
| 570 | // function removes all unnecessary ones from the tree. | |||
| 571 | static void checkAndStripPrefixes(xmlNodePtr Node, | |||
| 572 | std::vector<xmlNsPtr> &RequiredPrefixes) { | |||
| 573 | for (xmlNodePtr Child = Node->children; Child; Child = Child->next) { | |||
| 574 | checkAndStripPrefixes(Child, RequiredPrefixes); | |||
| 575 | } | |||
| 576 | if (Node->ns && Node->ns->prefix != nullptr) { | |||
| 577 | xmlNsPtr ClosestDefault = getClosestDefault(Node); | |||
| 578 | if (ClosestDefault && | |||
| 579 | xmlStringsEqual(ClosestDefault->href, Node->ns->href)) { | |||
| 580 | Node->ns = ClosestDefault; | |||
| 581 | } else if (!llvm::is_contained(RequiredPrefixes, Node->ns)) { | |||
| 582 | RequiredPrefixes.push_back(Node->ns); | |||
| 583 | } | |||
| 584 | } | |||
| 585 | for (xmlAttrPtr Attribute = Node->properties; Attribute; | |||
| 586 | Attribute = Attribute->next) { | |||
| 587 | if (Attribute->ns && Attribute->ns->prefix != nullptr) { | |||
| 588 | xmlNsPtr ClosestDefault = getClosestDefault(Node); | |||
| 589 | if (ClosestDefault && | |||
| 590 | xmlStringsEqual(ClosestDefault->href, Attribute->ns->href)) { | |||
| 591 | Attribute->ns = ClosestDefault; | |||
| 592 | } else if (!llvm::is_contained(RequiredPrefixes, Node->ns)) { | |||
| 593 | RequiredPrefixes.push_back(Attribute->ns); | |||
| 594 | } | |||
| 595 | } | |||
| 596 | } | |||
| 597 | xmlNsPtr Prev; | |||
| 598 | xmlNs Temp; | |||
| 599 | for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) { | |||
| 600 | if (!Def->prefix || llvm::is_contained(RequiredPrefixes, Def)) { | |||
| 601 | Prev = Def; | |||
| 602 | continue; | |||
| 603 | } | |||
| 604 | if (Def == Node->nsDef) { | |||
| 605 | Node->nsDef = Def->next; | |||
| 606 | } else { | |||
| 607 | Prev->next = Def->next; | |||
| ||||
| 608 | } | |||
| 609 | Temp.next = Def->next; | |||
| 610 | xmlFreeNs(Def); | |||
| 611 | Def = &Temp; | |||
| 612 | } | |||
| 613 | } | |||
| 614 | ||||
| 615 | WindowsManifestMerger::WindowsManifestMergerImpl::~WindowsManifestMergerImpl() { | |||
| 616 | for (auto &Doc : MergedDocs) | |||
| 617 | xmlFreeDoc(Doc); | |||
| 618 | } | |||
| 619 | ||||
| 620 | Error WindowsManifestMerger::WindowsManifestMergerImpl::merge( | |||
| 621 | MemoryBufferRef Manifest) { | |||
| 622 | if (Merged) | |||
| 623 | return make_error<WindowsManifestError>( | |||
| 624 | "merge after getMergedManifest is not supported"); | |||
| 625 | if (Manifest.getBufferSize() == 0) | |||
| 626 | return make_error<WindowsManifestError>( | |||
| 627 | "attempted to merge empty manifest"); | |||
| 628 | xmlSetGenericErrorFunc((void *)this, | |||
| 629 | WindowsManifestMergerImpl::errorCallback); | |||
| 630 | xmlDocPtr ManifestXML = xmlReadMemory( | |||
| 631 | Manifest.getBufferStart(), Manifest.getBufferSize(), "manifest.xml", | |||
| 632 | nullptr, XML_PARSE_NOBLANKS | XML_PARSE_NODICT); | |||
| 633 | xmlSetGenericErrorFunc(nullptr, nullptr); | |||
| 634 | if (auto E = getParseError()) | |||
| 635 | return E; | |||
| 636 | xmlNodePtr AdditionalRoot = xmlDocGetRootElement(ManifestXML); | |||
| 637 | stripComments(AdditionalRoot); | |||
| 638 | setAttributeNamespaces(AdditionalRoot); | |||
| 639 | if (CombinedDoc == nullptr) { | |||
| 640 | CombinedDoc = ManifestXML; | |||
| 641 | } else { | |||
| 642 | xmlNodePtr CombinedRoot = xmlDocGetRootElement(CombinedDoc); | |||
| 643 | if (!xmlStringsEqual(CombinedRoot->name, AdditionalRoot->name) || | |||
| 644 | !isMergeableElement(AdditionalRoot->name) || | |||
| 645 | !hasRecognizedNamespace(AdditionalRoot)) { | |||
| 646 | return make_error<WindowsManifestError>("multiple root nodes"); | |||
| 647 | } | |||
| 648 | if (auto E = treeMerge(CombinedRoot, AdditionalRoot)) { | |||
| 649 | return E; | |||
| 650 | } | |||
| 651 | } | |||
| 652 | MergedDocs.push_back(ManifestXML); | |||
| 653 | return Error::success(); | |||
| 654 | } | |||
| 655 | ||||
| 656 | std::unique_ptr<MemoryBuffer> | |||
| 657 | WindowsManifestMerger::WindowsManifestMergerImpl::getMergedManifest() { | |||
| 658 | if (!Merged) { | |||
| 659 | Merged = true; | |||
| 660 | ||||
| 661 | if (!CombinedDoc) | |||
| 662 | return nullptr; | |||
| 663 | ||||
| 664 | xmlNodePtr CombinedRoot = xmlDocGetRootElement(CombinedDoc); | |||
| 665 | std::vector<xmlNsPtr> RequiredPrefixes; | |||
| 666 | checkAndStripPrefixes(CombinedRoot, RequiredPrefixes); | |||
| 667 | std::unique_ptr<xmlDoc, XmlDeleter> OutputDoc( | |||
| 668 | xmlNewDoc((const unsigned char *)"1.0")); | |||
| 669 | xmlDocSetRootElement(OutputDoc.get(), CombinedRoot); | |||
| 670 | assert(nullptr == xmlDocGetRootElement(CombinedDoc))(static_cast <bool> (nullptr == xmlDocGetRootElement(CombinedDoc )) ? void (0) : __assert_fail ("nullptr == xmlDocGetRootElement(CombinedDoc)" , "llvm/lib/WindowsManifest/WindowsManifestMerger.cpp", 670, __extension__ __PRETTY_FUNCTION__)); | |||
| 671 | ||||
| 672 | xmlKeepBlanksDefault(0); | |||
| 673 | xmlChar *Buff = nullptr; | |||
| 674 | xmlDocDumpFormatMemoryEnc(OutputDoc.get(), &Buff, &BufferSize, "UTF-8", 1); | |||
| 675 | Buffer.reset(Buff); | |||
| 676 | } | |||
| 677 | ||||
| 678 | return BufferSize ? MemoryBuffer::getMemBufferCopy(StringRef( | |||
| 679 | FROM_XML_CHAR(Buffer.get())reinterpret_cast<const char *>(Buffer.get()), (size_t)BufferSize)) | |||
| 680 | : nullptr; | |||
| 681 | } | |||
| 682 | ||||
| 683 | bool windows_manifest::isAvailable() { return true; } | |||
| 684 | ||||
| 685 | #else | |||
| 686 | ||||
| 687 | WindowsManifestMerger::WindowsManifestMergerImpl::~WindowsManifestMergerImpl() { | |||
| 688 | } | |||
| 689 | ||||
| 690 | Error WindowsManifestMerger::WindowsManifestMergerImpl::merge( | |||
| 691 | MemoryBufferRef Manifest) { | |||
| 692 | return make_error<WindowsManifestError>("no libxml2"); | |||
| 693 | } | |||
| 694 | ||||
| 695 | std::unique_ptr<MemoryBuffer> | |||
| 696 | WindowsManifestMerger::WindowsManifestMergerImpl::getMergedManifest() { | |||
| 697 | return nullptr; | |||
| 698 | } | |||
| 699 | ||||
| 700 | bool windows_manifest::isAvailable() { return false; } | |||
| 701 | ||||
| 702 | #endif | |||
| 703 | ||||
| 704 | WindowsManifestMerger::WindowsManifestMerger() | |||
| 705 | : Impl(std::make_unique<WindowsManifestMergerImpl>()) {} | |||
| 706 | ||||
| 707 | WindowsManifestMerger::~WindowsManifestMerger() = default; | |||
| 708 | ||||
| 709 | Error WindowsManifestMerger::merge(MemoryBufferRef Manifest) { | |||
| 710 | return Impl->merge(Manifest); | |||
| 711 | } | |||
| 712 | ||||
| 713 | std::unique_ptr<MemoryBuffer> WindowsManifestMerger::getMergedManifest() { | |||
| 714 | return Impl->getMergedManifest(); | |||
| ||||
| 715 | } | |||
| 716 | ||||
| 717 | void WindowsManifestMerger::WindowsManifestMergerImpl::errorCallback( | |||
| 718 | void *Ctx, const char *Format, ...) { | |||
| 719 | auto *Merger = (WindowsManifestMergerImpl *)Ctx; | |||
| 720 | Merger->ParseErrorOccurred = true; | |||
| 721 | } | |||
| 722 | ||||
| 723 | Error WindowsManifestMerger::WindowsManifestMergerImpl::getParseError() { | |||
| 724 | if (!ParseErrorOccurred) | |||
| 725 | return Error::success(); | |||
| 726 | return make_error<WindowsManifestError>("invalid xml document"); | |||
| 727 | } |