3f5108ec3245f611368ca0ef420d66318f95204c
[pyelftools.git] / examples / dwarf_decode_address.py
1 #-------------------------------------------------------------------------------
2 # elftools example: dwarf_decode_address.py
3 #
4 # Decode an address in an ELF file to find out which function it belongs to
5 # and from which filename/line it comes in the original source file.
6 #
7 # Eli Bendersky (eliben@gmail.com)
8 # This code is in the public domain
9 #-------------------------------------------------------------------------------
10 from __future__ import print_function
11 import sys
12
13 # If pyelftools is not installed, the example can also run from the root or
14 # examples/ dir of the source distribution.
15 sys.path[0:0] = ['.', '..']
16
17 from elftools.common.py3compat import maxint, bytes2str
18 from elftools.elf.elffile import ELFFile
19
20
21 def process_file(filename, address):
22 print('Processing file:', filename)
23 with open(filename, 'rb') as f:
24 elffile = ELFFile(f)
25
26 if not elffile.has_dwarf_info():
27 print(' file has no DWARF info')
28 return
29
30 # get_dwarf_info returns a DWARFInfo context object, which is the
31 # starting point for all DWARF-based processing in pyelftools.
32 dwarfinfo = elffile.get_dwarf_info()
33
34 funcname = decode_funcname(dwarfinfo, address)
35 file, line = decode_file_line(dwarfinfo, address)
36
37 print('Function:', bytes2str(funcname))
38 print('File:', bytes2str(file))
39 print('Line:', line)
40
41
42 def decode_funcname(dwarfinfo, address):
43 # Go over all DIEs in the DWARF information, looking for a subprogram
44 # entry with an address range that includes the given address. Note that
45 # this simplifies things by disregarding subprograms that may have
46 # split address ranges.
47 for CU in dwarfinfo.iter_CUs():
48 for DIE in CU.iter_DIEs():
49 try:
50 if DIE.tag == 'DW_TAG_subprogram':
51 lowpc = DIE.attributes['DW_AT_low_pc'].value
52 highpc = DIE.attributes['DW_AT_high_pc'].value
53 if lowpc <= address <= highpc:
54 return DIE.attributes['DW_AT_name'].value
55 except KeyError:
56 continue
57 return None
58
59
60 def decode_file_line(dwarfinfo, address):
61 # Go over all the line programs in the DWARF information, looking for
62 # one that describes the given address.
63 for CU in dwarfinfo.iter_CUs():
64 # First, look at line programs to find the file/line for the address
65 lineprog = dwarfinfo.line_program_for_CU(CU)
66 prevstate = None
67 for entry in lineprog.get_entries():
68 # We're interested in those entries where a new state is assigned
69 if entry.state is None or entry.state.end_sequence:
70 continue
71 # Looking for a range of addresses in two consecutive states that
72 # contain the required address.
73 if prevstate and prevstate.address <= address < entry.state.address:
74 filename = lineprog['file_entry'][prevstate.file - 1].name
75 line = prevstate.line
76 return filename, line
77 prevstate = entry.state
78 return None, None
79
80
81 if __name__ == '__main__':
82 if sys.argv[1] == '--test':
83 process_file(sys.argv[2], 0x400503)
84 sys.exit(0)
85
86 if len(sys.argv) < 3:
87 print('Expected usage: {0} <address> <executable>'.format(sys.argv[0]))
88 sys.exit(1)
89 addr = int(sys.argv[1], 0)
90 process_file(sys.argv[2], addr)