| // Copyright 2016 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // 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. |
| // |
| // Classes for reading .debug_info and .debug_types. |
| // |
| // Usage overview: |
| // |
| // // Stores/caches abbreviation info and CU names. |
| // dwarf::InfoReader reader; |
| // |
| // // Iterator type for enumerating CUs. Initially positioned at the beginning |
| // // of the given section unless you pass an explicit offset. |
| // dwarf::CUIter iter = reader.GetCUIter( |
| // dwarf::InfoReader::Section::kDebugInfo); |
| // |
| // // Represents a single CU and vends a lot of useful data about it, like its |
| // // name. Starts out empty/undefined until you call NextCU(). |
| // dwarf::CU cu; |
| // |
| // while (iter.NextCU(reader, &cu)) { |
| // std::cout << "Parsing CU with name=" << cu.unit_name() << "\n"; |
| // |
| // // Iterator for enumerating DIEs in a given CU. |
| // dwarf::DIEReader die_reader = cu.GetDIEReader(); |
| // while (auto abbrev = die_reader.ReadCode(cu)) { |
| // if (IsInteresting(abbrev->tag)) { |
| // die_reader.ReadAttributes( |
| // cu, abbrev, [](uint16_t tag, dwarf::AttrValue val) { |
| // // Process attribute. |
| // }); |
| // } else { |
| // die_reader.SkipChildren(cu, abbrev); |
| // } |
| // } |
| // } |
| |
| #ifndef BLOATY_DWARF_DEBUG_INFO_H_ |
| #define BLOATY_DWARF_DEBUG_INFO_H_ |
| |
| #include <functional> |
| #include <string_view> |
| #include <unordered_map> |
| |
| #include "absl/strings/substitute.h" |
| #include "dwarf/attr.h" |
| #include "dwarf/dwarf_util.h" |
| #include "dwarf_constants.h" |
| #include "util.h" |
| |
| namespace bloaty { |
| |
| class InputFile; |
| class RangeSink; |
| |
| namespace dwarf { |
| |
| struct File; |
| |
| typedef void OpenDwarf(const InputFile &file, File *dwarf, RangeSink *sink); |
| |
| struct File { |
| std::string_view debug_abbrev; |
| std::string_view debug_addr; |
| std::string_view debug_aranges; |
| std::string_view debug_info; |
| std::string_view debug_line; |
| std::string_view debug_loc; |
| std::string_view debug_pubnames; |
| std::string_view debug_pubtypes; |
| std::string_view debug_ranges; |
| std::string_view debug_rnglists; |
| std::string_view debug_str; |
| std::string_view debug_str_offsets; |
| std::string_view debug_line_str; |
| std::string_view debug_types; |
| const InputFile* file; |
| OpenDwarf* open; |
| |
| std::string_view* GetFieldByName(std::string_view name); |
| void SetFieldByName(std::string_view name, std::string_view contents) { |
| std::string_view *member = GetFieldByName(name); |
| if (member) *member = contents; |
| } |
| }; |
| |
| // A class that represents the DWARF version and address sizes for a given |
| // compilation unit. |
| class CompilationUnitSizes { |
| public: |
| // When true, DWARF offsets are 64 bits, otherwise they are 32 bit. |
| bool dwarf64() const { return dwarf64_; } |
| |
| // The size of addresses. Guaranteed to be either 4 or 8. |
| uint8_t address_size() const { return addr8_ ? 8 : 4; } |
| |
| // DWARF version of this unit. |
| uint8_t dwarf_version() const { return dwarf_version_; } |
| |
| void SetAddressSize(uint8_t address_size) { |
| if (address_size != 4 && address_size != 8) { |
| THROWF("Unexpected address size: $0", address_size); |
| } |
| addr8_ = address_size == 8; |
| } |
| |
| // Reads a DWARF offset based on whether we are reading dwarf32 or dwarf64 |
| // format. |
| uint64_t ReadDWARFOffset(std::string_view* data) const { |
| return dwarf64_ ? ReadFixed<uint64_t>(data) : ReadFixed<uint32_t>(data); |
| } |
| |
| // Reads an address according to the expected address_size. |
| uint64_t ReadAddress(std::string_view* data) const { |
| return addr8_ ? ReadFixed<uint64_t>(data) : ReadFixed<uint32_t>(data); |
| } |
| |
| uint64_t MaxAddress() const { |
| return addr8_ ? 0xffffffffffffffff : 0xffffffff; |
| } |
| |
| // Reads an "initial length" as specified in many DWARF headers. This |
| // contains either a 32-bit or a 64-bit length, and signals whether we are |
| // using the 32-bit or 64-bit DWARF format (so it sets dwarf64 appropriately). |
| // |
| // Returns the range for this section and stores the remaining data |
| // in |remaining|. |
| std::string_view ReadInitialLength(std::string_view* remaining); |
| |
| void ReadDWARFVersion(std::string_view* data) { |
| dwarf_version_ = ReadFixed<uint16_t>(data); |
| } |
| |
| private: |
| uint16_t dwarf_version_; |
| bool dwarf64_; |
| bool addr8_; |
| }; |
| |
| // AbbrevTable ///////////////////////////////////////////////////////////////// |
| |
| // Parses and stores a representation of (a portion of) the .debug_abbrev |
| // section of a DWARF file. An abbreviation is defined by a unique "code" |
| // (unique within one table), and defines the DIE tag and set of attributes. |
| // The encoding of the DIE then contains just the abbreviation code and the |
| // attribute values -- thanks to the abbreviation table, the tag and attribute |
| // keys/names are not required. |
| // |
| // The abbreviations are an internal detail of the DWARF format and users should |
| // not need to care about them. |
| |
| class AbbrevTable { |
| public: |
| // Reads abbreviations until a terminating abbreviation is seen. |
| void ReadAbbrevs(std::string_view data); |
| |
| // In a DWARF abbreviation, each attribute has a name and a form. |
| struct Attribute { |
| uint16_t name; |
| uint16_t form; |
| }; |
| |
| // The representation of a single abbreviation. |
| struct Abbrev { |
| uint32_t code; |
| uint16_t tag; |
| bool has_child; |
| std::vector<Attribute> attr; |
| }; |
| |
| bool IsEmpty() const { return abbrev_.empty(); } |
| std::string_view abbrev_data() const { return abbrev_data_; } |
| |
| // Looks for an abbreviation with the given code. Returns true if the lookup |
| // succeeded. |
| bool GetAbbrev(uint32_t code, const Abbrev** abbrev) const { |
| auto it = abbrev_.find(code); |
| if (it != abbrev_.end()) { |
| *abbrev = &it->second; |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| private: |
| // Keyed by abbreviation code. |
| // Generally we expect these to be small, so we could almost use a vector<>. |
| // But you never know what crazy input data is going to do... |
| std::unordered_map<uint32_t, Abbrev> abbrev_; |
| std::string_view abbrev_data_; |
| }; |
| |
| class CUIter; |
| class CU; |
| class DIEReader; |
| |
| // Stores/caches abbreviation info and CU names. |
| class InfoReader { |
| public: |
| InfoReader(const File& file) : dwarf_(file) {} |
| InfoReader(const File& file, const CU* skeleton) |
| : dwarf_(file), skeleton_(skeleton) {} |
| InfoReader(const InfoReader&) = delete; |
| InfoReader& operator=(const InfoReader&) = delete; |
| |
| const File& dwarf() const { return dwarf_; } |
| |
| // DIEs exist in both .debug_info and .debug_types. |
| enum class Section { |
| kDebugInfo, |
| kDebugTypes |
| }; |
| |
| CUIter GetCUIter(Section section, uint64_t offset = 0); |
| |
| private: |
| friend class CU; |
| const File& dwarf_; |
| const CU* skeleton_ = nullptr; |
| |
| std::unordered_map<uint64_t, std::string> stmt_list_map_; |
| |
| // All of the AbbrevTables we've read from .debug_abbrev, indexed by their |
| // offset within .debug_abbrev. |
| std::unordered_map<uint64_t, AbbrevTable> abbrev_tables_; |
| }; |
| |
| class CUIter { |
| public: |
| bool NextCU(InfoReader& reader, CU* cu); |
| |
| private: |
| friend class InfoReader; |
| CUIter(InfoReader::Section section, std::string_view next_unit) |
| : section_(section), next_unit_(next_unit) {} |
| |
| // Data for the next compilation unit. |
| InfoReader::Section section_; |
| std::string_view next_unit_; |
| }; |
| |
| // CompilationUnit: stores info about a single compilation unit in .debug_info |
| // or .debug_types. |
| class CU { |
| public: |
| DIEReader GetDIEReader(); |
| |
| const File& dwarf() const { return *dwarf_; } |
| const CU& skeleton() const { return *skeleton_; } |
| const CompilationUnitSizes& unit_sizes() const { return unit_sizes_; } |
| const std::string& unit_name() const { return unit_name_; } |
| std::string_view entire_unit() const { return entire_unit_; } |
| uint64_t addr_base() const { return addr_base_; } |
| uint64_t str_offsets_base() const { return str_offsets_base_; } |
| uint64_t range_lists_base() const { return range_lists_base_; } |
| const AbbrevTable& unit_abbrev() const { return *unit_abbrev_; } |
| |
| void AddIndirectString(std::string_view range) const { |
| if (strp_callback_) { |
| strp_callback_(range); |
| } |
| } |
| |
| void SetIndirectStringCallback( |
| std::function<void(std::string_view)> strp_sink) { |
| strp_callback_ = strp_sink; |
| } |
| |
| bool IsValidDwarfAddress(uint64_t addr) const { |
| return dwarf::IsValidDwarfAddress(addr, unit_sizes_.address_size()); |
| } |
| |
| private: |
| friend class CUIter; |
| friend class DIEReader; |
| |
| void ReadHeader(std::string_view entire_unit, std::string_view data, |
| InfoReader::Section section, InfoReader& reader); |
| void ReadTopLevelDIE(InfoReader& reader); |
| |
| const File* dwarf_; |
| |
| // Info that comes from the CU header. |
| std::string_view entire_unit_; // Entire CU's range. |
| std::string_view data_; // Entire unit excluding CU header. |
| CompilationUnitSizes unit_sizes_; |
| AbbrevTable* unit_abbrev_; |
| |
| // Only for skeleton and split CUs. |
| uint8_t unit_type_; |
| uint64_t dwo_id_; |
| const CU* skeleton_; |
| |
| // Only for .debug_types |
| uint64_t unit_type_signature_; |
| uint64_t unit_type_offset_; |
| |
| // Info that comes from the top-level DIE. |
| std::string unit_name_; |
| uint64_t addr_base_ = 0; |
| uint64_t str_offsets_base_ = 0; |
| uint64_t range_lists_base_ = 0; |
| |
| std::function<void(std::string_view)> strp_callback_; |
| }; |
| |
| // DIEReader: for reading a sequence of Debugging Information Entries in a |
| // compilation unit. |
| class DIEReader { |
| public: |
| // Abbreviation for the current entry. |
| const AbbrevTable::Abbrev* ReadCode(const CU& cu); |
| |
| template <class T> |
| void ReadAttributes(const CU& cu, const AbbrevTable::Abbrev* code, T&& func); |
| |
| void SkipChildren(const CU& cu, const AbbrevTable::Abbrev* code); |
| |
| private: |
| // Internal APIs. |
| friend class CU; |
| |
| DIEReader(std::string_view data) : remaining_(data) {} |
| void SkipNullEntries(); |
| |
| // Our current read position. |
| std::string_view remaining_; |
| int depth_ = 0; |
| }; |
| |
| inline uint64_t ReadIndirectAddress(const CU& cu, uint64_t val) { |
| std::string_view addrs = cu.skeleton().dwarf().debug_addr; |
| uint64_t base = cu.skeleton().addr_base(); |
| switch (cu.unit_sizes().address_size()) { |
| case 4: |
| SkipBytes((val * 4) + base, &addrs); |
| return ReadFixed<uint32_t>(&addrs); |
| case 8: |
| SkipBytes((val * 8) + base, &addrs); |
| return ReadFixed<uint64_t>(&addrs); |
| default: |
| BLOATY_UNREACHABLE(); |
| } |
| } |
| |
| // Reads all attributes for this DIE, calling the given function for each one. |
| template <class T> |
| void DIEReader::ReadAttributes(const CU& cu, const AbbrevTable::Abbrev* abbrev, |
| T&& func) { |
| for (auto attr : abbrev->attr) { |
| AttrValue value = AttrValue::ParseAttr(cu, attr.form, &remaining_); |
| func(attr.name, value); |
| } |
| } |
| |
| inline DIEReader CU::GetDIEReader() { return DIEReader(data_); } |
| |
| } // namespace dwarf |
| } // namespace bloaty |
| |
| #endif // BLOATY_DWARF_DEBUG_INFO_H_ |