Index: src/common/dwarf/dwarf2reader.h |
diff --git a/src/common/dwarf/dwarf2reader.h b/src/common/dwarf/dwarf2reader.h |
index 1f5b37a04d9d34c120bb8b70c3aaf7c9a814ce18..064c42bc8a50f765cf5cea73c456a9bbcf0d8663 100644 |
--- a/src/common/dwarf/dwarf2reader.h |
+++ b/src/common/dwarf/dwarf2reader.h |
@@ -47,16 +47,19 @@ |
#include <string> |
#include <utility> |
#include <vector> |
+#include <memory> |
#include "common/dwarf/bytereader.h" |
#include "common/dwarf/dwarf2enums.h" |
#include "common/dwarf/types.h" |
#include "common/using_std_string.h" |
+#include "common/dwarf/elf_reader.h" |
namespace dwarf2reader { |
struct LineStateMachine; |
class Dwarf2Handler; |
class LineInfoHandler; |
+class DwpReader; |
// This maps from a string naming a section to a pair containing a |
// the data for the section, and the size of the section. |
@@ -184,6 +187,106 @@ class LineInfoHandler { |
uint32 file_num, uint32 line_num, uint32 column_num) { } |
}; |
+// This class is the main interface between the reader and the |
+// client. The virtual functions inside this get called for |
+// interesting events that happen during DWARF2 reading. |
+// The default implementation skips everything. |
+class Dwarf2Handler { |
+ public: |
+ Dwarf2Handler() { } |
+ |
+ virtual ~Dwarf2Handler() { } |
+ |
+ // Start to process a compilation unit at OFFSET from the beginning of the |
+ // .debug_info section. Return false if you would like to skip this |
+ // compilation unit. |
+ virtual bool StartCompilationUnit(uint64 offset, uint8 address_size, |
+ uint8 offset_size, uint64 cu_length, |
+ uint8 dwarf_version) { return false; } |
+ |
+ // When processing a skeleton compilation unit, resulting from a split |
+ // DWARF compilation, once the skeleton debug info has been read, |
+ // the reader will call this function to ask the client if it needs |
+ // the full debug info from the .dwo or .dwp file. Return true if |
+ // you need it, or false to skip processing the split debug info. |
+ virtual bool NeedSplitDebugInfo() { return true; } |
+ |
+ // Start to process a split compilation unit at OFFSET from the beginning of |
+ // the debug_info section in the .dwp/.dwo file. Return false if you would |
+ // like to skip this compilation unit. |
+ virtual bool StartSplitCompilationUnit(uint64 offset, |
+ uint64 cu_length) { return false; } |
+ |
+ // Start to process a DIE at OFFSET from the beginning of the .debug_info |
+ // section. Return false if you would like to skip this DIE. |
+ virtual bool StartDIE(uint64 offset, enum DwarfTag tag) { return false; } |
+ |
+ // Called when we have an attribute with unsigned data to give to our |
+ // handler. The attribute is for the DIE at OFFSET from the beginning of the |
+ // .debug_info section. Its name is ATTR, its form is FORM, and its value is |
+ // DATA. |
+ virtual void ProcessAttributeUnsigned(uint64 offset, |
+ enum DwarfAttribute attr, |
+ enum DwarfForm form, |
+ uint64 data) { } |
+ |
+ // Called when we have an attribute with signed data to give to our handler. |
+ // The attribute is for the DIE at OFFSET from the beginning of the |
+ // .debug_info section. Its name is ATTR, its form is FORM, and its value is |
+ // DATA. |
+ virtual void ProcessAttributeSigned(uint64 offset, |
+ enum DwarfAttribute attr, |
+ enum DwarfForm form, |
+ int64 data) { } |
+ |
+ // Called when we have an attribute whose value is a reference to |
+ // another DIE. The attribute belongs to the DIE at OFFSET from the |
+ // beginning of the .debug_info section. Its name is ATTR, its form |
+ // is FORM, and the offset of the DIE being referred to from the |
+ // beginning of the .debug_info section is DATA. |
+ virtual void ProcessAttributeReference(uint64 offset, |
+ enum DwarfAttribute attr, |
+ enum DwarfForm form, |
+ uint64 data) { } |
+ |
+ // Called when we have an attribute with a buffer of data to give to our |
+ // handler. The attribute is for the DIE at OFFSET from the beginning of the |
+ // .debug_info section. Its name is ATTR, its form is FORM, DATA points to |
+ // the buffer's contents, and its length in bytes is LENGTH. The buffer is |
+ // owned by the caller, not the callee, and may not persist for very long. |
+ // If you want the data to be available later, it needs to be copied. |
+ virtual void ProcessAttributeBuffer(uint64 offset, |
+ enum DwarfAttribute attr, |
+ enum DwarfForm form, |
+ const uint8_t *data, |
+ uint64 len) { } |
+ |
+ // Called when we have an attribute with string data to give to our handler. |
+ // The attribute is for the DIE at OFFSET from the beginning of the |
+ // .debug_info section. Its name is ATTR, its form is FORM, and its value is |
+ // DATA. |
+ virtual void ProcessAttributeString(uint64 offset, |
+ enum DwarfAttribute attr, |
+ enum DwarfForm form, |
+ const string& data) { } |
+ |
+ // Called when we have an attribute whose value is the 64-bit signature |
+ // of a type unit in the .debug_types section. OFFSET is the offset of |
+ // the DIE whose attribute we're reporting. ATTR and FORM are the |
+ // attribute's name and form. SIGNATURE is the type unit's signature. |
+ virtual void ProcessAttributeSignature(uint64 offset, |
+ enum DwarfAttribute attr, |
+ enum DwarfForm form, |
+ uint64 signature) { } |
+ |
+ // Called when finished processing the DIE at OFFSET. |
+ // Because DWARF2/3 specifies a tree of DIEs, you may get starts |
+ // before ends of the previous DIE, as we process children before |
+ // ending the parent. |
+ virtual void EndDIE(uint64 offset) { } |
+ |
+}; |
+ |
// The base of DWARF2/3 debug info is a DIE (Debugging Information |
// Entry. |
// DWARF groups DIE's into a tree and calls the root of this tree a |
@@ -225,12 +328,21 @@ class CompilationUnit { |
// Initialize a compilation unit. This requires a map of sections, |
// the offset of this compilation unit in the .debug_info section, a |
// ByteReader, and a Dwarf2Handler class to call callbacks in. |
- CompilationUnit(const SectionMap& sections, uint64 offset, |
+ CompilationUnit(const string& path, const SectionMap& sections, uint64 offset, |
ByteReader* reader, Dwarf2Handler* handler); |
virtual ~CompilationUnit() { |
if (abbrevs_) delete abbrevs_; |
} |
+ // Initialize a compilation unit from a .dwo or .dwp file. |
+ // In this case, we need the .debug_addr section from the |
+ // executable file that contains the corresponding skeleton |
+ // compilation unit. We also inherit the Dwarf2Handler from |
+ // the executable file, and call it as if we were still |
+ // processing the original compilation unit. |
+ void SetSplitDwarf(const uint8_t* addr_buffer, uint64 addr_buffer_length, |
+ uint64 addr_base, uint64 ranges_base, uint64 dwo_id); |
+ |
// Begin reading a Dwarf2 compilation unit, and calling the |
// callbacks in the Dwarf2Handler |
@@ -281,6 +393,73 @@ class CompilationUnit { |
enum DwarfAttribute attr, |
enum DwarfForm form); |
+ // Called when we have an attribute with unsigned data to give to |
+ // our handler. The attribute is for the DIE at OFFSET from the |
+ // beginning of compilation unit, has a name of ATTR, a form of |
+ // FORM, and the actual data of the attribute is in DATA. |
+ // If we see a DW_AT_GNU_dwo_id attribute, save the value so that |
+ // we can find the debug info in a .dwo or .dwp file. |
+ void ProcessAttributeUnsigned(uint64 offset, |
+ enum DwarfAttribute attr, |
+ enum DwarfForm form, |
+ uint64 data) { |
+ if (attr == DW_AT_GNU_dwo_id) { |
+ dwo_id_ = data; |
+ } |
+ else if (attr == DW_AT_GNU_addr_base) { |
+ addr_base_ = data; |
+ } |
+ else if (attr == DW_AT_GNU_ranges_base) { |
+ ranges_base_ = data; |
+ } |
+ // TODO(yunlian): When we add DW_AT_ranges_base from DWARF-5, |
+ // that base will apply to DW_AT_ranges attributes in the |
+ // skeleton CU as well as in the .dwo/.dwp files. |
+ else if (attr == DW_AT_ranges && is_split_dwarf_) { |
+ data += ranges_base_; |
+ } |
+ handler_->ProcessAttributeUnsigned(offset, attr, form, data); |
+ } |
+ |
+ // Called when we have an attribute with signed data to give to |
+ // our handler. The attribute is for the DIE at OFFSET from the |
+ // beginning of compilation unit, has a name of ATTR, a form of |
+ // FORM, and the actual data of the attribute is in DATA. |
+ void ProcessAttributeSigned(uint64 offset, |
+ enum DwarfAttribute attr, |
+ enum DwarfForm form, |
+ int64 data) { |
+ handler_->ProcessAttributeSigned(offset, attr, form, data); |
+ } |
+ |
+ // Called when we have an attribute with a buffer of data to give to |
+ // our handler. The attribute is for the DIE at OFFSET from the |
+ // beginning of compilation unit, has a name of ATTR, a form of |
+ // FORM, and the actual data of the attribute is in DATA, and the |
+ // length of the buffer is LENGTH. |
+ void ProcessAttributeBuffer(uint64 offset, |
+ enum DwarfAttribute attr, |
+ enum DwarfForm form, |
+ const uint8_t* data, |
+ uint64 len) { |
+ handler_->ProcessAttributeBuffer(offset, attr, form, data, len); |
+ } |
+ |
+ // Called when we have an attribute with string data to give to |
+ // our handler. The attribute is for the DIE at OFFSET from the |
+ // beginning of compilation unit, has a name of ATTR, a form of |
+ // FORM, and the actual data of the attribute is in DATA. |
+ // If we see a DW_AT_GNU_dwo_name attribute, save the value so |
+ // that we can find the debug info in a .dwo or .dwp file. |
+ void ProcessAttributeString(uint64 offset, |
+ enum DwarfAttribute attr, |
+ enum DwarfForm form, |
+ const char* data) { |
+ if (attr == DW_AT_GNU_dwo_name) |
+ dwo_name_ = data; |
+ handler_->ProcessAttributeString(offset, attr, form, data); |
+ } |
+ |
// Processes all DIEs for this compilation unit |
void ProcessDIEs(); |
@@ -292,6 +471,16 @@ class CompilationUnit { |
// new place to position the stream to. |
const uint8_t *SkipAttribute(const uint8_t *start, enum DwarfForm form); |
+ // Process the actual debug information in a split DWARF file. |
+ void ProcessSplitDwarf(); |
+ |
+ // Read the debug sections from a .dwo file. |
+ void ReadDebugSectionsFromDwo(ElfReader* elf_reader, |
+ SectionMap* sections); |
+ |
+ // Path of the file containing the debug information. |
+ const string path_; |
+ |
// Offset from section start is the offset of this compilation unit |
// from the beginning of the .debug_info section. |
uint64 offset_from_section_start_; |
@@ -322,94 +511,141 @@ class CompilationUnit { |
// ProcessAttribute, which is in the hot path for DWARF2 reading. |
const uint8_t *string_buffer_; |
uint64 string_buffer_length_; |
-}; |
-// This class is the main interface between the reader and the |
-// client. The virtual functions inside this get called for |
-// interesting events that happen during DWARF2 reading. |
-// The default implementation skips everything. |
+ // String offsets section buffer and length, if we have a string offsets |
+ // section (.debug_str_offsets or .debug_str_offsets.dwo). |
+ const uint8_t* str_offsets_buffer_; |
+ uint64 str_offsets_buffer_length_; |
-class Dwarf2Handler { |
+ // Address section buffer and length, if we have an address section |
+ // (.debug_addr). |
+ const uint8_t* addr_buffer_; |
+ uint64 addr_buffer_length_; |
+ |
+ // Flag indicating whether this compilation unit is part of a .dwo |
+ // or .dwp file. If true, we are reading this unit because a |
+ // skeleton compilation unit in an executable file had a |
+ // DW_AT_GNU_dwo_name or DW_AT_GNU_dwo_id attribute. |
+ // In a .dwo file, we expect the string offsets section to |
+ // have a ".dwo" suffix, and we will use the ".debug_addr" section |
+ // associated with the skeleton compilation unit. |
+ bool is_split_dwarf_; |
+ |
+ // The value of the DW_AT_GNU_dwo_id attribute, if any. |
+ uint64 dwo_id_; |
+ |
+ // The value of the DW_AT_GNU_dwo_name attribute, if any. |
+ const char* dwo_name_; |
+ |
+ // If this is a split DWARF CU, the value of the DW_AT_GNU_dwo_id attribute |
+ // from the skeleton CU. |
+ uint64 skeleton_dwo_id_; |
+ |
+ // The value of the DW_AT_GNU_ranges_base attribute, if any. |
+ uint64 ranges_base_; |
+ |
+ // The value of the DW_AT_GNU_addr_base attribute, if any. |
+ uint64 addr_base_; |
+ |
+ // True if we have already looked for a .dwp file. |
+ bool have_checked_for_dwp_; |
+ |
+ // Path to the .dwp file. |
+ string dwp_path_; |
+ |
+ // ByteReader for the DWP file. |
+ std::unique_ptr<ByteReader> dwp_byte_reader_; |
+ |
+ // DWP reader. |
+ std::unique_ptr<DwpReader> dwp_reader_; |
+}; |
+ |
+// A Reader for a .dwp file. Supports the fetching of DWARF debug |
+// info for a given dwo_id. |
+// |
+// There are two versions of .dwp files. In both versions, the |
+// .dwp file is an ELF file containing only debug sections. |
+// In Version 1, the file contains many copies of each debug |
+// section, one for each .dwo file that is packaged in the .dwp |
+// file, and the .debug_cu_index section maps from the dwo_id |
+// to a set of section indexes. In Version 2, the file contains |
+// one of each debug section, and the .debug_cu_index section |
+// maps from the dwo_id to a set of offsets and lengths that |
+// identify each .dwo file's contribution to the larger sections. |
+ |
+class DwpReader { |
public: |
- Dwarf2Handler() { } |
+ DwpReader(const ByteReader& byte_reader, ElfReader* elf_reader); |
- virtual ~Dwarf2Handler() { } |
+ ~DwpReader(); |
- // Start to process a compilation unit at OFFSET from the beginning of the |
- // .debug_info section. Return false if you would like to skip this |
- // compilation unit. |
- virtual bool StartCompilationUnit(uint64 offset, uint8 address_size, |
- uint8 offset_size, uint64 cu_length, |
- uint8 dwarf_version) { return false; } |
+ // Read the CU index and initialize data members. |
+ void Initialize(); |
- // Start to process a DIE at OFFSET from the beginning of the .debug_info |
- // section. Return false if you would like to skip this DIE. |
- virtual bool StartDIE(uint64 offset, enum DwarfTag tag) { return false; } |
+ // Read the debug sections for the given dwo_id. |
+ void ReadDebugSectionsForCU(uint64 dwo_id, SectionMap* sections); |
- // Called when we have an attribute with unsigned data to give to our |
- // handler. The attribute is for the DIE at OFFSET from the beginning of the |
- // .debug_info section. Its name is ATTR, its form is FORM, and its value is |
- // DATA. |
- virtual void ProcessAttributeUnsigned(uint64 offset, |
- enum DwarfAttribute attr, |
- enum DwarfForm form, |
- uint64 data) { } |
+ private: |
+ // Search a v1 hash table for "dwo_id". Returns the slot index |
+ // where the dwo_id was found, or -1 if it was not found. |
+ int LookupCU(uint64 dwo_id); |
- // Called when we have an attribute with signed data to give to our handler. |
- // The attribute is for the DIE at OFFSET from the beginning of the |
- // .debug_info section. Its name is ATTR, its form is FORM, and its value is |
- // DATA. |
- virtual void ProcessAttributeSigned(uint64 offset, |
- enum DwarfAttribute attr, |
- enum DwarfForm form, |
- int64 data) { } |
+ // Search a v2 hash table for "dwo_id". Returns the row index |
+ // in the offsets and sizes tables, or 0 if it was not found. |
+ uint32 LookupCUv2(uint64 dwo_id); |
- // Called when we have an attribute whose value is a reference to |
- // another DIE. The attribute belongs to the DIE at OFFSET from the |
- // beginning of the .debug_info section. Its name is ATTR, its form |
- // is FORM, and the offset of the DIE being referred to from the |
- // beginning of the .debug_info section is DATA. |
- virtual void ProcessAttributeReference(uint64 offset, |
- enum DwarfAttribute attr, |
- enum DwarfForm form, |
- uint64 data) { } |
+ // The ELF reader for the .dwp file. |
+ ElfReader* elf_reader_; |
- // Called when we have an attribute with a buffer of data to give to our |
- // handler. The attribute is for the DIE at OFFSET from the beginning of the |
- // .debug_info section. Its name is ATTR, its form is FORM, DATA points to |
- // the buffer's contents, and its length in bytes is LENGTH. The buffer is |
- // owned by the caller, not the callee, and may not persist for very long. |
- // If you want the data to be available later, it needs to be copied. |
- virtual void ProcessAttributeBuffer(uint64 offset, |
- enum DwarfAttribute attr, |
- enum DwarfForm form, |
- const uint8_t *data, |
- uint64 len) { } |
+ // The ByteReader for the .dwp file. |
+ const ByteReader& byte_reader_; |
- // Called when we have an attribute with string data to give to our handler. |
- // The attribute is for the DIE at OFFSET from the beginning of the |
- // .debug_info section. Its name is ATTR, its form is FORM, and its value is |
- // DATA. |
- virtual void ProcessAttributeString(uint64 offset, |
- enum DwarfAttribute attr, |
- enum DwarfForm form, |
- const string& data) { } |
+ // Pointer to the .debug_cu_index section. |
+ const char* cu_index_; |
- // Called when we have an attribute whose value is the 64-bit signature |
- // of a type unit in the .debug_types section. OFFSET is the offset of |
- // the DIE whose attribute we're reporting. ATTR and FORM are the |
- // attribute's name and form. SIGNATURE is the type unit's signature. |
- virtual void ProcessAttributeSignature(uint64 offset, |
- enum DwarfAttribute attr, |
- enum DwarfForm form, |
- uint64 signature) { } |
+ // Size of the .debug_cu_index section. |
+ size_t cu_index_size_; |
- // Called when finished processing the DIE at OFFSET. |
- // Because DWARF2/3 specifies a tree of DIEs, you may get starts |
- // before ends of the previous DIE, as we process children before |
- // ending the parent. |
- virtual void EndDIE(uint64 offset) { } |
+ // Pointer to the .debug_str.dwo section. |
+ const char* string_buffer_; |
+ |
+ // Size of the .debug_str.dwo section. |
+ size_t string_buffer_size_; |
+ |
+ // Version of the .dwp file. We support versions 1 and 2 currently. |
+ int version_; |
+ |
+ // Number of columns in the section tables (version 2). |
+ unsigned int ncolumns_; |
+ |
+ // Number of units in the section tables (version 2). |
+ unsigned int nunits_; |
+ |
+ // Number of slots in the hash table. |
+ unsigned int nslots_; |
+ |
+ // Pointer to the beginning of the hash table. |
+ const char* phash_; |
+ |
+ // Pointer to the beginning of the index table. |
+ const char* pindex_; |
+ |
+ // Pointer to the beginning of the section index pool (version 1). |
+ const char* shndx_pool_; |
+ |
+ // Pointer to the beginning of the section offset table (version 2). |
+ const char* offset_table_; |
+ |
+ // Pointer to the beginning of the section size table (version 2). |
+ const char* size_table_; |
+ // Contents of the sections of interest (version 2). |
+ const char* abbrev_data_; |
+ size_t abbrev_size_; |
+ const char* info_data_; |
+ size_t info_size_; |
+ const char* str_offsets_data_; |
+ size_t str_offsets_size_; |
}; |
// This class is a reader for DWARF's Call Frame Information. CFI |