Eli Bendersky | eb3dc08 | 2012-07-05 06:32:09 +0300 | [diff] [blame] | 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 | |
Eli Bendersky | cc1e557 | 2013-04-09 21:25:54 -0700 | [diff] [blame] | 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] = ['.', '..'] |
Eli Bendersky | eb3dc08 | 2012-07-05 06:32:09 +0300 | [diff] [blame] | 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 |
Eli Bendersky | dd71c43 | 2013-04-08 06:38:57 -0700 | [diff] [blame] | 45 | # this simplifies things by disregarding subprograms that may have |
Eli Bendersky | eb3dc08 | 2012-07-05 06:32:09 +0300 | [diff] [blame] | 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) |
Eli Bendersky | d79a0a3 | 2013-04-12 06:29:37 -0700 | [diff] [blame] | 66 | prevstate = None |
Eli Bendersky | eb3dc08 | 2012-07-05 06:32:09 +0300 | [diff] [blame] | 67 | for entry in lineprog.get_entries(): |
| 68 | # We're interested in those entries where a new state is assigned |
Eli Bendersky | d79a0a3 | 2013-04-12 06:29:37 -0700 | [diff] [blame] | 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 |
Eli Bendersky | eb3dc08 | 2012-07-05 06:32:09 +0300 | [diff] [blame] | 78 | return None, None |
| 79 | |
| 80 | |
| 81 | if __name__ == '__main__': |
| 82 | for filename in sys.argv[1:]: |
| 83 | # For testing we use a hardcoded address. |
| 84 | process_file(filename, 0x400503) |
| 85 | |