1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- mode: C++ -*-
3 //
4 // Copyright 2022-2024 Google LLC
5 //
6 // Licensed under the Apache License v2.0 with LLVM Exceptions (the
7 // "License"); you may not use this file except in compliance with the
8 // License. You may obtain a copy of the License at
9 //
10 // https://llvm.org/LICENSE.txt
11 //
12 // Unless required by applicable law or agreed to in writing, software
13 // distributed under the License is distributed on an "AS IS" BASIS,
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 // See the License for the specific language governing permissions and
16 // limitations under the License.
17 //
18 // Author: Siddharth Nayyar
19
20 #include "proto_reader.h"
21
22 #include <algorithm>
23 #include <array>
24 #include <cerrno>
25 #include <cstdint>
26 #include <fstream>
27 #include <map>
28 #include <optional>
29 #include <string>
30 #include <string_view>
31 #include <unordered_map>
32 #include <vector>
33
34 #include <google/protobuf/io/zero_copy_stream_impl.h>
35 #include <google/protobuf/repeated_field.h>
36 #include <google/protobuf/repeated_ptr_field.h>
37 #include <google/protobuf/text_format.h>
38 #include "error.h"
39 #include "graph.h"
40 #include "stg.pb.h"
41
42 namespace stg {
43 namespace proto {
44
45 namespace {
46
47 struct Transformer {
Transformerstg::proto::__anond87b5a3c0111::Transformer48 explicit Transformer(Graph& graph) : graph(graph) {}
49
50 Id Transform(const proto::STG&);
51
52 Id GetId(uint32_t);
53
54 template <typename ProtoType>
55 void AddNodes(const google::protobuf::RepeatedPtrField<ProtoType>&);
56 void AddNode(const Void&);
57 void AddNode(const Variadic&);
58 void AddNode(const Special&);
59 void AddNode(const PointerReference&);
60 void AddNode(const PointerToMember&);
61 void AddNode(const Typedef&);
62 void AddNode(const Qualified&);
63 void AddNode(const Primitive&);
64 void AddNode(const Array&);
65 void AddNode(const BaseClass&);
66 void AddNode(const Method&);
67 void AddNode(const Member&);
68 void AddNode(const Variant&);
69 void AddNode(const StructUnion&);
70 void AddNode(const Enumeration&);
71 void AddNode(const VariantMember&);
72 void AddNode(const Function&);
73 void AddNode(const ElfSymbol&);
74 void AddNode(const Symbols&);
75 void AddNode(const Interface&);
76 template <typename STGType, typename... Args>
77 void AddNode(Args&&...);
78
79 std::vector<Id> Transform(const google::protobuf::RepeatedField<uint32_t>&);
80 template <typename GetKey>
81 std::map<std::string, Id> Transform(GetKey,
82 const google::protobuf::RepeatedField<uint32_t>&);
83 stg::Special::Kind Transform(Special::Kind);
84 stg::PointerReference::Kind Transform(PointerReference::Kind);
85 stg::Qualifier Transform(Qualified::Qualifier);
86 stg::Primitive::Encoding Transform(Primitive::Encoding);
87 stg::BaseClass::Inheritance Transform(BaseClass::Inheritance);
88 stg::StructUnion::Kind Transform(StructUnion::Kind);
89 stg::ElfSymbol::SymbolType Transform(ElfSymbol::SymbolType);
90 stg::ElfSymbol::Binding Transform(ElfSymbol::Binding);
91 stg::ElfSymbol::Visibility Transform(ElfSymbol::Visibility);
92 stg::Enumeration::Enumerators Transform(
93 const google::protobuf::RepeatedPtrField<Enumeration::Enumerator>&);
94 template <typename STGType, typename ProtoType>
95 std::optional<STGType> Transform(bool, const ProtoType&);
96 template <typename Type>
97 Type Transform(const Type&);
98
99 Graph& graph;
100 std::unordered_map<uint32_t, Id> id_map;
101 };
102
Transform(const proto::STG & x)103 Id Transformer::Transform(const proto::STG& x) {
104 AddNodes(x.void_()); // deprecated
105 AddNodes(x.variadic()); // deprecated
106 AddNodes(x.special());
107 AddNodes(x.pointer_reference());
108 AddNodes(x.pointer_to_member());
109 AddNodes(x.typedef_());
110 AddNodes(x.qualified());
111 AddNodes(x.primitive());
112 AddNodes(x.array());
113 AddNodes(x.base_class());
114 AddNodes(x.method());
115 AddNodes(x.member());
116 AddNodes(x.variant_member());
117 AddNodes(x.struct_union());
118 AddNodes(x.enumeration());
119 AddNodes(x.variant());
120 AddNodes(x.function());
121 AddNodes(x.elf_symbol());
122 AddNodes(x.symbols());
123 AddNodes(x.interface());
124 return GetId(x.root_id());
125 }
126
GetId(uint32_t id)127 Id Transformer::GetId(uint32_t id) {
128 auto [it, inserted] = id_map.emplace(id, 0);
129 if (inserted) {
130 it->second = graph.Allocate();
131 }
132 return it->second;
133 }
134
135 template <typename ProtoType>
AddNodes(const google::protobuf::RepeatedPtrField<ProtoType> & x)136 void Transformer::AddNodes(const google::protobuf::RepeatedPtrField<ProtoType>& x) {
137 for (const ProtoType& proto : x) {
138 AddNode(proto);
139 }
140 }
141
AddNode(const Void & x)142 void Transformer::AddNode(const Void& x) {
143 AddNode<stg::Special>(GetId(x.id()), stg::Special::Kind::VOID);
144 }
145
AddNode(const Variadic & x)146 void Transformer::AddNode(const Variadic& x) {
147 AddNode<stg::Special>(GetId(x.id()), stg::Special::Kind::VARIADIC);
148 }
149
AddNode(const Special & x)150 void Transformer::AddNode(const Special& x) {
151 AddNode<stg::Special>(GetId(x.id()), x.kind());
152 }
153
AddNode(const PointerReference & x)154 void Transformer::AddNode(const PointerReference& x) {
155 AddNode<stg::PointerReference>(GetId(x.id()), x.kind(),
156 GetId(x.pointee_type_id()));
157 }
158
AddNode(const PointerToMember & x)159 void Transformer::AddNode(const PointerToMember& x) {
160 AddNode<stg::PointerToMember>(GetId(x.id()), GetId(x.containing_type_id()),
161 GetId(x.pointee_type_id()));
162 }
163
AddNode(const Typedef & x)164 void Transformer::AddNode(const Typedef& x) {
165 AddNode<stg::Typedef>(GetId(x.id()), x.name(), GetId(x.referred_type_id()));
166 }
167
AddNode(const Qualified & x)168 void Transformer::AddNode(const Qualified& x) {
169 AddNode<stg::Qualified>(GetId(x.id()), x.qualifier(),
170 GetId(x.qualified_type_id()));
171 }
172
AddNode(const Primitive & x)173 void Transformer::AddNode(const Primitive& x) {
174 const auto& encoding =
175 Transform<stg::Primitive::Encoding>(x.has_encoding(), x.encoding());
176 AddNode<stg::Primitive>(GetId(x.id()), x.name(), encoding, x.bytesize());
177 }
178
AddNode(const Array & x)179 void Transformer::AddNode(const Array& x) {
180 AddNode<stg::Array>(GetId(x.id()), x.number_of_elements(),
181 GetId(x.element_type_id()));
182 }
183
AddNode(const BaseClass & x)184 void Transformer::AddNode(const BaseClass& x) {
185 AddNode<stg::BaseClass>(GetId(x.id()), GetId(x.type_id()), x.offset(),
186 x.inheritance());
187 }
188
AddNode(const Method & x)189 void Transformer::AddNode(const Method& x) {
190 AddNode<stg::Method>(GetId(x.id()), x.mangled_name(), x.name(),
191 x.vtable_offset(), GetId(x.type_id()));
192 }
193
AddNode(const Member & x)194 void Transformer::AddNode(const Member& x) {
195 AddNode<stg::Member>(GetId(x.id()), x.name(), GetId(x.type_id()), x.offset(),
196 x.bitsize());
197 }
198
AddNode(const VariantMember & x)199 void Transformer::AddNode(const VariantMember& x) {
200 const auto& discr_value = x.has_discriminant_value()
201 ? std::make_optional(x.discriminant_value())
202 : std::nullopt;
203 AddNode<stg::VariantMember>(GetId(x.id()), x.name(), discr_value,
204 GetId(x.type_id()));
205 }
206
AddNode(const StructUnion & x)207 void Transformer::AddNode(const StructUnion& x) {
208 if (x.has_definition()) {
209 AddNode<stg::StructUnion>(
210 GetId(x.id()), x.kind(), x.name(), x.definition().bytesize(),
211 x.definition().base_class_id(), x.definition().method_id(),
212 x.definition().member_id());
213 } else {
214 AddNode<stg::StructUnion>(GetId(x.id()), x.kind(), x.name());
215 }
216 }
217
AddNode(const Enumeration & x)218 void Transformer::AddNode(const Enumeration& x) {
219 if (x.has_definition()) {
220 AddNode<stg::Enumeration>(GetId(x.id()), x.name(),
221 GetId(x.definition().underlying_type_id()),
222 x.definition().enumerator());
223 return;
224 } else {
225 AddNode<stg::Enumeration>(GetId(x.id()), x.name());
226 }
227 }
228
AddNode(const Variant & x)229 void Transformer::AddNode(const Variant& x) {
230 const auto& discriminant = x.has_discriminant()
231 ? std::make_optional(GetId(x.discriminant()))
232 : std::nullopt;
233 AddNode<stg::Variant>(GetId(x.id()), x.name(), x.bytesize(), discriminant,
234 x.member_id());
235 }
236
AddNode(const Function & x)237 void Transformer::AddNode(const Function& x) {
238 AddNode<stg::Function>(GetId(x.id()), GetId(x.return_type_id()),
239 x.parameter_id());
240 }
241
AddNode(const ElfSymbol & x)242 void Transformer::AddNode(const ElfSymbol& x) {
243 auto make_version_info = [](const ElfSymbol::VersionInfo& x) {
244 return std::make_optional(
245 stg::ElfSymbol::VersionInfo{x.is_default(), x.name()});
246 };
247 std::optional<stg::ElfSymbol::VersionInfo> version_info =
248 x.has_version_info() ? make_version_info(x.version_info()) : std::nullopt;
249 const auto& crc = x.has_crc()
250 ? std::make_optional<stg::ElfSymbol::CRC>(x.crc())
251 : std::nullopt;
252 const auto& ns = Transform<std::string>(x.has_namespace_(), x.namespace_());
253 const auto& type_id =
254 x.has_type_id() ? std::make_optional(GetId(x.type_id())) : std::nullopt;
255 const auto& full_name =
256 Transform<std::string>(x.has_full_name(), x.full_name());
257
258 AddNode<stg::ElfSymbol>(GetId(x.id()), x.name(), version_info, x.is_defined(),
259 x.symbol_type(), x.binding(), x.visibility(), crc, ns,
260 type_id, full_name);
261 }
262
AddNode(const Symbols & x)263 void Transformer::AddNode(const Symbols& x) {
264 std::map<std::string, Id> symbols;
265 for (const auto& [symbol, id] : x.symbol()) {
266 symbols.emplace(symbol, GetId(id));
267 }
268 AddNode<stg::Interface>(GetId(x.id()), symbols);
269 }
270
AddNode(const Interface & x)271 void Transformer::AddNode(const Interface& x) {
272 const InterfaceKey get_key(graph);
273 AddNode<stg::Interface>(GetId(x.id()), Transform(get_key, x.symbol_id()),
274 Transform(get_key, x.type_id()));
275 }
276
277 template <typename STGType, typename... Args>
AddNode(Args &&...args)278 void Transformer::AddNode(Args&&... args) {
279 graph.Set<STGType>(Transform(args)...);
280 }
281
Transform(const google::protobuf::RepeatedField<uint32_t> & ids)282 std::vector<Id> Transformer::Transform(
283 const google::protobuf::RepeatedField<uint32_t>& ids) {
284 std::vector<Id> result;
285 result.reserve(ids.size());
286 for (uint32_t id : ids) {
287 result.push_back(GetId(id));
288 }
289 return result;
290 }
291
292 template <typename GetKey>
Transform(GetKey get_key,const google::protobuf::RepeatedField<uint32_t> & ids)293 std::map<std::string, Id> Transformer::Transform(
294 GetKey get_key, const google::protobuf::RepeatedField<uint32_t>& ids) {
295 std::map<std::string, Id> result;
296 for (auto id : ids) {
297 const Id stg_id = GetId(id);
298 const auto [it, inserted] = result.emplace(get_key(stg_id), stg_id);
299 if (!inserted) {
300 Die() << "conflicting interface nodes: " << it->first;
301 }
302 }
303 return result;
304 }
305
Transform(Special::Kind x)306 stg::Special::Kind Transformer::Transform(Special::Kind x) {
307 switch (x) {
308 case Special::VOID:
309 return stg::Special::Kind::VOID;
310 case Special::VARIADIC:
311 return stg::Special::Kind::VARIADIC;
312 case Special::NULLPTR:
313 return stg::Special::Kind::NULLPTR;
314 default:
315 Die() << "unknown Special::Kind " << x;
316 }
317 }
318
Transform(PointerReference::Kind x)319 stg::PointerReference::Kind Transformer::Transform(PointerReference::Kind x) {
320 switch (x) {
321 case PointerReference::POINTER:
322 return stg::PointerReference::Kind::POINTER;
323 case PointerReference::LVALUE_REFERENCE:
324 return stg::PointerReference::Kind::LVALUE_REFERENCE;
325 case PointerReference::RVALUE_REFERENCE:
326 return stg::PointerReference::Kind::RVALUE_REFERENCE;
327 default:
328 Die() << "unknown PointerReference::Kind " << x;
329 }
330 }
331
Transform(Qualified::Qualifier x)332 stg::Qualifier Transformer::Transform(Qualified::Qualifier x) {
333 switch (x) {
334 case Qualified::CONST:
335 return stg::Qualifier::CONST;
336 case Qualified::VOLATILE:
337 return stg::Qualifier::VOLATILE;
338 case Qualified::RESTRICT:
339 return stg::Qualifier::RESTRICT;
340 case Qualified::ATOMIC:
341 return stg::Qualifier::ATOMIC;
342 default:
343 Die() << "unknown Qualified::Qualifier " << x;
344 }
345 }
346
Transform(Primitive::Encoding x)347 stg::Primitive::Encoding Transformer::Transform(Primitive::Encoding x) {
348 switch (x) {
349 case Primitive::BOOLEAN:
350 return stg::Primitive::Encoding::BOOLEAN;
351 case Primitive::SIGNED_INTEGER:
352 return stg::Primitive::Encoding::SIGNED_INTEGER;
353 case Primitive::UNSIGNED_INTEGER:
354 return stg::Primitive::Encoding::UNSIGNED_INTEGER;
355 case Primitive::SIGNED_CHARACTER:
356 return stg::Primitive::Encoding::SIGNED_CHARACTER;
357 case Primitive::UNSIGNED_CHARACTER:
358 return stg::Primitive::Encoding::UNSIGNED_CHARACTER;
359 case Primitive::REAL_NUMBER:
360 return stg::Primitive::Encoding::REAL_NUMBER;
361 case Primitive::COMPLEX_NUMBER:
362 return stg::Primitive::Encoding::COMPLEX_NUMBER;
363 case Primitive::UTF:
364 return stg::Primitive::Encoding::UTF;
365 default:
366 Die() << "unknown Primitive::Encoding " << x;
367 }
368 }
369
Transform(BaseClass::Inheritance x)370 stg::BaseClass::Inheritance Transformer::Transform(BaseClass::Inheritance x) {
371 switch (x) {
372 case BaseClass::NON_VIRTUAL:
373 return stg::BaseClass::Inheritance::NON_VIRTUAL;
374 case BaseClass::VIRTUAL:
375 return stg::BaseClass::Inheritance::VIRTUAL;
376 default:
377 Die() << "unknown BaseClass::Inheritance " << x;
378 }
379 }
380
Transform(StructUnion::Kind x)381 stg::StructUnion::Kind Transformer::Transform(StructUnion::Kind x) {
382 switch (x) {
383 case StructUnion::STRUCT:
384 return stg::StructUnion::Kind::STRUCT;
385 case StructUnion::UNION:
386 return stg::StructUnion::Kind::UNION;
387 default:
388 Die() << "unknown StructUnion::Kind " << x;
389 }
390 }
391
Transform(ElfSymbol::SymbolType x)392 stg::ElfSymbol::SymbolType Transformer::Transform(ElfSymbol::SymbolType x) {
393 switch (x) {
394 case ElfSymbol::OBJECT:
395 return stg::ElfSymbol::SymbolType::OBJECT;
396 case ElfSymbol::FUNCTION:
397 return stg::ElfSymbol::SymbolType::FUNCTION;
398 case ElfSymbol::COMMON:
399 return stg::ElfSymbol::SymbolType::COMMON;
400 case ElfSymbol::TLS:
401 return stg::ElfSymbol::SymbolType::TLS;
402 case ElfSymbol::GNU_IFUNC:
403 return stg::ElfSymbol::SymbolType::GNU_IFUNC;
404 default:
405 Die() << "unknown ElfSymbol::SymbolType " << x;
406 }
407 }
408
Transform(ElfSymbol::Binding x)409 stg::ElfSymbol::Binding Transformer::Transform(ElfSymbol::Binding x) {
410 switch (x) {
411 case ElfSymbol::GLOBAL:
412 return stg::ElfSymbol::Binding::GLOBAL;
413 case ElfSymbol::LOCAL:
414 return stg::ElfSymbol::Binding::LOCAL;
415 case ElfSymbol::WEAK:
416 return stg::ElfSymbol::Binding::WEAK;
417 case ElfSymbol::GNU_UNIQUE:
418 return stg::ElfSymbol::Binding::GNU_UNIQUE;
419 default:
420 Die() << "unknown ElfSymbol::Binding " << x;
421 }
422 }
423
Transform(ElfSymbol::Visibility x)424 stg::ElfSymbol::Visibility Transformer::Transform(ElfSymbol::Visibility x) {
425 switch (x) {
426 case ElfSymbol::DEFAULT:
427 return stg::ElfSymbol::Visibility::DEFAULT;
428 case ElfSymbol::PROTECTED:
429 return stg::ElfSymbol::Visibility::PROTECTED;
430 case ElfSymbol::HIDDEN:
431 return stg::ElfSymbol::Visibility::HIDDEN;
432 case ElfSymbol::INTERNAL:
433 return stg::ElfSymbol::Visibility::INTERNAL;
434 default:
435 Die() << "unknown ElfSymbol::Visibility " << x;
436 }
437 }
438
Transform(const google::protobuf::RepeatedPtrField<Enumeration::Enumerator> & x)439 stg::Enumeration::Enumerators Transformer::Transform(
440 const google::protobuf::RepeatedPtrField<Enumeration::Enumerator>& x) {
441 stg::Enumeration::Enumerators enumerators;
442 enumerators.reserve(x.size());
443 for (const auto& enumerator : x) {
444 enumerators.emplace_back(enumerator.name(), enumerator.value());
445 }
446 return enumerators;
447 }
448
449 template <typename STGType, typename ProtoType>
Transform(bool has_field,const ProtoType & field)450 std::optional<STGType> Transformer::Transform(bool has_field,
451 const ProtoType& field) {
452 return has_field ? std::make_optional<STGType>(Transform(field))
453 : std::nullopt;
454 }
455
456 template <typename Type>
Transform(const Type & x)457 Type Transformer::Transform(const Type& x) {
458 return x;
459 }
460
461 const std::array<uint32_t, 3> kSupportedFormatVersions = {0, 1, 2};
462
CheckFormatVersion(uint32_t version,std::optional<std::string> path)463 void CheckFormatVersion(uint32_t version, std::optional<std::string> path) {
464 Check(std::count(kSupportedFormatVersions.begin(),
465 kSupportedFormatVersions.end(), version) > 0)
466 << "STG format version " << version
467 << " is not supported, minimum supported version: "
468 << kSupportedFormatVersions.front();
469 if (version != kSupportedFormatVersions.back()) {
470 auto warn = Warn();
471 warn << "STG format version " << version
472 << " is deprecated, consider upgrading stg format to latest version ("
473 << kSupportedFormatVersions.back() << ")";
474 if (path) {
475 warn << " with: stg --stg " << *path << " --output " << *path;
476 }
477 }
478 }
479
480 } // namespace
481
Read(Graph & graph,const std::string & path)482 Id Read(Graph& graph, const std::string& path) {
483 std::ifstream ifs(path);
484 Check(ifs.good()) << "error opening file '" << path
485 << "' for reading: " << Error(errno);
486 google::protobuf::io::IstreamInputStream is(&ifs);
487 proto::STG stg;
488 google::protobuf::TextFormat::Parse(&is, &stg);
489 CheckFormatVersion(stg.version(), path);
490 return Transformer(graph).Transform(stg);
491 }
492
ReadFromString(Graph & graph,const std::string_view input)493 Id ReadFromString(Graph& graph, const std::string_view input) {
494 proto::STG stg;
495 google::protobuf::TextFormat::ParseFromString(std::string(input), &stg);
496 CheckFormatVersion(stg.version(), std::nullopt);
497 return Transformer(graph).Transform(stg);
498 }
499
500 } // namespace proto
501 } // namespace stg
502