diff mbox series

[U-Boot,v2,16/37] binman: Add a function to decode an ELF file

Message ID 20190708191856.138863-17-sjg@chromium.org
State Accepted
Delegated to: Simon Glass
Headers show
Series binman: Add CBFS support | expand

Commit Message

Simon Glass July 8, 2019, 7:18 p.m. UTC
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 <sjg@chromium.org>
---

Changes in v2: None

 tools/binman/README      |  4 +++
 tools/binman/elf.py      | 76 ++++++++++++++++++++++++++++++++++++++++
 tools/binman/elf_test.py | 21 +++++++++++
 3 files changed, 101 insertions(+)

Comments

Simon Glass July 18, 2019, 1:59 a.m. UTC | #1
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 <sjg@chromium.org>
---

Changes in v2: None

 tools/binman/README      |  4 +++
 tools/binman/elf.py      | 76 ++++++++++++++++++++++++++++++++++++++++
 tools/binman/elf_test.py | 21 +++++++++++
 3 files changed, 101 insertions(+)

Applied to u-boot-dm, thanks!
diff mbox series

Patch

diff --git a/tools/binman/README b/tools/binman/README
index 28624fadb33..0ff30ef6fd9 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 <board_name>
diff --git a/tools/binman/elf.py b/tools/binman/elf.py
index e6dc6ec1a9b..8147b3437dd 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
@@ -226,3 +242,63 @@  SECTIONS
                             '-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 3172982427d..e2506377f26 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()