#------------------------------------------------------------------------------- # 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)