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