| /* DWARF aranges handling |
| |
| Copyright (C) 1994-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/>. */ |
| |
| #include "dwarf2/aranges.h" |
| #include "dwarf2/read.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; |
| |
| std::unordered_map<sect_offset, |
| dwarf2_per_cu_data *, |
| gdb::hash_enum<sect_offset>> |
| 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); |
| } |
| |
| std::set<sect_offset> debug_info_offset_seen; |
| const bfd_endian dwarf5_byte_order = gdbarch_byte_order (gdbarch); |
| 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_data *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 = 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; |
| } |