Better DWARF v4 support for decoding function ranges.
authorEli Bendersky <eliben@gmail.com>
Thu, 9 Jul 2015 13:37:01 +0000 (06:37 -0700)
committerEli Bendersky <eliben@gmail.com>
Thu, 9 Jul 2015 13:37:01 +0000 (06:37 -0700)
CHANGES
elftools/dwarf/constants.py
elftools/dwarf/descriptions.py
examples/dwarf_decode_address.py

diff --git a/CHANGES b/CHANGES
index b453a269fdb248bc10d0fc7c7d5c0945d8a78b05..5bba7df218872c9e37f6282dc3ebe20d7e94fc7c 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -6,6 +6,12 @@ Changelog
   - Retrieve symbols by name - get_symbol_by_name (#58).
   - Symbol/section names are strings internally now, not bytestrings (this may
     affect API usage in Python 3) (#76).
+  - Added DT_MIPS_* constants to ENUM_D_TAG (#79)
+  - Made dwarf_decode_address example a bit more useful for command-line
+    invocation.
+  - More DWARF v4 support w.r.t decoding function ranges; DW_AT_high_pc value
+    is now either absolute or relative to DW_AT_low_pc, depending on the class
+    of the form encoded in the file.
 
 + Version 0.23 (08.11.2014)
 
index 8bf38e870ea6202ab197d9a255491939e8d503c0..b79c124875849dee1ef7bdec59667823abd2d9d0 100644 (file)
@@ -173,5 +173,3 @@ DW_CFA_def_cfa_offset_sf = 0x13
 DW_CFA_val_offset = 0x14\r
 DW_CFA_val_offset_sf = 0x15\r
 DW_CFA_val_expression = 0x16\r
-\r
-\r
index 4cd62fe444742692eeb0c03e218b1d25d91ce848..9c56d67a8ee3a217daa5ed2cb645741877e00427 100644 (file)
@@ -166,6 +166,18 @@ def describe_reg_name(regnum, machine_arch=None, default=True):
     else:
         return None
 
+def describe_form_class(form):
+    """For a given form name, determine its value class.
+
+    For example, given 'DW_FORM_data1' returns 'constant'.
+
+    For some forms, like DW_FORM_indirect and DW_FORM_sec_offset, the class is
+    not hard-coded and extra information is required. For these, None is
+    returned.
+    """
+    return _FORM_CLASS[form]
+
+
 #-------------------------------------------------------------------------------
 
 # The machine architecture. Set globally via set_global_machine_arch
@@ -244,6 +256,33 @@ _ATTR_DESCRIPTION_MAP = defaultdict(
     DW_FORM_ref_sig8=_describe_attr_ref,
 )
 
+_FORM_CLASS = dict(
+    DW_FORM_addr='address',
+    DW_FORM_block2='block',
+    DW_FORM_block4='block',
+    DW_FORM_data2='constant',
+    DW_FORM_data4='constant',
+    DW_FORM_data8='constant',
+    DW_FORM_string='string',
+    DW_FORM_block='block',
+    DW_FORM_block1='block',
+    DW_FORM_data1='constant',
+    DW_FORM_flag='flag',
+    DW_FORM_sdata='constant',
+    DW_FORM_strp='string',
+    DW_FORM_udata='constant',
+    DW_FORM_ref_addr='reference',
+    DW_FORM_ref1='reference',
+    DW_FORM_ref2='reference',
+    DW_FORM_ref4='reference',
+    DW_FORM_ref8='reference',
+    DW_FORM_ref_udata='reference',
+    DW_FORM_indirect=None,
+    DW_FORM_sec_offset=None,
+    DW_FORM_exprloc='exprloc',
+    DW_FORM_flag_present='flag',
+    DW_FORM_ref_sig8='reference',
+)
 
 _DESCR_DW_INL = {
     DW_INL_not_inlined: '(not inlined)',
index 3f5108ec3245f611368ca0ef420d66318f95204c..7b7d3e0186c09314652c7aed33db6aa7559d2f48 100644 (file)
@@ -15,6 +15,7 @@ import sys
 sys.path[0:0] = ['.', '..']
 
 from elftools.common.py3compat import maxint, bytes2str
+from elftools.dwarf.descriptions import describe_form_class
 from elftools.elf.elffile import ELFFile
 
 
@@ -49,7 +50,23 @@ def decode_funcname(dwarfinfo, address):
             try:
                 if DIE.tag == 'DW_TAG_subprogram':
                     lowpc = DIE.attributes['DW_AT_low_pc'].value
-                    highpc = DIE.attributes['DW_AT_high_pc'].value
+
+                    # DWARF v4 in section 2.17 describes how to interpret the
+                    # DW_AT_high_pc attribute based on the class of its form.
+                    # For class 'address' it's taken as an absolute address
+                    # (similarly to DW_AT_low_pc); for class 'constant', it's
+                    # an offset from DW_AT_low_pc.
+                    highpc_attr = DIE.attributes['DW_AT_high_pc']
+                    highpc_attr_class = describe_form_class(highpc_attr.form)
+                    if highpc_attr_class == 'address':
+                        highpc = highpc_attr.value
+                    elif highpc_attr_class == 'constant':
+                        highpc = lowpc + highpc_attr.value
+                    else:
+                        print('Error: invalid DW_AT_high_pc class:',
+                              highpc_attr_class)
+                        continue
+
                     if lowpc <= address <= highpc:
                         return DIE.attributes['DW_AT_name'].value
             except KeyError: