From eb3dc08a7881f485d52c084ab298fe9c139cd867 Mon Sep 17 00:00:00 2001 From: Eli Bendersky Date: Thu, 5 Jul 2012 06:32:09 +0300 Subject: [PATCH] Added dwarf_decode_address.py example --- CHANGES | 5 ++ elftools/common/py3compat.py | 4 + examples/dwarf_decode_address.py | 86 +++++++++++++++++++ .../reference_output/dwarf_decode_address.out | 4 + 4 files changed, 99 insertions(+) create mode 100644 examples/dwarf_decode_address.py create mode 100644 examples/reference_output/dwarf_decode_address.out diff --git a/CHANGES b/CHANGES index 0e52284..dc64e66 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,11 @@ Changelog ========= ++ Version 0.21 (??) + + - Added new example: dwarf_decode_address - decode function name and + file & line information from an address. + + Version 0.20 (27.01.2012) - Python 3 support diff --git a/elftools/common/py3compat.py b/elftools/common/py3compat.py index 9b4529f..d57567d 100644 --- a/elftools/common/py3compat.py +++ b/elftools/common/py3compat.py @@ -28,6 +28,8 @@ if PY3: def byte2int(b): return b ifilter = filter + + maxint = sys.maxsize else: import cStringIO StringIO = BytesIO = cStringIO.StringIO @@ -45,6 +47,8 @@ else: from itertools import ifilter + maxint = sys.maxint + def iterkeys(d): """Return an iterator over the keys of a dictionary.""" diff --git a/examples/dwarf_decode_address.py b/examples/dwarf_decode_address.py new file mode 100644 index 0000000..831b4fc --- /dev/null +++ b/examples/dwarf_decode_address.py @@ -0,0 +1,86 @@ +#------------------------------------------------------------------------------- +# 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) + diff --git a/examples/reference_output/dwarf_decode_address.out b/examples/reference_output/dwarf_decode_address.out new file mode 100644 index 0000000..73ca9ee --- /dev/null +++ b/examples/reference_output/dwarf_decode_address.out @@ -0,0 +1,4 @@ +Processing file: ./examples/sample_exe64.elf +Function: main +File: z.c +Line: 4 -- 2.30.2