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