blob: a913970150a6104bbc81469cb416fd88ba898c24 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glassb50e5612017-11-13 18:54:54 -07002# Copyright (c) 2017 Google, Inc
3# Written by Simon Glass <sjg@chromium.org>
4#
Simon Glassb50e5612017-11-13 18:54:54 -07005# Test for the elf module
6
7import os
Simon Glassf58558a2019-07-08 13:18:34 -06008import shutil
Simon Glassb50e5612017-11-13 18:54:54 -07009import sys
Simon Glassf58558a2019-07-08 13:18:34 -060010import tempfile
Simon Glassb50e5612017-11-13 18:54:54 -070011import unittest
12
Simon Glassf58558a2019-07-08 13:18:34 -060013import command
Simon Glassb50e5612017-11-13 18:54:54 -070014import elf
Simon Glassc3f94542018-07-06 10:27:34 -060015import test_util
Simon Glasse0e62752018-10-01 21:12:41 -060016import tools
Simon Glass9f297b02019-07-20 12:23:36 -060017import tout
Simon Glassb50e5612017-11-13 18:54:54 -070018
19binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
Simon Glass19790632017-11-13 18:55:01 -070020
Simon Glass19790632017-11-13 18:55:01 -070021
22class FakeEntry:
Simon Glassb2b0df82018-07-17 13:25:26 -060023 """A fake Entry object, usedfor testing
24
25 This supports an entry with a given size.
26 """
Simon Glass19790632017-11-13 18:55:01 -070027 def __init__(self, contents_size):
28 self.contents_size = contents_size
Simon Glassc6c10e72019-05-17 22:00:46 -060029 self.data = tools.GetBytes(ord('a'), contents_size)
Simon Glass19790632017-11-13 18:55:01 -070030
31 def GetPath(self):
32 return 'entry_path'
33
Simon Glassb2b0df82018-07-17 13:25:26 -060034
Simon Glassf55382b2018-06-01 09:38:13 -060035class FakeSection:
Simon Glassb2b0df82018-07-17 13:25:26 -060036 """A fake Section object, used for testing
37
38 This has the minimum feature set needed to support testing elf functions.
39 A LookupSymbol() function is provided which returns a fake value for amu
40 symbol requested.
41 """
Simon Glass19790632017-11-13 18:55:01 -070042 def __init__(self, sym_value=1):
43 self.sym_value = sym_value
44
45 def GetPath(self):
Simon Glassf55382b2018-06-01 09:38:13 -060046 return 'section_path'
Simon Glass19790632017-11-13 18:55:01 -070047
48 def LookupSymbol(self, name, weak, msg):
Simon Glassb2b0df82018-07-17 13:25:26 -060049 """Fake implementation which returns the same value for all symbols"""
Simon Glass19790632017-11-13 18:55:01 -070050 return self.sym_value
Simon Glassb50e5612017-11-13 18:54:54 -070051
Simon Glassb2b0df82018-07-17 13:25:26 -060052
Simon Glass53e22bf2019-08-24 07:22:53 -060053def BuildElfTestFiles(target_dir):
54 """Build ELF files used for testing in binman
55
56 This compiles and links the test files into the specified directory. It the
57 Makefile and source files in the binman test/ directory.
58
59 Args:
60 target_dir: Directory to put the files into
61 """
62 if not os.path.exists(target_dir):
63 os.mkdir(target_dir)
64 testdir = os.path.join(binman_dir, 'test')
65
66 # If binman is involved from the main U-Boot Makefile the -r and -R
67 # flags are set in MAKEFLAGS. This prevents this Makefile from working
68 # correctly. So drop any make flags here.
69 if 'MAKEFLAGS' in os.environ:
70 del os.environ['MAKEFLAGS']
71 tools.Run('make', '-C', target_dir, '-f',
72 os.path.join(testdir, 'Makefile'), 'SRC=%s/' % testdir,
Simon Glass1542c8b2019-08-24 07:22:56 -060073 'bss_data', 'u_boot_ucode_ptr', 'u_boot_no_ucode_ptr',
Simon Glasse9d2ee32019-08-24 07:22:57 -060074 'u_boot_binman_syms', 'u_boot_binman_syms.bin',
75 'u_boot_binman_syms_size')
Simon Glass53e22bf2019-08-24 07:22:53 -060076
77
Simon Glassb50e5612017-11-13 18:54:54 -070078class TestElf(unittest.TestCase):
Simon Glasse0e62752018-10-01 21:12:41 -060079 @classmethod
Simon Glassf514d8f2019-08-24 07:22:54 -060080 def setUpClass(cls):
81 cls._indir = tempfile.mkdtemp(prefix='elf.')
Simon Glasse0e62752018-10-01 21:12:41 -060082 tools.SetInputDirs(['.'])
Simon Glassf514d8f2019-08-24 07:22:54 -060083 BuildElfTestFiles(cls._indir)
84
85 @classmethod
86 def tearDownClass(cls):
87 if cls._indir:
88 shutil.rmtree(cls._indir)
89
90 @classmethod
91 def ElfTestFile(cls, fname):
92 return os.path.join(cls._indir, fname)
Simon Glasse0e62752018-10-01 21:12:41 -060093
Simon Glassb50e5612017-11-13 18:54:54 -070094 def testAllSymbols(self):
Simon Glassb2b0df82018-07-17 13:25:26 -060095 """Test that we can obtain a symbol from the ELF file"""
Simon Glassf514d8f2019-08-24 07:22:54 -060096 fname = self.ElfTestFile('u_boot_ucode_ptr')
Simon Glassb50e5612017-11-13 18:54:54 -070097 syms = elf.GetSymbols(fname, [])
98 self.assertIn('.ucode', syms)
99
100 def testRegexSymbols(self):
Simon Glassb2b0df82018-07-17 13:25:26 -0600101 """Test that we can obtain from the ELF file by regular expression"""
Simon Glassf514d8f2019-08-24 07:22:54 -0600102 fname = self.ElfTestFile('u_boot_ucode_ptr')
Simon Glassb50e5612017-11-13 18:54:54 -0700103 syms = elf.GetSymbols(fname, ['ucode'])
104 self.assertIn('.ucode', syms)
105 syms = elf.GetSymbols(fname, ['missing'])
106 self.assertNotIn('.ucode', syms)
107 syms = elf.GetSymbols(fname, ['missing', 'ucode'])
108 self.assertIn('.ucode', syms)
109
Simon Glass19790632017-11-13 18:55:01 -0700110 def testMissingFile(self):
Simon Glassb2b0df82018-07-17 13:25:26 -0600111 """Test that a missing file is detected"""
Simon Glass19790632017-11-13 18:55:01 -0700112 entry = FakeEntry(10)
Simon Glassf55382b2018-06-01 09:38:13 -0600113 section = FakeSection()
Simon Glass19790632017-11-13 18:55:01 -0700114 with self.assertRaises(ValueError) as e:
Simon Glassf55382b2018-06-01 09:38:13 -0600115 syms = elf.LookupAndWriteSymbols('missing-file', entry, section)
Simon Glass19790632017-11-13 18:55:01 -0700116 self.assertIn("Filename 'missing-file' not found in input path",
117 str(e.exception))
118
119 def testOutsideFile(self):
Simon Glassb2b0df82018-07-17 13:25:26 -0600120 """Test a symbol which extends outside the entry area is detected"""
Simon Glass19790632017-11-13 18:55:01 -0700121 entry = FakeEntry(10)
Simon Glassf55382b2018-06-01 09:38:13 -0600122 section = FakeSection()
Simon Glass1542c8b2019-08-24 07:22:56 -0600123 elf_fname = self.ElfTestFile('u_boot_binman_syms')
Simon Glass19790632017-11-13 18:55:01 -0700124 with self.assertRaises(ValueError) as e:
Simon Glassf55382b2018-06-01 09:38:13 -0600125 syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
Simon Glass19790632017-11-13 18:55:01 -0700126 self.assertIn('entry_path has offset 4 (size 8) but the contents size '
127 'is a', str(e.exception))
128
129 def testMissingImageStart(self):
Simon Glassb2b0df82018-07-17 13:25:26 -0600130 """Test that we detect a missing __image_copy_start symbol
131
132 This is needed to mark the start of the image. Without it we cannot
133 locate the offset of a binman symbol within the image.
134 """
Simon Glass19790632017-11-13 18:55:01 -0700135 entry = FakeEntry(10)
Simon Glassf55382b2018-06-01 09:38:13 -0600136 section = FakeSection()
Simon Glass19790632017-11-13 18:55:01 -0700137 elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_bad')
Simon Glassf55382b2018-06-01 09:38:13 -0600138 self.assertEqual(elf.LookupAndWriteSymbols(elf_fname, entry, section),
Simon Glass19790632017-11-13 18:55:01 -0700139 None)
140
141 def testBadSymbolSize(self):
Simon Glassb2b0df82018-07-17 13:25:26 -0600142 """Test that an attempt to use an 8-bit symbol are detected
143
144 Only 32 and 64 bits are supported, since we need to store an offset
145 into the image.
146 """
Simon Glass19790632017-11-13 18:55:01 -0700147 entry = FakeEntry(10)
Simon Glassf55382b2018-06-01 09:38:13 -0600148 section = FakeSection()
Simon Glasse9d2ee32019-08-24 07:22:57 -0600149 elf_fname =self.ElfTestFile('u_boot_binman_syms_size')
Simon Glass19790632017-11-13 18:55:01 -0700150 with self.assertRaises(ValueError) as e:
Simon Glassf55382b2018-06-01 09:38:13 -0600151 syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
Simon Glass19790632017-11-13 18:55:01 -0700152 self.assertIn('has size 1: only 4 and 8 are supported',
153 str(e.exception))
154
155 def testNoValue(self):
Simon Glassb2b0df82018-07-17 13:25:26 -0600156 """Test the case where we have no value for the symbol
157
158 This should produce -1 values for all thress symbols, taking up the
159 first 16 bytes of the image.
160 """
Simon Glass19790632017-11-13 18:55:01 -0700161 entry = FakeEntry(20)
Simon Glassf55382b2018-06-01 09:38:13 -0600162 section = FakeSection(sym_value=None)
Simon Glass1542c8b2019-08-24 07:22:56 -0600163 elf_fname = self.ElfTestFile('u_boot_binman_syms')
Simon Glassf55382b2018-06-01 09:38:13 -0600164 syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
Simon Glassc6c10e72019-05-17 22:00:46 -0600165 self.assertEqual(tools.GetBytes(255, 16) + tools.GetBytes(ord('a'), 4),
166 entry.data)
Simon Glass19790632017-11-13 18:55:01 -0700167
168 def testDebug(self):
Simon Glassb2b0df82018-07-17 13:25:26 -0600169 """Check that enabling debug in the elf module produced debug output"""
Simon Glass9f297b02019-07-20 12:23:36 -0600170 try:
171 tout.Init(tout.DEBUG)
172 entry = FakeEntry(20)
173 section = FakeSection()
Simon Glass1542c8b2019-08-24 07:22:56 -0600174 elf_fname = self.ElfTestFile('u_boot_binman_syms')
Simon Glass9f297b02019-07-20 12:23:36 -0600175 with test_util.capture_sys_output() as (stdout, stderr):
176 syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
177 self.assertTrue(len(stdout.getvalue()) > 0)
178 finally:
179 tout.Init(tout.WARNING)
Simon Glass19790632017-11-13 18:55:01 -0700180
Simon Glassf58558a2019-07-08 13:18:34 -0600181 def testMakeElf(self):
182 """Test for the MakeElf function"""
183 outdir = tempfile.mkdtemp(prefix='elf.')
184 expected_text = b'1234'
185 expected_data = b'wxyz'
186 elf_fname = os.path.join(outdir, 'elf')
Simon Glass9d44a7e2019-08-24 07:22:45 -0600187 bin_fname = os.path.join(outdir, 'bin')
Simon Glassf58558a2019-07-08 13:18:34 -0600188
189 # Make an Elf file and then convert it to a fkat binary file. This
190 # should produce the original data.
191 elf.MakeElf(elf_fname, expected_text, expected_data)
192 stdout = command.Output('objcopy', '-O', 'binary', elf_fname, bin_fname)
193 with open(bin_fname, 'rb') as fd:
194 data = fd.read()
195 self.assertEqual(expected_text + expected_data, data)
196 shutil.rmtree(outdir)
197
Simon Glassd8d40742019-07-08 13:18:35 -0600198 def testDecodeElf(self):
199 """Test for the MakeElf function"""
200 if not elf.ELF_TOOLS:
201 self.skipTest('Python elftools not available')
202 outdir = tempfile.mkdtemp(prefix='elf.')
203 expected_text = b'1234'
204 expected_data = b'wxyz'
205 elf_fname = os.path.join(outdir, 'elf')
206 elf.MakeElf(elf_fname, expected_text, expected_data)
207 data = tools.ReadFile(elf_fname)
208
209 load = 0xfef20000
210 entry = load + 2
211 expected = expected_text + expected_data
212 self.assertEqual(elf.ElfInfo(expected, load, entry, len(expected)),
213 elf.DecodeElf(data, 0))
214 self.assertEqual(elf.ElfInfo(b'\0\0' + expected[2:],
215 load, entry, len(expected)),
216 elf.DecodeElf(data, load + 2))
Simon Glassf514d8f2019-08-24 07:22:54 -0600217 shutil.rmtree(outdir)
Simon Glassd8d40742019-07-08 13:18:35 -0600218
Simon Glass19790632017-11-13 18:55:01 -0700219
Simon Glassb50e5612017-11-13 18:54:54 -0700220if __name__ == '__main__':
221 unittest.main()