blob: 122872ae7c58643083315a517aaee7ad386a83a4 [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_writer.h"
#include <algorithm>
#include <cstdint>
#include <functional>
#include <iomanip>
#include <ios>
#include <ostream>
#include <sstream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <google/protobuf/repeated_ptr_field.h>
#include <google/protobuf/text_format.h>
#include "graph.h"
#include "stable_hash.h"
#include "stg.pb.h"
namespace stg {
namespace proto {
namespace {
class StableId {
public:
explicit StableId(const Graph& graph) : stable_hash_(graph) {}
uint32_t operator()(Id id) {
return stable_hash_(id).value;
}
private:
StableHash stable_hash_;
};
template <typename MapId>
struct Transform {
Transform(const Graph& graph, proto::STG& stg, MapId& map_id)
: graph(graph), stg(stg), map_id(map_id) {}
uint32_t operator()(Id);
void operator()(const stg::Special&, uint32_t);
void operator()(const stg::PointerReference&, uint32_t);
void operator()(const stg::PointerToMember&, uint32_t);
void operator()(const stg::Typedef&, uint32_t);
void operator()(const stg::Qualified&, uint32_t);
void operator()(const stg::Primitive&, uint32_t);
void operator()(const stg::Array&, uint32_t);
void operator()(const stg::BaseClass&, uint32_t);
void operator()(const stg::Method&, uint32_t);
void operator()(const stg::Member&, uint32_t);
void operator()(const stg::StructUnion&, uint32_t);
void operator()(const stg::Enumeration&, uint32_t);
void operator()(const stg::Function&, uint32_t);
void operator()(const stg::ElfSymbol&, uint32_t);
void operator()(const stg::Interface&, uint32_t);
Special::Kind operator()(stg::Special::Kind);
PointerReference::Kind operator()(stg::PointerReference::Kind);
Qualified::Qualifier operator()(stg::Qualifier);
Primitive::Encoding operator()(stg::Primitive::Encoding);
BaseClass::Inheritance operator()(stg::BaseClass::Inheritance);
StructUnion::Kind operator()(stg::StructUnion::Kind);
ElfSymbol::SymbolType operator()(stg::ElfSymbol::SymbolType);
ElfSymbol::Binding operator()(stg::ElfSymbol::Binding);
ElfSymbol::Visibility operator()(stg::ElfSymbol::Visibility);
const Graph& graph;
proto::STG& stg;
std::unordered_map<Id, uint32_t> external_id;
std::unordered_set<uint32_t> used_ids;
// Function object: Id -> uint32_t
MapId& map_id;
};
template <typename MapId>
uint32_t Transform<MapId>::operator()(Id id) {
auto [it, inserted] = external_id.emplace(id, 0);
if (inserted) {
uint32_t mapped_id = map_id(id);
// Ensure uniqueness of external ids. It is best to probe here since id
// generators will not in general guarantee that the mapping from internal
// ids to external ids will be injective.
while (!used_ids.insert(mapped_id).second) {
++mapped_id;
}
it->second = mapped_id;
graph.Apply<void>(*this, id, mapped_id);
}
return it->second;
}
template <typename MapId>
void Transform<MapId>::operator()(const stg::Special& x, uint32_t id) {
auto& special = *stg.add_special();
special.set_id(id);
special.set_kind((*this)(x.kind));
}
template <typename MapId>
void Transform<MapId>::operator()(const stg::PointerReference& x, uint32_t id) {
auto& pointer_reference = *stg.add_pointer_reference();
pointer_reference.set_id(id);
pointer_reference.set_kind((*this)(x.kind));
pointer_reference.set_pointee_type_id((*this)(x.pointee_type_id));
}
template <typename MapId>
void Transform<MapId>::operator()(const stg::PointerToMember& x, uint32_t id) {
auto& pointer_to_member = *stg.add_pointer_to_member();
pointer_to_member.set_id(id);
pointer_to_member.set_containing_type_id((*this)(x.containing_type_id));
pointer_to_member.set_pointee_type_id((*this)(x.pointee_type_id));
}
template <typename MapId>
void Transform<MapId>::operator()(const stg::Typedef& x, uint32_t id) {
auto& typedef_ = *stg.add_typedef_();
typedef_.set_id(id);
typedef_.set_name(x.name);
typedef_.set_referred_type_id((*this)(x.referred_type_id));
}
template <typename MapId>
void Transform<MapId>::operator()(const stg::Qualified& x, uint32_t id) {
auto& qualified = *stg.add_qualified();
qualified.set_id(id);
qualified.set_qualifier((*this)(x.qualifier));
qualified.set_qualified_type_id((*this)(x.qualified_type_id));
}
template <typename MapId>
void Transform<MapId>::operator()(const stg::Primitive& x, uint32_t id) {
auto& primitive = *stg.add_primitive();
primitive.set_id(id);
primitive.set_name(x.name);
if (x.encoding) {
primitive.set_encoding((*this)(*x.encoding));
}
primitive.set_bytesize(x.bytesize);
}
template <typename MapId>
void Transform<MapId>::operator()(const stg::Array& x, uint32_t id) {
auto& array = *stg.add_array();
array.set_id(id);
array.set_number_of_elements(x.number_of_elements);
array.set_element_type_id((*this)(x.element_type_id));
}
template <typename MapId>
void Transform<MapId>::operator()(const stg::BaseClass& x, uint32_t id) {
auto& base_class = *stg.add_base_class();
base_class.set_id(id);
base_class.set_type_id((*this)(x.type_id));
base_class.set_offset(x.offset);
base_class.set_inheritance((*this)(x.inheritance));
}
template <typename MapId>
void Transform<MapId>::operator()(const stg::Method& x, uint32_t id) {
auto& method = *stg.add_method();
method.set_id(id);
method.set_mangled_name(x.mangled_name);
method.set_name(x.name);
method.set_vtable_offset(x.vtable_offset);
method.set_type_id((*this)(x.type_id));
}
template <typename MapId>
void Transform<MapId>::operator()(const stg::Member& x, uint32_t id) {
auto& member = *stg.add_member();
member.set_id(id);
member.set_name(x.name);
member.set_type_id((*this)(x.type_id));
member.set_offset(x.offset);
member.set_bitsize(x.bitsize);
}
template <typename MapId>
void Transform<MapId>::operator()(const stg::StructUnion& x, uint32_t id) {
auto& struct_union = *stg.add_struct_union();
struct_union.set_id(id);
struct_union.set_kind((*this)(x.kind));
struct_union.set_name(x.name);
if (x.definition) {
auto& definition = *struct_union.mutable_definition();
definition.set_bytesize(x.definition->bytesize);
for (const auto id : x.definition->base_classes) {
definition.add_base_class_id((*this)(id));
}
for (const auto id : x.definition->methods) {
definition.add_method_id((*this)(id));
}
for (const auto id : x.definition->members) {
definition.add_member_id((*this)(id));
}
}
}
template <typename MapId>
void Transform<MapId>::operator()(const stg::Enumeration& x, uint32_t id) {
auto& enumeration = *stg.add_enumeration();
enumeration.set_id(id);
enumeration.set_name(x.name);
if (x.definition) {
auto& definition = *enumeration.mutable_definition();
definition.set_underlying_type_id(
(*this)(x.definition->underlying_type_id));
for (const auto& [name, value] : x.definition->enumerators) {
auto& enumerator = *definition.add_enumerator();
enumerator.set_name(name);
enumerator.set_value(value);
}
}
}
template <typename MapId>
void Transform<MapId>::operator()(const stg::Function& x, uint32_t id) {
auto& function = *stg.add_function();
function.set_id(id);
function.set_return_type_id((*this)(x.return_type_id));
for (const auto id : x.parameters) {
function.add_parameter_id((*this)(id));
}
}
template <typename MapId>
void Transform<MapId>::operator()(const stg::ElfSymbol& x, uint32_t id) {
auto& elf_symbol = *stg.add_elf_symbol();
elf_symbol.set_id(id);
elf_symbol.set_name(x.symbol_name);
if (x.version_info) {
auto& version_info = *elf_symbol.mutable_version_info();
version_info.set_is_default(x.version_info->is_default);
version_info.set_name(x.version_info->name);
}
elf_symbol.set_is_defined(x.is_defined);
elf_symbol.set_symbol_type((*this)(x.symbol_type));
elf_symbol.set_binding((*this)(x.binding));
elf_symbol.set_visibility((*this)(x.visibility));
if (x.crc) {
elf_symbol.set_crc(x.crc->number);
}
if (x.ns) {
elf_symbol.set_namespace_(*x.ns);
}
if (x.type_id) {
elf_symbol.set_type_id((*this)(*x.type_id));
}
if (x.full_name) {
elf_symbol.set_full_name(*x.full_name);
}
}
template <typename MapId>
void Transform<MapId>::operator()(const stg::Interface& x, uint32_t id) {
auto& interface = *stg.add_interface();
interface.set_id(id);
for (const auto& [_, id] : x.symbols) {
interface.add_symbol_id((*this)(id));
}
for (const auto& [_, id] : x.types) {
interface.add_type_id((*this)(id));
}
}
template <typename MapId>
PointerReference::Kind Transform<MapId>::operator()(
stg::PointerReference::Kind x) {
switch (x) {
case stg::PointerReference::Kind::POINTER:
return PointerReference::POINTER;
case stg::PointerReference::Kind::LVALUE_REFERENCE:
return PointerReference::LVALUE_REFERENCE;
case stg::PointerReference::Kind::RVALUE_REFERENCE:
return PointerReference::RVALUE_REFERENCE;
}
}
template <typename MapId>
Special::Kind Transform<MapId>::operator()(
stg::Special::Kind x) {
switch (x) {
case stg::Special::Kind::VOID:
return Special::VOID;
case stg::Special::Kind::VARIADIC:
return Special::VARIADIC;
case stg::Special::Kind::NULLPTR:
return Special::NULLPTR;
}
}
template <typename MapId>
Qualified::Qualifier Transform<MapId>::operator()(stg::Qualifier x) {
switch (x) {
case stg::Qualifier::CONST:
return Qualified::CONST;
case stg::Qualifier::VOLATILE:
return Qualified::VOLATILE;
case stg::Qualifier::RESTRICT:
return Qualified::RESTRICT;
case stg::Qualifier::ATOMIC:
return Qualified::ATOMIC;
}
}
template <typename MapId>
Primitive::Encoding Transform<MapId>::operator()(stg::Primitive::Encoding x) {
switch (x) {
case stg::Primitive::Encoding::BOOLEAN:
return Primitive::BOOLEAN;
case stg::Primitive::Encoding::SIGNED_INTEGER:
return Primitive::SIGNED_INTEGER;
case stg::Primitive::Encoding::UNSIGNED_INTEGER:
return Primitive::UNSIGNED_INTEGER;
case stg::Primitive::Encoding::SIGNED_CHARACTER:
return Primitive::SIGNED_CHARACTER;
case stg::Primitive::Encoding::UNSIGNED_CHARACTER:
return Primitive::UNSIGNED_CHARACTER;
case stg::Primitive::Encoding::REAL_NUMBER:
return Primitive::REAL_NUMBER;
case stg::Primitive::Encoding::COMPLEX_NUMBER:
return Primitive::COMPLEX_NUMBER;
case stg::Primitive::Encoding::UTF:
return Primitive::UTF;
}
}
template <typename MapId>
BaseClass::Inheritance Transform<MapId>::operator()(
stg::BaseClass::Inheritance x) {
switch (x) {
case stg::BaseClass::Inheritance::NON_VIRTUAL:
return BaseClass::NON_VIRTUAL;
case stg::BaseClass::Inheritance::VIRTUAL:
return BaseClass::VIRTUAL;
}
}
template <typename MapId>
StructUnion::Kind Transform<MapId>::operator()(stg::StructUnion::Kind x) {
switch (x) {
case stg::StructUnion::Kind::STRUCT:
return StructUnion::STRUCT;
case stg::StructUnion::Kind::UNION:
return StructUnion::UNION;
}
}
template <typename MapId>
ElfSymbol::SymbolType Transform<MapId>::operator()(
stg::ElfSymbol::SymbolType x) {
switch (x) {
case stg::ElfSymbol::SymbolType::OBJECT:
return ElfSymbol::OBJECT;
case stg::ElfSymbol::SymbolType::FUNCTION:
return ElfSymbol::FUNCTION;
case stg::ElfSymbol::SymbolType::COMMON:
return ElfSymbol::COMMON;
case stg::ElfSymbol::SymbolType::TLS:
return ElfSymbol::TLS;
case stg::ElfSymbol::SymbolType::GNU_IFUNC:
return ElfSymbol::GNU_IFUNC;
}
}
template <typename MapId>
ElfSymbol::Binding Transform<MapId>::operator()(stg::ElfSymbol::Binding x) {
switch (x) {
case stg::ElfSymbol::Binding::GLOBAL:
return ElfSymbol::GLOBAL;
case stg::ElfSymbol::Binding::LOCAL:
return ElfSymbol::LOCAL;
case stg::ElfSymbol::Binding::WEAK:
return ElfSymbol::WEAK;
case stg::ElfSymbol::Binding::GNU_UNIQUE:
return ElfSymbol::GNU_UNIQUE;
}
}
template <typename MapId>
ElfSymbol::Visibility Transform<MapId>::operator()(
stg::ElfSymbol::Visibility x) {
switch (x) {
case stg::ElfSymbol::Visibility::DEFAULT:
return ElfSymbol::DEFAULT;
case stg::ElfSymbol::Visibility::PROTECTED:
return ElfSymbol::PROTECTED;
case stg::ElfSymbol::Visibility::HIDDEN:
return ElfSymbol::HIDDEN;
case stg::ElfSymbol::Visibility::INTERNAL:
return ElfSymbol::INTERNAL;
}
}
template <typename ProtoNode>
void SortNodesById(google::protobuf::RepeatedPtrField<ProtoNode>& nodes) {
std::sort(
nodes.pointer_begin(), nodes.pointer_end(),
[](const auto* lhs, const auto* rhs) { return lhs->id() < rhs->id(); });
}
template <typename ProtoNode>
void SortNodesByName(google::protobuf::RepeatedPtrField<ProtoNode>& nodes) {
const auto compare = [](const auto* lhs, const auto* rhs) {
const int comparison = lhs->name().compare(rhs->name());
return comparison < 0 || (comparison == 0 && lhs->id() < rhs->id());
};
std::sort(nodes.pointer_begin(), nodes.pointer_end(), compare);
}
void SortMethodsByMangledName(google::protobuf::RepeatedPtrField<Method>& methods) {
const auto compare = [](const Method* lhs, const Method* rhs) {
const int comparison = lhs->mangled_name().compare(rhs->mangled_name());
return comparison < 0 || (comparison == 0 && lhs->id() < rhs->id());
};
std::sort(methods.pointer_begin(), methods.pointer_end(), compare);
}
void SortElfSymbolsByVersionedName(
google::protobuf::RepeatedPtrField<ElfSymbol>& elf_symbols) {
// TODO: use spaceship operator <=>
const auto compare = [](const ElfSymbol* lhs, const ElfSymbol* rhs) {
if (const int c = lhs->name().compare(rhs->name()); c != 0) {
return c < 0;
}
// Put symbols with version info after those without version info.
if (lhs->has_version_info() != rhs->has_version_info()) {
return rhs->has_version_info();
}
if (lhs->has_version_info()) {
const auto& l_version = lhs->version_info();
const auto& r_version = rhs->version_info();
if (const int c = l_version.name().compare(r_version.name()); c != 0) {
return c < 0;
}
// Put symbols with default version before those with non-default version.
if (l_version.is_default() != r_version.is_default()) {
return r_version.is_default();
}
}
return lhs->id() < rhs->id();
};
std::sort(elf_symbols.pointer_begin(), elf_symbols.pointer_end(), compare);
}
void SortNodes(STG& stg) {
SortNodesById(*stg.mutable_void_());
SortNodesById(*stg.mutable_variadic());
SortNodesById(*stg.mutable_pointer_reference());
SortNodesById(*stg.mutable_pointer_to_member());
SortNodesByName(*stg.mutable_typedef_());
SortNodesById(*stg.mutable_qualified());
SortNodesById(*stg.mutable_primitive());
SortNodesById(*stg.mutable_array());
SortNodesById(*stg.mutable_base_class());
SortMethodsByMangledName(*stg.mutable_method());
SortNodesByName(*stg.mutable_member());
SortNodesByName(*stg.mutable_struct_union());
SortNodesByName(*stg.mutable_enumeration());
SortNodesById(*stg.mutable_function());
SortElfSymbolsByVersionedName(*stg.mutable_elf_symbol());
}
class HexPrinter : public google::protobuf::TextFormat::FastFieldValuePrinter {
void PrintUInt32(
uint32_t value,
google::protobuf::TextFormat::BaseTextGenerator* generator) const override {
std::ostringstream os;
// 0x01234567
os << std::showbase << std::hex << std::setfill('0') << std::internal
<< std::setw(10) << value;
generator->PrintString(os.str());
}
};
const uint32_t kWrittenFormatVersion = 2;
} // namespace
void Print(const STG& stg, std::ostream& os) {
google::protobuf::TextFormat::Printer printer;
printer.SetDefaultFieldValuePrinter(new HexPrinter());
std::string output;
printer.PrintToString(stg, &output);
os << output;
}
void Writer::Write(const Id& root, std::ostream& os) {
proto::STG stg;
StableId stable_id(graph_);
stg.set_root_id(Transform<StableId>(graph_, stg, stable_id)(root));
SortNodes(stg);
stg.set_version(kWrittenFormatVersion);
Print(stg, os);
}
} // namespace proto
} // namespace stg