Simon Glass | 0248932 | 2022-03-04 08:43:07 -0700 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # SPDX-License-Identifier: GPL-2.0+ |
| 3 | |
| 4 | """Decode the evspy_info linker list in a U-Boot ELF image""" |
| 5 | |
| 6 | from argparse import ArgumentParser |
| 7 | import os |
| 8 | import re |
| 9 | import struct |
| 10 | import sys |
| 11 | |
| 12 | our_path = os.path.dirname(os.path.realpath(__file__)) |
| 13 | src_path = os.path.dirname(our_path) |
| 14 | |
| 15 | sys.path.insert(1, os.path.join(our_path, '../tools')) |
| 16 | |
| 17 | from binman import elf |
| 18 | from patman import tools |
| 19 | |
Simon Glass | b169e21 | 2022-09-06 20:26:56 -0600 | [diff] [blame] | 20 | # A typical symbol looks like this: |
| 21 | # _u_boot_list_2_evspy_info_2_EVT_MISC_INIT_F_3_sandbox_misc_init_f |
Simon Glass | 0248932 | 2022-03-04 08:43:07 -0700 | [diff] [blame] | 22 | PREFIX = '_u_boot_list_2_evspy_info_2_' |
Simon Glass | b169e21 | 2022-09-06 20:26:56 -0600 | [diff] [blame] | 23 | RE_EVTYPE = re.compile('%s(.*)_3_.*' % PREFIX) |
Simon Glass | 0248932 | 2022-03-04 08:43:07 -0700 | [diff] [blame] | 24 | |
| 25 | def show_sym(fname, data, endian, evtype, sym): |
| 26 | """Show information about an evspy entry |
| 27 | |
| 28 | Args: |
| 29 | fname (str): Filename of ELF file |
| 30 | data (bytes): Data for this symbol |
| 31 | endian (str): Endianness to use ('little', 'big', 'auto') |
| 32 | evtype (str): Event type, e.g. 'MISC_INIT_F' |
| 33 | sym (elf.Symbol): Symbol to show |
| 34 | """ |
| 35 | def _unpack_val(sym_data, offset): |
| 36 | start = offset * func_size |
| 37 | val_data = sym_data[start:start + func_size] |
| 38 | fmt = '%s%s' % ('>' if endian == 'big' else '<', |
| 39 | 'L' if func_size == 4 else 'Q') |
| 40 | val = struct.unpack(fmt, val_data)[0] |
| 41 | return val |
| 42 | |
| 43 | # Get the data, which is a struct evspy_info |
| 44 | sym_data = data[sym.offset:sym.offset + sym.size] |
| 45 | |
| 46 | # Figure out the word size of the struct |
| 47 | func_size = 4 if sym.size < 16 else 8 |
| 48 | |
| 49 | # Read the function name for evspy_info->func |
| 50 | while True: |
| 51 | # Switch to big-endian if we see a failure |
| 52 | func_addr = _unpack_val(sym_data, 0) |
| 53 | func_name = elf.GetSymbolFromAddress(fname, func_addr) |
| 54 | if not func_name and endian == 'auto': |
| 55 | endian = 'big' |
| 56 | else: |
| 57 | break |
| 58 | has_id = sym.size in [12, 24] |
| 59 | if has_id: |
| 60 | # Find the address of evspy_info->id in the ELF |
| 61 | id_addr = _unpack_val(sym_data, 2) |
| 62 | |
| 63 | # Get the file offset for that address |
| 64 | id_ofs = elf.GetFileOffset(fname, id_addr) |
| 65 | |
| 66 | # Read out a nul-terminated string |
| 67 | id_data = data[id_ofs:id_ofs + 80] |
| 68 | pos = id_data.find(0) |
| 69 | if pos: |
| 70 | id_data = id_data[:pos] |
| 71 | id_str = id_data.decode('utf-8') |
| 72 | else: |
| 73 | id_str = None |
| 74 | |
| 75 | # Find the file/line for the function |
| 76 | cmd = ['addr2line', '-e', fname, '%x' % func_addr] |
| 77 | out = tools.run(*cmd).strip() |
| 78 | |
| 79 | # Drop the full path if it is the current directory |
| 80 | if out.startswith(src_path): |
| 81 | out = out[len(src_path) + 1:] |
| 82 | print('%-20s %-30s %s' % (evtype, id_str or f'f:{func_name}', out)) |
| 83 | |
| 84 | def show_event_spy_list(fname, endian): |
| 85 | """Show a the event-spy- list from a U-Boot image |
| 86 | |
| 87 | Args: |
| 88 | fname (str): Filename of ELF file |
| 89 | endian (str): Endianness to use ('little', 'big', 'auto') |
| 90 | """ |
| 91 | syms = elf.GetSymbolFileOffset(fname, [PREFIX]) |
| 92 | data = tools.read_file(fname) |
| 93 | print('%-20s %-30s %s' % ('Event type', 'Id', 'Source location')) |
| 94 | print('%-20s %-30s %s' % ('-' * 20, '-' * 30, '-' * 30)) |
| 95 | for name, sym in syms.items(): |
| 96 | m_evtype = RE_EVTYPE.search(name) |
| 97 | evtype = m_evtype .group(1) |
| 98 | show_sym(fname, data, endian, evtype, sym) |
| 99 | |
| 100 | def main(argv): |
| 101 | """Main program |
| 102 | |
| 103 | Args: |
| 104 | argv (list of str): List of program arguments, excluding arvg[0] |
| 105 | """ |
| 106 | epilog = 'Show a list of even spies in a U-Boot EFL file' |
| 107 | parser = ArgumentParser(epilog=epilog) |
| 108 | parser.add_argument('elf', type=str, help='ELF file to decode') |
| 109 | parser.add_argument('-e', '--endian', type=str, default='auto', |
| 110 | help='Big-endian image') |
| 111 | parser.add_argument('-t', '--test', action='store_true', |
| 112 | help='Big-endian image') |
| 113 | args = parser.parse_args(argv) |
| 114 | show_event_spy_list(args.elf, args.endian) |
| 115 | |
| 116 | if __name__ == "__main__": |
| 117 | main(sys.argv[1:]) |