blob: 831b4fc3110c78bf8c04c936396abb93c16ebd5b [file] [log] [blame]
# elftools example:
# 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 (
# 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
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')
# 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():
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:
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)