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