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 = '_u_boot_list_2_evspy_info_2_' 23RE_EVTYPE = re.compile('%s(.*)_3_.*' % PREFIX) 24 25def 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 84def 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 100def 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 args = parser.parse_args(argv) 112 show_event_spy_list(args.elf, args.endian) 113 114if __name__ == "__main__": 115 main(sys.argv[1:]) 116