text: #-------------------------------------------------------------------------------
  # elftools: dwarf/aranges.py
  #
  # DWARF aranges section decoding (.debug_aranges)
  #
  # Dorothy Chen (dorothchen@gmail.com)
  # This code is in the public domain
  #-------------------------------------------------------------------------------
  import os
  from collections import namedtuple
  from ..common.utils import struct_parse
  from bisect import bisect_right
  import math
  
  # An entry in the aranges table;
  # begin_addr: The beginning address in the CU
  # length: The length of the address range in this entry
  # info_offset: The CU's offset into .debug_info
  # see 6.1.2 in DWARF4 docs for explanation of the remaining fields
  ARangeEntry = namedtuple('ARangeEntry',
      'begin_addr length info_offset unit_length version address_size segment_size')
  
  class ARanges(object):
      """ ARanges table in DWARF
  
          stream, size:
              A stream holding the .debug_aranges section, and its size
  
          structs:
              A DWARFStructs instance for parsing the data
      """
      def __init__(self, stream, size, structs):
          self.stream = stream
          self.size = size
          self.structs = structs
  
          # Get entries of aranges table in the form of ARangeEntry tuples
          self.entries = self._get_entries()
  
          # Sort entries by the beginning address
          self.entries.sort(key=lambda entry: entry.begin_addr)
  
          # Create list of keys (first addresses) for better searching
          self.keys = [entry.begin_addr for entry in self.entries]
  
  
      def cu_offset_at_addr(self, addr):
          """ Given an address, get the offset of the CU it belongs to, where
              'offset' refers to the offset in the .debug_info section.
          """
          tup = self.entries[bisect_right(self.keys, addr) - 1]
          if tup.begin_addr <= addr < tup.begin_addr + tup.length:
              return tup.info_offset
          else:
              return None
  
  
      #------ PRIVATE ------#
      def _get_entries(self):
          """ Populate self.entries with ARangeEntry tuples for each range of addresses
          """
          self.stream.seek(0)
          entries = []
          offset = 0
  
          # one loop == one "set" == one CU
          while offset < self.size:
              aranges_header = struct_parse(self.structs.Dwarf_aranges_header,
                  self.stream, offset)
              addr_size = self._get_addr_size_struct(aranges_header["address_size"])
  
              # No segmentation
              if aranges_header["segment_size"] != 0:
                  raise NotImplementedError("Segmentation not implemented")
  
              # pad to nearest multiple of tuple size
              tuple_size = aranges_header["address_size"] * 2
              fp = self.stream.tell()
              seek_to = int(math.ceil(fp/float(tuple_size)) * tuple_size)
              self.stream.seek(seek_to)
  
              # entries in this set/CU
              addr = struct_parse(addr_size('addr'), self.stream)
              length = struct_parse(addr_size('length'), self.stream)
              while addr != 0 or length != 0:
                  # 'begin_addr length info_offset version address_size segment_size'
                  entries.append(
                      ARangeEntry(begin_addr=addr,
                          length=length,
                          info_offset=aranges_header["debug_info_offset"],
                          unit_length=aranges_header["unit_length"],
                          version=aranges_header["version"],
                          address_size=aranges_header["address_size"],
                          segment_size=aranges_header["segment_size"]))
                  addr = struct_parse(addr_size('addr'), self.stream)
                  length = struct_parse(addr_size('length'), self.stream)
              offset = (offset
                  + aranges_header.unit_length
                  + self.structs.initial_length_field_size())
  
          return entries
  
      def _get_addr_size_struct(self, addr_header_value):
          """ Given this set's header value (int) for the address size,
              get the Construct representation of that size
          """
          if addr_header_value == 4:
              return self.structs.Dwarf_uint32
          assert addr_header_value == 8
          return self.structs.Dwarf_uint64
  