--- /dev/null
+#-------------------------------------------------------------------------------
+# elftools example: dwarf_decode_address.py
+#
+# Decode an address in an ELF file to find out which function it belongs to
+# and from which filename/line it comes in the original source file.
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+from __future__ import print_function
+import sys
+
+# If elftools is not installed, maybe we're running from the root or examples
+# dir of the source distribution
+try:
+ import elftools
+except ImportError:
+ sys.path.extend(['.', '..'])
+
+from elftools.common.py3compat import maxint, bytes2str
+from elftools.elf.elffile import ELFFile
+
+
+def process_file(filename, address):
+ print('Processing file:', filename)
+ with open(filename, 'rb') as f:
+ elffile = ELFFile(f)
+
+ if not elffile.has_dwarf_info():
+ print(' file has no DWARF info')
+ return
+
+ # get_dwarf_info returns a DWARFInfo context object, which is the
+ # starting point for all DWARF-based processing in pyelftools.
+ dwarfinfo = elffile.get_dwarf_info()
+
+ funcname = decode_funcname(dwarfinfo, address)
+ file, line = decode_file_line(dwarfinfo, address)
+
+ print('Function:', bytes2str(funcname))
+ print('File:', bytes2str(file))
+ print('Line:', line)
+
+
+def decode_funcname(dwarfinfo, address):
+ # Go over all DIEs in the DWARF information, looking for a subprogram
+ # entry with an address range that includes the given address. Note that
+ # this simplifies things by disregarding subprograms that may have
+ # split address ranges.
+ for CU in dwarfinfo.iter_CUs():
+ for DIE in CU.iter_DIEs():
+ try:
+ if DIE.tag == 'DW_TAG_subprogram':
+ lowpc = DIE.attributes['DW_AT_low_pc'].value
+ highpc = DIE.attributes['DW_AT_high_pc'].value
+ if lowpc <= address <= highpc:
+ return DIE.attributes['DW_AT_name'].value
+ except KeyError:
+ continue
+ return None
+
+
+def decode_file_line(dwarfinfo, address):
+ # Go over all the line programs in the DWARF information, looking for
+ # one that describes the given address.
+ for CU in dwarfinfo.iter_CUs():
+ # First, look at line programs to find the file/line for the address
+ lineprog = dwarfinfo.line_program_for_CU(CU)
+ prevaddr = maxint
+ for entry in lineprog.get_entries():
+ # We're interested in those entries where a new state is assigned
+ state = entry.state
+ if state is not None and not state.end_sequence:
+ if prevaddr <= address <= state.address:
+ filename = lineprog['file_entry'][state.file - 1].name
+ line = state.line
+ return filename, line
+ prevaddr = state.address
+ return None, None
+
+
+if __name__ == '__main__':
+ for filename in sys.argv[1:]:
+ # For testing we use a hardcoded address.
+ process_file(filename, 0x400503)
+