xref: /openbmc/u-boot/tools/binman/elf.py (revision 642c1f51)
1# SPDX-License-Identifier: GPL-2.0+
2# Copyright (c) 2016 Google, Inc
3# Written by Simon Glass <sjg@chromium.org>
4#
5# Handle various things related to ELF images
6#
7
8from collections import namedtuple, OrderedDict
9import command
10import os
11import re
12import struct
13
14import tools
15
16# This is enabled from control.py
17debug = False
18
19Symbol = namedtuple('Symbol', ['section', 'address', 'size', 'weak'])
20
21
22def GetSymbols(fname, patterns):
23    """Get the symbols from an ELF file
24
25    Args:
26        fname: Filename of the ELF file to read
27        patterns: List of regex patterns to search for, each a string
28
29    Returns:
30        None, if the file does not exist, or Dict:
31          key: Name of symbol
32          value: Hex value of symbol
33    """
34    stdout = command.Output('objdump', '-t', fname, raise_on_error=False)
35    lines = stdout.splitlines()
36    if patterns:
37        re_syms = re.compile('|'.join(patterns))
38    else:
39        re_syms = None
40    syms = {}
41    syms_started = False
42    for line in lines:
43        if not line or not syms_started:
44            if 'SYMBOL TABLE' in line:
45                syms_started = True
46            line = None  # Otherwise code coverage complains about 'continue'
47            continue
48        if re_syms and not re_syms.search(line):
49            continue
50
51        space_pos = line.find(' ')
52        value, rest = line[:space_pos], line[space_pos + 1:]
53        flags = rest[:7]
54        parts = rest[7:].split()
55        section, size =  parts[:2]
56        if len(parts) > 2:
57            name = parts[2]
58            syms[name] = Symbol(section, int(value, 16), int(size,16),
59                                flags[1] == 'w')
60    return syms
61
62def GetSymbolAddress(fname, sym_name):
63    """Get a value of a symbol from an ELF file
64
65    Args:
66        fname: Filename of the ELF file to read
67        patterns: List of regex patterns to search for, each a string
68
69    Returns:
70        Symbol value (as an integer) or None if not found
71    """
72    syms = GetSymbols(fname, [sym_name])
73    sym = syms.get(sym_name)
74    if not sym:
75        return None
76    return sym.address
77
78def LookupAndWriteSymbols(elf_fname, entry, section):
79    """Replace all symbols in an entry with their correct values
80
81    The entry contents is updated so that values for referenced symbols will be
82    visible at run time. This is done by finding out the symbols positions in
83    the entry (using the ELF file) and replacing them with values from binman's
84    data structures.
85
86    Args:
87        elf_fname: Filename of ELF image containing the symbol information for
88            entry
89        entry: Entry to process
90        section: Section which can be used to lookup symbol values
91    """
92    fname = tools.GetInputFilename(elf_fname)
93    syms = GetSymbols(fname, ['image', 'binman'])
94    if not syms:
95        return
96    base = syms.get('__image_copy_start')
97    if not base:
98        return
99    for name, sym in syms.iteritems():
100        if name.startswith('_binman'):
101            msg = ("Section '%s': Symbol '%s'\n   in entry '%s'" %
102                   (section.GetPath(), name, entry.GetPath()))
103            offset = sym.address - base.address
104            if offset < 0 or offset + sym.size > entry.contents_size:
105                raise ValueError('%s has offset %x (size %x) but the contents '
106                                 'size is %x' % (entry.GetPath(), offset,
107                                                 sym.size, entry.contents_size))
108            if sym.size == 4:
109                pack_string = '<I'
110            elif sym.size == 8:
111                pack_string = '<Q'
112            else:
113                raise ValueError('%s has size %d: only 4 and 8 are supported' %
114                                 (msg, sym.size))
115
116            # Look up the symbol in our entry tables.
117            value = section.LookupSymbol(name, sym.weak, msg)
118            if value is not None:
119                value += base.address
120            else:
121                value = -1
122                pack_string = pack_string.lower()
123            value_bytes = struct.pack(pack_string, value)
124            if debug:
125                print('%s:\n   insert %s, offset %x, value %x, length %d' %
126                      (msg, name, offset, value, len(value_bytes)))
127            entry.data = (entry.data[:offset] + value_bytes +
128                        entry.data[offset + sym.size:])
129