From d8d40748dd6f202948a8173073cf34f3b6468b6d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 8 Jul 2019 13:18:35 -0600 Subject: [PATCH] binman: Add a function to decode an ELF file Add a function which decodes an ELF file, working out where in memory each part of the data should be written. Signed-off-by: Simon Glass --- tools/binman/README | 4 +++ tools/binman/elf.py | 77 ++++++++++++++++++++++++++++++++++++++++ tools/binman/elf_test.py | 21 +++++++++++ 3 files changed, 102 insertions(+) diff --git a/tools/binman/README b/tools/binman/README index 28624fadb3..0ff30ef6fd 100644 --- a/tools/binman/README +++ b/tools/binman/README @@ -181,6 +181,10 @@ the configuration of the Intel-format descriptor. Running binman -------------- +First install prerequisites, e.g. + + sudo apt-get install python-pyelftools python3-pyelftools + Type: binman -b diff --git a/tools/binman/elf.py b/tools/binman/elf.py index 82ea8c3857..8147b3437d 100644 --- a/tools/binman/elf.py +++ b/tools/binman/elf.py @@ -9,6 +9,7 @@ from __future__ import print_function from collections import namedtuple, OrderedDict import command +import io import os import re import shutil @@ -17,11 +18,26 @@ import tempfile import tools +ELF_TOOLS = True +try: + from elftools.elf.elffile import ELFFile + from elftools.elf.sections import SymbolTableSection +except: # pragma: no cover + ELF_TOOLS = False + # This is enabled from control.py debug = 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 @@ -225,3 +241,64 @@ SECTIONS stdout = command.Output('cc', '-static', '-nostdlib', '-Wl,--build-id=none', '-m32','-T', lds_file, '-o', elf_fname, s_file) 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) diff --git a/tools/binman/elf_test.py b/tools/binman/elf_test.py index 3172982427..e2506377f2 100644 --- a/tools/binman/elf_test.py +++ b/tools/binman/elf_test.py @@ -156,6 +156,27 @@ class TestElf(unittest.TestCase): self.assertEqual(expected_text + expected_data, data) shutil.rmtree(outdir) + def testDecodeElf(self): + """Test for the MakeElf function""" + if not elf.ELF_TOOLS: + self.skipTest('Python elftools not available') + outdir = tempfile.mkdtemp(prefix='elf.') + expected_text = b'1234' + expected_data = b'wxyz' + elf_fname = os.path.join(outdir, 'elf') + elf.MakeElf(elf_fname, expected_text, expected_data) + data = tools.ReadFile(elf_fname) + + load = 0xfef20000 + entry = load + 2 + expected = expected_text + expected_data + self.assertEqual(elf.ElfInfo(expected, load, entry, len(expected)), + elf.DecodeElf(data, 0)) + self.assertEqual(elf.ElfInfo(b'\0\0' + expected[2:], + load, entry, len(expected)), + elf.DecodeElf(data, load + 2)) + #shutil.rmtree(outdir) + if __name__ == '__main__': unittest.main() -- 2.25.1