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 <limits>
28 #include <map>
29 #include <optional>
30 #include <string>
31 #include <string_view>
32 #include <vector>
33
34 #include <google/protobuf/io/tokenizer.h>
35 #include <google/protobuf/io/zero_copy_stream.h>
36 #include <google/protobuf/io/zero_copy_stream_impl.h>
37 #include <google/protobuf/io/zero_copy_stream_impl_lite.h>
38 #include <google/protobuf/repeated_field.h>
39 #include <google/protobuf/repeated_ptr_field.h>
40 #include <google/protobuf/text_format.h>
41 #include "error.h"
42 #include "graph.h"
43 #include "hex.h"
44 #include "runtime.h"
45 #include "stg.pb.h"
46
47 namespace stg {
48 namespace proto {
49
50 namespace {
51
52 struct Transformer {
Transformerstg::proto::__anonc9b4b7de0111::Transformer53 explicit Transformer(Graph& graph) : graph(graph), maker(graph) {}
54
55 Id Transform(const proto::STG&);
56
57 Id GetId(uint32_t);
58
59 template <typename ProtoType>
60 void AddNodes(const google::protobuf::RepeatedPtrField<ProtoType>&);
61 void AddNode(const Void&);
62 void AddNode(const Variadic&);
63 void AddNode(const Special&);
64 void AddNode(const PointerReference&);
65 void AddNode(const PointerToMember&);
66 void AddNode(const Typedef&);
67 void AddNode(const Qualified&);
68 void AddNode(const Primitive&);
69 void AddNode(const Array&);
70 void AddNode(const BaseClass&);
71 void AddNode(const Method&);
72 void AddNode(const Member&);
73 void AddNode(const Variant&);
74 void AddNode(const StructUnion&);
75 void AddNode(const Enumeration&);
76 void AddNode(const VariantMember&);
77 void AddNode(const Function&);
78 void AddNode(const ElfSymbol&);
79 void AddNode(const Symbols&);
80 void AddNode(const Interface&);
81 template <typename STGType, typename... Args>
82 void AddNode(uint32_t, Args&&...);
83
84 std::vector<Id> Transform(const google::protobuf::RepeatedField<uint32_t>&);
85 template <typename GetKey>
86 std::map<std::string, Id> Transform(GetKey,
87 const google::protobuf::RepeatedField<uint32_t>&);
88 stg::Special::Kind Transform(Special::Kind);
89 stg::PointerReference::Kind Transform(PointerReference::Kind);
90 stg::Qualifier Transform(Qualified::Qualifier);
91 stg::Primitive::Encoding Transform(Primitive::Encoding);
92 stg::BaseClass::Inheritance Transform(BaseClass::Inheritance);
93 stg::StructUnion::Kind Transform(StructUnion::Kind);
94 stg::ElfSymbol::SymbolType Transform(ElfSymbol::SymbolType);
95 stg::ElfSymbol::Binding Transform(ElfSymbol::Binding);
96 stg::ElfSymbol::Visibility Transform(ElfSymbol::Visibility);
97 stg::Enumeration::Enumerators Transform(
98 const google::protobuf::RepeatedPtrField<Enumeration::Enumerator>&);
99 template <typename STGType, typename ProtoType>
100 std::optional<STGType> Transform(bool, const ProtoType&);
101 template <typename Type>
102 Type Transform(const Type&);
103
104 Graph& graph;
105 Maker<Hex<uint32_t>> maker;
106 };
107
Transform(const proto::STG & x)108 Id Transformer::Transform(const proto::STG& x) {
109 AddNodes(x.void_()); // deprecated
110 AddNodes(x.variadic()); // deprecated
111 AddNodes(x.special());
112 AddNodes(x.pointer_reference());
113 AddNodes(x.pointer_to_member());
114 AddNodes(x.typedef_());
115 AddNodes(x.qualified());
116 AddNodes(x.primitive());
117 AddNodes(x.array());
118 AddNodes(x.base_class());
119 AddNodes(x.method());
120 AddNodes(x.member());
121 AddNodes(x.variant_member());
122 AddNodes(x.struct_union());
123 AddNodes(x.enumeration());
124 AddNodes(x.variant());
125 AddNodes(x.function());
126 AddNodes(x.elf_symbol());
127 AddNodes(x.symbols());
128 AddNodes(x.interface());
129 return GetId(x.root_id());
130 }
131
GetId(uint32_t id)132 Id Transformer::GetId(uint32_t id) {
133 return maker.Get(Hex(id));
134 }
135
136 template <typename ProtoType>
AddNodes(const google::protobuf::RepeatedPtrField<ProtoType> & x)137 void Transformer::AddNodes(const google::protobuf::RepeatedPtrField<ProtoType>& x) {
138 for (const ProtoType& proto : x) {
139 AddNode(proto);
140 }
141 }
142
AddNode(const Void & x)143 void Transformer::AddNode(const Void& x) {
144 AddNode<stg::Special>(x.id(), stg::Special::Kind::VOID);
145 }
146
AddNode(const Variadic & x)147 void Transformer::AddNode(const Variadic& x) {
148 AddNode<stg::Special>(x.id(), stg::Special::Kind::VARIADIC);
149 }
150
AddNode(const Special & x)151 void Transformer::AddNode(const Special& x) {
152 AddNode<stg::Special>(x.id(), x.kind());
153 }
154
AddNode(const PointerReference & x)155 void Transformer::AddNode(const PointerReference& x) {
156 AddNode<stg::PointerReference>(x.id(), x.kind(), GetId(x.pointee_type_id()));
157 }
158
AddNode(const PointerToMember & x)159 void Transformer::AddNode(const PointerToMember& x) {
160 AddNode<stg::PointerToMember>(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>(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>(x.id(), x.qualifier(), GetId(x.qualified_type_id()));
170 }
171
AddNode(const Primitive & x)172 void Transformer::AddNode(const Primitive& x) {
173 const auto& encoding =
174 Transform<stg::Primitive::Encoding>(x.has_encoding(), x.encoding());
175 AddNode<stg::Primitive>(x.id(), x.name(), encoding, x.bytesize());
176 }
177
AddNode(const Array & x)178 void Transformer::AddNode(const Array& x) {
179 AddNode<stg::Array>(x.id(), x.number_of_elements(),
180 GetId(x.element_type_id()));
181 }
182
AddNode(const BaseClass & x)183 void Transformer::AddNode(const BaseClass& x) {
184 AddNode<stg::BaseClass>(x.id(), GetId(x.type_id()), x.offset(),
185 x.inheritance());
186 }
187
AddNode(const Method & x)188 void Transformer::AddNode(const Method& x) {
189 AddNode<stg::Method>(x.id(), x.mangled_name(), x.name(), x.vtable_offset(),
190 GetId(x.type_id()));
191 }
192
AddNode(const Member & x)193 void Transformer::AddNode(const Member& x) {
194 AddNode<stg::Member>(x.id(), x.name(), GetId(x.type_id()), x.offset(),
195 x.bitsize());
196 }
197
AddNode(const VariantMember & x)198 void Transformer::AddNode(const VariantMember& x) {
199 const auto& discr_value = x.has_discriminant_value()
200 ? std::make_optional(x.discriminant_value())
201 : std::nullopt;
202 AddNode<stg::VariantMember>(x.id(), x.name(), discr_value,
203 GetId(x.type_id()));
204 }
205
AddNode(const StructUnion & x)206 void Transformer::AddNode(const StructUnion& x) {
207 if (x.has_definition()) {
208 AddNode<stg::StructUnion>(
209 x.id(), x.kind(), x.name(), x.definition().bytesize(),
210 x.definition().base_class_id(), x.definition().method_id(),
211 x.definition().member_id());
212 } else {
213 AddNode<stg::StructUnion>(x.id(), x.kind(), x.name());
214 }
215 }
216
AddNode(const Enumeration & x)217 void Transformer::AddNode(const Enumeration& x) {
218 if (x.has_definition()) {
219 AddNode<stg::Enumeration>(x.id(), x.name(),
220 GetId(x.definition().underlying_type_id()),
221 x.definition().enumerator());
222 return;
223 } else {
224 AddNode<stg::Enumeration>(x.id(), x.name());
225 }
226 }
227
AddNode(const Variant & x)228 void Transformer::AddNode(const Variant& x) {
229 const auto& discriminant = x.has_discriminant()
230 ? std::make_optional(GetId(x.discriminant()))
231 : std::nullopt;
232 AddNode<stg::Variant>(x.id(), x.name(), x.bytesize(), discriminant,
233 x.member_id());
234 }
235
AddNode(const Function & x)236 void Transformer::AddNode(const Function& x) {
237 AddNode<stg::Function>(x.id(), GetId(x.return_type_id()), x.parameter_id());
238 }
239
AddNode(const ElfSymbol & x)240 void Transformer::AddNode(const ElfSymbol& x) {
241 auto make_version_info = [](const ElfSymbol::VersionInfo& x) {
242 return std::make_optional(
243 stg::ElfSymbol::VersionInfo{x.is_default(), x.name()});
244 };
245 const std::optional<stg::ElfSymbol::VersionInfo> version_info =
246 x.has_version_info() ? make_version_info(x.version_info()) : std::nullopt;
247 const auto& crc = x.has_crc()
248 ? std::make_optional<stg::ElfSymbol::CRC>(x.crc())
249 : std::nullopt;
250 const auto& ns = Transform<std::string>(x.has_namespace_(), x.namespace_());
251 const auto& type_id =
252 x.has_type_id() ? std::make_optional(GetId(x.type_id())) : std::nullopt;
253 const auto& full_name =
254 Transform<std::string>(x.has_full_name(), x.full_name());
255
256 AddNode<stg::ElfSymbol>(x.id(), x.name(), version_info, x.is_defined(),
257 x.symbol_type(), x.binding(), x.visibility(), crc, ns,
258 type_id, full_name);
259 }
260
AddNode(const Symbols & x)261 void Transformer::AddNode(const Symbols& x) {
262 std::map<std::string, Id> symbols;
263 for (const auto& [symbol, id] : x.symbol()) {
264 symbols.emplace(symbol, GetId(id));
265 }
266 AddNode<stg::Interface>(x.id(), symbols);
267 }
268
AddNode(const Interface & x)269 void Transformer::AddNode(const Interface& x) {
270 const InterfaceKey get_key(graph);
271 AddNode<stg::Interface>(x.id(), Transform(get_key, x.symbol_id()),
272 Transform(get_key, x.type_id()));
273 }
274
275 template <typename STGType, typename... Args>
AddNode(uint32_t id,Args &&...args)276 void Transformer::AddNode(uint32_t id, Args&&... args) {
277 maker.Set<STGType>(Hex(id), Transform(args)...);
278 }
279
Transform(const google::protobuf::RepeatedField<uint32_t> & ids)280 std::vector<Id> Transformer::Transform(
281 const google::protobuf::RepeatedField<uint32_t>& ids) {
282 std::vector<Id> result;
283 result.reserve(ids.size());
284 for (const uint32_t id : ids) {
285 result.push_back(GetId(id));
286 }
287 return result;
288 }
289
290 template <typename GetKey>
Transform(GetKey get_key,const google::protobuf::RepeatedField<uint32_t> & ids)291 std::map<std::string, Id> Transformer::Transform(
292 GetKey get_key, const google::protobuf::RepeatedField<uint32_t>& ids) {
293 std::map<std::string, Id> result;
294 for (auto id : ids) {
295 const Id stg_id = GetId(id);
296 const auto [it, inserted] = result.emplace(get_key(stg_id), stg_id);
297 if (!inserted) {
298 Die() << "conflicting interface nodes: " << it->first;
299 }
300 }
301 return result;
302 }
303
Transform(Special::Kind x)304 stg::Special::Kind Transformer::Transform(Special::Kind x) {
305 switch (x) {
306 case Special::VOID:
307 return stg::Special::Kind::VOID;
308 case Special::VARIADIC:
309 return stg::Special::Kind::VARIADIC;
310 case Special::NULLPTR:
311 return stg::Special::Kind::NULLPTR;
312 default:
313 Die() << "unknown Special::Kind " << x;
314 }
315 }
316
Transform(PointerReference::Kind x)317 stg::PointerReference::Kind Transformer::Transform(PointerReference::Kind x) {
318 switch (x) {
319 case PointerReference::POINTER:
320 return stg::PointerReference::Kind::POINTER;
321 case PointerReference::LVALUE_REFERENCE:
322 return stg::PointerReference::Kind::LVALUE_REFERENCE;
323 case PointerReference::RVALUE_REFERENCE:
324 return stg::PointerReference::Kind::RVALUE_REFERENCE;
325 default:
326 Die() << "unknown PointerReference::Kind " << x;
327 }
328 }
329
Transform(Qualified::Qualifier x)330 stg::Qualifier Transformer::Transform(Qualified::Qualifier x) {
331 switch (x) {
332 case Qualified::CONST:
333 return stg::Qualifier::CONST;
334 case Qualified::VOLATILE:
335 return stg::Qualifier::VOLATILE;
336 case Qualified::RESTRICT:
337 return stg::Qualifier::RESTRICT;
338 case Qualified::ATOMIC:
339 return stg::Qualifier::ATOMIC;
340 default:
341 Die() << "unknown Qualified::Qualifier " << x;
342 }
343 }
344
Transform(Primitive::Encoding x)345 stg::Primitive::Encoding Transformer::Transform(Primitive::Encoding x) {
346 switch (x) {
347 case Primitive::BOOLEAN:
348 return stg::Primitive::Encoding::BOOLEAN;
349 case Primitive::SIGNED_INTEGER:
350 return stg::Primitive::Encoding::SIGNED_INTEGER;
351 case Primitive::UNSIGNED_INTEGER:
352 return stg::Primitive::Encoding::UNSIGNED_INTEGER;
353 case Primitive::SIGNED_CHARACTER:
354 return stg::Primitive::Encoding::SIGNED_CHARACTER;
355 case Primitive::UNSIGNED_CHARACTER:
356 return stg::Primitive::Encoding::UNSIGNED_CHARACTER;
357 case Primitive::REAL_NUMBER:
358 return stg::Primitive::Encoding::REAL_NUMBER;
359 case Primitive::COMPLEX_NUMBER:
360 return stg::Primitive::Encoding::COMPLEX_NUMBER;
361 case Primitive::UTF:
362 return stg::Primitive::Encoding::UTF;
363 default:
364 Die() << "unknown Primitive::Encoding " << x;
365 }
366 }
367
Transform(BaseClass::Inheritance x)368 stg::BaseClass::Inheritance Transformer::Transform(BaseClass::Inheritance x) {
369 switch (x) {
370 case BaseClass::NON_VIRTUAL:
371 return stg::BaseClass::Inheritance::NON_VIRTUAL;
372 case BaseClass::VIRTUAL:
373 return stg::BaseClass::Inheritance::VIRTUAL;
374 default:
375 Die() << "unknown BaseClass::Inheritance " << x;
376 }
377 }
378
Transform(StructUnion::Kind x)379 stg::StructUnion::Kind Transformer::Transform(StructUnion::Kind x) {
380 switch (x) {
381 case StructUnion::STRUCT:
382 return stg::StructUnion::Kind::STRUCT;
383 case StructUnion::UNION:
384 return stg::StructUnion::Kind::UNION;
385 default:
386 Die() << "unknown StructUnion::Kind " << x;
387 }
388 }
389
Transform(ElfSymbol::SymbolType x)390 stg::ElfSymbol::SymbolType Transformer::Transform(ElfSymbol::SymbolType x) {
391 switch (x) {
392 case ElfSymbol::NOTYPE:
393 return stg::ElfSymbol::SymbolType::NOTYPE;
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)463 void CheckFormatVersion(uint32_t version) {
464 Check(std::binary_search(kSupportedFormatVersions.begin(),
465 kSupportedFormatVersions.end(), version))
466 << "STG format version " << version
467 << " is not supported, minimum supported version: "
468 << kSupportedFormatVersions.front();
469 if (version != kSupportedFormatVersions.back()) {
470 Warn() << "STG format version " << version
471 << " is deprecated, consider upgrading to the latest version ("
472 << kSupportedFormatVersions.back() << ")";
473 }
474 }
475
476 class ErrorSink : public google::protobuf::io::ErrorCollector {
477 public:
AddError(int line,google::protobuf::io::ColumnNumber column,const std::string & message)478 void AddError(int line, google::protobuf::io::ColumnNumber column,
479 const std::string& message) final {
480 Moan("error", line, column, message);
481 }
AddWarning(int line,google::protobuf::io::ColumnNumber column,const std::string & message)482 void AddWarning(int line, google::protobuf::io::ColumnNumber column,
483 const std::string& message) final {
484 Moan("warning", line, column, message);
485 }
486
487 private:
Moan(std::string_view which,int line,google::protobuf::io::ColumnNumber column,const std::string & message)488 static void Moan(std::string_view which, int line,
489 google::protobuf::io::ColumnNumber column,
490 const std::string& message) {
491 Warn() << "google::protobuf::TextFormat " << which << " at line " << (line + 1)
492 << " column " << (column + 1) << ": " << message;
493 }
494 };
495
ReadHelper(Runtime & runtime,Graph & graph,google::protobuf::io::ZeroCopyInputStream & is)496 Id ReadHelper(Runtime& runtime, Graph& graph,
497 google::protobuf::io::ZeroCopyInputStream& is) {
498 proto::STG stg;
499 {
500 const Time t(runtime, "proto.Parse");
501 ErrorSink error_sink;
502 google::protobuf::TextFormat::Parser parser;
503 parser.RecordErrorsTo(&error_sink);
504 Check(parser.Parse(&is, &stg)) << "failed to parse input as STG";
505 }
506 {
507 const Time t(runtime, "proto.Transform");
508 CheckFormatVersion(stg.version());
509 return Transformer(graph).Transform(stg);
510 }
511 }
512
513 } // namespace
514
Read(Runtime & runtime,Graph & graph,const std::string & path)515 Id Read(Runtime& runtime, Graph& graph, const std::string& path) {
516 std::ifstream ifs(path);
517 Check(ifs.good()) << "error opening file '" << path << "' for reading: "
518 << Error(errno);
519 google::protobuf::io::IstreamInputStream is(&ifs);
520 return ReadHelper(runtime, graph, is);
521 }
522
ReadFromString(Runtime & runtime,Graph & graph,std::string_view input)523 Id ReadFromString(Runtime& runtime, Graph& graph, std::string_view input) {
524 Check(input.size() <= std::numeric_limits<int>::max()) << "input too big";
525 google::protobuf::io::ArrayInputStream is(input.data(), static_cast<int>(input.size()));
526 return ReadHelper(runtime, graph, is);
527 }
528
529 } // namespace proto
530 } // namespace stg
531