blob: d5572a0b8d265b9efc1bc06074479a5368847da8 [file] [log] [blame]
#include "dwarf_processor.h"
#include <dwarf.h>
#include <ios>
#include <iostream>
#include <optional>
#include <sstream>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "dwarf_wrappers.h"
#include "error.h"
#include "graph.h"
namespace stg {
namespace dwarf {
namespace {
std::string EntryToString(Entry& entry) {
std::ostringstream os;
os << "DWARF entry <0x" << std::hex << entry.GetOffset() << ">";
return os.str();
}
std::optional<std::string> MaybeGetName(Entry& entry) {
return entry.MaybeGetString(DW_AT_name);
}
std::string GetName(Entry& entry) {
auto result = MaybeGetName(entry);
if (!result.has_value()) {
Die() << "Name was not found for " << EntryToString(entry);
}
return std::move(*result);
}
size_t GetBitSize(Entry& entry) {
if (auto byte_size = entry.MaybeGetUnsignedConstant(DW_AT_byte_size)) {
return *byte_size * 8;
} else if (auto bit_size = entry.MaybeGetUnsignedConstant(DW_AT_bit_size)) {
return *bit_size;
}
Die() << "Bit size was not found for " << EntryToString(entry);
}
Primitive::Encoding GetEncoding(Entry& entry) {
auto dwarf_encoding = entry.MaybeGetUnsignedConstant(DW_AT_encoding);
if (!dwarf_encoding) {
Die() << "Encoding was not found for " << EntryToString(entry);
}
switch (*dwarf_encoding) {
case DW_ATE_boolean:
return Primitive::Encoding::BOOLEAN;
case DW_ATE_complex_float:
return Primitive::Encoding::COMPLEX_NUMBER;
case DW_ATE_float:
return Primitive::Encoding::REAL_NUMBER;
case DW_ATE_signed:
return Primitive::Encoding::SIGNED_INTEGER;
case DW_ATE_signed_char:
return Primitive::Encoding::SIGNED_CHARACTER;
case DW_ATE_unsigned:
return Primitive::Encoding::UNSIGNED_INTEGER;
case DW_ATE_unsigned_char:
return Primitive::Encoding::UNSIGNED_CHARACTER;
case DW_ATE_UTF:
return Primitive::Encoding::UTF;
default:
Die() << "Unknown encoding 0x" << std::hex << *dwarf_encoding << " for "
<< EntryToString(entry);
}
}
std::optional<Entry> MaybeGetReferredType(Entry& entry) {
return entry.MaybeGetReference(DW_AT_type);
}
} // namespace
// Transforms DWARF entries to STG.
class Processor {
public:
Processor(Graph& graph, Id void_id, Types& result)
: graph_(graph), void_id_(void_id), result_(result) {}
void Process(Entry& entry) {
++result_.processed_entries;
auto tag = entry.GetTag();
switch (tag) {
case DW_TAG_compile_unit:
ProcessCompileUnit(entry);
break;
case DW_TAG_typedef:
ProcessTypedef(entry);
break;
case DW_TAG_base_type:
ProcessBaseType(entry);
break;
default:
// TODO: die on unexpected tag, when this switch contains
// all expected tags
break;
}
}
std::vector<Id> GetUnresolvedIds() {
std::vector<Id> result;
for (const auto& [offset, id] : id_map_) {
if (!graph_.Is(id)) {
result.push_back(id);
}
}
return result;
}
private:
void ProcessAllChildren(Entry& entry) {
for (auto& child : entry.GetChildren()) {
Process(child);
}
}
void CheckNoChildren(Entry& entry) {
if (!entry.GetChildren().empty()) {
Die() << "Entry expected to have no children";
}
}
void ProcessCompileUnit(Entry& entry) {
ProcessAllChildren(entry);
}
void ProcessBaseType(Entry& entry) {
CheckNoChildren(entry);
auto type_name = GetName(entry);
size_t bit_size = GetBitSize(entry);
// Round up bit_size / 8 to get minimal needed storage size in bytes.
size_t byte_size = (bit_size + 7) / 8;
AddProcessedNode<Primitive>(entry, type_name, GetEncoding(entry), bit_size,
byte_size);
}
void ProcessTypedef(Entry& entry) {
std::string type_name = GetName(entry);
auto referred_type_id = GetIdForReferredType(MaybeGetReferredType(entry));
AddProcessedNode<Typedef>(entry, std::move(type_name), referred_type_id);
}
// Allocate or get already allocated STG Id for Entry.
Id GetIdForEntry(Entry& entry) {
const auto offset = entry.GetOffset();
const auto [it, emplaced] = id_map_.emplace(offset, Id(-1));
if (emplaced) {
it->second = graph_.Allocate();
}
return it->second;
}
// Same as GetIdForEntry, but returns "void_id_" for "unspecified" references,
// because it is normal for DWARF (5.2 Unspecified Type Entries).
Id GetIdForReferredType(std::optional<Entry> referred_type) {
return referred_type ? GetIdForEntry(*referred_type) : void_id_;
}
// Populate Id from method above with processed Node.
template <typename Node, typename... Args>
Id AddProcessedNode(Entry& entry, Args&&... args) {
auto id = GetIdForEntry(entry);
graph_.Set<Node>(id, std::forward<Args>(args)...);
result_.all_ids.push_back(id);
return id;
}
Graph& graph_;
Id void_id_;
Types& result_;
std::unordered_map<Dwarf_Off, Id> id_map_;
};
Types ProcessEntries(std::vector<Entry> entries, Graph& graph) {
Types result;
Id void_id = graph.Add<Void>();
Processor processor(graph, void_id, result);
for (auto& entry : entries) {
processor.Process(entry);
}
for (const auto& id : processor.GetUnresolvedIds()) {
// TODO: replace with "Die"
graph.Set<Variadic>(id);
}
return result;
}
Types Process(Handler& dwarf, Graph& graph) {
return ProcessEntries(dwarf.GetCompilationUnits(), graph);
}
} // namespace dwarf
} // namespace stg