binman: Support writing symbols into ELF files

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>
This commit is contained in:
Simon Glass 2022-10-20 18:22:47 -06:00 committed by Tom Rini
parent 3fbba5568c
commit d2afb9edce
8 changed files with 151 additions and 12 deletions

View File

@ -210,7 +210,7 @@ def GetPackString(sym, msg):
raise ValueError('%s has size %d: only 4 and 8 are supported' % raise ValueError('%s has size %d: only 4 and 8 are supported' %
(msg, sym.size)) (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 """Replace all symbols in an entry with their correct values
The entry contents is updated so that values for referenced symbols will be 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) fname = tools.get_input_filename(elf_fname)
syms = GetSymbols(fname, ['image', 'binman']) 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: if not syms:
tout.debug('LookupAndWriteSymbols: no syms')
return return
base = syms.get('__image_copy_start') base = syms.get('__image_copy_start')
if not base: if not base and not is_elf:
tout.debug('LookupAndWriteSymbols: no base')
return return
base_addr = 0 if is_elf else base.address
for name, sym in syms.items(): for name, sym in syms.items():
if name.startswith('_binman'): if name.startswith('_binman'):
msg = ("Section '%s': Symbol '%s'\n in entry '%s'" % msg = ("Section '%s': Symbol '%s'\n in entry '%s'" %
(section.GetPath(), name, entry.GetPath())) (section.GetPath(), name, entry.GetPath()))
offset = sym.address - base.address if is_elf:
if offset < 0 or offset + sym.size > entry.contents_size: # For ELF files, use the file offset
raise ValueError('%s has offset %x (size %x) but the contents ' offset = sym.offset
'size is %x' % (entry.GetPath(), offset, else:
sym.size, entry.contents_size)) # 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) pack_string = GetPackString(sym, msg)
if name == '_binman_sym_magic': if name == '_binman_sym_magic':
value = BINMAN_SYM_MAGIC_VALUE value = BINMAN_SYM_MAGIC_VALUE
else: else:
# Look up the symbol in our entry tables. # Look up the symbol in our entry tables.
value = section.GetImage().LookupImageSymbol(name, sym.weak, value = section.GetImage().LookupImageSymbol(name, sym.weak,
msg, base.address) msg, base_addr)
if value is None: if value is None:
value = -1 value = -1
pack_string = pack_string.lower() pack_string = pack_string.lower()

View File

@ -656,7 +656,10 @@ class Entry(object):
section: Section containing the entry section: Section containing the entry
""" """
if self.auto_write_symbols: 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): def CheckEntries(self):
"""Check that the entry offsets are correct """Check that the entry offsets are correct

View File

@ -550,7 +550,7 @@ class Entry_section(Entry):
for name in entries: for name in entries:
if name.startswith(root): if name.startswith(root):
rest = name[len(root):] rest = name[len(root):]
if rest in ['', '-img', '-nodtb']: if rest in ['', '-elf', '-img', '-nodtb']:
entry = entries[name] entry = entries[name]
return entry, entry_name, prop_name return entry, entry_name, prop_name

View File

@ -18,7 +18,8 @@ class Entry_u_boot_spl_elf(Entry_blob):
be relocated to any address for execution. be relocated to any address for execution.
""" """
def __init__(self, section, etype, node): 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): def GetDefaultFilename(self):
return 'spl/u-boot-spl' return 'spl/u-boot-spl'

View File

@ -18,7 +18,8 @@ class Entry_u_boot_tpl_elf(Entry_blob):
be relocated to any address for execution. be relocated to any address for execution.
""" """
def __init__(self, section, etype, node): 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): def GetDefaultFilename(self):
return 'tpl/u-boot-tpl' return 'tpl/u-boot-tpl'

View File

@ -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'

View File

@ -6005,5 +6005,62 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
self.assertTrue(os.path.islink(sname)) self.assertTrue(os.path.islink(sname))
self.assertEqual(os.readlink(sname), fname) 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__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -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 {
};
};
};