// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -*- mode: C++ -*- // // Copyright 2022-2024 Google LLC // // Licensed under the Apache License v2.0 with LLVM Exceptions (the // "License"); you may not use this file except in compliance with the // License. You may obtain a copy of the License at // // https://llvm.org/LICENSE.txt // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: Siddharth Nayyar #include "proto_reader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "error.h" #include "graph.h" #include "hex.h" #include "runtime.h" #include "stg.pb.h" namespace stg { namespace proto { namespace { struct Transformer { explicit Transformer(Graph& graph) : graph(graph), maker(graph) {} Id Transform(const proto::STG&); Id GetId(uint32_t); template void AddNodes(const google::protobuf::RepeatedPtrField&); void AddNode(const Void&); void AddNode(const Variadic&); void AddNode(const Special&); void AddNode(const PointerReference&); void AddNode(const PointerToMember&); void AddNode(const Typedef&); void AddNode(const Qualified&); void AddNode(const Primitive&); void AddNode(const Array&); void AddNode(const BaseClass&); void AddNode(const Method&); void AddNode(const Member&); void AddNode(const Variant&); void AddNode(const StructUnion&); void AddNode(const Enumeration&); void AddNode(const VariantMember&); void AddNode(const Function&); void AddNode(const ElfSymbol&); void AddNode(const Symbols&); void AddNode(const Interface&); template void AddNode(uint32_t, Args&&...); std::vector Transform(const google::protobuf::RepeatedField&); template std::map Transform(GetKey, const google::protobuf::RepeatedField&); stg::Special::Kind Transform(Special::Kind); stg::PointerReference::Kind Transform(PointerReference::Kind); stg::Qualifier Transform(Qualified::Qualifier); stg::Primitive::Encoding Transform(Primitive::Encoding); stg::BaseClass::Inheritance Transform(BaseClass::Inheritance); stg::StructUnion::Kind Transform(StructUnion::Kind); stg::ElfSymbol::SymbolType Transform(ElfSymbol::SymbolType); stg::ElfSymbol::Binding Transform(ElfSymbol::Binding); stg::ElfSymbol::Visibility Transform(ElfSymbol::Visibility); stg::Enumeration::Enumerators Transform( const google::protobuf::RepeatedPtrField&); template std::optional Transform(bool, const ProtoType&); template Type Transform(const Type&); Graph& graph; Maker> maker; }; Id Transformer::Transform(const proto::STG& x) { AddNodes(x.void_()); // deprecated AddNodes(x.variadic()); // deprecated AddNodes(x.special()); AddNodes(x.pointer_reference()); AddNodes(x.pointer_to_member()); AddNodes(x.typedef_()); AddNodes(x.qualified()); AddNodes(x.primitive()); AddNodes(x.array()); AddNodes(x.base_class()); AddNodes(x.method()); AddNodes(x.member()); AddNodes(x.variant_member()); AddNodes(x.struct_union()); AddNodes(x.enumeration()); AddNodes(x.variant()); AddNodes(x.function()); AddNodes(x.elf_symbol()); AddNodes(x.symbols()); AddNodes(x.interface()); return GetId(x.root_id()); } Id Transformer::GetId(uint32_t id) { return maker.Get(Hex(id)); } template void Transformer::AddNodes(const google::protobuf::RepeatedPtrField& x) { for (const ProtoType& proto : x) { AddNode(proto); } } void Transformer::AddNode(const Void& x) { AddNode(x.id(), stg::Special::Kind::VOID); } void Transformer::AddNode(const Variadic& x) { AddNode(x.id(), stg::Special::Kind::VARIADIC); } void Transformer::AddNode(const Special& x) { AddNode(x.id(), x.kind()); } void Transformer::AddNode(const PointerReference& x) { AddNode(x.id(), x.kind(), GetId(x.pointee_type_id())); } void Transformer::AddNode(const PointerToMember& x) { AddNode(x.id(), GetId(x.containing_type_id()), GetId(x.pointee_type_id())); } void Transformer::AddNode(const Typedef& x) { AddNode(x.id(), x.name(), GetId(x.referred_type_id())); } void Transformer::AddNode(const Qualified& x) { AddNode(x.id(), x.qualifier(), GetId(x.qualified_type_id())); } void Transformer::AddNode(const Primitive& x) { const auto& encoding = Transform(x.has_encoding(), x.encoding()); AddNode(x.id(), x.name(), encoding, x.bytesize()); } void Transformer::AddNode(const Array& x) { AddNode(x.id(), x.number_of_elements(), GetId(x.element_type_id())); } void Transformer::AddNode(const BaseClass& x) { AddNode(x.id(), GetId(x.type_id()), x.offset(), x.inheritance()); } void Transformer::AddNode(const Method& x) { AddNode(x.id(), x.mangled_name(), x.name(), x.vtable_offset(), GetId(x.type_id())); } void Transformer::AddNode(const Member& x) { AddNode(x.id(), x.name(), GetId(x.type_id()), x.offset(), x.bitsize()); } void Transformer::AddNode(const VariantMember& x) { const auto& discr_value = x.has_discriminant_value() ? std::make_optional(x.discriminant_value()) : std::nullopt; AddNode(x.id(), x.name(), discr_value, GetId(x.type_id())); } void Transformer::AddNode(const StructUnion& x) { if (x.has_definition()) { AddNode( x.id(), x.kind(), x.name(), x.definition().bytesize(), x.definition().base_class_id(), x.definition().method_id(), x.definition().member_id()); } else { AddNode(x.id(), x.kind(), x.name()); } } void Transformer::AddNode(const Enumeration& x) { if (x.has_definition()) { AddNode(x.id(), x.name(), GetId(x.definition().underlying_type_id()), x.definition().enumerator()); return; } else { AddNode(x.id(), x.name()); } } void Transformer::AddNode(const Variant& x) { const auto& discriminant = x.has_discriminant() ? std::make_optional(GetId(x.discriminant())) : std::nullopt; AddNode(x.id(), x.name(), x.bytesize(), discriminant, x.member_id()); } void Transformer::AddNode(const Function& x) { AddNode(x.id(), GetId(x.return_type_id()), x.parameter_id()); } void Transformer::AddNode(const ElfSymbol& x) { auto make_version_info = [](const ElfSymbol::VersionInfo& x) { return std::make_optional( stg::ElfSymbol::VersionInfo{x.is_default(), x.name()}); }; const std::optional version_info = x.has_version_info() ? make_version_info(x.version_info()) : std::nullopt; const auto& crc = x.has_crc() ? std::make_optional(x.crc()) : std::nullopt; const auto& ns = Transform(x.has_namespace_(), x.namespace_()); const auto& type_id = x.has_type_id() ? std::make_optional(GetId(x.type_id())) : std::nullopt; const auto& full_name = Transform(x.has_full_name(), x.full_name()); AddNode(x.id(), x.name(), version_info, x.is_defined(), x.symbol_type(), x.binding(), x.visibility(), crc, ns, type_id, full_name); } void Transformer::AddNode(const Symbols& x) { std::map symbols; for (const auto& [symbol, id] : x.symbol()) { symbols.emplace(symbol, GetId(id)); } AddNode(x.id(), symbols); } void Transformer::AddNode(const Interface& x) { const InterfaceKey get_key(graph); AddNode(x.id(), Transform(get_key, x.symbol_id()), Transform(get_key, x.type_id())); } template void Transformer::AddNode(uint32_t id, Args&&... args) { maker.Set(Hex(id), Transform(args)...); } std::vector Transformer::Transform( const google::protobuf::RepeatedField& ids) { std::vector result; result.reserve(ids.size()); for (const uint32_t id : ids) { result.push_back(GetId(id)); } return result; } template std::map Transformer::Transform( GetKey get_key, const google::protobuf::RepeatedField& ids) { std::map result; for (auto id : ids) { const Id stg_id = GetId(id); const auto [it, inserted] = result.emplace(get_key(stg_id), stg_id); if (!inserted) { Die() << "conflicting interface nodes: " << it->first; } } return result; } stg::Special::Kind Transformer::Transform(Special::Kind x) { switch (x) { case Special::VOID: return stg::Special::Kind::VOID; case Special::VARIADIC: return stg::Special::Kind::VARIADIC; case Special::NULLPTR: return stg::Special::Kind::NULLPTR; default: Die() << "unknown Special::Kind " << x; } } stg::PointerReference::Kind Transformer::Transform(PointerReference::Kind x) { switch (x) { case PointerReference::POINTER: return stg::PointerReference::Kind::POINTER; case PointerReference::LVALUE_REFERENCE: return stg::PointerReference::Kind::LVALUE_REFERENCE; case PointerReference::RVALUE_REFERENCE: return stg::PointerReference::Kind::RVALUE_REFERENCE; default: Die() << "unknown PointerReference::Kind " << x; } } stg::Qualifier Transformer::Transform(Qualified::Qualifier x) { switch (x) { case Qualified::CONST: return stg::Qualifier::CONST; case Qualified::VOLATILE: return stg::Qualifier::VOLATILE; case Qualified::RESTRICT: return stg::Qualifier::RESTRICT; case Qualified::ATOMIC: return stg::Qualifier::ATOMIC; default: Die() << "unknown Qualified::Qualifier " << x; } } stg::Primitive::Encoding Transformer::Transform(Primitive::Encoding x) { switch (x) { case Primitive::BOOLEAN: return stg::Primitive::Encoding::BOOLEAN; case Primitive::SIGNED_INTEGER: return stg::Primitive::Encoding::SIGNED_INTEGER; case Primitive::UNSIGNED_INTEGER: return stg::Primitive::Encoding::UNSIGNED_INTEGER; case Primitive::SIGNED_CHARACTER: return stg::Primitive::Encoding::SIGNED_CHARACTER; case Primitive::UNSIGNED_CHARACTER: return stg::Primitive::Encoding::UNSIGNED_CHARACTER; case Primitive::REAL_NUMBER: return stg::Primitive::Encoding::REAL_NUMBER; case Primitive::COMPLEX_NUMBER: return stg::Primitive::Encoding::COMPLEX_NUMBER; case Primitive::UTF: return stg::Primitive::Encoding::UTF; default: Die() << "unknown Primitive::Encoding " << x; } } stg::BaseClass::Inheritance Transformer::Transform(BaseClass::Inheritance x) { switch (x) { case BaseClass::NON_VIRTUAL: return stg::BaseClass::Inheritance::NON_VIRTUAL; case BaseClass::VIRTUAL: return stg::BaseClass::Inheritance::VIRTUAL; default: Die() << "unknown BaseClass::Inheritance " << x; } } stg::StructUnion::Kind Transformer::Transform(StructUnion::Kind x) { switch (x) { case StructUnion::STRUCT: return stg::StructUnion::Kind::STRUCT; case StructUnion::UNION: return stg::StructUnion::Kind::UNION; default: Die() << "unknown StructUnion::Kind " << x; } } stg::ElfSymbol::SymbolType Transformer::Transform(ElfSymbol::SymbolType x) { switch (x) { case ElfSymbol::NOTYPE: return stg::ElfSymbol::SymbolType::NOTYPE; case ElfSymbol::OBJECT: return stg::ElfSymbol::SymbolType::OBJECT; case ElfSymbol::FUNCTION: return stg::ElfSymbol::SymbolType::FUNCTION; case ElfSymbol::COMMON: return stg::ElfSymbol::SymbolType::COMMON; case ElfSymbol::TLS: return stg::ElfSymbol::SymbolType::TLS; case ElfSymbol::GNU_IFUNC: return stg::ElfSymbol::SymbolType::GNU_IFUNC; default: Die() << "unknown ElfSymbol::SymbolType " << x; } } stg::ElfSymbol::Binding Transformer::Transform(ElfSymbol::Binding x) { switch (x) { case ElfSymbol::GLOBAL: return stg::ElfSymbol::Binding::GLOBAL; case ElfSymbol::LOCAL: return stg::ElfSymbol::Binding::LOCAL; case ElfSymbol::WEAK: return stg::ElfSymbol::Binding::WEAK; case ElfSymbol::GNU_UNIQUE: return stg::ElfSymbol::Binding::GNU_UNIQUE; default: Die() << "unknown ElfSymbol::Binding " << x; } } stg::ElfSymbol::Visibility Transformer::Transform(ElfSymbol::Visibility x) { switch (x) { case ElfSymbol::DEFAULT: return stg::ElfSymbol::Visibility::DEFAULT; case ElfSymbol::PROTECTED: return stg::ElfSymbol::Visibility::PROTECTED; case ElfSymbol::HIDDEN: return stg::ElfSymbol::Visibility::HIDDEN; case ElfSymbol::INTERNAL: return stg::ElfSymbol::Visibility::INTERNAL; default: Die() << "unknown ElfSymbol::Visibility " << x; } } stg::Enumeration::Enumerators Transformer::Transform( const google::protobuf::RepeatedPtrField& x) { stg::Enumeration::Enumerators enumerators; enumerators.reserve(x.size()); for (const auto& enumerator : x) { enumerators.emplace_back(enumerator.name(), enumerator.value()); } return enumerators; } template std::optional Transformer::Transform(bool has_field, const ProtoType& field) { return has_field ? std::make_optional(Transform(field)) : std::nullopt; } template Type Transformer::Transform(const Type& x) { return x; } const std::array kSupportedFormatVersions = {0, 1, 2}; void CheckFormatVersion(uint32_t version) { Check(std::binary_search(kSupportedFormatVersions.begin(), kSupportedFormatVersions.end(), version)) << "STG format version " << version << " is not supported, minimum supported version: " << kSupportedFormatVersions.front(); if (version != kSupportedFormatVersions.back()) { Warn() << "STG format version " << version << " is deprecated, consider upgrading to the latest version (" << kSupportedFormatVersions.back() << ")"; } } class ErrorSink : public google::protobuf::io::ErrorCollector { public: void AddError(int line, google::protobuf::io::ColumnNumber column, const std::string& message) final { Moan("error", line, column, message); } void AddWarning(int line, google::protobuf::io::ColumnNumber column, const std::string& message) final { Moan("warning", line, column, message); } private: static void Moan(std::string_view which, int line, google::protobuf::io::ColumnNumber column, const std::string& message) { Warn() << "google::protobuf::TextFormat " << which << " at line " << (line + 1) << " column " << (column + 1) << ": " << message; } }; Id ReadHelper(Runtime& runtime, Graph& graph, google::protobuf::io::ZeroCopyInputStream& is) { proto::STG stg; { const Time t(runtime, "proto.Parse"); ErrorSink error_sink; google::protobuf::TextFormat::Parser parser; parser.RecordErrorsTo(&error_sink); Check(parser.Parse(&is, &stg)) << "failed to parse input as STG"; } { const Time t(runtime, "proto.Transform"); CheckFormatVersion(stg.version()); return Transformer(graph).Transform(stg); } } } // namespace Id Read(Runtime& runtime, Graph& graph, const std::string& path) { std::ifstream ifs(path); Check(ifs.good()) << "error opening file '" << path << "' for reading: " << Error(errno); google::protobuf::io::IstreamInputStream is(&ifs); return ReadHelper(runtime, graph, is); } Id ReadFromString(Runtime& runtime, Graph& graph, std::string_view input) { Check(input.size() <= std::numeric_limits::max()) << "input too big"; google::protobuf::io::ArrayInputStream is(input.data(), static_cast(input.size())); return ReadHelper(runtime, graph, is); } } // namespace proto } // namespace stg