xref: /openbmc/u-boot/tools/binman/elf.py (revision 83d290c5)
1*83d290c5STom Rini# SPDX-License-Identifier: GPL-2.0+
2b50e5611SSimon Glass# Copyright (c) 2016 Google, Inc
3b50e5611SSimon Glass# Written by Simon Glass <sjg@chromium.org>
4b50e5611SSimon Glass#
5b50e5611SSimon Glass# Handle various things related to ELF images
6b50e5611SSimon Glass#
7b50e5611SSimon Glass
8b50e5611SSimon Glassfrom collections import namedtuple, OrderedDict
9b50e5611SSimon Glassimport command
10b50e5611SSimon Glassimport os
11b50e5611SSimon Glassimport re
12b50e5611SSimon Glassimport struct
13b50e5611SSimon Glass
14b50e5611SSimon Glassimport tools
15b50e5611SSimon Glass
167fe9173bSSimon Glass# This is enabled from control.py
177fe9173bSSimon Glassdebug = False
187fe9173bSSimon Glass
19b50e5611SSimon GlassSymbol = namedtuple('Symbol', ['section', 'address', 'size', 'weak'])
20b50e5611SSimon Glass
21b50e5611SSimon Glass
22b50e5611SSimon Glassdef GetSymbols(fname, patterns):
23b50e5611SSimon Glass    """Get the symbols from an ELF file
24b50e5611SSimon Glass
25b50e5611SSimon Glass    Args:
26b50e5611SSimon Glass        fname: Filename of the ELF file to read
27b50e5611SSimon Glass        patterns: List of regex patterns to search for, each a string
28b50e5611SSimon Glass
29b50e5611SSimon Glass    Returns:
30b50e5611SSimon Glass        None, if the file does not exist, or Dict:
31b50e5611SSimon Glass          key: Name of symbol
32b50e5611SSimon Glass          value: Hex value of symbol
33b50e5611SSimon Glass    """
34b50e5611SSimon Glass    stdout = command.Output('objdump', '-t', fname, raise_on_error=False)
35b50e5611SSimon Glass    lines = stdout.splitlines()
36b50e5611SSimon Glass    if patterns:
37b50e5611SSimon Glass        re_syms = re.compile('|'.join(patterns))
38b50e5611SSimon Glass    else:
39b50e5611SSimon Glass        re_syms = None
40b50e5611SSimon Glass    syms = {}
41b50e5611SSimon Glass    syms_started = False
42b50e5611SSimon Glass    for line in lines:
43b50e5611SSimon Glass        if not line or not syms_started:
44b50e5611SSimon Glass            if 'SYMBOL TABLE' in line:
45b50e5611SSimon Glass                syms_started = True
46b50e5611SSimon Glass            line = None  # Otherwise code coverage complains about 'continue'
47b50e5611SSimon Glass            continue
48b50e5611SSimon Glass        if re_syms and not re_syms.search(line):
49b50e5611SSimon Glass            continue
50b50e5611SSimon Glass
51b50e5611SSimon Glass        space_pos = line.find(' ')
52b50e5611SSimon Glass        value, rest = line[:space_pos], line[space_pos + 1:]
53b50e5611SSimon Glass        flags = rest[:7]
54b50e5611SSimon Glass        parts = rest[7:].split()
55b50e5611SSimon Glass        section, size =  parts[:2]
56b50e5611SSimon Glass        if len(parts) > 2:
57b50e5611SSimon Glass            name = parts[2]
58b50e5611SSimon Glass            syms[name] = Symbol(section, int(value, 16), int(size,16),
59b50e5611SSimon Glass                                flags[1] == 'w')
60b50e5611SSimon Glass    return syms
61b50e5611SSimon Glass
62b50e5611SSimon Glassdef GetSymbolAddress(fname, sym_name):
63b50e5611SSimon Glass    """Get a value of a symbol from an ELF file
64b50e5611SSimon Glass
65b50e5611SSimon Glass    Args:
66b50e5611SSimon Glass        fname: Filename of the ELF file to read
67b50e5611SSimon Glass        patterns: List of regex patterns to search for, each a string
68b50e5611SSimon Glass
69b50e5611SSimon Glass    Returns:
70b50e5611SSimon Glass        Symbol value (as an integer) or None if not found
71b50e5611SSimon Glass    """
72b50e5611SSimon Glass    syms = GetSymbols(fname, [sym_name])
73b50e5611SSimon Glass    sym = syms.get(sym_name)
74b50e5611SSimon Glass    if not sym:
75b50e5611SSimon Glass        return None
76b50e5611SSimon Glass    return sym.address
7719790632SSimon Glass
7819790632SSimon Glassdef LookupAndWriteSymbols(elf_fname, entry, image):
7919790632SSimon Glass    """Replace all symbols in an entry with their correct values
8019790632SSimon Glass
8119790632SSimon Glass    The entry contents is updated so that values for referenced symbols will be
8219790632SSimon Glass    visible at run time. This is done by finding out the symbols positions in
8319790632SSimon Glass    the entry (using the ELF file) and replacing them with values from binman's
8419790632SSimon Glass    data structures.
8519790632SSimon Glass
8619790632SSimon Glass    Args:
8719790632SSimon Glass        elf_fname: Filename of ELF image containing the symbol information for
8819790632SSimon Glass            entry
8919790632SSimon Glass        entry: Entry to process
9019790632SSimon Glass        image: Image which can be used to lookup symbol values
9119790632SSimon Glass    """
9219790632SSimon Glass    fname = tools.GetInputFilename(elf_fname)
9319790632SSimon Glass    syms = GetSymbols(fname, ['image', 'binman'])
9419790632SSimon Glass    if not syms:
9519790632SSimon Glass        return
9619790632SSimon Glass    base = syms.get('__image_copy_start')
9719790632SSimon Glass    if not base:
9819790632SSimon Glass        return
9919790632SSimon Glass    for name, sym in syms.iteritems():
10019790632SSimon Glass        if name.startswith('_binman'):
10119790632SSimon Glass            msg = ("Image '%s': Symbol '%s'\n   in entry '%s'" %
10219790632SSimon Glass                   (image.GetPath(), name, entry.GetPath()))
10319790632SSimon Glass            offset = sym.address - base.address
10419790632SSimon Glass            if offset < 0 or offset + sym.size > entry.contents_size:
10519790632SSimon Glass                raise ValueError('%s has offset %x (size %x) but the contents '
10619790632SSimon Glass                                 'size is %x' % (entry.GetPath(), offset,
10719790632SSimon Glass                                                 sym.size, entry.contents_size))
10819790632SSimon Glass            if sym.size == 4:
10919790632SSimon Glass                pack_string = '<I'
11019790632SSimon Glass            elif sym.size == 8:
11119790632SSimon Glass                pack_string = '<Q'
11219790632SSimon Glass            else:
11319790632SSimon Glass                raise ValueError('%s has size %d: only 4 and 8 are supported' %
11419790632SSimon Glass                                 (msg, sym.size))
11519790632SSimon Glass
11619790632SSimon Glass            # Look up the symbol in our entry tables.
11719790632SSimon Glass            value = image.LookupSymbol(name, sym.weak, msg)
11819790632SSimon Glass            if value is not None:
11919790632SSimon Glass                value += base.address
12019790632SSimon Glass            else:
12119790632SSimon Glass                value = -1
12219790632SSimon Glass                pack_string = pack_string.lower()
12319790632SSimon Glass            value_bytes = struct.pack(pack_string, value)
12419790632SSimon Glass            if debug:
12519790632SSimon Glass                print('%s:\n   insert %s, offset %x, value %x, length %d' %
12619790632SSimon Glass                      (msg, name, offset, value, len(value_bytes)))
12719790632SSimon Glass            entry.data = (entry.data[:offset] + value_bytes +
12819790632SSimon Glass                        entry.data[offset + sym.size:])
129