aboutsummaryrefslogtreecommitdiffstats
path: root/roms/u-boot/tools/binman/elf.py
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/tools/binman/elf.py
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/tools/binman/elf.py')
-rw-r--r--roms/u-boot/tools/binman/elf.py303
1 files changed, 303 insertions, 0 deletions
diff --git a/roms/u-boot/tools/binman/elf.py b/roms/u-boot/tools/binman/elf.py
new file mode 100644
index 000000000..03b49d716
--- /dev/null
+++ b/roms/u-boot/tools/binman/elf.py
@@ -0,0 +1,303 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Handle various things related to ELF images
+#
+
+from collections import namedtuple, OrderedDict
+import io
+import os
+import re
+import shutil
+import struct
+import tempfile
+
+from patman import command
+from patman import tools
+from patman import tout
+
+ELF_TOOLS = True
+try:
+ from elftools.elf.elffile import ELFFile
+ from elftools.elf.sections import SymbolTableSection
+except: # pragma: no cover
+ ELF_TOOLS = False
+
+Symbol = namedtuple('Symbol', ['section', 'address', 'size', 'weak'])
+
+# Information about an ELF file:
+# data: Extracted program contents of ELF file (this would be loaded by an
+# ELF loader when reading this file
+# load: Load address of code
+# entry: Entry address of code
+# memsize: Number of bytes in memory occupied by loading this ELF file
+ElfInfo = namedtuple('ElfInfo', ['data', 'load', 'entry', 'memsize'])
+
+
+def GetSymbols(fname, patterns):
+ """Get the symbols from an ELF file
+
+ Args:
+ fname: Filename of the ELF file to read
+ patterns: List of regex patterns to search for, each a string
+
+ Returns:
+ None, if the file does not exist, or Dict:
+ key: Name of symbol
+ value: Hex value of symbol
+ """
+ stdout = tools.Run('objdump', '-t', fname)
+ lines = stdout.splitlines()
+ if patterns:
+ re_syms = re.compile('|'.join(patterns))
+ else:
+ re_syms = None
+ syms = {}
+ syms_started = False
+ for line in lines:
+ if not line or not syms_started:
+ if 'SYMBOL TABLE' in line:
+ syms_started = True
+ line = None # Otherwise code coverage complains about 'continue'
+ continue
+ if re_syms and not re_syms.search(line):
+ continue
+
+ space_pos = line.find(' ')
+ value, rest = line[:space_pos], line[space_pos + 1:]
+ flags = rest[:7]
+ parts = rest[7:].split()
+ section, size = parts[:2]
+ if len(parts) > 2:
+ name = parts[2] if parts[2] != '.hidden' else parts[3]
+ syms[name] = Symbol(section, int(value, 16), int(size,16),
+ flags[1] == 'w')
+
+ # Sort dict by address
+ return OrderedDict(sorted(syms.items(), key=lambda x: x[1].address))
+
+def GetSymbolAddress(fname, sym_name):
+ """Get a value of a symbol from an ELF file
+
+ Args:
+ fname: Filename of the ELF file to read
+ patterns: List of regex patterns to search for, each a string
+
+ Returns:
+ Symbol value (as an integer) or None if not found
+ """
+ syms = GetSymbols(fname, [sym_name])
+ sym = syms.get(sym_name)
+ if not sym:
+ return None
+ return sym.address
+
+def LookupAndWriteSymbols(elf_fname, entry, section):
+ """Replace all symbols in an entry with their correct values
+
+ The entry contents is updated so that values for referenced symbols will be
+ visible at run time. This is done by finding out the symbols offsets in the
+ entry (using the ELF file) and replacing them with values from binman's data
+ structures.
+
+ Args:
+ elf_fname: Filename of ELF image containing the symbol information for
+ entry
+ entry: Entry to process
+ section: Section which can be used to lookup symbol values
+ """
+ fname = tools.GetInputFilename(elf_fname)
+ syms = GetSymbols(fname, ['image', 'binman'])
+ if not syms:
+ return
+ base = syms.get('__image_copy_start')
+ if not base:
+ return
+ 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 sym.size == 4:
+ pack_string = '<I'
+ elif sym.size == 8:
+ pack_string = '<Q'
+ else:
+ raise ValueError('%s has size %d: only 4 and 8 are supported' %
+ (msg, sym.size))
+
+ # Look up the symbol in our entry tables.
+ value = section.GetImage().LookupImageSymbol(name, sym.weak, msg,
+ base.address)
+ if value is None:
+ value = -1
+ pack_string = pack_string.lower()
+ value_bytes = struct.pack(pack_string, value)
+ tout.Debug('%s:\n insert %s, offset %x, value %x, length %d' %
+ (msg, name, offset, value, len(value_bytes)))
+ entry.data = (entry.data[:offset] + value_bytes +
+ entry.data[offset + sym.size:])
+
+def MakeElf(elf_fname, text, data):
+ """Make an elf file with the given data in a single section
+
+ The output file has a several section including '.text' and '.data',
+ containing the info provided in arguments.
+
+ Args:
+ elf_fname: Output filename
+ text: Text (code) to put in the file's .text section
+ data: Data to put in the file's .data section
+ """
+ outdir = tempfile.mkdtemp(prefix='binman.elf.')
+ s_file = os.path.join(outdir, 'elf.S')
+
+ # Spilt the text into two parts so that we can make the entry point two
+ # bytes after the start of the text section
+ text_bytes1 = ['\t.byte\t%#x' % byte for byte in text[:2]]
+ text_bytes2 = ['\t.byte\t%#x' % byte for byte in text[2:]]
+ data_bytes = ['\t.byte\t%#x' % byte for byte in data]
+ with open(s_file, 'w') as fd:
+ print('''/* Auto-generated C program to produce an ELF file for testing */
+
+.section .text
+.code32
+.globl _start
+.type _start, @function
+%s
+_start:
+%s
+.ident "comment"
+
+.comm fred,8,4
+
+.section .empty
+.globl _empty
+_empty:
+.byte 1
+
+.globl ernie
+.data
+.type ernie, @object
+.size ernie, 4
+ernie:
+%s
+''' % ('\n'.join(text_bytes1), '\n'.join(text_bytes2), '\n'.join(data_bytes)),
+ file=fd)
+ lds_file = os.path.join(outdir, 'elf.lds')
+
+ # Use a linker script to set the alignment and text address.
+ with open(lds_file, 'w') as fd:
+ print('''/* Auto-generated linker script to produce an ELF file for testing */
+
+PHDRS
+{
+ text PT_LOAD ;
+ data PT_LOAD ;
+ empty PT_LOAD FLAGS ( 6 ) ;
+ note PT_NOTE ;
+}
+
+SECTIONS
+{
+ . = 0xfef20000;
+ ENTRY(_start)
+ .text . : SUBALIGN(0)
+ {
+ *(.text)
+ } :text
+ .data : {
+ *(.data)
+ } :data
+ _bss_start = .;
+ .empty : {
+ *(.empty)
+ } :empty
+ /DISCARD/ : {
+ *(.note.gnu.property)
+ }
+ .note : {
+ *(.comment)
+ } :note
+ .bss _bss_start (OVERLAY) : {
+ *(.bss)
+ }
+}
+''', file=fd)
+ # -static: Avoid requiring any shared libraries
+ # -nostdlib: Don't link with C library
+ # -Wl,--build-id=none: Don't generate a build ID, so that we just get the
+ # text section at the start
+ # -m32: Build for 32-bit x86
+ # -T...: Specifies the link script, which sets the start address
+ cc, args = tools.GetTargetCompileTool('cc')
+ args += ['-static', '-nostdlib', '-Wl,--build-id=none', '-m32', '-T',
+ lds_file, '-o', elf_fname, s_file]
+ stdout = command.Output(cc, *args)
+ shutil.rmtree(outdir)
+
+def DecodeElf(data, location):
+ """Decode an ELF file and return information about it
+
+ Args:
+ data: Data from ELF file
+ location: Start address of data to return
+
+ Returns:
+ ElfInfo object containing information about the decoded ELF file
+ """
+ file_size = len(data)
+ with io.BytesIO(data) as fd:
+ elf = ELFFile(fd)
+ data_start = 0xffffffff;
+ data_end = 0;
+ mem_end = 0;
+ virt_to_phys = 0;
+
+ for i in range(elf.num_segments()):
+ segment = elf.get_segment(i)
+ if segment['p_type'] != 'PT_LOAD' or not segment['p_memsz']:
+ skipped = 1 # To make code-coverage see this line
+ continue
+ start = segment['p_paddr']
+ mend = start + segment['p_memsz']
+ rend = start + segment['p_filesz']
+ data_start = min(data_start, start)
+ data_end = max(data_end, rend)
+ mem_end = max(mem_end, mend)
+ if not virt_to_phys:
+ virt_to_phys = segment['p_paddr'] - segment['p_vaddr']
+
+ output = bytearray(data_end - data_start)
+ for i in range(elf.num_segments()):
+ segment = elf.get_segment(i)
+ if segment['p_type'] != 'PT_LOAD' or not segment['p_memsz']:
+ skipped = 1 # To make code-coverage see this line
+ continue
+ start = segment['p_paddr']
+ offset = 0
+ if start < location:
+ offset = location - start
+ start = location
+ # A legal ELF file can have a program header with non-zero length
+ # but zero-length file size and a non-zero offset which, added
+ # together, are greater than input->size (i.e. the total file size).
+ # So we need to not even test in the case that p_filesz is zero.
+ # Note: All of this code is commented out since we don't have a test
+ # case for it.
+ size = segment['p_filesz']
+ #if not size:
+ #continue
+ #end = segment['p_offset'] + segment['p_filesz']
+ #if end > file_size:
+ #raise ValueError('Underflow copying out the segment. File has %#x bytes left, segment end is %#x\n',
+ #file_size, end)
+ output[start - data_start:start - data_start + size] = (
+ segment.data()[offset:])
+ return ElfInfo(output, data_start, elf.header['e_entry'] + virt_to_phys,
+ mem_end - data_start)