Chromium Code Reviews| Index: src/common/dwarf/dwarf2reader.h |
| diff --git a/src/common/dwarf/dwarf2reader.h b/src/common/dwarf/dwarf2reader.h |
| index 1f5b37a04d9d34c120bb8b70c3aaf7c9a814ce18..47daf9d2a07397d616a9cfecc09a282d3e15a8d0 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,107 @@ 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. |
| + |
|
ivanpe
2016/04/27 01:11:44
No need to leave a blank line.
yunlian
2016/04/27 16:35:29
Done.
|
| +class Dwarf2Handler { |
| + public: |
| + Dwarf2Handler() { } |
| + |
| + virtual ~Dwarf2Handler() { } |
| + |
| + // Start to process a compilation unit at OFFSET from the beginning of the |
|
ivanpe
2016/04/27 01:11:45
It looks like the code that follows comes from:
ht
|
| + // .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 |
|
ivanpe
2016/04/27 01:11:44
indent is off
yunlian
2016/04/27 16:35:29
Done.
|
| + // 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 +329,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 +394,69 @@ 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) |
|
ivanpe
2016/04/27 01:11:44
I would suggest to put curly braces around these.
yunlian
2016/04/27 16:35:29
Done.
|
| + 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(ccoutant): When we add DW_AT_ranges_base from DWARF-5, |
|
ivanpe
2016/04/27 01:11:45
Why not TODO(yunlian): ?
yunlian
2016/04/27 16:35:29
Done.
|
| + // 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 +468,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 +508,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 |