diff --git a/tools/binman/elf.py b/tools/binman/elf.py index fec2116880..fe50bf542c 100644 --- a/tools/binman/elf.py +++ b/tools/binman/elf.py @@ -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() diff --git a/tools/binman/entry.py b/tools/binman/entry.py index bdf53ddd92..1be31a05e0 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -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 diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index f7ef5f8983..da561e2bcc 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -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 diff --git a/tools/binman/etype/u_boot_spl_elf.py b/tools/binman/etype/u_boot_spl_elf.py index 7f1236bcbb..7b7b4e0149 100644 --- a/tools/binman/etype/u_boot_spl_elf.py +++ b/tools/binman/etype/u_boot_spl_elf.py @@ -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' diff --git a/tools/binman/etype/u_boot_tpl_elf.py b/tools/binman/etype/u_boot_tpl_elf.py index 3f24d3aa7b..fd100019b3 100644 --- a/tools/binman/etype/u_boot_tpl_elf.py +++ b/tools/binman/etype/u_boot_tpl_elf.py @@ -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 index 0000000000..2c68679019 --- /dev/null +++ b/tools/binman/etype/u_boot_vpl_elf.py @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2022 Google LLC +# Written by Simon Glass +# +# 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' diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 232ac2cf18..e849d96587 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -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 index 0000000000..0fae118fc1 --- /dev/null +++ b/tools/binman/test/260_symbols_elf.dts @@ -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 { + }; + }; +};