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 | |
| 13 | # If elftools is not installed, maybe we're running from the root or examples |
| 14 | # dir of the source distribution |
| 15 | try: |
| 16 | import elftools |
| 17 | except ImportError: |
Eli Bendersky | dd71c43 | 2013-04-08 06:38:57 -0700 | [diff] [blame] | 18 | sys.path[0:0] = ['.', '..'] |
Eli Bendersky | eb3dc08 | 2012-07-05 06:32:09 +0300 | [diff] [blame] | 19 | |
| 20 | from elftools.common.py3compat import maxint, bytes2str |
| 21 | from elftools.elf.elffile import ELFFile |
| 22 | |
| 23 | |
| 24 | def process_file(filename, address): |
| 25 | print('Processing file:', filename) |
| 26 | with open(filename, 'rb') as f: |
| 27 | elffile = ELFFile(f) |
| 28 | |
| 29 | if not elffile.has_dwarf_info(): |
| 30 | print(' file has no DWARF info') |
| 31 | return |
| 32 | |
| 33 | # get_dwarf_info returns a DWARFInfo context object, which is the |
| 34 | # starting point for all DWARF-based processing in pyelftools. |
| 35 | dwarfinfo = elffile.get_dwarf_info() |
| 36 | |
| 37 | funcname = decode_funcname(dwarfinfo, address) |
| 38 | file, line = decode_file_line(dwarfinfo, address) |
| 39 | |
| 40 | print('Function:', bytes2str(funcname)) |
| 41 | print('File:', bytes2str(file)) |
| 42 | print('Line:', line) |
| 43 | |
| 44 | |
| 45 | def decode_funcname(dwarfinfo, address): |
| 46 | # Go over all DIEs in the DWARF information, looking for a subprogram |
| 47 | # entry with an address range that includes the given address. Note that |
Eli Bendersky | dd71c43 | 2013-04-08 06:38:57 -0700 | [diff] [blame] | 48 | # this simplifies things by disregarding subprograms that may have |
Eli Bendersky | eb3dc08 | 2012-07-05 06:32:09 +0300 | [diff] [blame] | 49 | # split address ranges. |
| 50 | for CU in dwarfinfo.iter_CUs(): |
| 51 | for DIE in CU.iter_DIEs(): |
| 52 | try: |
| 53 | if DIE.tag == 'DW_TAG_subprogram': |
| 54 | lowpc = DIE.attributes['DW_AT_low_pc'].value |
| 55 | highpc = DIE.attributes['DW_AT_high_pc'].value |
| 56 | if lowpc <= address <= highpc: |
| 57 | return DIE.attributes['DW_AT_name'].value |
| 58 | except KeyError: |
| 59 | continue |
| 60 | return None |
| 61 | |
| 62 | |
| 63 | def decode_file_line(dwarfinfo, address): |
| 64 | # Go over all the line programs in the DWARF information, looking for |
| 65 | # one that describes the given address. |
| 66 | for CU in dwarfinfo.iter_CUs(): |
| 67 | # First, look at line programs to find the file/line for the address |
| 68 | lineprog = dwarfinfo.line_program_for_CU(CU) |
| 69 | prevaddr = maxint |
| 70 | for entry in lineprog.get_entries(): |
| 71 | # We're interested in those entries where a new state is assigned |
| 72 | state = entry.state |
| 73 | if state is not None and not state.end_sequence: |
| 74 | if prevaddr <= address <= state.address: |
| 75 | filename = lineprog['file_entry'][state.file - 1].name |
| 76 | line = state.line |
| 77 | return filename, line |
| 78 | prevaddr = state.address |
| 79 | return None, None |
| 80 | |
| 81 | |
| 82 | if __name__ == '__main__': |
| 83 | for filename in sys.argv[1:]: |
| 84 | # For testing we use a hardcoded address. |
| 85 | process_file(filename, 0x400503) |
| 86 | |