047ce3b56281abd4938b3e6e793a69a42d09bfdf
1 #-------------------------------------------------------------------------------
2 # elftools example: dwarf_decode_address.py
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.
7 # Eli Bendersky (eliben@gmail.com)
8 # This code is in the public domain
9 #-------------------------------------------------------------------------------
10 from __future__
import print_function
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] = ['.', '..']
17 from elftools
.common
.py3compat
import maxint
, bytes2str
18 from elftools
.dwarf
.descriptions
import describe_form_class
19 from elftools
.elf
.elffile
import ELFFile
22 def process_file(filename
, address
):
23 print('Processing file:', filename
)
24 with
open(filename
, 'rb') as f
:
27 if not elffile
.has_dwarf_info():
28 print(' file has no DWARF info')
31 # get_dwarf_info returns a DWARFInfo context object, which is the
32 # starting point for all DWARF-based processing in pyelftools.
33 dwarfinfo
= elffile
.get_dwarf_info()
35 funcname
= decode_funcname(dwarfinfo
, address
)
36 file, line
= decode_file_line(dwarfinfo
, address
)
38 print('Function:', bytes2str(funcname
))
39 print('File:', bytes2str(file))
43 def decode_funcname(dwarfinfo
, address
):
44 # Go over all DIEs in the DWARF information, looking for a subprogram
45 # entry with an address range that includes the given address. Note that
46 # this simplifies things by disregarding subprograms that may have
47 # split address ranges.
48 for CU
in dwarfinfo
.iter_CUs():
49 for DIE
in CU
.iter_DIEs():
51 if DIE
.tag
== 'DW_TAG_subprogram':
52 lowpc
= DIE
.attributes
['DW_AT_low_pc'].value
54 # DWARF v4 in section 2.17 describes how to interpret the
55 # DW_AT_high_pc attribute based on the class of its form.
56 # For class 'address' it's taken as an absolute address
57 # (similarly to DW_AT_low_pc); for class 'constant', it's
58 # an offset from DW_AT_low_pc.
59 highpc_attr
= DIE
.attributes
['DW_AT_high_pc']
60 highpc_attr_class
= describe_form_class(highpc_attr
.form
)
61 if highpc_attr_class
== 'address':
62 highpc
= highpc_attr
.value
63 elif highpc_attr_class
== 'constant':
64 highpc
= lowpc
+ highpc_attr
.value
66 print('Error: invalid DW_AT_high_pc class:',
70 if lowpc
<= address
<= highpc
:
71 return DIE
.attributes
['DW_AT_name'].value
77 def decode_file_line(dwarfinfo
, address
):
78 # Go over all the line programs in the DWARF information, looking for
79 # one that describes the given address.
80 for CU
in dwarfinfo
.iter_CUs():
81 # First, look at line programs to find the file/line for the address
82 lineprog
= dwarfinfo
.line_program_for_CU(CU
)
84 for entry
in lineprog
.get_entries():
85 # We're interested in those entries where a new state is assigned
86 if entry
.state
is None:
88 if entry
.state
.end_sequence
:
89 # if the line number sequence ends, clear prevstate.
92 # Looking for a range of addresses in two consecutive states that
93 # contain the required address.
94 if prevstate
and prevstate
.address
<= address
< entry
.state
.address
:
95 filename
= lineprog
['file_entry'][prevstate
.file - 1].name
98 prevstate
= entry
.state
102 if __name__
== '__main__':
103 if sys
.argv
[1] == '--test':
104 process_file(sys
.argv
[2], 0x400503)
107 if len(sys
.argv
) < 3:
108 print('Expected usage: {0} <address> <executable>'.format(sys
.argv
[0]))
110 addr
= int(sys
.argv
[1], 0)
111 process_file(sys
.argv
[2], addr
)