binman: Support writing symbols into ELF files
authorSimon Glass <sjg@chromium.org>
Fri, 21 Oct 2022 00:22:47 +0000 (18:22 -0600)
committerTom Rini <trini@konsulko.com>
Mon, 31 Oct 2022 15:02:44 +0000 (11:02 -0400)
In some cases the ELF version of SPL builds may be packaged, rather
than a binary .bin file. Add support for this.

Signed-off-by: Simon Glass <sjg@chromium.org>
tools/binman/elf.py
tools/binman/entry.py
tools/binman/etype/section.py
tools/binman/etype/u_boot_spl_elf.py
tools/binman/etype/u_boot_tpl_elf.py
tools/binman/etype/u_boot_vpl_elf.py [new file with mode: 0644]
tools/binman/ftest.py
tools/binman/test/260_symbols_elf.dts [new file with mode: 0644]

index fec2116..fe50bf5 100644 (file)
@@ -210,7 +210,7 @@ def GetPackString(sym, msg):
         raise ValueError('%s has size %d: only 4 and 8 are supported' %
                          (msg, sym.size))
 
-def LookupAndWriteSymbols(elf_fname, entry, section):
+def LookupAndWriteSymbols(elf_fname, entry, section, is_elf=False):
     """Replace all symbols in an entry with their correct values
 
     The entry contents is updated so that values for referenced symbols will be
@@ -226,27 +226,52 @@ def LookupAndWriteSymbols(elf_fname, entry, section):
     """
     fname = tools.get_input_filename(elf_fname)
     syms = GetSymbols(fname, ['image', 'binman'])
+    if is_elf:
+        if not ELF_TOOLS:
+            msg = ("Section '%s': entry '%s'" %
+                   (section.GetPath(), entry.GetPath()))
+            raise ValueError(f'{msg}: Cannot write symbols to an ELF file without Python elftools')
+        new_syms = {}
+        with open(fname, 'rb') as fd:
+            elf = ELFFile(fd)
+            for name, sym in syms.items():
+                offset = _GetFileOffset(elf, sym.address)
+                new_syms[name] = Symbol(sym.section, sym.address, sym.size,
+                                        sym.weak, offset)
+            syms = new_syms
+
     if not syms:
+        tout.debug('LookupAndWriteSymbols: no syms')
         return
     base = syms.get('__image_copy_start')
-    if not base:
+    if not base and not is_elf:
+        tout.debug('LookupAndWriteSymbols: no base')
         return
+    base_addr = 0 if is_elf else base.address
     for name, sym in syms.items():
         if name.startswith('_binman'):
             msg = ("Section '%s': Symbol '%s'\n   in entry '%s'" %
                    (section.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 is_elf:
+                # For ELF files, use the file offset
+                offset = sym.offset
+            else:
+                # For blobs use the offset of the symbol, calculated by
+                # subtracting the base address which by definition is at the
+                # start
+                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))
             pack_string = GetPackString(sym, msg)
             if name == '_binman_sym_magic':
                 value = BINMAN_SYM_MAGIC_VALUE
             else:
                 # Look up the symbol in our entry tables.
                 value = section.GetImage().LookupImageSymbol(name, sym.weak,
-                                                             msg, base.address)
+                                                             msg, base_addr)
             if value is None:
                 value = -1
                 pack_string = pack_string.lower()
index bdf53dd..1be31a0 100644 (file)
@@ -656,7 +656,10 @@ class Entry(object):
           section: Section containing the entry
         """
         if self.auto_write_symbols:
-            elf.LookupAndWriteSymbols(self.elf_fname, self, section.GetImage())
+            # Check if we are writing symbols into an ELF file
+            is_elf = self.GetDefaultFilename() == self.elf_fname
+            elf.LookupAndWriteSymbols(self.elf_fname, self, section.GetImage(),
+                                      is_elf)
 
     def CheckEntries(self):
         """Check that the entry offsets are correct
index f7ef5f8..da561e2 100644 (file)
@@ -550,7 +550,7 @@ class Entry_section(Entry):
                 for name in entries:
                     if name.startswith(root):
                         rest = name[len(root):]
-                        if rest in ['', '-img', '-nodtb']:
+                        if rest in ['', '-elf', '-img', '-nodtb']:
                             entry = entries[name]
         return entry, entry_name, prop_name
 
index 7f1236b..7b7b4e0 100644 (file)
@@ -18,7 +18,8 @@ class Entry_u_boot_spl_elf(Entry_blob):
     be relocated to any address for execution.
     """
     def __init__(self, section, etype, node):
-        super().__init__(section, etype, node)
+        super().__init__(section, etype, node, auto_write_symbols=True)
+        self.elf_fname = 'spl/u-boot-spl'
 
     def GetDefaultFilename(self):
         return 'spl/u-boot-spl'
index 3f24d3a..fd10001 100644 (file)
@@ -18,7 +18,8 @@ class Entry_u_boot_tpl_elf(Entry_blob):
     be relocated to any address for execution.
     """
     def __init__(self, section, etype, node):
-        super().__init__(section, etype, node)
+        super().__init__(section, etype, node, auto_write_symbols=True)
+        self.elf_fname = 'tpl/u-boot-tpl'
 
     def GetDefaultFilename(self):
         return 'tpl/u-boot-tpl'
diff --git a/tools/binman/etype/u_boot_vpl_elf.py b/tools/binman/etype/u_boot_vpl_elf.py
new file mode 100644 (file)
index 0000000..2c68679
--- /dev/null
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright 2022 Google LLC
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for U-Boot VPL ELF image
+#
+
+from binman.entry import Entry
+from binman.etype.blob import Entry_blob
+
+class Entry_u_boot_vpl_elf(Entry_blob):
+    """U-Boot VPL ELF image
+
+    Properties / Entry arguments:
+        - filename: Filename of VPL u-boot (default 'vpl/u-boot-vpl')
+
+    This is the U-Boot VPL ELF image. It does not include a device tree but can
+    be relocated to any address for execution.
+    """
+    def __init__(self, section, etype, node):
+        super().__init__(section, etype, node, auto_write_symbols=True)
+        self.elf_fname = 'vpl/u-boot-vpl'
+
+    def GetDefaultFilename(self):
+        return 'vpl/u-boot-vpl'
index 232ac2c..e849d96 100644 (file)
@@ -6005,5 +6005,62 @@ fdt         fdtmap                Extract the devicetree blob from the fdtmap
         self.assertTrue(os.path.islink(sname))
         self.assertEqual(os.readlink(sname), fname)
 
+    def testSymbolsElf(self):
+        """Test binman can assign symbols embedded in an ELF file"""
+        if not elf.ELF_TOOLS:
+            self.skipTest('Python elftools not available')
+        self._SetupTplElf('u_boot_binman_syms')
+        self._SetupVplElf('u_boot_binman_syms')
+        self._SetupSplElf('u_boot_binman_syms')
+        data = self._DoReadFileDtb('260_symbols_elf.dts')[0]
+        image_fname = tools.get_output_filename('image.bin')
+
+        image = control.images['image']
+        entries = image.GetEntries()
+
+        for entry in entries.values():
+            # No symbols in u-boot and it has faked contents anyway
+            if entry.name == 'u-boot':
+                continue
+            edata = data[entry.image_pos:entry.image_pos + entry.size]
+            efname = tools.get_output_filename(f'edata-{entry.name}')
+            tools.write_file(efname, edata)
+
+            syms = elf.GetSymbolFileOffset(efname, ['_binman_u_boot'])
+            re_name = re.compile('_binman_(u_boot_(.*))_prop_(.*)')
+            for name, sym in syms.items():
+                msg = 'test'
+                val = elf.GetSymbolValue(sym, edata, msg)
+                entry_m = re_name.match(name)
+                if entry_m:
+                    ename, prop = entry_m.group(1), entry_m.group(3)
+                entry, entry_name, prop_name = image.LookupEntry(entries,
+                                                                 name, msg)
+                if prop_name == 'offset':
+                    expect_val = entry.offset
+                elif prop_name == 'image_pos':
+                    expect_val = entry.image_pos
+                elif prop_name == 'size':
+                    expect_val = entry.size
+                self.assertEqual(expect_val, val)
+
+    def testSymbolsElfBad(self):
+        """Check error when trying to write symbols without the elftools lib"""
+        if not elf.ELF_TOOLS:
+            self.skipTest('Python elftools not available')
+        self._SetupTplElf('u_boot_binman_syms')
+        self._SetupVplElf('u_boot_binman_syms')
+        self._SetupSplElf('u_boot_binman_syms')
+        try:
+            elf.ELF_TOOLS = False
+            with self.assertRaises(ValueError) as exc:
+                self._DoReadFileDtb('260_symbols_elf.dts')
+        finally:
+            elf.ELF_TOOLS = True
+        self.assertIn(
+            "Section '/binman': entry '/binman/u-boot-spl-elf': "
+            'Cannot write symbols to an ELF file without Python elftools',
+            str(exc.exception))
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/tools/binman/test/260_symbols_elf.dts b/tools/binman/test/260_symbols_elf.dts
new file mode 100644 (file)
index 0000000..0fae118
--- /dev/null
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               pad-byte = <0xff>;
+               u-boot-spl-elf {
+               };
+
+               u-boot {
+               };
+
+               u-boot-spl2 {
+                       type = "u-boot-spl-elf";
+               };
+
+               u-boot-tpl-elf {
+               };
+
+               u-boot-vpl-elf {
+               };
+       };
+};