blob: ac69a95b6547c13c761c35a5782b435744dae2b1 [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 Glass303f62f2019-05-17 22:00:46 -060030 self.data = tools.GetBytes(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
59 This compiles and links the test files into the specified directory. It the
60 Makefile and source files in the binman test/ directory.
61
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:
75 tools.Run('make', '-C', target_dir, '-f',
76 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 Glass752e7552018-10-01 21:12:41 -060086 tools.SetInputDirs(['.'])
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, [])
102 self.assertIn('.ucode', syms)
103
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'])
108 self.assertIn('.ucode', syms)
109 syms = elf.GetSymbols(fname, ['missing'])
110 self.assertNotIn('.ucode', syms)
111 syms = elf.GetSymbols(fname, ['missing', 'ucode'])
112 self.assertIn('.ucode', syms)
113
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 Glass3f8ff012019-08-24 07:23:05 -0600169 self.assertEqual(tools.GetBytes(255, 20) + tools.GetBytes(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:
175 tout.Init(tout.DEBUG)
176 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:
183 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)
Alper Nebi Yasak5cd321d2020-09-06 14:46:05 +0300196 objcopy, args = tools.GetTargetCompileTool('objcopy')
197 args += ['-O', 'binary', elf_fname, bin_fname]
198 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)
213 data = tools.ReadFile(elf_fname)
214
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
234 data = tools.ReadFile(fname)
235 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 Glass4ca8e042017-11-13 18:55:01 -0700261
Simon Glass24ad3652017-11-13 18:54:54 -0700262if __name__ == '__main__':
263 unittest.main()