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