From 19fafd6e2dc521fa64bfffe1220589bf09711ad8 Mon Sep 17 00:00:00 2001 From: Anders Dellien Date: Fri, 2 Aug 2019 15:56:49 +0200 Subject: [PATCH] Improved handling of location information (#225) This commit moves some of the location-handling code from the examples to a new class (LocationParser) in order to make it more reusable. Also adds two test files containing location information. --- elftools/dwarf/locationlists.py | 56 +++++++++++++- ...cation_lists.py => dwarf_location_info.py} | 72 +++++++++--------- ...tion_lists.out => dwarf_location_info.out} | 10 +++ .../testfiles_for_location_info/test-dwarf2.o | Bin 0 -> 10256 bytes .../testfiles_for_location_info/test-dwarf4.o | Bin 0 -> 10232 bytes 5 files changed, 102 insertions(+), 36 deletions(-) rename examples/{dwarf_location_lists.py => dwarf_location_info.py} (57%) rename examples/reference_output/{dwarf_location_lists.out => dwarf_location_info.out} (81%) create mode 100755 test/testfiles_for_location_info/test-dwarf2.o create mode 100755 test/testfiles_for_location_info/test-dwarf4.o diff --git a/elftools/dwarf/locationlists.py b/elftools/dwarf/locationlists.py index 3d97af3..5fba0c3 100644 --- a/elftools/dwarf/locationlists.py +++ b/elftools/dwarf/locationlists.py @@ -11,11 +11,10 @@ from collections import namedtuple from ..common.utils import struct_parse - +LocationExpr = namedtuple('LocationExpr', 'loc_expr') LocationEntry = namedtuple('LocationEntry', 'begin_offset end_offset loc_expr') BaseAddressEntry = namedtuple('BaseAddressEntry', 'base_address') - class LocationLists(object): """ A single location list is a Python list consisting of LocationEntry or BaseAddressEntry objects. @@ -69,3 +68,56 @@ class LocationLists(object): end_offset=end_offset, loc_expr=loc_expr)) return lst + +class LocationParser(object): + """ A parser for location information in DIEs. + Handles both location information contained within the attribute + itself (represented as a LocationExpr object) and references to + location lists in the .debug_loc section (represented as a + list). + """ + def __init__(self, location_lists): + self.location_lists = location_lists + + @staticmethod + def attribute_has_location(attr, dwarf_version): + """ Checks if a DIE attribute contains location information. + """ + return (LocationParser._attribute_is_loclistptr_class(attr) and + (LocationParser._attribute_has_loc_expr(attr, dwarf_version) or + LocationParser._attribute_has_loc_list(attr, dwarf_version))) + + def parse_from_attribute(self, attr, dwarf_version): + """ Parses a DIE attribute and returns either a LocationExpr or + a list. + """ + if self.attribute_has_location(attr, dwarf_version): + if self._attribute_has_loc_expr(attr, dwarf_version): + return LocationExpr(attr.value) + elif self._attribute_has_loc_list(attr, dwarf_version): + return self.location_lists.get_location_list_at_offset( + attr.value) + else: + raise ValueError("Attribute does not have location information") + + #------ PRIVATE ------# + + @staticmethod + def _attribute_has_loc_expr(attr, dwarf_version): + return (dwarf_version < 4 and attr.form == 'DW_FORM_block1' or + attr.form == 'DW_FORM_exprloc') + + @staticmethod + def _attribute_has_loc_list(attr, dwarf_version): + return ((dwarf_version < 4 and + attr.form in ('DW_FORM_data4', 'DW_FORM_data8')) or + attr.form == 'DW_FORM_sec_offset') + + @staticmethod + def _attribute_is_loclistptr_class(attr): + return (attr.name in ( 'DW_AT_location', 'DW_AT_string_length', + 'DW_AT_const_value', 'DW_AT_return_addr', + 'DW_AT_data_member_location', + 'DW_AT_frame_base', 'DW_AT_segment', + 'DW_AT_static_link', 'DW_AT_use_location', + 'DW_AT_vtable_elem_location')) diff --git a/examples/dwarf_location_lists.py b/examples/dwarf_location_info.py similarity index 57% rename from examples/dwarf_location_lists.py rename to examples/dwarf_location_info.py index a3a3982..5258e49 100644 --- a/examples/dwarf_location_lists.py +++ b/examples/dwarf_location_info.py @@ -1,8 +1,20 @@ #------------------------------------------------------------------------------- -# elftools example: dwarf_location_lists.py +# elftools example: dwarf_location_info.py # -# Examine DIE entries which have location list values, and decode these -# location lists. +# Examine DIE entries which have either location list values or location +# expression values and decode that information. +# +# Location information can either be completely contained within a DIE +# (using 'DW_FORM_exprloc' in DWARFv4 or 'DW_FORM_block1' in earlier +# versions) or be a reference to a location list contained within +# the .debug_loc section (using 'DW_FORM_sec_offset' in DWARFv4 or +# 'DW_FORM_data4' / 'DW_FORM_data8' in earlier versions). +# +# The LocationParser object parses the DIE attributes and handles both +# formats. +# +# The directory 'test/testfiles_for_location_info' contains test files with +# location information represented in both DWARFv4 and DWARFv2 forms. # # Eli Bendersky (eliben@gmail.com) # This code is in the public domain @@ -14,13 +26,12 @@ import sys # examples/ dir of the source distribution. sys.path[0:0] = ['.', '..'] - from elftools.common.py3compat import itervalues from elftools.elf.elffile import ELFFile from elftools.dwarf.descriptions import ( describe_DWARF_expr, set_global_machine_arch) -from elftools.dwarf.locationlists import LocationEntry - +from elftools.dwarf.locationlists import ( + LocationEntry, LocationExpr, LocationParser) def process_file(filename): print('Processing file:', filename) @@ -43,6 +54,10 @@ def process_file(filename): # register names contained in DWARF expressions. set_global_machine_arch(elffile.get_machine_arch()) + # Create a LocationParser object that parses the DIE attributes and + # creates objects representing the actual location information. + loc_parser = LocationParser(location_lists) + for CU in dwarfinfo.iter_CUs(): # DWARFInfo allows to iterate over the compile units contained in # the .debug_info section. CU is a CompileUnit object, with some @@ -58,18 +73,23 @@ def process_file(filename): # AttributeValue object (from elftools.dwarf.die), which we # can examine. for attr in itervalues(DIE.attributes): - if attribute_has_location_list(attr, CU['version']): - # This is a location list. Its value is an offset into - # the .debug_loc section, so we can use the location - # lists object to decode it. - loclist = location_lists.get_location_list_at_offset( - attr.value) - - print(' DIE %s. attr %s.\n%s' % ( - DIE.tag, - attr.name, - show_loclist(loclist, dwarfinfo, indent=' '))) - + # Check if this attribute contains location information + if loc_parser.attribute_has_location(attr, CU['version']): + print(' DIE %s. attr %s.' % (DIE.tag, attr.name)) + loc = loc_parser.parse_from_attribute(attr, + CU['version']) + # We either get a list (in case the attribute is a + # reference to the .debug_loc section) or a LocationExpr + # object (in case the attribute itself contains location + # information). + if isinstance(loc, LocationExpr): + print(' %s' % ( + describe_DWARF_expr(loc.loc_expr, + dwarfinfo.structs))) + elif isinstance(loc, list): + print(show_loclist(loc, + dwarfinfo, + indent=' ')) def show_loclist(loclist, dwarfinfo, indent): """ Display a location list nicely, decoding the DWARF expressions @@ -85,22 +105,6 @@ def show_loclist(loclist, dwarfinfo, indent): d.append(str(loc_entity)) return '\n'.join(indent + s for s in d) - -def attribute_has_location_list(attr, dwarf_version): - """ Only some attributes can have location list values, if they have the - required DW_FORM (loclistptr "class" in DWARF spec v3) - """ - if (attr.name in ( 'DW_AT_location', 'DW_AT_string_length', - 'DW_AT_const_value', 'DW_AT_return_addr', - 'DW_AT_data_member_location', 'DW_AT_frame_base', - 'DW_AT_segment', 'DW_AT_static_link', - 'DW_AT_use_location', 'DW_AT_vtable_elem_location')): - if (dwarf_version < 4 and attr.form in ('DW_FORM_data4', 'DW_FORM_data8') or - attr.form == 'DW_FORM_sec_offset'): - return True - return False - - if __name__ == '__main__': if sys.argv[1] == '--test': for filename in sys.argv[2:]: diff --git a/examples/reference_output/dwarf_location_lists.out b/examples/reference_output/dwarf_location_info.out similarity index 81% rename from examples/reference_output/dwarf_location_lists.out rename to examples/reference_output/dwarf_location_info.out index 8788755..9e1fe8e 100644 --- a/examples/reference_output/dwarf_location_lists.out +++ b/examples/reference_output/dwarf_location_info.out @@ -1,12 +1,22 @@ Processing file: ./examples/sample_exe64.elf Found a compile unit at offset 0, length 115 Found a compile unit at offset 119, length 135 + DIE DW_TAG_variable. attr DW_AT_location. + (DW_OP_addr: 400608) Found a compile unit at offset 258, length 156 DIE DW_TAG_subprogram. attr DW_AT_frame_base. LocationEntry(begin_offset=0, end_offset=1, loc_expr=[119, 8]) <<(DW_OP_breg7 (rsp): 8)>> LocationEntry(begin_offset=1, end_offset=4, loc_expr=[119, 16]) <<(DW_OP_breg7 (rsp): 16)>> LocationEntry(begin_offset=4, end_offset=43, loc_expr=[118, 16]) <<(DW_OP_breg6 (rbp): 16)>> + DIE DW_TAG_formal_parameter. attr DW_AT_location. + (DW_OP_fbreg: -20) + DIE DW_TAG_formal_parameter. attr DW_AT_location. + (DW_OP_fbreg: -32) + DIE DW_TAG_variable. attr DW_AT_location. + (DW_OP_addr: 601018) Found a compile unit at offset 418, length 300 + DIE DW_TAG_subprogram. attr DW_AT_frame_base. + (DW_OP_breg7 (rsp): 8) DIE DW_TAG_subprogram. attr DW_AT_frame_base. LocationEntry(begin_offset=16, end_offset=64, loc_expr=[119, 8]) <<(DW_OP_breg7 (rsp): 8)>> LocationEntry(begin_offset=64, end_offset=153, loc_expr=[119, 192, 0]) <<(DW_OP_breg7 (rsp): 64)>> diff --git a/test/testfiles_for_location_info/test-dwarf2.o b/test/testfiles_for_location_info/test-dwarf2.o new file mode 100755 index 0000000000000000000000000000000000000000..9bc2a280de97488092ff11093be452e958b87dc9 GIT binary patch literal 10256 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 literal 0 HcmV?d00001 diff --git a/test/testfiles_for_location_info/test-dwarf4.o b/test/testfiles_for_location_info/test-dwarf4.o new file mode 100755 index 0000000000000000000000000000000000000000..187ce70f817d9513e9a4c2d4b6cd051df38fe0f5 GIT binary patch literal 10232 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 V{9VHLW2N@*qN56z!@0Ble*r-7nJWMQ literal 0 HcmV?d00001 -- 2.30.2