diff options
Diffstat (limited to 'tools')
-rwxr-xr-x | tools/binman/binman.py | 4 | ||||
-rw-r--r-- | tools/binman/control.py | 4 | ||||
-rw-r--r-- | tools/binman/elf.py | 55 | ||||
-rw-r--r-- | tools/binman/elf_test.py | 92 | ||||
-rw-r--r-- | tools/binman/etype/entry.py | 8 | ||||
-rw-r--r-- | tools/binman/etype/u_boot_spl.py | 6 | ||||
-rw-r--r-- | tools/binman/ftest.py | 19 | ||||
-rw-r--r-- | tools/binman/image.py | 79 | ||||
-rw-r--r-- | tools/binman/image_test.py | 46 | ||||
-rw-r--r-- | tools/binman/test/53_symbols.dts | 20 |
10 files changed, 323 insertions, 10 deletions
diff --git a/tools/binman/binman.py b/tools/binman/binman.py index aa51396266..1c8e8dbff6 100755 --- a/tools/binman/binman.py +++ b/tools/binman/binman.py @@ -37,6 +37,7 @@ def RunTests(debug): import entry_test import fdt_test import ftest + import image_test import test import doctest @@ -53,7 +54,8 @@ def RunTests(debug): # 'entry' module. suite = unittest.TestLoader().loadTestsFromTestCase(entry_test.TestEntry) suite.run(result) - for module in (ftest.TestFunctional, fdt_test.TestFdt, elf_test.TestElf): + for module in (ftest.TestFunctional, fdt_test.TestFdt, elf_test.TestElf, + image_test.TestImage): suite = unittest.TestLoader().loadTestsFromTestCase(module) suite.run(result) diff --git a/tools/binman/control.py b/tools/binman/control.py index e175e8d41b..ffa2bbd80f 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -90,8 +90,7 @@ def Binman(options, args): try: tout.Init(options.verbosity) - if options.debug: - elf.debug = True + elf.debug = options.debug try: tools.SetInputDirs(options.indir) tools.PrepareOutputDir(options.outdir, options.preserve) @@ -112,6 +111,7 @@ def Binman(options, args): image.CheckSize() image.CheckEntries() image.ProcessEntryContents() + image.WriteSymbols() image.BuildImage() finally: tools.FinaliseOutputDir() diff --git a/tools/binman/elf.py b/tools/binman/elf.py index 0fb5a4a8ed..80ff2253f0 100644 --- a/tools/binman/elf.py +++ b/tools/binman/elf.py @@ -19,9 +19,6 @@ debug = False Symbol = namedtuple('Symbol', ['section', 'address', 'size', 'weak']) -# Used for tests which don't have an ELF file to read -ignore_missing_files = False - def GetSymbols(fname, patterns): """Get the symbols from an ELF file @@ -78,3 +75,55 @@ def GetSymbolAddress(fname, sym_name): if not sym: return None return sym.address + +def LookupAndWriteSymbols(elf_fname, entry, image): + """Replace all symbols in an entry with their correct values + + The entry contents is updated so that values for referenced symbols will be + visible at run time. This is done by finding out the symbols positions in + the entry (using the ELF file) and replacing them with values from binman's + data structures. + + Args: + elf_fname: Filename of ELF image containing the symbol information for + entry + entry: Entry to process + image: Image which can be used to lookup symbol values + """ + fname = tools.GetInputFilename(elf_fname) + syms = GetSymbols(fname, ['image', 'binman']) + if not syms: + return + base = syms.get('__image_copy_start') + if not base: + return + for name, sym in syms.iteritems(): + if name.startswith('_binman'): + msg = ("Image '%s': Symbol '%s'\n in entry '%s'" % + (image.GetPath(), name, entry.GetPath())) + offset = sym.address - base.address + if offset < 0 or offset + sym.size > entry.contents_size: + raise ValueError('%s has offset %x (size %x) but the contents ' + 'size is %x' % (entry.GetPath(), offset, + sym.size, entry.contents_size)) + if sym.size == 4: + pack_string = '<I' + elif sym.size == 8: + pack_string = '<Q' + else: + raise ValueError('%s has size %d: only 4 and 8 are supported' % + (msg, sym.size)) + + # Look up the symbol in our entry tables. + value = image.LookupSymbol(name, sym.weak, msg) + if value is not None: + value += base.address + else: + value = -1 + pack_string = pack_string.lower() + value_bytes = struct.pack(pack_string, value) + if debug: + print('%s:\n insert %s, offset %x, value %x, length %d' % + (msg, name, offset, value, len(value_bytes))) + entry.data = (entry.data[:offset] + value_bytes + + entry.data[offset + sym.size:]) diff --git a/tools/binman/elf_test.py b/tools/binman/elf_test.py index dca7bc59f9..e5fc28258d 100644 --- a/tools/binman/elf_test.py +++ b/tools/binman/elf_test.py @@ -6,21 +6,60 @@ # # Test for the elf module +from contextlib import contextmanager import os import sys import unittest +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + import elf binman_dir = os.path.dirname(os.path.realpath(sys.argv[0])) -fname = os.path.join(binman_dir, 'test', 'u_boot_ucode_ptr') + +# Use this to suppress stdout/stderr output: +# with capture_sys_output() as (stdout, stderr) +# ...do something... +@contextmanager +def capture_sys_output(): + capture_out, capture_err = StringIO(), StringIO() + old_out, old_err = sys.stdout, sys.stderr + try: + sys.stdout, sys.stderr = capture_out, capture_err + yield capture_out, capture_err + finally: + sys.stdout, sys.stderr = old_out, old_err + + +class FakeEntry: + def __init__(self, contents_size): + self.contents_size = contents_size + self.data = 'a' * contents_size + + def GetPath(self): + return 'entry_path' + +class FakeImage: + def __init__(self, sym_value=1): + self.sym_value = sym_value + + def GetPath(self): + return 'image_path' + + def LookupSymbol(self, name, weak, msg): + return self.sym_value class TestElf(unittest.TestCase): def testAllSymbols(self): + fname = os.path.join(binman_dir, 'test', 'u_boot_ucode_ptr') syms = elf.GetSymbols(fname, []) self.assertIn('.ucode', syms) def testRegexSymbols(self): + fname = os.path.join(binman_dir, 'test', 'u_boot_ucode_ptr') syms = elf.GetSymbols(fname, ['ucode']) self.assertIn('.ucode', syms) syms = elf.GetSymbols(fname, ['missing']) @@ -28,5 +67,56 @@ class TestElf(unittest.TestCase): syms = elf.GetSymbols(fname, ['missing', 'ucode']) self.assertIn('.ucode', syms) + def testMissingFile(self): + entry = FakeEntry(10) + image = FakeImage() + with self.assertRaises(ValueError) as e: + syms = elf.LookupAndWriteSymbols('missing-file', entry, image) + self.assertIn("Filename 'missing-file' not found in input path", + str(e.exception)) + + def testOutsideFile(self): + entry = FakeEntry(10) + image = FakeImage() + elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms') + with self.assertRaises(ValueError) as e: + syms = elf.LookupAndWriteSymbols(elf_fname, entry, image) + self.assertIn('entry_path has offset 4 (size 8) but the contents size ' + 'is a', str(e.exception)) + + def testMissingImageStart(self): + entry = FakeEntry(10) + image = FakeImage() + elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_bad') + self.assertEqual(elf.LookupAndWriteSymbols(elf_fname, entry, image), + None) + + def testBadSymbolSize(self): + entry = FakeEntry(10) + image = FakeImage() + elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_size') + with self.assertRaises(ValueError) as e: + syms = elf.LookupAndWriteSymbols(elf_fname, entry, image) + self.assertIn('has size 1: only 4 and 8 are supported', + str(e.exception)) + + def testNoValue(self): + entry = FakeEntry(20) + image = FakeImage(sym_value=None) + elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms') + syms = elf.LookupAndWriteSymbols(elf_fname, entry, image) + self.assertEqual(chr(255) * 16 + 'a' * 4, entry.data) + + def testDebug(self): + elf.debug = True + entry = FakeEntry(20) + image = FakeImage() + elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms') + with capture_sys_output() as (stdout, stderr): + syms = elf.LookupAndWriteSymbols(elf_fname, entry, image) + elf.debug = False + self.assertTrue(len(stdout.getvalue()) > 0) + + if __name__ == '__main__': unittest.main() diff --git a/tools/binman/etype/entry.py b/tools/binman/etype/entry.py index 67c57341ca..5541887d47 100644 --- a/tools/binman/etype/entry.py +++ b/tools/binman/etype/entry.py @@ -198,3 +198,11 @@ class Entry(object): def ProcessContents(self): pass + + def WriteSymbols(self, image): + """Write symbol values into binary files for access at run time + + Args: + image: Image containing the entry + """ + pass diff --git a/tools/binman/etype/u_boot_spl.py b/tools/binman/etype/u_boot_spl.py index 68b0148427..3720b47fef 100644 --- a/tools/binman/etype/u_boot_spl.py +++ b/tools/binman/etype/u_boot_spl.py @@ -6,12 +6,18 @@ # Entry-type module for spl/u-boot-spl.bin # +import elf + from entry import Entry from blob import Entry_blob class Entry_u_boot_spl(Entry_blob): def __init__(self, image, etype, node): Entry_blob.__init__(self, image, etype, node) + self.elf_fname = 'spl/u-boot-spl' def GetDefaultFilename(self): return 'spl/u-boot-spl.bin' + + def WriteSymbols(self, image): + elf.LookupAndWriteSymbols(self.elf_fname, self, image) diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 2bee6a168f..5812ab397c 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -20,6 +20,7 @@ import binman import cmdline import command import control +import elf import fdt import fdt_util import tools @@ -573,6 +574,8 @@ class TestFunctional(unittest.TestCase): def testImagePadByte(self): """Test that the image pad byte can be specified""" + with open(self.TestFile('bss_data')) as fd: + TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read()) data = self._DoReadFile('21_image_pad.dts') self.assertEqual(U_BOOT_SPL_DATA + (chr(0xff) * 1) + U_BOOT_DATA, data) @@ -889,6 +892,22 @@ class TestFunctional(unittest.TestCase): data = self._DoReadFile('52_u_boot_spl_nodtb.dts') self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)]) + def testSymbols(self): + """Test binman can assign symbols embedded in U-Boot""" + elf_fname = self.TestFile('u_boot_binman_syms') + syms = elf.GetSymbols(elf_fname, ['binman', 'image']) + addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start') + self.assertEqual(syms['_binman_u_boot_spl_prop_pos'].address, addr) + + with open(self.TestFile('u_boot_binman_syms')) as fd: + TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read()) + data = self._DoReadFile('53_symbols.dts') + sym_values = struct.pack('<LQL', 0x24 + 0, 0x24 + 24, 0x24 + 20) + expected = (sym_values + U_BOOT_SPL_DATA[16:] + chr(0xff) + + U_BOOT_DATA + + sym_values + U_BOOT_SPL_DATA[16:]) + self.assertEqual(expected, data) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/image.py b/tools/binman/image.py index 24c4f6f578..741630f091 100644 --- a/tools/binman/image.py +++ b/tools/binman/image.py @@ -6,8 +6,12 @@ # Class for an image, the output of binman # +from __future__ import print_function + from collections import OrderedDict from operator import attrgetter +import re +import sys import fdt_util import tools @@ -45,7 +49,7 @@ class Image: address. _entries: OrderedDict() of entries """ - def __init__(self, name, node): + def __init__(self, name, node, test=False): global entry global Entry import entry @@ -64,8 +68,9 @@ class Image: self._end_4gb = False self._entries = OrderedDict() - self._ReadNode() - self._ReadEntries() + if not test: + self._ReadNode() + self._ReadEntries() def _ReadNode(self): """Read properties from the image node""" @@ -119,6 +124,14 @@ class Image: """ raise ValueError("Image '%s': %s" % (self._node.path, msg)) + def GetPath(self): + """Get the path of an image (in the FDT) + + Returns: + Full path of the node for this image + """ + return self._node.path + def _ReadEntries(self): for node in self._node.subnodes: self._entries[node.name] = Entry.Create(self, node) @@ -220,6 +233,11 @@ class Image: for entry in self._entries.values(): entry.ProcessContents() + def WriteSymbols(self): + """Write symbol values into binary files for access at run time""" + for entry in self._entries.values(): + entry.WriteSymbols(self) + def BuildImage(self): """Write the image to a file""" fname = tools.GetOutputFilename(self._filename) @@ -230,3 +248,58 @@ class Image: data = entry.GetData() fd.seek(self._pad_before + entry.pos - self._skip_at_start) fd.write(data) + + def LookupSymbol(self, sym_name, optional, msg): + """Look up a symbol in an ELF file + + Looks up a symbol in an ELF file. Only entry types which come from an + ELF image can be used by this function. + + At present the only entry property supported is pos. + + Args: + sym_name: Symbol name in the ELF file to look up in the format + _binman_<entry>_prop_<property> where <entry> is the name of + the entry and <property> is the property to find (e.g. + _binman_u_boot_prop_pos). As a special case, you can append + _any to <entry> to have it search for any matching entry. E.g. + _binman_u_boot_any_prop_pos will match entries called u-boot, + u-boot-img and u-boot-nodtb) + optional: True if the symbol is optional. If False this function + will raise if the symbol is not found + msg: Message to display if an error occurs + + Returns: + Value that should be assigned to that symbol, or None if it was + optional and not found + + Raises: + ValueError if the symbol is invalid or not found, or references a + property which is not supported + """ + m = re.match(r'^_binman_(\w+)_prop_(\w+)$', sym_name) + if not m: + raise ValueError("%s: Symbol '%s' has invalid format" % + (msg, sym_name)) + entry_name, prop_name = m.groups() + entry_name = entry_name.replace('_', '-') + entry = self._entries.get(entry_name) + if not entry: + if entry_name.endswith('-any'): + root = entry_name[:-4] + for name in self._entries: + if name.startswith(root): + rest = name[len(root):] + if rest in ['', '-img', '-nodtb']: + entry = self._entries[name] + if not entry: + err = ("%s: Entry '%s' not found in list (%s)" % + (msg, entry_name, ','.join(self._entries.keys()))) + if optional: + print('Warning: %s' % err, file=sys.stderr) + return None + raise ValueError(err) + if prop_name == 'pos': + return entry.pos + else: + raise ValueError("%s: No such property '%s'" % (msg, prop_name)) diff --git a/tools/binman/image_test.py b/tools/binman/image_test.py new file mode 100644 index 0000000000..1b50dda4dc --- /dev/null +++ b/tools/binman/image_test.py @@ -0,0 +1,46 @@ +# +# Copyright (c) 2017 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# SPDX-License-Identifier: GPL-2.0+ +# +# Test for the image module + +import unittest + +from image import Image +from elf_test import capture_sys_output + +class TestImage(unittest.TestCase): + def testInvalidFormat(self): + image = Image('name', 'node', test=True) + with self.assertRaises(ValueError) as e: + image.LookupSymbol('_binman_something_prop_', False, 'msg') + self.assertIn( + "msg: Symbol '_binman_something_prop_' has invalid format", + str(e.exception)) + + def testMissingSymbol(self): + image = Image('name', 'node', test=True) + image._entries = {} + with self.assertRaises(ValueError) as e: + image.LookupSymbol('_binman_type_prop_pname', False, 'msg') + self.assertIn("msg: Entry 'type' not found in list ()", + str(e.exception)) + + def testMissingSymbolOptional(self): + image = Image('name', 'node', test=True) + image._entries = {} + with capture_sys_output() as (stdout, stderr): + val = image.LookupSymbol('_binman_type_prop_pname', True, 'msg') + self.assertEqual(val, None) + self.assertEqual("Warning: msg: Entry 'type' not found in list ()\n", + stderr.getvalue()) + self.assertEqual('', stdout.getvalue()) + + def testBadProperty(self): + image = Image('name', 'node', test=True) + image._entries = {'u-boot': 1} + with self.assertRaises(ValueError) as e: + image.LookupSymbol('_binman_u_boot_prop_bad', False, 'msg') + self.assertIn("msg: No such property 'bad", str(e.exception)) diff --git a/tools/binman/test/53_symbols.dts b/tools/binman/test/53_symbols.dts new file mode 100644 index 0000000000..980b066eb2 --- /dev/null +++ b/tools/binman/test/53_symbols.dts @@ -0,0 +1,20 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0xff>; + u-boot-spl { + }; + + u-boot { + pos = <20>; + }; + + u-boot-spl2 { + type = "u-boot-spl"; + }; + }; +}; |