|  | /* DWARF aranges handling | 
|  |  | 
|  | Copyright (C) 1994-2025 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/>.  */ | 
|  |  | 
|  | #include "dwarf2/aranges.h" | 
|  | #include "dwarf2/read.h" | 
|  | #include "extract-store-integer.h" | 
|  |  | 
|  | /* See aranges.h.  */ | 
|  |  | 
|  | bool | 
|  | read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile, | 
|  | dwarf2_section_info *section, | 
|  | addrmap_mutable *mutable_map, | 
|  | deferred_warnings *warn) | 
|  | { | 
|  | /* Caller must ensure that the section has already been read.  */ | 
|  | gdb_assert (section->readin); | 
|  | if (section->empty ()) | 
|  | return false; | 
|  |  | 
|  | struct objfile *objfile = per_objfile->objfile; | 
|  | bfd *abfd = objfile->obfd.get (); | 
|  | struct gdbarch *gdbarch = objfile->arch (); | 
|  | dwarf2_per_bfd *per_bfd = per_objfile->per_bfd; | 
|  |  | 
|  | gdb::unordered_map<sect_offset, dwarf2_per_cu *> debug_info_offset_to_per_cu; | 
|  | for (const auto &per_cu : per_bfd->all_units) | 
|  | { | 
|  | /* A TU will not need aranges, and skipping them here is an easy | 
|  | way of ignoring .debug_types -- and possibly seeing a | 
|  | duplicate section offset -- entirely.  The same applies to | 
|  | units coming from a dwz file.  */ | 
|  | if (per_cu->is_debug_types || per_cu->is_dwz) | 
|  | continue; | 
|  |  | 
|  | const auto insertpair | 
|  | = debug_info_offset_to_per_cu.emplace (per_cu->sect_off, | 
|  | per_cu.get ()); | 
|  |  | 
|  | /* Assume no duplicate offsets in all_units.  */ | 
|  | gdb_assert (insertpair.second); | 
|  | } | 
|  |  | 
|  | gdb::unordered_set<sect_offset> debug_info_offset_seen; | 
|  | const bfd_endian dwarf5_byte_order = gdbarch_byte_order (gdbarch); | 
|  | const int signed_addr_p = bfd_get_sign_extend_vma (abfd); | 
|  | const gdb_byte *addr = section->buffer; | 
|  | while (addr < section->buffer + section->size) | 
|  | { | 
|  | const gdb_byte *const entry_addr = addr; | 
|  | unsigned int bytes_read; | 
|  |  | 
|  | const LONGEST entry_length = read_initial_length (abfd, addr, | 
|  | &bytes_read); | 
|  | addr += bytes_read; | 
|  |  | 
|  | const gdb_byte *const entry_end = addr + entry_length; | 
|  | const bool dwarf5_is_dwarf64 = bytes_read != 4; | 
|  | const uint8_t offset_size = dwarf5_is_dwarf64 ? 8 : 4; | 
|  | if (addr + entry_length > section->buffer + section->size) | 
|  | { | 
|  | warn->warn (_("Section .debug_aranges in %s entry at offset %s " | 
|  | "length %s exceeds section length %s, " | 
|  | "ignoring .debug_aranges."), | 
|  | objfile_name (objfile), | 
|  | plongest (entry_addr - section->buffer), | 
|  | plongest (bytes_read + entry_length), | 
|  | pulongest (section->size)); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* The version number.  */ | 
|  | const uint16_t version = read_2_bytes (abfd, addr); | 
|  | addr += 2; | 
|  | if (version != 2) | 
|  | { | 
|  | warn->warn | 
|  | (_("Section .debug_aranges in %s entry at offset %s " | 
|  | "has unsupported version %d, ignoring .debug_aranges."), | 
|  | objfile_name (objfile), | 
|  | plongest (entry_addr - section->buffer), version); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | const uint64_t debug_info_offset | 
|  | = extract_unsigned_integer (addr, offset_size, dwarf5_byte_order); | 
|  | addr += offset_size; | 
|  | const auto per_cu_it | 
|  | = debug_info_offset_to_per_cu.find (sect_offset (debug_info_offset)); | 
|  | if (per_cu_it == debug_info_offset_to_per_cu.cend ()) | 
|  | { | 
|  | warn->warn (_("Section .debug_aranges in %s entry at offset %s " | 
|  | "debug_info_offset %s does not exists, " | 
|  | "ignoring .debug_aranges."), | 
|  | objfile_name (objfile), | 
|  | plongest (entry_addr - section->buffer), | 
|  | pulongest (debug_info_offset)); | 
|  | return false; | 
|  | } | 
|  | const auto insertpair | 
|  | = debug_info_offset_seen.insert (sect_offset (debug_info_offset)); | 
|  | if (!insertpair.second) | 
|  | { | 
|  | warn->warn (_("Section .debug_aranges in %s has duplicate " | 
|  | "debug_info_offset %s, ignoring .debug_aranges."), | 
|  | objfile_name (objfile), | 
|  | sect_offset_str (sect_offset (debug_info_offset))); | 
|  | return false; | 
|  | } | 
|  | dwarf2_per_cu *const per_cu = per_cu_it->second; | 
|  |  | 
|  | const uint8_t address_size = *addr++; | 
|  | if (address_size < 1 || address_size > 8) | 
|  | { | 
|  | warn->warn | 
|  | (_("Section .debug_aranges in %s entry at offset %s " | 
|  | "address_size %u is invalid, ignoring .debug_aranges."), | 
|  | objfile_name (objfile), | 
|  | plongest (entry_addr - section->buffer), address_size); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | const uint8_t segment_selector_size = *addr++; | 
|  | if (segment_selector_size != 0) | 
|  | { | 
|  | warn->warn (_("Section .debug_aranges in %s entry at offset %s " | 
|  | "segment_selector_size %u is not supported, " | 
|  | "ignoring .debug_aranges."), | 
|  | objfile_name (objfile), | 
|  | plongest (entry_addr - section->buffer), | 
|  | segment_selector_size); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* Must pad to an alignment boundary that is twice the address | 
|  | size.  It is undocumented by the DWARF standard but GCC does | 
|  | use it.  However, not every compiler does this.  We can see | 
|  | whether it has happened by looking at the total length of the | 
|  | contents of the aranges for this CU -- it if isn't a multiple | 
|  | of twice the address size, then we skip any leftover | 
|  | bytes.  */ | 
|  | addr += (entry_end - addr) % (2 * address_size); | 
|  |  | 
|  | while (addr < entry_end) | 
|  | { | 
|  | if (addr + 2 * address_size > entry_end) | 
|  | { | 
|  | warn->warn (_("Section .debug_aranges in %s entry at offset %s " | 
|  | "address list is not properly terminated, " | 
|  | "ignoring .debug_aranges."), | 
|  | objfile_name (objfile), | 
|  | plongest (entry_addr - section->buffer)); | 
|  | return false; | 
|  | } | 
|  | ULONGEST start; | 
|  | if (signed_addr_p) | 
|  | start = extract_signed_integer (addr, address_size, | 
|  | dwarf5_byte_order); | 
|  | else | 
|  | start = extract_unsigned_integer (addr, address_size, | 
|  | dwarf5_byte_order); | 
|  | addr += address_size; | 
|  | ULONGEST length = extract_unsigned_integer (addr, address_size, | 
|  | dwarf5_byte_order); | 
|  | addr += address_size; | 
|  | if (start == 0 && length == 0) | 
|  | { | 
|  | /* This can happen on some targets with --gc-sections. | 
|  | This pair of values is also used to mark the end of | 
|  | the entries for a given CU, but we ignore it and | 
|  | instead handle termination using the check at the top | 
|  | of the loop.  */ | 
|  | continue; | 
|  | } | 
|  | if (start == 0 && !per_bfd->has_section_at_zero) | 
|  | { | 
|  | /* Symbol was eliminated due to a COMDAT group.  */ | 
|  | continue; | 
|  | } | 
|  | ULONGEST end = start + length; | 
|  | mutable_map->set_empty (start, end - 1, per_cu); | 
|  | } | 
|  |  | 
|  | per_cu->addresses_seen = true; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } |