| #------------------------------------------------------------------------------- |
| # 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] |
| return tup.info_offset |
| |
| |
| #------ 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: |
| # 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) |
| # Segmentation exists in executable |
| elif aranges_header["segment_size"] != 0: |
| raise NotImplementedError("Segmentation not implemented") |
| |
| 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 |
| else: |
| assert addr_header_value == 8 |
| return self.structs.Dwarf_uint64 |