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 <sjg@chromium.org>
This commit is contained in:
Simon Glass 2019-07-08 13:18:35 -06:00
parent f58558a5ae
commit d8d40748dd
3 changed files with 102 additions and 0 deletions

View file

@ -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>

View file

@ -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)

View file

@ -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()