|  | /* DIE indexing | 
|  |  | 
|  | Copyright (C) 2024 Free Software Foundation, Inc. | 
|  |  | 
|  | This file is part of GDB. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #ifndef GDB_DWARF2_PARENT_MAP_H | 
|  | #define GDB_DWARF2_PARENT_MAP_H | 
|  |  | 
|  | #include "addrmap.h" | 
|  | #include "dwarf2/types.h" | 
|  | #include "gdbsupport/gdb_obstack.h" | 
|  |  | 
|  | class cooked_index_entry; | 
|  |  | 
|  | /* A class that handles mapping from a DIE range to a parent | 
|  | entry. | 
|  |  | 
|  | The generated DWARF can sometimes have the declaration for a method | 
|  | in a class (or perhaps namespace) scope, with the definition | 
|  | appearing outside this scope... just one of the many bad things | 
|  | about DWARF. | 
|  |  | 
|  | For example, a program like this: | 
|  |  | 
|  | struct X { int method (); }; | 
|  | int X::method () { return 23; } | 
|  |  | 
|  | ... ends up with DWARF like: | 
|  |  | 
|  | <1><2e>: Abbrev Number: 2 (DW_TAG_structure_type) | 
|  | <2f>   DW_AT_name        : X | 
|  | ... | 
|  | <2><39>: Abbrev Number: 3 (DW_TAG_subprogram) | 
|  | <3a>   DW_AT_external    : 1 | 
|  | <3a>   DW_AT_name        : (indirect string, offset: 0xf): method | 
|  | ... | 
|  | <1><66>: Abbrev Number: 8 (DW_TAG_subprogram) | 
|  | <67>   DW_AT_specification: <0x39> | 
|  |  | 
|  | Here, the name of DIE 0x66 can't be determined without knowing the | 
|  | parent of DIE 0x39. | 
|  |  | 
|  | In order to handle this situation, we defer certain entries until | 
|  | the end of scanning, at which point we'll know the containing | 
|  | context of all the DIEs that we might have scanned.  */ | 
|  | class parent_map | 
|  | { | 
|  | public: | 
|  |  | 
|  | parent_map () = default; | 
|  | ~parent_map () = default; | 
|  |  | 
|  | /* Move only.  */ | 
|  | DISABLE_COPY_AND_ASSIGN (parent_map); | 
|  | parent_map (parent_map &&) = default; | 
|  | parent_map &operator= (parent_map &&) = default; | 
|  |  | 
|  | /* A reasonably opaque type that is used as part of a DIE range.  */ | 
|  | enum addr_type : CORE_ADDR { }; | 
|  |  | 
|  | /* Turn a section offset into a value that can be used in a parent | 
|  | map.  */ | 
|  | static addr_type form_addr (const gdb_byte *info_ptr) | 
|  | { | 
|  | static_assert (sizeof (addr_type) >= sizeof (uintptr_t)); | 
|  | return (addr_type) (uintptr_t) info_ptr; | 
|  | } | 
|  |  | 
|  | /* Add a new entry to this map.  DIEs from START to END, inclusive, | 
|  | are mapped to PARENT.  */ | 
|  | void add_entry (addr_type start, addr_type end, | 
|  | const cooked_index_entry *parent) | 
|  | { | 
|  | gdb_assert (parent != nullptr); | 
|  | m_map.set_empty (start, end, (void *) parent); | 
|  | } | 
|  |  | 
|  | /* Look up an entry in this map.  */ | 
|  | const cooked_index_entry *find (addr_type search) const | 
|  | { | 
|  | return static_cast<const cooked_index_entry *> (m_map.find (search)); | 
|  | } | 
|  |  | 
|  | /* Return a fixed addrmap that is equivalent to this map.  */ | 
|  | addrmap_fixed *to_fixed (struct obstack *obstack) const | 
|  | { | 
|  | return new (obstack) addrmap_fixed (obstack, &m_map); | 
|  | } | 
|  |  | 
|  | /* Dump a human-readable form of this map.  */ | 
|  | void dump (dwarf2_per_bfd *per_bfd) const; | 
|  |  | 
|  | private: | 
|  |  | 
|  | /* An addrmap that maps from section offsets to cooked_index_entry *.  */ | 
|  | addrmap_mutable m_map; | 
|  | }; | 
|  |  | 
|  | /* Keep a collection of parent_map objects, and allow for lookups | 
|  | across all of them.  */ | 
|  | class parent_map_map | 
|  | { | 
|  | public: | 
|  |  | 
|  | parent_map_map () = default; | 
|  | ~parent_map_map () = default; | 
|  |  | 
|  | DISABLE_COPY_AND_ASSIGN (parent_map_map); | 
|  |  | 
|  | /* Add a parent_map to this map.  Note that a copy of MAP is made -- | 
|  | modifications to MAP after this call will have no effect.  */ | 
|  | void add_map (const parent_map &map) | 
|  | { | 
|  | m_maps.push_back (map.to_fixed (&m_storage)); | 
|  | } | 
|  |  | 
|  | /* Look up an entry in this map.  */ | 
|  | const cooked_index_entry *find (parent_map::addr_type search) const | 
|  | { | 
|  | for (const auto &iter : m_maps) | 
|  | { | 
|  | const cooked_index_entry *result | 
|  | = static_cast<const cooked_index_entry *> (iter->find (search)); | 
|  | if (result != nullptr) | 
|  | return result; | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | /* Dump a human-readable form of this collection of parent_maps.  */ | 
|  | void dump (dwarf2_per_bfd *per_bfd) const; | 
|  |  | 
|  | private: | 
|  |  | 
|  | /* Storage for the convert maps.  */ | 
|  | auto_obstack m_storage; | 
|  |  | 
|  | /* While conceptually this class is a combination of parent_maps, in | 
|  | practice it is just a number of fixed maps.  This is important | 
|  | because we want to allow concurrent lookups, but a mutable | 
|  | addrmap is based on a splay-tree, which is not thread-safe, even | 
|  | for nominally read-only lookups.  */ | 
|  | std::vector<addrmap_fixed *> m_maps; | 
|  | }; | 
|  |  | 
|  | #endif /* GDB_DWARF2_PARENT_MAP_H */ |