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