elf: Add a way to read segment information from an ELF file
Add a function which reads the segments and the entry address.
Also fix a comment nit in the tests while we are here.
Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/tools/binman/elf.py b/tools/binman/elf.py
index bc4966e..5e7d6ae 100644
--- a/tools/binman/elf.py
+++ b/tools/binman/elf.py
@@ -20,6 +20,7 @@
ELF_TOOLS = True
try:
from elftools.elf.elffile import ELFFile
+ from elftools.elf.elffile import ELFError
from elftools.elf.sections import SymbolTableSection
except: # pragma: no cover
ELF_TOOLS = False
@@ -369,3 +370,39 @@
newdata += data[syms[end_sym].offset:]
tools.write_file(outfile, newdata)
tout.info('Written to offset %#x' % syms[start_sym].offset)
+
+def read_segments(data):
+ """Read segments from an ELF file
+
+ Args:
+ data (bytes): Contents of file
+
+ Returns:
+ tuple:
+ list of segments, each:
+ int: Segment number (0 = first)
+ int: Start address of segment in memory
+ bytes: Contents of segment
+ int: entry address for image
+
+ Raises:
+ ValueError: elftools is not available
+ """
+ if not ELF_TOOLS:
+ raise ValueError('Python elftools package is not available')
+ with io.BytesIO(data) as inf:
+ try:
+ elf = ELFFile(inf)
+ except ELFError as err:
+ raise ValueError(err)
+ entry = elf.header['e_entry']
+ segments = []
+ 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_offset']
+ rend = start + segment['p_filesz']
+ segments.append((i, segment['p_paddr'], data[start:rend]))
+ return segments, entry
diff --git a/tools/binman/elf_test.py b/tools/binman/elf_test.py
index 47ebfba..f92352d 100644
--- a/tools/binman/elf_test.py
+++ b/tools/binman/elf_test.py
@@ -56,8 +56,8 @@
def BuildElfTestFiles(target_dir):
"""Build ELF files used for testing in binman
- This compiles and links the test files into the specified directory. It the
- Makefile and source files in the binman test/ directory.
+ This compiles and links the test files into the specified directory. It uses
+ the Makefile and source files in the binman test/ directory.
Args:
target_dir: Directory to put the files into
@@ -258,6 +258,33 @@
offset = elf.GetSymbolFileOffset(fname, ['missing_sym'])
self.assertEqual({}, offset)
+ def test_read_segments(self):
+ """Test for read_segments()"""
+ if not elf.ELF_TOOLS:
+ self.skipTest('Python elftools not available')
+ fname = self.ElfTestFile('embed_data')
+ segments, entry = elf.read_segments(tools.ReadFile(fname))
+
+ def test_read_segments_fail(self):
+ """Test for read_segments() without elftools"""
+ try:
+ old_val = elf.ELF_TOOLS
+ elf.ELF_TOOLS = False
+ fname = self.ElfTestFile('embed_data')
+ with self.assertRaises(ValueError) as e:
+ elf.read_segments(tools.ReadFile(fname))
+ self.assertIn('Python elftools package is not available',
+ str(e.exception))
+ finally:
+ elf.ELF_TOOLS = old_val
+
+ def test_read_segments_bad_data(self):
+ """Test for read_segments() with an invalid ELF file"""
+ fname = self.ElfTestFile('embed_data')
+ with self.assertRaises(ValueError) as e:
+ elf.read_segments(tools.GetBytes(100, 100))
+ self.assertIn('Magic number does not match', str(e.exception))
+
if __name__ == '__main__':
unittest.main()