blob: a67915bda63fd396d07a96ba30aaddbc65711e2e [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glass24ad3652017-11-13 18:54:54 -07002# Copyright (c) 2017 Google, Inc
3# Written by Simon Glass <sjg@chromium.org>
4#
Simon Glass24ad3652017-11-13 18:54:54 -07005# Test for the elf module
6
7import os
Simon Glass4f379ea2019-07-08 13:18:34 -06008import shutil
Simon Glassa4e259e2021-11-03 21:09:16 -06009import struct
Simon Glass24ad3652017-11-13 18:54:54 -070010import sys
Simon Glass4f379ea2019-07-08 13:18:34 -060011import tempfile
Simon Glass24ad3652017-11-13 18:54:54 -070012import unittest
13
Simon Glassc585dd42020-04-17 18:09:03 -060014from binman import elf
Simon Glassa997ea52020-04-17 18:09:04 -060015from patman import command
16from patman import test_util
17from patman import tools
18from patman import tout
Simon Glass24ad3652017-11-13 18:54:54 -070019
20binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
Simon Glass4ca8e042017-11-13 18:55:01 -070021
Simon Glass4ca8e042017-11-13 18:55:01 -070022
23class FakeEntry:
Simon Glass4114f972018-07-17 13:25:26 -060024 """A fake Entry object, usedfor testing
25
26 This supports an entry with a given size.
27 """
Simon Glass4ca8e042017-11-13 18:55:01 -070028 def __init__(self, contents_size):
29 self.contents_size = contents_size
Simon Glass80025522022-01-29 14:14:04 -070030 self.data = tools.get_bytes(ord('a'), contents_size)
Simon Glass4ca8e042017-11-13 18:55:01 -070031
32 def GetPath(self):
33 return 'entry_path'
34
Simon Glass4114f972018-07-17 13:25:26 -060035
Simon Glass8a6f56e2018-06-01 09:38:13 -060036class FakeSection:
Simon Glass4114f972018-07-17 13:25:26 -060037 """A fake Section object, used for testing
38
39 This has the minimum feature set needed to support testing elf functions.
40 A LookupSymbol() function is provided which returns a fake value for amu
41 symbol requested.
42 """
Simon Glass4ca8e042017-11-13 18:55:01 -070043 def __init__(self, sym_value=1):
44 self.sym_value = sym_value
45
46 def GetPath(self):
Simon Glass8a6f56e2018-06-01 09:38:13 -060047 return 'section_path'
Simon Glass4ca8e042017-11-13 18:55:01 -070048
Simon Glassecbe4732021-01-06 21:35:15 -070049 def LookupImageSymbol(self, name, weak, msg, base_addr):
Simon Glass4114f972018-07-17 13:25:26 -060050 """Fake implementation which returns the same value for all symbols"""
Simon Glass4ca8e042017-11-13 18:55:01 -070051 return self.sym_value
Simon Glass24ad3652017-11-13 18:54:54 -070052
Simon Glassecbe4732021-01-06 21:35:15 -070053 def GetImage(self):
54 return self
Simon Glass4114f972018-07-17 13:25:26 -060055
Simon Glassf6290892019-08-24 07:22:53 -060056def BuildElfTestFiles(target_dir):
57 """Build ELF files used for testing in binman
58
Simon Glass571adc82022-02-08 11:49:55 -070059 This compiles and links the test files into the specified directory. It uses
60 the Makefile and source files in the binman test/ directory.
Simon Glassf6290892019-08-24 07:22:53 -060061
62 Args:
63 target_dir: Directory to put the files into
64 """
65 if not os.path.exists(target_dir):
66 os.mkdir(target_dir)
67 testdir = os.path.join(binman_dir, 'test')
68
69 # If binman is involved from the main U-Boot Makefile the -r and -R
70 # flags are set in MAKEFLAGS. This prevents this Makefile from working
71 # correctly. So drop any make flags here.
72 if 'MAKEFLAGS' in os.environ:
73 del os.environ['MAKEFLAGS']
Simon Glass271fd8f2021-11-03 21:09:15 -060074 try:
Simon Glass80025522022-01-29 14:14:04 -070075 tools.run('make', '-C', target_dir, '-f',
Simon Glass271fd8f2021-11-03 21:09:15 -060076 os.path.join(testdir, 'Makefile'), 'SRC=%s/' % testdir)
77 except ValueError as e:
78 # The test system seems to suppress this in a strange way
79 print(e)
Simon Glassf6290892019-08-24 07:22:53 -060080
81
Simon Glass24ad3652017-11-13 18:54:54 -070082class TestElf(unittest.TestCase):
Simon Glass752e7552018-10-01 21:12:41 -060083 @classmethod
Simon Glass4affd4b2019-08-24 07:22:54 -060084 def setUpClass(cls):
85 cls._indir = tempfile.mkdtemp(prefix='elf.')
Simon Glass80025522022-01-29 14:14:04 -070086 tools.set_input_dirs(['.'])
Simon Glass4affd4b2019-08-24 07:22:54 -060087 BuildElfTestFiles(cls._indir)
88
89 @classmethod
90 def tearDownClass(cls):
91 if cls._indir:
92 shutil.rmtree(cls._indir)
93
94 @classmethod
95 def ElfTestFile(cls, fname):
96 return os.path.join(cls._indir, fname)
Simon Glass752e7552018-10-01 21:12:41 -060097
Simon Glass24ad3652017-11-13 18:54:54 -070098 def testAllSymbols(self):
Simon Glass4114f972018-07-17 13:25:26 -060099 """Test that we can obtain a symbol from the ELF file"""
Simon Glass4affd4b2019-08-24 07:22:54 -0600100 fname = self.ElfTestFile('u_boot_ucode_ptr')
Simon Glass24ad3652017-11-13 18:54:54 -0700101 syms = elf.GetSymbols(fname, [])
Simon Glass315400e2022-01-09 20:13:37 -0700102 self.assertIn('_dt_ucode_base_size', syms)
Simon Glass24ad3652017-11-13 18:54:54 -0700103
104 def testRegexSymbols(self):
Simon Glass4114f972018-07-17 13:25:26 -0600105 """Test that we can obtain from the ELF file by regular expression"""
Simon Glass4affd4b2019-08-24 07:22:54 -0600106 fname = self.ElfTestFile('u_boot_ucode_ptr')
Simon Glass24ad3652017-11-13 18:54:54 -0700107 syms = elf.GetSymbols(fname, ['ucode'])
Simon Glass315400e2022-01-09 20:13:37 -0700108 self.assertIn('_dt_ucode_base_size', syms)
Simon Glass24ad3652017-11-13 18:54:54 -0700109 syms = elf.GetSymbols(fname, ['missing'])
Simon Glass315400e2022-01-09 20:13:37 -0700110 self.assertNotIn('_dt_ucode_base_size', syms)
Simon Glass24ad3652017-11-13 18:54:54 -0700111 syms = elf.GetSymbols(fname, ['missing', 'ucode'])
Simon Glass315400e2022-01-09 20:13:37 -0700112 self.assertIn('_dt_ucode_base_size', syms)
Simon Glass24ad3652017-11-13 18:54:54 -0700113
Simon Glass4ca8e042017-11-13 18:55:01 -0700114 def testMissingFile(self):
Simon Glass4114f972018-07-17 13:25:26 -0600115 """Test that a missing file is detected"""
Simon Glass4ca8e042017-11-13 18:55:01 -0700116 entry = FakeEntry(10)
Simon Glass8a6f56e2018-06-01 09:38:13 -0600117 section = FakeSection()
Simon Glass4ca8e042017-11-13 18:55:01 -0700118 with self.assertRaises(ValueError) as e:
Simon Glass8a6f56e2018-06-01 09:38:13 -0600119 syms = elf.LookupAndWriteSymbols('missing-file', entry, section)
Simon Glass4ca8e042017-11-13 18:55:01 -0700120 self.assertIn("Filename 'missing-file' not found in input path",
121 str(e.exception))
122
123 def testOutsideFile(self):
Simon Glass4114f972018-07-17 13:25:26 -0600124 """Test a symbol which extends outside the entry area is detected"""
Simon Glass4ca8e042017-11-13 18:55:01 -0700125 entry = FakeEntry(10)
Simon Glass8a6f56e2018-06-01 09:38:13 -0600126 section = FakeSection()
Simon Glass5d0c0262019-08-24 07:22:56 -0600127 elf_fname = self.ElfTestFile('u_boot_binman_syms')
Simon Glass4ca8e042017-11-13 18:55:01 -0700128 with self.assertRaises(ValueError) as e:
Simon Glass8a6f56e2018-06-01 09:38:13 -0600129 syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
Simon Glass4ca8e042017-11-13 18:55:01 -0700130 self.assertIn('entry_path has offset 4 (size 8) but the contents size '
131 'is a', str(e.exception))
132
133 def testMissingImageStart(self):
Simon Glass4114f972018-07-17 13:25:26 -0600134 """Test that we detect a missing __image_copy_start symbol
135
136 This is needed to mark the start of the image. Without it we cannot
137 locate the offset of a binman symbol within the image.
138 """
Simon Glass4ca8e042017-11-13 18:55:01 -0700139 entry = FakeEntry(10)
Simon Glass8a6f56e2018-06-01 09:38:13 -0600140 section = FakeSection()
Simon Glass46ea6912019-08-24 07:22:58 -0600141 elf_fname = self.ElfTestFile('u_boot_binman_syms_bad')
Simon Glass8a6f56e2018-06-01 09:38:13 -0600142 self.assertEqual(elf.LookupAndWriteSymbols(elf_fname, entry, section),
Simon Glass4ca8e042017-11-13 18:55:01 -0700143 None)
144
145 def testBadSymbolSize(self):
Simon Glass4114f972018-07-17 13:25:26 -0600146 """Test that an attempt to use an 8-bit symbol are detected
147
148 Only 32 and 64 bits are supported, since we need to store an offset
149 into the image.
150 """
Simon Glass4ca8e042017-11-13 18:55:01 -0700151 entry = FakeEntry(10)
Simon Glass8a6f56e2018-06-01 09:38:13 -0600152 section = FakeSection()
Simon Glassb8deb122019-08-24 07:22:57 -0600153 elf_fname =self.ElfTestFile('u_boot_binman_syms_size')
Simon Glass4ca8e042017-11-13 18:55:01 -0700154 with self.assertRaises(ValueError) as e:
Simon Glass8a6f56e2018-06-01 09:38:13 -0600155 syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
Simon Glass4ca8e042017-11-13 18:55:01 -0700156 self.assertIn('has size 1: only 4 and 8 are supported',
157 str(e.exception))
158
159 def testNoValue(self):
Simon Glass4114f972018-07-17 13:25:26 -0600160 """Test the case where we have no value for the symbol
161
162 This should produce -1 values for all thress symbols, taking up the
163 first 16 bytes of the image.
164 """
Simon Glass3f8ff012019-08-24 07:23:05 -0600165 entry = FakeEntry(24)
Simon Glass8a6f56e2018-06-01 09:38:13 -0600166 section = FakeSection(sym_value=None)
Simon Glass5d0c0262019-08-24 07:22:56 -0600167 elf_fname = self.ElfTestFile('u_boot_binman_syms')
Simon Glass8a6f56e2018-06-01 09:38:13 -0600168 syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
Simon Glass80025522022-01-29 14:14:04 -0700169 self.assertEqual(tools.get_bytes(255, 20) + tools.get_bytes(ord('a'), 4),
Simon Glass303f62f2019-05-17 22:00:46 -0600170 entry.data)
Simon Glass4ca8e042017-11-13 18:55:01 -0700171
172 def testDebug(self):
Simon Glass4114f972018-07-17 13:25:26 -0600173 """Check that enabling debug in the elf module produced debug output"""
Simon Glassb6dff4c2019-07-20 12:23:36 -0600174 try:
Simon Glass011f1b32022-01-29 14:14:15 -0700175 tout.init(tout.DEBUG)
Simon Glassb6dff4c2019-07-20 12:23:36 -0600176 entry = FakeEntry(20)
177 section = FakeSection()
Simon Glass5d0c0262019-08-24 07:22:56 -0600178 elf_fname = self.ElfTestFile('u_boot_binman_syms')
Simon Glassb6dff4c2019-07-20 12:23:36 -0600179 with test_util.capture_sys_output() as (stdout, stderr):
180 syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
181 self.assertTrue(len(stdout.getvalue()) > 0)
182 finally:
Simon Glass011f1b32022-01-29 14:14:15 -0700183 tout.init(tout.WARNING)
Simon Glass4ca8e042017-11-13 18:55:01 -0700184
Simon Glass4f379ea2019-07-08 13:18:34 -0600185 def testMakeElf(self):
186 """Test for the MakeElf function"""
187 outdir = tempfile.mkdtemp(prefix='elf.')
188 expected_text = b'1234'
189 expected_data = b'wxyz'
190 elf_fname = os.path.join(outdir, 'elf')
Simon Glassd349ada2019-08-24 07:22:45 -0600191 bin_fname = os.path.join(outdir, 'bin')
Simon Glass4f379ea2019-07-08 13:18:34 -0600192
193 # Make an Elf file and then convert it to a fkat binary file. This
194 # should produce the original data.
195 elf.MakeElf(elf_fname, expected_text, expected_data)
Simon Glass80025522022-01-29 14:14:04 -0700196 objcopy, args = tools.get_target_compile_tool('objcopy')
Alper Nebi Yasak5cd321d2020-09-06 14:46:05 +0300197 args += ['-O', 'binary', elf_fname, bin_fname]
Simon Glass840be732022-01-29 14:14:05 -0700198 stdout = command.output(objcopy, *args)
Simon Glass4f379ea2019-07-08 13:18:34 -0600199 with open(bin_fname, 'rb') as fd:
200 data = fd.read()
201 self.assertEqual(expected_text + expected_data, data)
202 shutil.rmtree(outdir)
203
Simon Glass567b6822019-07-08 13:18:35 -0600204 def testDecodeElf(self):
205 """Test for the MakeElf function"""
206 if not elf.ELF_TOOLS:
207 self.skipTest('Python elftools not available')
208 outdir = tempfile.mkdtemp(prefix='elf.')
209 expected_text = b'1234'
210 expected_data = b'wxyz'
211 elf_fname = os.path.join(outdir, 'elf')
212 elf.MakeElf(elf_fname, expected_text, expected_data)
Simon Glass80025522022-01-29 14:14:04 -0700213 data = tools.read_file(elf_fname)
Simon Glass567b6822019-07-08 13:18:35 -0600214
215 load = 0xfef20000
216 entry = load + 2
217 expected = expected_text + expected_data
218 self.assertEqual(elf.ElfInfo(expected, load, entry, len(expected)),
219 elf.DecodeElf(data, 0))
220 self.assertEqual(elf.ElfInfo(b'\0\0' + expected[2:],
221 load, entry, len(expected)),
222 elf.DecodeElf(data, load + 2))
Simon Glass4affd4b2019-08-24 07:22:54 -0600223 shutil.rmtree(outdir)
Simon Glass567b6822019-07-08 13:18:35 -0600224
Simon Glassa4e259e2021-11-03 21:09:16 -0600225 def testEmbedData(self):
226 """Test for the GetSymbolFileOffset() function"""
227 if not elf.ELF_TOOLS:
228 self.skipTest('Python elftools not available')
229
230 fname = self.ElfTestFile('embed_data')
231 offset = elf.GetSymbolFileOffset(fname, ['embed_start', 'embed_end'])
232 start = offset['embed_start'].offset
233 end = offset['embed_end'].offset
Simon Glass80025522022-01-29 14:14:04 -0700234 data = tools.read_file(fname)
Simon Glassa4e259e2021-11-03 21:09:16 -0600235 embed_data = data[start:end]
236 expect = struct.pack('<III', 0x1234, 0x5678, 0)
237 self.assertEqual(expect, embed_data)
238
239 def testEmbedFail(self):
240 """Test calling GetSymbolFileOffset() without elftools"""
241 try:
242 old_val = elf.ELF_TOOLS
243 elf.ELF_TOOLS = False
244 fname = self.ElfTestFile('embed_data')
245 with self.assertRaises(ValueError) as e:
246 elf.GetSymbolFileOffset(fname, ['embed_start', 'embed_end'])
247 self.assertIn('Python elftools package is not available',
248 str(e.exception))
249 finally:
250 elf.ELF_TOOLS = old_val
251
252 def testEmbedDataNoSym(self):
253 """Test for GetSymbolFileOffset() getting no symbols"""
254 if not elf.ELF_TOOLS:
255 self.skipTest('Python elftools not available')
256
257 fname = self.ElfTestFile('embed_data')
258 offset = elf.GetSymbolFileOffset(fname, ['missing_sym'])
259 self.assertEqual({}, offset)
260
Simon Glass571adc82022-02-08 11:49:55 -0700261 def test_read_segments(self):
262 """Test for read_segments()"""
263 if not elf.ELF_TOOLS:
264 self.skipTest('Python elftools not available')
265 fname = self.ElfTestFile('embed_data')
Simon Glass948dd3a2022-02-08 11:49:58 -0700266 segments, entry = elf.read_segments(tools.read_file(fname))
Simon Glass571adc82022-02-08 11:49:55 -0700267
268 def test_read_segments_fail(self):
269 """Test for read_segments() without elftools"""
270 try:
271 old_val = elf.ELF_TOOLS
272 elf.ELF_TOOLS = False
273 fname = self.ElfTestFile('embed_data')
274 with self.assertRaises(ValueError) as e:
Simon Glass948dd3a2022-02-08 11:49:58 -0700275 elf.read_segments(tools.read_file(fname))
Simon Glass571adc82022-02-08 11:49:55 -0700276 self.assertIn('Python elftools package is not available',
277 str(e.exception))
278 finally:
279 elf.ELF_TOOLS = old_val
280
281 def test_read_segments_bad_data(self):
282 """Test for read_segments() with an invalid ELF file"""
283 fname = self.ElfTestFile('embed_data')
284 with self.assertRaises(ValueError) as e:
Simon Glass948dd3a2022-02-08 11:49:58 -0700285 elf.read_segments(tools.get_bytes(100, 100))
Simon Glass571adc82022-02-08 11:49:55 -0700286 self.assertIn('Magic number does not match', str(e.exception))
287
Simon Glass4ca8e042017-11-13 18:55:01 -0700288
Simon Glass24ad3652017-11-13 18:54:54 -0700289if __name__ == '__main__':
290 unittest.main()