xref: /openbmc/u-boot/tools/binman/elf.py (revision fc82e768)
183d290c5STom 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')
6046d61a2fSSimon Glass
6146d61a2fSSimon Glass    # Sort dict by address
6246d61a2fSSimon Glass    return OrderedDict(sorted(syms.iteritems(), key=lambda x: x[1].address))
63b50e5611SSimon Glass
64b50e5611SSimon Glassdef GetSymbolAddress(fname, sym_name):
65b50e5611SSimon Glass    """Get a value of a symbol from an ELF file
66b50e5611SSimon Glass
67b50e5611SSimon Glass    Args:
68b50e5611SSimon Glass        fname: Filename of the ELF file to read
69b50e5611SSimon Glass        patterns: List of regex patterns to search for, each a string
70b50e5611SSimon Glass
71b50e5611SSimon Glass    Returns:
72b50e5611SSimon Glass        Symbol value (as an integer) or None if not found
73b50e5611SSimon Glass    """
74b50e5611SSimon Glass    syms = GetSymbols(fname, [sym_name])
75b50e5611SSimon Glass    sym = syms.get(sym_name)
76b50e5611SSimon Glass    if not sym:
77b50e5611SSimon Glass        return None
78b50e5611SSimon Glass    return sym.address
7919790632SSimon Glass
80f55382b5SSimon Glassdef LookupAndWriteSymbols(elf_fname, entry, section):
8119790632SSimon Glass    """Replace all symbols in an entry with their correct values
8219790632SSimon Glass
8319790632SSimon Glass    The entry contents is updated so that values for referenced symbols will be
84*3ab9598dSSimon Glass    visible at run time. This is done by finding out the symbols offsets in the
85*3ab9598dSSimon Glass    entry (using the ELF file) and replacing them with values from binman's data
86*3ab9598dSSimon Glass    structures.
8719790632SSimon Glass
8819790632SSimon Glass    Args:
8919790632SSimon Glass        elf_fname: Filename of ELF image containing the symbol information for
9019790632SSimon Glass            entry
9119790632SSimon Glass        entry: Entry to process
92f55382b5SSimon Glass        section: Section which can be used to lookup symbol values
9319790632SSimon Glass    """
9419790632SSimon Glass    fname = tools.GetInputFilename(elf_fname)
9519790632SSimon Glass    syms = GetSymbols(fname, ['image', 'binman'])
9619790632SSimon Glass    if not syms:
9719790632SSimon Glass        return
9819790632SSimon Glass    base = syms.get('__image_copy_start')
9919790632SSimon Glass    if not base:
10019790632SSimon Glass        return
10119790632SSimon Glass    for name, sym in syms.iteritems():
10219790632SSimon Glass        if name.startswith('_binman'):
103f55382b5SSimon Glass            msg = ("Section '%s': Symbol '%s'\n   in entry '%s'" %
104f55382b5SSimon Glass                   (section.GetPath(), name, entry.GetPath()))
10519790632SSimon Glass            offset = sym.address - base.address
10619790632SSimon Glass            if offset < 0 or offset + sym.size > entry.contents_size:
10719790632SSimon Glass                raise ValueError('%s has offset %x (size %x) but the contents '
10819790632SSimon Glass                                 'size is %x' % (entry.GetPath(), offset,
10919790632SSimon Glass                                                 sym.size, entry.contents_size))
11019790632SSimon Glass            if sym.size == 4:
11119790632SSimon Glass                pack_string = '<I'
11219790632SSimon Glass            elif sym.size == 8:
11319790632SSimon Glass                pack_string = '<Q'
11419790632SSimon Glass            else:
11519790632SSimon Glass                raise ValueError('%s has size %d: only 4 and 8 are supported' %
11619790632SSimon Glass                                 (msg, sym.size))
11719790632SSimon Glass
11819790632SSimon Glass            # Look up the symbol in our entry tables.
119f55382b5SSimon Glass            value = section.LookupSymbol(name, sym.weak, msg)
12019790632SSimon Glass            if value is not None:
12119790632SSimon Glass                value += base.address
12219790632SSimon Glass            else:
12319790632SSimon Glass                value = -1
12419790632SSimon Glass                pack_string = pack_string.lower()
12519790632SSimon Glass            value_bytes = struct.pack(pack_string, value)
12619790632SSimon Glass            if debug:
12719790632SSimon Glass                print('%s:\n   insert %s, offset %x, value %x, length %d' %
12819790632SSimon Glass                      (msg, name, offset, value, len(value_bytes)))
12919790632SSimon Glass            entry.data = (entry.data[:offset] + value_bytes +
13019790632SSimon Glass                        entry.data[offset + sym.size:])
131