• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- mode: C++ -*-
3 //
4 // Copyright 2021-2023 Google LLC
5 //
6 // Licensed under the Apache License v2.0 with LLVM Exceptions (the
7 // "License"); you may not use this file except in compliance with the
8 // License.  You may obtain a copy of the License at
9 //
10 //     https://llvm.org/LICENSE.txt
11 //
12 // Unless required by applicable law or agreed to in writing, software
13 // distributed under the License is distributed on an "AS IS" BASIS,
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 // See the License for the specific language governing permissions and
16 // limitations under the License.
17 //
18 // Author: Giuliano Procida
19 // Author: Ignes Simeonova
20 
21 #include "abigail_reader.h"
22 
23 #include <fcntl.h>
24 #include <unistd.h>
25 
26 #include <algorithm>
27 #include <array>
28 #include <cstddef>
29 #include <cstdint>
30 #include <functional>
31 #include <ios>
32 #include <map>
33 #include <memory>
34 #include <optional>
35 #include <set>
36 #include <sstream>
37 #include <string>
38 #include <string_view>
39 #include <type_traits>
40 #include <unordered_map>
41 #include <utility>
42 #include <vector>
43 
44 #include <libxml/parser.h>
45 #include <libxml/tree.h>
46 #include "error.h"
47 #include "file_descriptor.h"
48 #include "graph.h"
49 #include "runtime.h"
50 #include "scope.h"
51 #include "type_normalisation.h"
52 
53 namespace stg {
54 namespace abixml {
55 
56 namespace {
57 
58 // Cast a libxml string to C string and present it as a string_view.
FromLibxml(const xmlChar * str)59 std::string_view FromLibxml(const xmlChar* str) {
60   return reinterpret_cast<const char*>(str);
61 }
62 
63 // Cast a C string to a libxml string.
ToLibxml(const char * str)64 const xmlChar* ToLibxml(const char* str) {
65   return reinterpret_cast<const xmlChar*>(str);
66 }
67 
68 // Get the name of an XML element.
GetName(xmlNodePtr element)69 std::string_view GetName(xmlNodePtr element) {
70   return FromLibxml(element->name);
71 }
72 
CheckName(const char * name,xmlNodePtr element)73 void CheckName(const char* name, xmlNodePtr element) {
74   const auto element_name = GetName(element);
75   if (element_name != name) {
76     Die() << "expected element '" << name
77           << "' but got '" << element_name << "'";
78   }
79 }
80 
Child(xmlNodePtr node)81 xmlNodePtr Child(xmlNodePtr node) {
82   return node->children;
83 }
84 
Next(xmlNodePtr node)85 xmlNodePtr Next(xmlNodePtr node) {
86   return node->next;
87 }
88 
GetOnlyChild(xmlNodePtr element)89 xmlNodePtr GetOnlyChild(xmlNodePtr element) {
90   const xmlNodePtr child = Child(element);
91   if (child == nullptr || Next(child) != nullptr) {
92     Die() << "element '" << GetName(element) << "' without exactly one child";
93   }
94   return child;
95 }
96 
97 // Get an optional attribute.
GetAttribute(xmlNodePtr node,const char * name)98 std::optional<std::string> GetAttribute(xmlNodePtr node, const char* name) {
99   std::optional<std::string> result;
100   xmlChar* attribute = xmlGetProp(node, ToLibxml(name));
101   if (attribute) {
102     result.emplace(FromLibxml(attribute));
103     xmlFree(attribute);
104   }
105   return result;
106 }
107 
108 // Get an attribute.
GetAttributeOrDie(xmlNodePtr node,const char * name)109 std::string GetAttributeOrDie(xmlNodePtr node, const char* name) {
110   xmlChar* attribute = xmlGetProp(node, ToLibxml(name));
111   if (!attribute) {
112     Die() << "element '" << GetName(node)
113           << "' missing attribute '" << name << "'";
114   }
115   const std::string result(FromLibxml(attribute));
116   xmlFree(attribute);
117   return result;
118 }
119 
120 // Set an attribute value.
SetAttribute(xmlNodePtr node,const char * name,const std::string & value)121 void SetAttribute(xmlNodePtr node, const char* name, const std::string& value) {
122   xmlSetProp(node, ToLibxml(name), ToLibxml(value.c_str()));
123 }
124 
125 // Unset an attribute value.
UnsetAttribute(xmlNodePtr node,const char * name)126 void UnsetAttribute(xmlNodePtr node, const char* name) {
127   xmlUnsetProp(node, ToLibxml(name));
128 }
129 
130 // Remove a node and free its storage.
RemoveNode(xmlNodePtr node)131 void RemoveNode(xmlNodePtr node) {
132   xmlUnlinkNode(node);
133   xmlFreeNode(node);
134 }
135 
136 // Move a node to be the last child of another.
MoveNode(xmlNodePtr node,xmlNodePtr destination)137 void MoveNode(xmlNodePtr node, xmlNodePtr destination) {
138   xmlUnlinkNode(node);
139   xmlAddChild(destination, node);
140 }
141 
142 template <typename T>
Parse(const std::string & value)143 std::optional<T> Parse(const std::string& value) {
144   T result;
145   std::istringstream is(value);
146   is >> std::noskipws >> result;
147   if (is && is.eof()) {
148     return {result};
149   }
150   return {};
151 }
152 
153 template <>
Parse(const std::string & value)154 std::optional<bool> Parse<bool>(const std::string& value) {
155   if (value == "yes") {
156     return {true};
157   } else if (value == "no") {
158     return {false};
159   }
160   return {};
161 }
162 
163 template <>
Parse(const std::string & value)164 std::optional<ElfSymbol::SymbolType> Parse<ElfSymbol::SymbolType>(
165     const std::string& value) {
166   if (value == "object-type") {
167     return {ElfSymbol::SymbolType::OBJECT};
168   } else if (value == "func-type") {
169     return {ElfSymbol::SymbolType::FUNCTION};
170   } else if (value == "common-type") {
171     return {ElfSymbol::SymbolType::COMMON};
172   } else if (value == "tls-type") {
173     return {ElfSymbol::SymbolType::TLS};
174   } else if (value == "gnu-ifunc-type") {
175     return {ElfSymbol::SymbolType::GNU_IFUNC};
176   }
177   return {};
178 }
179 
180 template <>
Parse(const std::string & value)181 std::optional<ElfSymbol::Binding> Parse<ElfSymbol::Binding>(
182     const std::string& value) {
183   if (value == "global-binding") {
184     return {ElfSymbol::Binding::GLOBAL};
185   } else if (value == "local-binding") {
186     return {ElfSymbol::Binding::LOCAL};
187   } else if (value == "weak-binding") {
188     return {ElfSymbol::Binding::WEAK};
189   } else if (value == "gnu-unique-binding") {
190     return {ElfSymbol::Binding::GNU_UNIQUE};
191   }
192   return {};
193 }
194 
195 template <>
Parse(const std::string & value)196 std::optional<ElfSymbol::Visibility> Parse<ElfSymbol::Visibility>(
197     const std::string& value) {
198   if (value == "default-visibility") {
199     return {ElfSymbol::Visibility::DEFAULT};
200   } else if (value == "protected-visibility") {
201     return {ElfSymbol::Visibility::PROTECTED};
202   } else if (value == "hidden-visibility") {
203     return {ElfSymbol::Visibility::HIDDEN};
204   } else if (value == "internal-visibility") {
205     return {ElfSymbol::Visibility::INTERNAL};
206   }
207   return {};
208 }
209 
210 template <>
Parse(const std::string & value)211 std::optional<ElfSymbol::CRC> Parse<ElfSymbol::CRC>(const std::string& value) {
212   uint32_t number;
213   std::istringstream is(value);
214   is >> std::noskipws >> std::hex >> number;
215   if (is && is.eof()) {
216     return std::make_optional<ElfSymbol::CRC>(number);
217   }
218   return std::nullopt;
219 }
220 
221 template <typename T>
GetParsedValueOrDie(xmlNodePtr element,const char * name,const std::string & value,const std::optional<T> & parse)222 T GetParsedValueOrDie(xmlNodePtr element, const char* name,
223                       const std::string& value, const std::optional<T>& parse) {
224   if (parse) {
225     return *parse;
226   }
227   Die() << "element '" << GetName(element)
228         << "' has attribute '" << name
229         << "' with bad value '" << value << "'";
230 }
231 
232 template <typename T>
ReadAttributeOrDie(xmlNodePtr element,const char * name)233 T ReadAttributeOrDie(xmlNodePtr element, const char* name) {
234   const auto value = GetAttributeOrDie(element, name);
235   return GetParsedValueOrDie(element, name, value, Parse<T>(value));
236 }
237 
238 template <typename T>
ReadAttribute(xmlNodePtr element,const char * name)239 std::optional<T> ReadAttribute(xmlNodePtr element, const char* name) {
240   const auto value = GetAttribute(element, name);
241   if (value) {
242     return {GetParsedValueOrDie(element, name, *value, Parse<T>(*value))};
243   }
244   return {};
245 }
246 
247 template <typename T>
ReadAttribute(xmlNodePtr element,const char * name,const T & default_value)248 T ReadAttribute(xmlNodePtr element, const char* name, const T& default_value) {
249   const auto value = GetAttribute(element, name);
250   if (value) {
251     return GetParsedValueOrDie(element, name, *value, Parse<T>(*value));
252   }
253   return default_value;
254 }
255 
256 template <typename T>
ReadAttribute(xmlNodePtr element,const char * name,std::function<std::optional<T> (const std::string &)> parse)257 T ReadAttribute(xmlNodePtr element, const char* name,
258                 std::function<std::optional<T>(const std::string&)> parse) {
259   const auto value = GetAttributeOrDie(element, name);
260   return GetParsedValueOrDie(element, name, value, parse(value));
261 }
262 
263 // Remove non-element nodes, recursively.
264 //
265 // This simplifies subsequent manipulation. This should only remove comment,
266 // text and possibly CDATA nodes.
StripNonElements(xmlNodePtr node)267 void StripNonElements(xmlNodePtr node) {
268   switch (node->type) {
269     case XML_COMMENT_NODE:
270     case XML_TEXT_NODE:
271     case XML_CDATA_SECTION_NODE:
272       RemoveNode(node);
273       break;
274     case XML_ELEMENT_NODE: {
275       xmlNodePtr child = Child(node);
276       while (child) {
277         const xmlNodePtr next = Next(child);
278         StripNonElements(child);
279         child = next;
280       }
281       break;
282     }
283     default:
284       Die() << "unexpected XML node type: " << node->type;
285   }
286 }
287 
288 // Determine whether one XML element is a subtree of another, and optionally,
289 // actually equal to it.
SubOrEqualTree(bool also_equal,xmlNodePtr left,xmlNodePtr right)290 bool SubOrEqualTree(bool also_equal, xmlNodePtr left, xmlNodePtr right) {
291   // Node names must match.
292   const auto left_name = GetName(left);
293   const auto right_name = GetName(right);
294   if (left_name != right_name) {
295     return false;
296   }
297 
298   // Attributes may be missing on the left, but must match otherwise.
299   size_t left_attributes = 0;
300   for (auto* p = left->properties; p; p = p->next) {
301     ++left_attributes;
302     const auto attribute = FromLibxml(p->name);
303     const char* attribute_name = attribute.data();
304     const auto left_value = GetAttributeOrDie(left, attribute_name);
305     const auto right_value = GetAttribute(right, attribute_name);
306     if (!right_value || left_value != right_value.value()) {
307       return false;
308     }
309   }
310   // To also be equal, we just need to check the counts are the same.
311   if (also_equal) {
312     size_t right_attributes = 0;
313     for (auto* p = right->properties; p; p = p->next) {
314       ++right_attributes;
315     }
316     if (left_attributes != right_attributes) {
317       return false;
318     }
319   }
320 
321   // The left subelements must be a subsequence of the right ones and to also be
322   // equal, we must not have skipped any right ones.
323   xmlNodePtr left_child = Child(left);
324   xmlNodePtr right_child = Child(right);
325   while (left_child != nullptr && right_child != nullptr) {
326     if (SubOrEqualTree(also_equal, left_child, right_child)) {
327       left_child = Next(left_child);
328     } else if (also_equal) {
329       return false;
330     }
331     right_child = Next(right_child);
332   }
333   return left_child == nullptr && (right_child == nullptr || !also_equal);
334 }
335 
336 }  // namespace
337 
338 // Determine whether one XML element is a subtree of another.
SubTree(xmlNodePtr left,xmlNodePtr right)339 bool SubTree(xmlNodePtr left, xmlNodePtr right) {
340   return SubOrEqualTree(false, left, right);
341 }
342 
343 // Determine whether one XML element is the same as another.
EqualTree(xmlNodePtr left,xmlNodePtr right)344 bool EqualTree(xmlNodePtr left, xmlNodePtr right) {
345   return SubOrEqualTree(true, left, right);
346 }
347 
348 // Find a maximal XML element if one exists.
MaximalTree(const std::vector<xmlNodePtr> & nodes)349 std::optional<size_t> MaximalTree(const std::vector<xmlNodePtr>& nodes) {
350   if (nodes.empty()) {
351     return std::nullopt;
352   }
353 
354   // Find a potentially maximal candidate by scanning through and retaining the
355   // new node if it's a supertree of the current candidate.
356   const auto count = nodes.size();
357   std::vector<bool> ok(count);
358   size_t candidate = 0;
359   ok[candidate] = true;
360   for (size_t ix = 1; ix < count; ++ix) {
361     if (SubTree(nodes[candidate], nodes[ix])) {
362       candidate = ix;
363       ok[candidate] = true;
364     }
365   }
366 
367   // Verify the candidate is indeed maximal by comparing it with the nodes not
368   // already known to be subtrees of it.
369   const auto& candidate_node = nodes[candidate];
370   for (size_t ix = 0; ix < count; ++ix) {
371     const auto& node = nodes[ix];
372     if (!ok[ix] && !SubTree(node, candidate_node)) {
373       return std::nullopt;
374     }
375   }
376 
377   return std::make_optional(candidate);
378 }
379 
380 namespace {
381 
382 // Check if string_view is in an array.
383 template<size_t N>
Contains(const std::array<std::string_view,N> & haystack,std::string_view needle)384 bool Contains(const std::array<std::string_view, N>& haystack,
385               std::string_view needle) {
386   return std::find(haystack.begin(), haystack.end(), needle) != haystack.end();
387 }
388 
389 // Remove source location attributes.
390 //
391 // This simplifies element comparison later.
StripLocationInfo(xmlNodePtr node)392 void StripLocationInfo(xmlNodePtr node) {
393   static const std::array<std::string_view, 7> has_location_info = {
394     "class-decl",
395     "enum-decl",
396     "function-decl",
397     "parameter",
398     "typedef-decl",
399     "union-decl",
400     "var-decl"
401   };
402 
403   if (Contains(has_location_info, GetName(node))) {
404     UnsetAttribute(node, "filepath");
405     UnsetAttribute(node, "line");
406     UnsetAttribute(node, "column");
407   }
408   for (auto* child = Child(node); child; child = Next(child)) {
409     StripLocationInfo(child);
410   }
411 }
412 
413 // Remove access attribute.
414 //
415 // This simplifies element comparison later in a very specific way: libabigail
416 // (possibly older versions) uses the access specifier for the type it's trying
417 // to "emit in scope", even for its containing types, making deduplicating types
418 // trickier. We don't care about access anyway, so just remove it everywhere.
StripAccess(xmlNodePtr node)419 void StripAccess(xmlNodePtr node) {
420   static const std::array<std::string_view, 5> has_access = {
421     "base-class",
422     "data-member",
423     "member-function",
424     "member-template",
425     "member-type",
426   };
427 
428   if (Contains(has_access, GetName(node))) {
429     UnsetAttribute(node, "access");
430   }
431   for (auto* child = Child(node); child; child = Next(child)) {
432     StripAccess(child);
433   }
434 }
435 
436 // Elements corresponding to named types that can be anonymous or marked as
437 // unreachable by libabigail, so user-defined types, excepting typedefs.
438 const std::array<std::string_view, 3> kNamedTypes = {
439   "class-decl",
440   "enum-decl",
441   "union-decl",
442 };
443 
444 // Remove attributes emitted by abidw --load-all-types.
445 //
446 // With this invocation and if any user-defined types are deemed unreachable,
447 // libabigail will output a tracking-non-reachable-types attribute on top-level
448 // elements and an is-non-reachable attribute on each such type element.
449 //
450 // We have our own graph-theoretic notion of reachability and these attributes
451 // have no ABI relevance and can interfere with element comparisons.
StripReachabilityAttributes(xmlNodePtr node)452 void StripReachabilityAttributes(xmlNodePtr node) {
453   const auto node_name = GetName(node);
454 
455   if (node_name == "abi-corpus-group" || node_name == "abi-corpus") {
456     UnsetAttribute(node, "tracking-non-reachable-types");
457   } else if (Contains(kNamedTypes, node_name)) {
458     UnsetAttribute(node, "is-non-reachable");
459   }
460 
461   for (auto* child = Child(node); child; child = Next(child)) {
462     StripReachabilityAttributes(child);
463   }
464 }
465 
466 // Fix bad DWARF -> ELF links caused by size zero symbol confusion.
467 //
468 // libabigail used to be confused by these sorts of symbols, resulting in
469 // declarations pointing at the wrong ELF symbols:
470 //
471 // 573623: ffffffc0122383c0   256 OBJECT  GLOBAL DEFAULT   33 vm_node_stat
472 // 573960: ffffffc0122383c0     0 OBJECT  GLOBAL DEFAULT   33 vm_numa_stat
FixBadDwarfElfLinks(xmlNodePtr root)473 void FixBadDwarfElfLinks(xmlNodePtr root) {
474   std::unordered_map<std::string, size_t> elf_links;
475 
476   // See which ELF symbol IDs have multiple declarations.
477   const std::function<void(xmlNodePtr)> count = [&](xmlNodePtr node) {
478     if (GetName(node) == "var-decl") {
479       const auto symbol_id = GetAttribute(node, "elf-symbol-id");
480       if (symbol_id) {
481         ++elf_links[symbol_id.value()];
482       }
483     }
484 
485     for (auto* child = Child(node); child; child = Next(child)) {
486       count(child);
487     }
488   };
489   count(root);
490 
491   // Fix up likely bad links from DWARF declaration to ELF symbol.
492   const std::function<void(xmlNodePtr)> fix = [&](xmlNodePtr node) {
493     if (GetName(node) == "var-decl") {
494       const auto name = GetAttributeOrDie(node, "name");
495       const auto mangled_name = GetAttribute(node, "mangled-name");
496       const auto symbol_id = GetAttribute(node, "elf-symbol-id");
497       if (mangled_name && symbol_id && name != symbol_id.value()
498           && elf_links[symbol_id.value()] > 1) {
499         if (mangled_name.value() == name) {
500           Warn() << "fixing up ELF symbol for '" << name
501                  << "' (was '" << symbol_id.value() << "')";
502           SetAttribute(node, "elf-symbol-id", name);
503         } else if (mangled_name.value() == symbol_id.value()) {
504           Warn() << "fixing up mangled name and ELF symbol for '" << name
505                  << "' (was '" << symbol_id.value() << "')";
506           SetAttribute(node, "mangled-name", name);
507           SetAttribute(node, "elf-symbol-id", name);
508         }
509       }
510     }
511 
512     for (auto* child = Child(node); child; child = Next(child)) {
513       fix(child);
514     }
515   };
516   fix(root);
517 }
518 
519 // Tidy anonymous types in various ways.
520 //
521 // 1. Normalise anonymous type names by dropping the name attribute.
522 //
523 // Anonymous type names take the form __anonymous_foo__N where foo is one of
524 // enum, struct or union and N is an optional numerical suffix. We don't care
525 // about these names but they may cause trouble when comparing elements.
526 //
527 // 2. Reanonymise anonymous types that have been given names.
528 //
529 // At some point abidw changed its behaviour given an anonymous with a naming
530 // typedef. In addition to linking the typedef and type in both directions, the
531 // code now gives (some) anonymous types the same name as the typedef. This
532 // misrepresents the original types.
533 //
534 // Such types should be anonymous. We set is-anonymous and drop the name.
535 //
536 // 3. Discard naming typedef backlinks.
537 //
538 // The attribute naming-typedef-id is a backwards link from an anonymous type to
539 // the typedef that refers to it.
540 //
541 // We don't care about these attributes and they may cause comparison issues.
TidyAnonymousTypes(xmlNodePtr node)542 void TidyAnonymousTypes(xmlNodePtr node) {
543   if (Contains(kNamedTypes, GetName(node))) {
544     const bool is_anon = ReadAttribute<bool>(node, "is-anonymous", false);
545     const auto naming_attribute = GetAttribute(node, "naming-typedef-id");
546     if (is_anon) {
547       UnsetAttribute(node, "name");
548     } else if (naming_attribute) {
549       SetAttribute(node, "is-anonymous", "yes");
550       UnsetAttribute(node, "name");
551     }
552     if (naming_attribute) {
553       UnsetAttribute(node, "naming-typedef-id");
554     }
555   }
556 
557   for (auto* child = Child(node); child; child = Next(child)) {
558     TidyAnonymousTypes(child);
559   }
560 }
561 
562 // Remove duplicate members.
RemoveDuplicateMembers(xmlNodePtr root)563 void RemoveDuplicateMembers(xmlNodePtr root) {
564   std::vector<xmlNodePtr> types;
565 
566   // find all structs and unions
567   std::function<void(xmlNodePtr)> dfs = [&](xmlNodePtr node) {
568     const auto node_name = GetName(node);
569     // preorder in case we delete a nested element
570     for (auto* child = Child(node); child; child = Next(child)) {
571       dfs(child);
572     }
573     if (node_name == "class-decl" || node_name == "union-decl") {
574       types.push_back(node);
575     }
576   };
577   dfs(root);
578 
579   for (const auto& node : types) {
580     // partition members by node name
581     std::map<std::string_view, std::vector<xmlNodePtr>> member_map;
582     for (auto* child = Child(node); child; child = Next(child)) {
583       member_map[GetName(child)].push_back(child);
584     }
585     // for each kind of member...
586     for (auto& [name, members] : member_map) {
587       // ... remove identical duplicate members - O(n^2)
588       for (size_t i = 0; i < members.size(); ++i) {
589         xmlNodePtr& i_node = members[i];
590         bool duplicate = false;
591         for (size_t j = 0; j < i; ++j) {
592           const xmlNodePtr& j_node = members[j];
593           if (j_node != nullptr && EqualTree(i_node, j_node)) {
594             duplicate = true;
595             break;
596           }
597         }
598         if (duplicate) {
599           RemoveNode(i_node);
600           i_node = nullptr;
601         }
602       }
603     }
604   }
605 }
606 
607 // Eliminate non-conflicting / report conflicting duplicate definitions.
608 //
609 // XML elements representing types are sometimes emitted multiple times,
610 // identically. Also, member typedefs are sometimes emitted separately from
611 // their types, resulting in duplicate XML fragments.
612 //
613 // Both these issues can be resolved by first detecting duplicate occurrences of
614 // a given type id and then checking to see if there's an instance that subsumes
615 // the others, which can then be eliminated.
616 //
617 // This function eliminates exact type duplicates and duplicates where there is
618 // at least one maximal definition. It can report the remaining duplicate
619 // definitions.
620 //
621 // If a type has duplicate definitions in multiple namespace scopes or
622 // definitions with different effective names, these are considered to be
623 // *conflicting* duplicate definitions. TODO: update text
HandleDuplicateTypes(xmlNodePtr root)624 void HandleDuplicateTypes(xmlNodePtr root) {
625   // Convenience typedef referring to a namespace scope.
626   using namespace_scope = std::vector<std::string>;
627   // map of type-id to pair of set of namespace scopes and vector of
628   // xmlNodes
629   std::unordered_map<
630       std::string,
631       std::pair<
632           std::set<namespace_scope>,
633           std::vector<xmlNodePtr>>> types;
634   namespace_scope namespaces;
635 
636   // find all type occurrences
637   std::function<void(xmlNodePtr)> dfs = [&](xmlNodePtr node) {
638     const auto node_name = GetName(node);
639     std::optional<std::string> namespace_name;
640     if (node_name == "namespace-decl") {
641       namespace_name = GetAttribute(node, "name");
642     }
643     if (namespace_name) {
644       namespaces.push_back(namespace_name.value());
645     }
646     if (node_name == "abi-corpus-group"
647         || node_name == "abi-corpus"
648         || node_name == "abi-instr"
649         || namespace_name) {
650       for (auto* child = Child(node); child; child = Next(child)) {
651         dfs(child);
652       }
653     } else {
654       const auto id = GetAttribute(node, "id");
655       if (id) {
656         auto& info = types[id.value()];
657         info.first.insert(namespaces);
658         info.second.push_back(node);
659       }
660     }
661     if (namespace_name) {
662       namespaces.pop_back();
663     }
664   };
665   dfs(root);
666 
667   for (const auto& [id, scopes_and_definitions] : types) {
668     const auto& [scopes, definitions] = scopes_and_definitions;
669 
670     if (scopes.size() > 1) {
671       Warn() << "conflicting scopes found for type '" << id << '\'';
672       continue;
673     }
674 
675     const auto possible_maximal = MaximalTree(definitions);
676     if (possible_maximal) {
677       // Remove all but the maximal definition.
678       const size_t maximal = possible_maximal.value();
679       for (size_t ix = 0; ix < definitions.size(); ++ix) {
680         if (ix != maximal) {
681           RemoveNode(definitions[ix]);
682         }
683       }
684       continue;
685     }
686 
687     // As a rare alternative, check for a stray anonymous member that has been
688     // separated from the main definition.
689     size_t strays = 0;
690     std::optional<size_t> stray;
691     std::optional<size_t> non_stray;
692     for (size_t ix = 0; ix < definitions.size(); ++ix) {
693       auto node = definitions[ix];
694       auto member = Child(node);
695       if (member && !Next(member) && GetName(member) == "data-member") {
696         auto decl = Child(member);
697         if (decl && !Next(decl) && GetName(decl) == "var-decl") {
698           auto name = GetAttribute(decl, "name");
699           if (name && name.value().empty()) {
700             ++strays;
701             stray = ix;
702             continue;
703           }
704         }
705       }
706       non_stray = ix;
707     }
708     if (strays + 1 == definitions.size() && stray.has_value()
709         && non_stray.has_value()) {
710       const auto stray_index = stray.value();
711       const auto non_stray_index = non_stray.value();
712       bool good = true;
713       for (size_t ix = 0; ix < definitions.size(); ++ix) {
714         if (ix == stray_index || ix == non_stray_index) {
715           continue;
716         }
717         if (EqualTree(definitions[stray_index], definitions[ix])) {
718           // it doesn't hurt if we remove exact duplicates and then fail
719           RemoveNode(definitions[ix]);
720         } else {
721           good = false;
722           break;
723         }
724       }
725       if (good) {
726         MoveNode(Child(definitions[stray_index]), definitions[non_stray_index]);
727         RemoveNode(definitions[stray_index]);
728         continue;
729       }
730     }
731 
732     Warn() << "unresolvable duplicate definitions found for type '" << id
733            << '\'';
734   }
735 }
736 
737 }  // namespace
738 
739 // Remove XML nodes and attributes that are neither used or wanted.
Clean(xmlNodePtr root)740 void Clean(xmlNodePtr root) {
741   // Strip non-element nodes to simplify other operations.
742   StripNonElements(root);
743 
744   // Strip location information.
745   StripLocationInfo(root);
746 
747   // Strip access.
748   StripAccess(root);
749 
750   // Strip reachability attributes.
751   StripReachabilityAttributes(root);
752 }
753 
754 namespace {
755 
756 // Transform XML elements to improve their semantics.
Tidy(xmlNodePtr root)757 void Tidy(xmlNodePtr root) {
758   // Fix bad ELF symbol links
759   FixBadDwarfElfLinks(root);
760 
761   // Normalise anonymous type names.
762   // Reanonymise anonymous types.
763   // Discard naming typedef backlinks.
764   TidyAnonymousTypes(root);
765 
766   // Remove duplicate members.
767   RemoveDuplicateMembers(root);
768 
769   // Eliminate complete duplicates and extra fragments of types.
770   // Report conflicting duplicate defintions.
771   // Record whether there are conflicting duplicate definitions.
772   HandleDuplicateTypes(root);
773 }
774 
ParseLength(const std::string & value)775 std::optional<uint64_t> ParseLength(const std::string& value) {
776   if (value == "infinite" || value == "unknown") {
777     return {0};
778   }
779   return Parse<uint64_t>(value);
780 }
781 
ParseReferenceKind(const std::string & value)782 std::optional<PointerReference::Kind> ParseReferenceKind(
783     const std::string& value) {
784   if (value == "lvalue") {
785     return {PointerReference::Kind::LVALUE_REFERENCE};
786   } else if (value == "rvalue") {
787     return {PointerReference::Kind::RVALUE_REFERENCE};
788   }
789   return {};
790 }
791 
792 }  // namespace
793 
Abigail(Graph & graph)794 Abigail::Abigail(Graph& graph) : graph_(graph) {}
795 
GetNode(const std::string & type_id)796 Id Abigail::GetNode(const std::string& type_id) {
797   const auto [it, inserted] = type_ids_.insert({type_id, Id(0)});
798   if (inserted) {
799     it->second = graph_.Allocate();
800   }
801   return it->second;
802 }
803 
GetEdge(xmlNodePtr element)804 Id Abigail::GetEdge(xmlNodePtr element) {
805   return GetNode(GetAttributeOrDie(element, "type-id"));
806 }
807 
GetVariadic()808 Id Abigail::GetVariadic() {
809   if (!variadic_) {
810     variadic_ = {graph_.Add<Special>(Special::Kind::VARIADIC)};
811   }
812   return *variadic_;
813 }
814 
MakeFunctionType(xmlNodePtr function)815 Function Abigail::MakeFunctionType(xmlNodePtr function) {
816   std::vector<Id> parameters;
817   std::optional<Id> return_type;
818   for (auto* child = Child(function); child; child = Next(child)) {
819     const auto child_name = GetName(child);
820     if (return_type) {
821       Die() << "unexpected element after return-type";
822     }
823     if (child_name == "parameter") {
824       const auto is_variadic = ReadAttribute<bool>(child, "is-variadic", false);
825       parameters.push_back(is_variadic ? GetVariadic() : GetEdge(child));
826     } else if (child_name == "return") {
827       return_type = {GetEdge(child)};
828     } else {
829       Die() << "unrecognised " << GetName(function)
830             << " child element '" << child_name << "'";
831     }
832   }
833   if (!return_type) {
834     Die() << "missing return-type";
835   }
836   return Function(*return_type, parameters);
837 }
838 
ProcessRoot(xmlNodePtr root)839 Id Abigail::ProcessRoot(xmlNodePtr root) {
840   Clean(root);
841   Tidy(root);
842   const auto name = GetName(root);
843   if (name == "abi-corpus-group") {
844     ProcessCorpusGroup(root);
845   } else if (name == "abi-corpus") {
846     ProcessCorpus(root);
847   } else {
848     Die() << "unrecognised root element '" << name << "'";
849   }
850   for (const auto& [type_id, id] : type_ids_) {
851     if (!graph_.Is(id)) {
852       Warn() << "no definition found for type '" << type_id << "'";
853     }
854   }
855   const Id id = BuildSymbols();
856   RemoveUselessQualifiers(graph_, id);
857   return id;
858 }
859 
ProcessCorpusGroup(xmlNodePtr group)860 void Abigail::ProcessCorpusGroup(xmlNodePtr group) {
861   for (auto* corpus = Child(group); corpus; corpus = Next(corpus)) {
862     CheckName("abi-corpus", corpus);
863     ProcessCorpus(corpus);
864   }
865 }
866 
ProcessCorpus(xmlNodePtr corpus)867 void Abigail::ProcessCorpus(xmlNodePtr corpus) {
868   for (auto* element = Child(corpus); element; element = Next(element)) {
869     const auto name = GetName(element);
870     if (name == "elf-function-symbols" || name == "elf-variable-symbols") {
871       ProcessSymbols(element);
872     } else if (name == "elf-needed") {
873       // ignore this
874     } else if (name == "abi-instr") {
875       ProcessInstr(element);
876     } else {
877       Die() << "unrecognised abi-corpus child element '" << name << "'";
878     }
879   }
880 }
881 
ProcessSymbols(xmlNodePtr symbols)882 void Abigail::ProcessSymbols(xmlNodePtr symbols) {
883   for (auto* element = Child(symbols); element; element = Next(element)) {
884     CheckName("elf-symbol", element);
885     ProcessSymbol(element);
886   }
887 }
888 
ProcessSymbol(xmlNodePtr symbol)889 void Abigail::ProcessSymbol(xmlNodePtr symbol) {
890   // Symbol processing is done in two parts. In this first part, we parse just
891   // enough XML attributes to generate a symbol id and determine any aliases.
892   // Symbol ids in this format can be found in elf-symbol alias attributes and
893   // in {var,function}-decl elf-symbol-id attributes.
894   const auto name = GetAttributeOrDie(symbol, "name");
895   const auto version =
896       ReadAttribute<std::string>(symbol, "version", std::string());
897   const bool is_default_version =
898       ReadAttribute<bool>(symbol, "is-default-version", false);
899   const auto alias = GetAttribute(symbol, "alias");
900 
901   std::string elf_symbol_id = name;
902   std::optional<ElfSymbol::VersionInfo> version_info;
903   if (!version.empty()) {
904     version_info = ElfSymbol::VersionInfo{is_default_version, version};
905     elf_symbol_id += VersionInfoToString(*version_info);
906   }
907 
908   Check(symbol_info_map_
909             .emplace(elf_symbol_id, SymbolInfo{name, version_info, symbol})
910             .second)
911       << "multiple symbols with id " << elf_symbol_id;
912 
913   if (alias) {
914     std::istringstream is(*alias);
915     std::string item;
916     while (std::getline(is, item, ',')) {
917       Check(alias_to_main_.insert({item, elf_symbol_id}).second)
918           << "multiple aliases with id " << elf_symbol_id;
919     }
920   }
921 }
922 
ProcessUserDefinedType(std::string_view name,Id id,xmlNodePtr decl)923 bool Abigail::ProcessUserDefinedType(std::string_view name, Id id,
924                                      xmlNodePtr decl) {
925   if (name == "typedef-decl") {
926     ProcessTypedef(id, decl);
927   } else if (name == "class-decl") {
928     ProcessStructUnion(id, true, decl);
929   } else if (name == "union-decl") {
930     ProcessStructUnion(id, false, decl);
931   } else if (name == "enum-decl") {
932     ProcessEnum(id, decl);
933   } else {
934     return false;
935   }
936   return true;
937 }
938 
ProcessScope(xmlNodePtr scope)939 void Abigail::ProcessScope(xmlNodePtr scope) {
940   for (auto* element = Child(scope); element; element = Next(element)) {
941     const auto name = GetName(element);
942     const auto type_id = GetAttribute(element, "id");
943     // all type elements have "id", all non-types do not
944     if (type_id) {
945       const auto id = GetNode(*type_id);
946       if (graph_.Is(id)) {
947         Warn() << "duplicate definition of type '" << *type_id << '\'';
948         continue;
949       }
950       if (name == "function-type") {
951         ProcessFunctionType(id, element);
952       } else if (name == "pointer-type-def") {
953         ProcessPointer(id, true, element);
954       } else if (name == "reference-type-def") {
955         ProcessPointer(id, false, element);
956       } else if (name == "qualified-type-def") {
957         ProcessQualified(id, element);
958       } else if (name == "array-type-def") {
959         ProcessArray(id, element);
960       } else if (name == "type-decl") {
961         ProcessTypeDecl(id, element);
962       } else if (!ProcessUserDefinedType(name, id, element)) {
963         Die() << "bad abi-instr type child element '" << name << "'";
964       }
965     } else {
966       if (name == "var-decl") {
967         ProcessDecl(true, element);
968       } else if (name == "function-decl") {
969         ProcessDecl(false, element);
970       } else if (name == "namespace-decl") {
971         ProcessNamespace(element);
972       } else {
973         Die() << "bad abi-instr non-type child element '" << name << "'";
974       }
975     }
976   }
977 }
978 
ProcessInstr(xmlNodePtr instr)979 void Abigail::ProcessInstr(xmlNodePtr instr) {
980   ProcessScope(instr);
981 }
982 
ProcessNamespace(xmlNodePtr scope)983 void Abigail::ProcessNamespace(xmlNodePtr scope) {
984   const auto name = GetAttributeOrDie(scope, "name");
985   const PushScopeName push_scope_name(scope_name_, "namespace", name);
986   ProcessScope(scope);
987 }
988 
ProcessDecl(bool is_variable,xmlNodePtr decl)989 Id Abigail::ProcessDecl(bool is_variable, xmlNodePtr decl) {
990   const auto name = scope_name_ + GetAttributeOrDie(decl, "name");
991   const auto symbol_id = GetAttribute(decl, "elf-symbol-id");
992   const auto type = is_variable ? GetEdge(decl)
993                                 : graph_.Add<Function>(MakeFunctionType(decl));
994   if (symbol_id) {
995     // There's a link to an ELF symbol.
996     const auto [it, inserted] = symbol_id_and_full_name_.emplace(
997         *symbol_id, std::make_pair(type, name));
998     if (!inserted) {
999       Die() << "duplicate type for '" << *symbol_id << "'";
1000     }
1001   }
1002   return type;
1003 }
1004 
ProcessFunctionType(Id id,xmlNodePtr function)1005 void Abigail::ProcessFunctionType(Id id, xmlNodePtr function) {
1006   graph_.Set<Function>(id, MakeFunctionType(function));
1007 }
1008 
ProcessTypedef(Id id,xmlNodePtr type_definition)1009 void Abigail::ProcessTypedef(Id id, xmlNodePtr type_definition) {
1010   const auto name = scope_name_ + GetAttributeOrDie(type_definition, "name");
1011   const auto type = GetEdge(type_definition);
1012   graph_.Set<Typedef>(id, name, type);
1013 }
1014 
ProcessPointer(Id id,bool is_pointer,xmlNodePtr pointer)1015 void Abigail::ProcessPointer(Id id, bool is_pointer, xmlNodePtr pointer) {
1016   const auto type = GetEdge(pointer);
1017   const auto kind = is_pointer ? PointerReference::Kind::POINTER
1018                                : ReadAttribute<PointerReference::Kind>(
1019                                      pointer, "kind", &ParseReferenceKind);
1020   graph_.Set<PointerReference>(id, kind, type);
1021 }
1022 
ProcessQualified(Id id,xmlNodePtr qualified)1023 void Abigail::ProcessQualified(Id id, xmlNodePtr qualified) {
1024   std::vector<Qualifier> qualifiers;
1025   // Do these in reverse order so we get CVR ordering.
1026   if (ReadAttribute<bool>(qualified, "restrict", false)) {
1027     qualifiers.push_back(Qualifier::RESTRICT);
1028   }
1029   if (ReadAttribute<bool>(qualified, "volatile", false)) {
1030     qualifiers.push_back(Qualifier::VOLATILE);
1031   }
1032   if (ReadAttribute<bool>(qualified, "const", false)) {
1033     qualifiers.push_back(Qualifier::CONST);
1034   }
1035   Check(!qualifiers.empty()) << "qualified-type-def has no qualifiers";
1036   // Handle multiple qualifiers by unconditionally adding as new nodes all but
1037   // the last qualifier which is set into place.
1038   auto type = GetEdge(qualified);
1039   auto count = qualifiers.size();
1040   for (auto qualifier : qualifiers) {
1041     --count;
1042     const Qualified node(qualifier, type);
1043     if (count) {
1044       type = graph_.Add<Qualified>(node);
1045     } else {
1046       graph_.Set<Qualified>(id, node);
1047     }
1048   }
1049 }
1050 
ProcessArray(Id id,xmlNodePtr array)1051 void Abigail::ProcessArray(Id id, xmlNodePtr array) {
1052   std::vector<size_t> dimensions;
1053   for (auto* child = Child(array); child; child = Next(child)) {
1054     CheckName("subrange", child);
1055     const auto length = ReadAttribute<uint64_t>(child, "length", &ParseLength);
1056     dimensions.push_back(length);
1057   }
1058   Check(!dimensions.empty()) << "array-type-def element has no children";
1059   // int[M][N] means array[M] of array[N] of int
1060   //
1061   // We need to chain a bunch of types together:
1062   //
1063   // id = array[n] of id = ... = array[n] of id
1064   //
1065   // where the first id is the new type in slot ix
1066   // and the last id is the old type in slot type
1067   //
1068   // Use the same approach as for qualifiers.
1069   auto type = GetEdge(array);
1070   auto count = dimensions.size();
1071   for (auto it = dimensions.crbegin(); it != dimensions.crend(); ++it) {
1072     --count;
1073     const auto size = *it;
1074     const Array node(size, type);
1075     if (count) {
1076       type = graph_.Add<Array>(node);
1077     } else {
1078       graph_.Set<Array>(id, node);
1079     }
1080   }
1081 }
1082 
ProcessTypeDecl(Id id,xmlNodePtr type_decl)1083 void Abigail::ProcessTypeDecl(Id id, xmlNodePtr type_decl) {
1084   const auto name = scope_name_ + GetAttributeOrDie(type_decl, "name");
1085   const auto bits = ReadAttribute<size_t>(type_decl, "size-in-bits", 0);
1086   if (bits % 8) {
1087     Die() << "size-in-bits is not a multiple of 8";
1088   }
1089   const auto bytes = bits / 8;
1090 
1091   if (name == "void") {
1092     graph_.Set<Special>(id, Special::Kind::VOID);
1093   } else {
1094     // libabigail doesn't model encoding at all and we don't want to parse names
1095     // (which will not always work) in an attempt to reconstruct it.
1096     graph_.Set<Primitive>(id, name, /* encoding= */ std::nullopt, bytes);
1097   }
1098 }
1099 
ProcessStructUnion(Id id,bool is_struct,xmlNodePtr struct_union)1100 void Abigail::ProcessStructUnion(Id id, bool is_struct,
1101                                  xmlNodePtr struct_union) {
1102   // Libabigail sometimes reports is-declaration-only but still provides some
1103   // child elements. So we check both things.
1104   const bool forward =
1105       ReadAttribute<bool>(struct_union, "is-declaration-only", false)
1106       && Child(struct_union) == nullptr;
1107   const auto kind = is_struct
1108                     ? StructUnion::Kind::STRUCT
1109                     : StructUnion::Kind::UNION;
1110   const bool is_anonymous =
1111       ReadAttribute<bool>(struct_union, "is-anonymous", false);
1112   const auto name =
1113       is_anonymous ? std::string() : GetAttributeOrDie(struct_union, "name");
1114   const auto full_name =
1115       is_anonymous ? std::string() : scope_name_ + name;
1116   const PushScopeName push_scope_name(scope_name_, kind, name);
1117   if (forward) {
1118     graph_.Set<StructUnion>(id, kind, full_name);
1119     return;
1120   }
1121   const auto bits = ReadAttribute<size_t>(struct_union, "size-in-bits", 0);
1122   const auto bytes = (bits + 7) / 8;
1123 
1124   std::vector<Id> base_classes;
1125   std::vector<Id> methods;
1126   std::vector<Id> members;
1127   for (auto* child = Child(struct_union); child; child = Next(child)) {
1128     const auto child_name = GetName(child);
1129     if (child_name == "data-member") {
1130       if (const auto member = ProcessDataMember(is_struct, child)) {
1131         members.push_back(*member);
1132       }
1133     } else if (child_name == "member-type") {
1134       ProcessMemberType(child);
1135     } else if (child_name == "base-class") {
1136       base_classes.push_back(ProcessBaseClass(child));
1137     } else if (child_name == "member-function") {
1138       ProcessMemberFunction(methods, child);
1139     } else {
1140       Die() << "unrecognised " << kind << "-decl child element '" << child_name
1141             << "'";
1142     }
1143   }
1144 
1145   graph_.Set<StructUnion>(id, kind, full_name, bytes, base_classes, methods,
1146                           members);
1147 }
1148 
ProcessEnum(Id id,xmlNodePtr enumeration)1149 void Abigail::ProcessEnum(Id id, xmlNodePtr enumeration) {
1150   const bool forward =
1151       ReadAttribute<bool>(enumeration, "is-declaration-only", false);
1152   const auto name = ReadAttribute<bool>(enumeration, "is-anonymous", false)
1153                     ? std::string()
1154                     : scope_name_ + GetAttributeOrDie(enumeration, "name");
1155   if (forward) {
1156     graph_.Set<Enumeration>(id, name);
1157     return;
1158   }
1159 
1160   const xmlNodePtr underlying = Child(enumeration);
1161   Check(underlying != nullptr) << "enum-decl has no child elements";
1162   CheckName("underlying-type", underlying);
1163   const auto type = GetEdge(underlying);
1164 
1165   std::vector<std::pair<std::string, int64_t>> enumerators;
1166   for (auto* enumerator = Next(underlying); enumerator;
1167        enumerator = Next(enumerator)) {
1168     CheckName("enumerator", enumerator);
1169     const auto enumerator_name = GetAttributeOrDie(enumerator, "name");
1170     // libabigail currently supports anything that fits in an int64_t
1171     const auto enumerator_value =
1172         ReadAttributeOrDie<int64_t>(enumerator, "value");
1173     enumerators.emplace_back(enumerator_name, enumerator_value);
1174   }
1175 
1176   graph_.Set<Enumeration>(id, name, type, enumerators);
1177 }
1178 
ProcessBaseClass(xmlNodePtr base_class)1179 Id Abigail::ProcessBaseClass(xmlNodePtr base_class) {
1180   const auto& type = GetEdge(base_class);
1181   const auto offset =
1182       ReadAttributeOrDie<size_t>(base_class, "layout-offset-in-bits");
1183   const auto inheritance = ReadAttribute<bool>(base_class, "is-virtual", false)
1184                            ? BaseClass::Inheritance::VIRTUAL
1185                            : BaseClass::Inheritance::NON_VIRTUAL;
1186   return graph_.Add<BaseClass>(type, offset, inheritance);
1187 }
1188 
ProcessDataMember(bool is_struct,xmlNodePtr data_member)1189 std::optional<Id> Abigail::ProcessDataMember(bool is_struct,
1190                                              xmlNodePtr data_member) {
1191   const xmlNodePtr decl = GetOnlyChild(data_member);
1192   CheckName("var-decl", decl);
1193   if (ReadAttribute<bool>(data_member, "static", false)) {
1194     ProcessDecl(true, decl);
1195     return {};
1196   }
1197 
1198   const auto offset = is_struct
1199                       ? ReadAttributeOrDie<size_t>(data_member,
1200                                                    "layout-offset-in-bits")
1201                       : 0;
1202   const auto name = GetAttributeOrDie(decl, "name");
1203   const auto type = GetEdge(decl);
1204 
1205   // Note: libabigail does not model member size, yet
1206   return {graph_.Add<Member>(name, type, offset, 0)};
1207 }
1208 
ProcessMemberFunction(std::vector<Id> & methods,xmlNodePtr method)1209 void Abigail::ProcessMemberFunction(std::vector<Id>& methods,
1210                                     xmlNodePtr method) {
1211   const xmlNodePtr decl = GetOnlyChild(method);
1212   CheckName("function-decl", decl);
1213   // ProcessDecl creates symbol references so must be called unconditionally.
1214   const auto type = ProcessDecl(false, decl);
1215   const auto vtable_offset = ReadAttribute<uint64_t>(method, "vtable-offset");
1216   if (vtable_offset) {
1217     static const std::string missing = "{missing}";
1218     const auto mangled_name = ReadAttribute(decl, "mangled-name", missing);
1219     const auto name = GetAttributeOrDie(decl, "name");
1220     methods.push_back(
1221         graph_.Add<Method>(mangled_name, name, vtable_offset.value(), type));
1222   }
1223 }
1224 
ProcessMemberType(xmlNodePtr member_type)1225 void Abigail::ProcessMemberType(xmlNodePtr member_type) {
1226   const xmlNodePtr decl = GetOnlyChild(member_type);
1227   const auto type_id = GetAttributeOrDie(decl, "id");
1228   const auto id = GetNode(type_id);
1229   if (graph_.Is(id)) {
1230     Warn() << "duplicate definition of member type '" << type_id << '\'';
1231     return;
1232   }
1233   const auto name = GetName(decl);
1234   if (!ProcessUserDefinedType(name, id, decl)) {
1235     Die() << "unrecognised member-type child element '" << name << "'";
1236   }
1237 }
1238 
BuildSymbol(const SymbolInfo & info,std::optional<Id> type_id,const std::optional<std::string> & name)1239 Id Abigail::BuildSymbol(const SymbolInfo& info,
1240                         std::optional<Id> type_id,
1241                         const std::optional<std::string>& name) {
1242   const xmlNodePtr symbol = info.node;
1243   const bool is_defined = ReadAttributeOrDie<bool>(symbol, "is-defined");
1244   const auto crc = ReadAttribute<ElfSymbol::CRC>(symbol, "crc");
1245   const auto ns = ReadAttribute<std::string>(symbol, "namespace");
1246   const auto type = ReadAttributeOrDie<ElfSymbol::SymbolType>(symbol, "type");
1247   const auto binding =
1248       ReadAttributeOrDie<ElfSymbol::Binding>(symbol, "binding");
1249   const auto visibility =
1250       ReadAttributeOrDie<ElfSymbol::Visibility>(symbol, "visibility");
1251 
1252   return graph_.Add<ElfSymbol>(
1253       info.name, info.version_info,
1254       is_defined, type, binding, visibility, crc, ns, type_id, name);
1255 }
1256 
BuildSymbols()1257 Id Abigail::BuildSymbols() {
1258   // Libabigail's model is (approximately):
1259   //
1260   //   (alias)* -> main symbol <- some decl -> type
1261   //
1262   // which we turn into:
1263   //
1264   //   symbol / alias -> type
1265   //
1266   for (const auto& [alias, main] : alias_to_main_) {
1267     Check(!alias_to_main_.count(main))
1268         << "found main symbol and alias with id " << main;
1269   }
1270   // Build final symbol table, tying symbols to their types.
1271   std::map<std::string, Id> symbols;
1272   for (const auto& [id, symbol_info] : symbol_info_map_) {
1273     const auto main = alias_to_main_.find(id);
1274     const auto lookup = main != alias_to_main_.end() ? main->second : id;
1275     const auto type_id_and_name_it = symbol_id_and_full_name_.find(lookup);
1276     std::optional<Id> type_id;
1277     std::optional<std::string> name;
1278     if (type_id_and_name_it != symbol_id_and_full_name_.end()) {
1279       const auto& type_id_and_name = type_id_and_name_it->second;
1280       type_id = {type_id_and_name.first};
1281       name = {type_id_and_name.second};
1282     }
1283     symbols.insert({id, BuildSymbol(symbol_info, type_id, name)});
1284   }
1285   return graph_.Add<Interface>(symbols);
1286 }
1287 
Read(Runtime & runtime,const std::string & path)1288 Document Read(Runtime& runtime, const std::string& path) {
1289   // Open input for reading.
1290   const FileDescriptor fd(path.c_str(), O_RDONLY);
1291 
1292   // Read the XML.
1293   Document document(nullptr, xmlFreeDoc);
1294   {
1295     const Time t(runtime, "abigail.libxml_parse");
1296     const std::unique_ptr<
1297         std::remove_pointer_t<xmlParserCtxtPtr>, void(*)(xmlParserCtxtPtr)>
1298         context(xmlNewParserCtxt(), xmlFreeParserCtxt);
1299     document.reset(
1300         xmlCtxtReadFd(context.get(), fd.Value(), nullptr, nullptr,
1301                       XML_PARSE_NONET));
1302   }
1303   Check(document != nullptr) << "failed to parse input as XML";
1304 
1305   return document;
1306 }
1307 
Read(Runtime & runtime,Graph & graph,const std::string & path)1308 Id Read(Runtime& runtime, Graph& graph, const std::string& path) {
1309   const Document document = Read(runtime, path);
1310   const xmlNodePtr root = xmlDocGetRootElement(document.get());
1311   Check(root != nullptr) << "XML document has no root element";
1312   return Abigail(graph).ProcessRoot(root);
1313 }
1314 
1315 }  // namespace abixml
1316 }  // namespace stg
1317