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>
diff --git a/tools/binman/elf.py b/tools/binman/elf.py
index fec2116..fe50bf5 100644
--- a/tools/binman/elf.py
+++ b/tools/binman/elf.py
@@ -210,7 +210,7 @@
         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 @@
     """
     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()