blob: 246daacb6ae478de49f6778d7c79b2477b8d7cf1 [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright 2022 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 <algorithm>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/repeated_field.h>
#include <google/protobuf/text_format.h>
#include "error.h"
#include "graph.h"
#include "stg.pb.h"
namespace stg {
namespace proto {
namespace {
struct Transformer {
explicit Transformer(Graph& graph) : graph(graph) {}
Id Transform(const proto::STG&);
Id GetId(uint32_t);
template <typename ProtoType>
void AddNodes(const google::protobuf::RepeatedPtrField<ProtoType>&);
void AddNode(const Void&);
void AddNode(const Variadic&);
void AddNode(const PointerReference&);
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 StructUnion&);
void AddNode(const Enumeration&);
void AddNode(const Function&);
void AddNode(const ElfSymbol&);
void AddNode(const Symbols&);
template <typename STGType, typename... Args>
void AddNode(Args&&...);
std::vector<Id> Transform(const google::protobuf::RepeatedField<uint32_t>&);
stg::PointerReference::Kind Transform(PointerReference::Kind);
stg::Qualifier Transform(Qualified::Qualifier);
stg::Primitive::Encoding Transform(Primitive::Encoding);
stg::BaseClass::Inheritance Transform(BaseClass::Inheritance);
stg::Method::Kind Transform(Method::Kind);
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<Enumeration::Enumerator>&);
template <typename STGType, typename ProtoType>
std::optional<STGType> Transform(bool, const ProtoType&);
template <typename Type>
Type Transform(const Type&);
Graph& graph;
std::unordered_map<uint32_t, Id> id_map_;
};
Id Transformer::Transform(const proto::STG& x) {
AddNodes(x.void_());
AddNodes(x.variadic());
AddNodes(x.pointer_reference());
AddNodes(x.typedef_());
AddNodes(x.qualified());
AddNodes(x.primitive());
AddNodes(x.array());
AddNodes(x.base_class());
AddNodes(x.method());
AddNodes(x.member());
AddNodes(x.struct_union());
AddNodes(x.enumeration());
AddNodes(x.function());
AddNodes(x.elf_symbol());
if (x.has_symbols()) {
AddNode(x.symbols());
}
return GetId(x.root_id());
}
Id Transformer::GetId(uint32_t id) {
auto [it, inserted] = id_map_.emplace(id, 0);
if (inserted) {
it->second = graph.Allocate();
}
return it->second;
}
template <typename ProtoType>
void Transformer::AddNodes(const google::protobuf::RepeatedPtrField<ProtoType>& x) {
for (const ProtoType& proto : x) {
AddNode(proto);
}
}
void Transformer::AddNode(const Void& x) { AddNode<stg::Void>(GetId(x.id())); }
void Transformer::AddNode(const Variadic& x) {
AddNode<stg::Variadic>(GetId(x.id()));
}
void Transformer::AddNode(const PointerReference& x) {
AddNode<stg::PointerReference>(GetId(x.id()), x.kind(),
GetId(x.pointee_type_id()));
}
void Transformer::AddNode(const Typedef& x) {
AddNode<stg::Typedef>(GetId(x.id()), x.name(), GetId(x.referred_type_id()));
}
void Transformer::AddNode(const Qualified& x) {
AddNode<stg::Qualified>(GetId(x.id()), x.qualifier(),
GetId(x.qualified_type_id()));
}
void Transformer::AddNode(const Primitive& x) {
const auto& encoding =
Transform<stg::Primitive::Encoding>(x.has_encoding(), x.encoding());
AddNode<stg::Primitive>(GetId(x.id()), x.name(), encoding, x.bitsize(),
x.bytesize());
}
void Transformer::AddNode(const Array& x) {
AddNode<stg::Array>(GetId(x.id()), x.number_of_elements(),
GetId(x.element_type_id()));
}
void Transformer::AddNode(const BaseClass& x) {
AddNode<stg::BaseClass>(GetId(x.id()), GetId(x.type_id()), x.offset(),
x.inheritance());
}
void Transformer::AddNode(const Method& x) {
const auto& vtable_offset =
Transform<uint64_t>(x.has_vtable_offset(), x.vtable_offset());
AddNode<stg::Method>(GetId(x.id()), x.mangled_name(), x.name(), x.kind(),
vtable_offset, GetId(x.type_id()));
}
void Transformer::AddNode(const Member& x) {
AddNode<stg::Member>(GetId(x.id()), x.name(), GetId(x.type_id()), x.offset(),
x.bitsize());
}
void Transformer::AddNode(const StructUnion& x) {
if (x.has_definition()) {
AddNode<stg::StructUnion>(
GetId(x.id()), x.kind(), x.name(), x.definition().bytesize(),
x.definition().base_class_id(), x.definition().method_id(),
x.definition().member_id());
} else {
AddNode<stg::StructUnion>(GetId(x.id()), x.kind(), x.name());
}
}
void Transformer::AddNode(const Enumeration& x) {
if (x.has_definition()) {
AddNode<stg::Enumeration>(GetId(x.id()), x.name(),
x.definition().bytesize(),
x.definition().enumerator());
return;
} else {
AddNode<stg::Enumeration>(GetId(x.id()), x.name());
}
}
void Transformer::AddNode(const Function& x) {
AddNode<stg::Function>(GetId(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()});
};
std::optional<stg::ElfSymbol::VersionInfo> version_info =
x.has_version_info() ? make_version_info(x.version_info()) : std::nullopt;
const auto& crc = x.has_crc()
? std::make_optional<stg::ElfSymbol::CRC>(x.crc())
: std::nullopt;
const auto& ns = Transform<std::string>(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<std::string>(x.has_full_name(), x.full_name());
AddNode<stg::ElfSymbol>(GetId(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<std::string, Id> symbols;
for (const auto& [symbol, id] : x.symbol()) {
symbols.emplace(symbol, GetId(id));
}
AddNode<stg::Symbols>(GetId(x.id()), symbols);
}
template <typename STGType, typename... Args>
void Transformer::AddNode(Args&&... args) {
graph.Set<STGType>(Transform(args)...);
}
std::vector<Id> Transformer::Transform(
const google::protobuf::RepeatedField<uint32_t>& ids) {
std::vector<Id> result;
result.reserve(ids.size());
for (uint32_t id : ids) {
result.push_back(GetId(id));
}
return result;
}
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() << "Encountered unsupported value for PointerReference Kind " << x
<< '\n';
}
}
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;
default:
Die() << "Encountered unsupported value for Qualifier " << x << '\n';
}
}
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() << "Encountered unsupported value for Primitive type Encoding " << x
<< '\n';
}
}
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() << "Encountered unsupported value for BaseClass Inheritance " << x
<< '\n';
}
}
stg::Method::Kind Transformer::Transform(Method::Kind x) {
switch (x) {
case Method::NON_VIRTUAL:
return stg::Method::Kind::NON_VIRTUAL;
case Method::STATIC:
return stg::Method::Kind::STATIC;
case Method::VIRTUAL:
return stg::Method::Kind::VIRTUAL;
default:
Die() << "Encountered unsupported value for Method Kind " << x << '\n';
}
}
stg::StructUnion::Kind Transformer::Transform(StructUnion::Kind x) {
switch (x) {
case StructUnion::CLASS:
return stg::StructUnion::Kind::CLASS;
case StructUnion::STRUCT:
return stg::StructUnion::Kind::STRUCT;
case StructUnion::UNION:
return stg::StructUnion::Kind::UNION;
default:
Die() << "Encountered unsupported value for StructUnion Kind " << x
<< '\n';
}
}
stg::ElfSymbol::SymbolType Transformer::Transform(ElfSymbol::SymbolType x) {
switch (x) {
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;
default:
Die() << "Encountered unsupported value for ElfSymbol Type " << x << '\n';
}
}
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() << "Encountered unsupported value for ElfSymbol Binding " << x
<< '\n';
}
}
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() << "Encountered unsupported value for ElfSymbol Visibility " << x
<< '\n';
}
}
stg::Enumeration::Enumerators Transformer::Transform(
const google::protobuf::RepeatedPtrField<Enumeration::Enumerator>& x) {
stg::Enumeration::Enumerators enumerators;
enumerators.reserve(x.size());
for (const auto& enumerator : x) {
enumerators.emplace_back(enumerator.name(), enumerator.value());
}
return enumerators;
}
template <typename STGType, typename ProtoType>
std::optional<STGType> Transformer::Transform(bool has_field,
const ProtoType& field) {
return has_field ? std::make_optional<STGType>(Transform(field))
: std::nullopt;
}
template <typename Type>
Type Transformer::Transform(const Type& x) {
return x;
}
} // namespace
Id Read(Graph& graph, const std::string& path) {
std::ifstream ifs(path);
google::protobuf::io::IstreamInputStream is(&ifs);
proto::STG stg;
google::protobuf::TextFormat::Parse(&is, &stg);
return Transformer(graph).Transform(stg);
}
Id ReadFromString(Graph& graph, const std::string_view input) {
proto::STG stg;
google::protobuf::TextFormat::ParseFromString(std::string(input), &stg);
return Transformer(graph).Transform(stg);
}
} // namespace proto
} // namespace stg