--- /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)
+