Skip to content

Commit

Permalink
Renamed and cleaned up Type Unit retrieval methods. Added/updated doc…
Browse files Browse the repository at this point in the history
…strings. Added link to binutils bug
  • Loading branch information
Dinkar Khandalekar committed Dec 18, 2024
1 parent 21da16b commit d551cb4
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 57 deletions.
92 changes: 43 additions & 49 deletions elftools/dwarf/dwarfinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ def has_debug_info(self):
"""
return bool(self.debug_info_sec)

def has_debug_types(self):
""" Return whether this contains debug types information.
"""
return bool(self.debug_types_sec)

def get_DIE_from_lut_entry(self, lut_entry):
""" Get the DIE from the pubnames or putbtypes lookup table entry.
Expand All @@ -175,35 +180,6 @@ def get_DIE_from_refaddr(self, refaddr, cu=None):
cu = self.get_CU_containing(refaddr)
return cu.get_DIE_from_refaddr(refaddr)

def get_DIE_by_sig8(self, sig8):
""" Find and return a DIE referenced by its type signature.
sig8:
The 8 byte signature (as a 64-bit unsigned integer)
Returns the DIE with the given type signature by searching
for the Type Unit with the matching signature then finding
the DIE at the offset given by the type_die field in the
Type Unit header.
Signatures are an 64-bit unsigned integers computed by the
DWARF producer as specified in the DWARF standard. Each
Type Unit contains one signature and the offset to the
corresponding DW_AT_type DIE in its unit header.
Describing a type can generate several DIEs. By moving
a DIE and its related DIEs to a Type Unit and generating
a hash of the DIEs and attributes in a flattened form
multiple Compile Units in a linked object can reference
the same DIE in the overall DWARF structure.
In DWARF v4 type units are identified by their appearance in the
.debug_types section.
"""
self._parse_debug_types()
tu = self._type_units_by_sig.get(sig8)
if tu is None:
raise KeyError("Signature %016x not found in .debug_types" % sig8)
return tu.get_cached_DIE(tu.tu_offset + tu['type_offset'])

def get_CU_containing(self, refaddr):
""" Find the CU that includes the given reference address in the
.debug_info section.
Expand Down Expand Up @@ -258,6 +234,22 @@ def get_CU_at(self, offset):

return self._cached_CU_at_offset(offset)

def get_TU_by_sig8(self, sig8):
""" Find and return a Type Unit referenced by its signature
sig8:
The 8 byte unique signature (as a 64-bit unsigned integer)
Returns the TU with the given type signature by parsing the
.debug_types section.
"""
self._parse_debug_types()
tu = self._type_units_by_sig.get(sig8)
if tu is None:
raise KeyError("Signature %016x not found in .debug_types" % sig8)
return tu

def iter_CUs(self):
""" Yield all the compile units (CompileUnit objects) in the debug info
"""
Expand All @@ -266,7 +258,7 @@ def iter_CUs(self):
def iter_TUs(self):
"""Yield all the compile units (CompileUnit objects) in the debug_types
"""
return self.parse_TUs_iter()
return self._parse_TUs_iter()

def get_abbrev_table(self, offset):
""" Get an AbbrevTable from the given offset in the debug_abbrev
Expand Down Expand Up @@ -461,14 +453,22 @@ def _parse_CUs_iter(self, offset=0):
cu.structs.initial_length_field_size())
yield cu

def parse_TUs_iter(self, offset=0):
def _parse_TUs_iter(self, offset=0):
""" Iterate Type Unit objects in order of appearance in the debug_types section.
offset:
The offset of the first TU to yield. Additional iterations
will return the sequential unit objects.
See .iter_TUs().
"""
if self.debug_types_sec is None:
return

while offset < self.debug_types_sec.size:
tu = self._parse_TU_at_offset(offset)
# Compute the offset of the next CU in the section. The unit_length
# field of the CU header contains its size not including the length
# Compute the offset of the next TU in the section. The unit_length
# field of the TU header contains its size not including the length
# field itself.
offset = (offset +
tu['unit_length'] +
Expand All @@ -477,8 +477,10 @@ def parse_TUs_iter(self, offset=0):
yield tu

def _parse_debug_types(self):
""" Parse all the TU entries in the .debug_types section.
Place units into an OrderedDict keyed by type signature.
""" Check if the .debug_types section is previously parsed. If not,
parse all TUs and store them in an OrderedDict using their unique
64-bit signature as the key.
"""
if self._type_units_by_sig is not None:
return
Expand All @@ -487,17 +489,10 @@ def _parse_debug_types(self):
if self.debug_types_sec is None:
return

# Parse all the Type Units in the types section for access by sig8
offset = 0
while offset < self.debug_types_sec.size:
tu = self._parse_CU_at_offset(offset, types_section=True)
# Compute the offset of the next TU in the section. The unit_length
# field of the TU header contains its size not including the length
# field itself.
offset = (offset +
tu['unit_length'] +
tu.structs.initial_length_field_size())
self._type_units_by_sig[tu['type_signature']] = tu
# Collect all Type Units in the .debug_types section for access using
# their 8-byte unique signature
for tu in self._parse_TUs_iter():
self._type_units_by_sig[tu['signature']] = tu

def _cached_CU_at_offset(self, offset):
""" Return the CU with unit header at the given offset into the
Expand Down Expand Up @@ -583,12 +578,11 @@ def _parse_TU_at_offset(self, offset):
# instance suitable for this TU and use it to parse the rest.
#
initial_length = struct_parse(
self.structs.Dwarf_uint32(''), self.debug_types_sec.stream, offset)
self.structs.the_Dwarf_uint32, self.debug_types_sec.stream, offset)
dwarf_format = 64 if initial_length == 0xFFFFFFFF else 32

# Temporary structs for parsing the header
# The structs for the rest of the TU depend on the header data.
#
# The structs for the rest of the TUs depend on the header data.
tu_structs = DWARFStructs(
little_endian=self.config.little_endian,
dwarf_format=dwarf_format,
Expand Down
4 changes: 2 additions & 2 deletions elftools/dwarf/structs.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ class DWARFStructs(object):
Dwarf_CU_header (+):
Compilation unit header
Dwarf_CU_types_header (+):
Compilation unit (.debug_types section) header
Dwarf_TU_header (+):
Type unit header
Dwarf_abbrev_declaration (+):
Abbreviation table declaration - doesn't include the initial
Expand Down
5 changes: 2 additions & 3 deletions elftools/dwarf/typeunit.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class TypeUnit(object):
Type units are stored in the .debug_types section. This section was
introduced by the DWARFv4 standard (and removed in the DWARFv5 standard;
underlying type units were relocated to the .debug_types section)
underlying type units were relocated to the .debug_info section)
Serves as a container and context to DIEs that describe type definitions
referenced from compilation units and other type units.
Expand Down Expand Up @@ -96,8 +96,7 @@ def get_abbrev_table(self):
return self._abbrev_table

def get_top_DIE(self):
""" Get the top DIE (which is either a DW_TAG_compile_unit or
DW_TAG_partial_unit) of this TU
""" Get the top DIE (which is DW_TAG_type_unit entry) of this TU
"""

# Note that a top DIE always has minimal offset and is therefore
Expand Down
6 changes: 3 additions & 3 deletions test/run_readelf_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ def run_test_on_file(filename, verbose=False, opt=None):
options = [opt]

if filename.endswith('dwarf_debug_types.elf'):
# TODO: The offset calculation logic in dwarf/callframe.py starts and
# infinite loop with the IAR EWARM 9.20.4 generated .debug_frames section.
# Needs investigation
# TODO: excluding the binary with .debug_types section until the length
# the calculation for FDEs in binutils bug #31973 is fixed
# https://sourceware.org/bugzilla/show_bug.cgi?id=31973
options.remove('--debug-dump=frames')
options.remove('--debug-dump=frames-interp')

Expand Down

0 comments on commit d551cb4

Please sign in to comment.