binman: Support finding symbols in sub-sections

At present binman only supports resolving symbols in the same section as
the binary that uses it. This is quite limited because we often need to
group entries into different sections.

Enhance the algorithm to search the entire image for symbols.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass 2021-01-06 21:35:15 -07:00
parent 1ecf3b8f27
commit 870a9ead56
6 changed files with 123 additions and 7 deletions

View File

@ -132,7 +132,8 @@ def LookupAndWriteSymbols(elf_fname, entry, section):
(msg, sym.size))
# Look up the symbol in our entry tables.
value = section.LookupSymbol(name, sym.weak, msg, base.address)
value = section.GetImage().LookupImageSymbol(name, sym.weak, msg,
base.address)
if value is None:
value = -1
pack_string = pack_string.lower()

View File

@ -45,10 +45,12 @@ class FakeSection:
def GetPath(self):
return 'section_path'
def LookupSymbol(self, name, weak, msg, base_addr):
def LookupImageSymbol(self, name, weak, msg, base_addr):
"""Fake implementation which returns the same value for all symbols"""
return self.sym_value
def GetImage(self):
return self
def BuildElfTestFiles(target_dir):
"""Build ELF files used for testing in binman

View File

@ -385,7 +385,7 @@ class Entry_section(Entry):
return entry.GetData()
source_entry.Raise("Cannot find entry for node '%s'" % node.name)
def LookupSymbol(self, sym_name, optional, msg, base_addr):
def LookupSymbol(self, sym_name, optional, msg, base_addr, entries=None):
"""Look up a symbol in an ELF file
Looks up a symbol in an ELF file. Only entry types which come from an
@ -428,18 +428,20 @@ class Entry_section(Entry):
(msg, sym_name))
entry_name, prop_name = m.groups()
entry_name = entry_name.replace('_', '-')
entry = self._entries.get(entry_name)
if not entries:
entries = self._entries
entry = entries.get(entry_name)
if not entry:
if entry_name.endswith('-any'):
root = entry_name[:-4]
for name in self._entries:
for name in entries:
if name.startswith(root):
rest = name[len(root):]
if rest in ['', '-img', '-nodtb']:
entry = self._entries[name]
entry = entries[name]
if not entry:
err = ("%s: Entry '%s' not found in list (%s)" %
(msg, entry_name, ','.join(self._entries.keys())))
(msg, entry_name, ','.join(entries.keys())))
if optional:
print('Warning: %s' % err, file=sys.stderr)
return None
@ -648,3 +650,32 @@ class Entry_section(Entry):
"""
for entry in self._entries.values():
entry.CheckMissing(missing_list)
def _CollectEntries(self, entries, entries_by_name, add_entry):
"""Collect all the entries in an section
This builds up a dict of entries in this section and all subsections.
Entries are indexed by path and by name.
Since all paths are unique, entries will not have any conflicts. However
entries_by_name make have conflicts if two entries have the same name
(e.g. with different parent sections). In this case, an entry at a
higher level in the hierarchy will win over a lower-level entry.
Args:
entries: dict to put entries:
key: entry path
value: Entry object
entries_by_name: dict to put entries
key: entry name
value: Entry object
add_entry: Entry to add
"""
entries[add_entry.GetPath()] = add_entry
to_add = add_entry.GetEntries()
if to_add:
for entry in to_add.values():
entries[entry.GetPath()] = entry
for entry in to_add.values():
self._CollectEntries(entries, entries_by_name, entry)
entries_by_name[add_entry.name] = add_entry

View File

@ -4139,6 +4139,21 @@ class TestFunctional(unittest.TestCase):
}
self.assertEqual(expected, props)
def testSymbolsSubsection(self):
"""Test binman can assign symbols from a subsection"""
elf_fname = self.ElfTestFile('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_offset'].address, addr)
self._SetupSplElf('u_boot_binman_syms')
data = self._DoReadFile('187_symbols_sub.dts')
sym_values = struct.pack('<LQLL', 0x00, 0x1c, 0x28, 0x04)
expected = (sym_values + U_BOOT_SPL_DATA[20:] +
tools.GetBytes(0xff, 1) + U_BOOT_DATA + sym_values +
U_BOOT_SPL_DATA[20:])
self.assertEqual(expected, data)
if __name__ == "__main__":
unittest.main()

View File

@ -324,3 +324,48 @@ class Image(section.Entry_section):
_DoLine(lines, _EntryToStrings(entry))
selected_entries.append(entry)
return selected_entries, lines, widths
def LookupImageSymbol(self, sym_name, optional, msg, base_addr):
"""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.
This searches through this image including all of its subsections.
At present the only entry properties supported are:
offset
image_pos - 'base_addr' is added if this is not an end-at-4gb image
size
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_offset). 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_offset 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
base_addr: Base address of image. This is added to the returned
image_pos in most cases so that the returned position indicates
where the targeted entry/binary has actually been loaded. But
if end-at-4gb is used, this is not done, since the binary is
already assumed to be linked to the ROM position and using
execute-in-place (XIP).
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
"""
entries = OrderedDict()
entries_by_name = {}
self._CollectEntries(entries, entries_by_name, self)
return self.LookupSymbol(sym_name, optional, msg, base_addr,
entries_by_name)

View File

@ -0,0 +1,22 @@
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
section {
pad-byte = <0xff>;
u-boot-spl {
};
u-boot {
offset = <24>;
};
};
u-boot-spl2 {
type = "u-boot-spl";
};
};
};