1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2017 Google, Inc
3 # Written by Simon Glass <sjg@chromium.org>
5 # Test for the elf module
14 from binman import elf
15 from patman import command
16 from patman import test_util
17 from patman import tools
18 from patman import tout
20 binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
24 """A fake Entry object, usedfor testing
26 This supports an entry with a given size.
28 def __init__(self, contents_size):
29 self.contents_size = contents_size
30 self.data = tools.get_bytes(ord('a'), contents_size)
37 """A fake Section object, used for testing
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
43 def __init__(self, sym_value=1):
44 self.sym_value = sym_value
49 def LookupImageSymbol(self, name, weak, msg, base_addr):
50 """Fake implementation which returns the same value for all symbols"""
56 def BuildElfTestFiles(target_dir):
57 """Build ELF files used for testing in binman
59 This compiles and links the test files into the specified directory. It uses
60 the Makefile and source files in the binman test/ directory.
63 target_dir: Directory to put the files into
65 if not os.path.exists(target_dir):
67 testdir = os.path.join(binman_dir, 'test')
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']
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
82 class TestElf(unittest.TestCase):
85 cls._indir = tempfile.mkdtemp(prefix='elf.')
86 tools.set_input_dirs(['.'])
87 BuildElfTestFiles(cls._indir)
90 def tearDownClass(cls):
92 shutil.rmtree(cls._indir)
95 def ElfTestFile(cls, fname):
96 return os.path.join(cls._indir, fname)
98 def testAllSymbols(self):
99 """Test that we can obtain a symbol from the ELF file"""
100 fname = self.ElfTestFile('u_boot_ucode_ptr')
101 syms = elf.GetSymbols(fname, [])
102 self.assertIn('_dt_ucode_base_size', syms)
104 def testRegexSymbols(self):
105 """Test that we can obtain from the ELF file by regular expression"""
106 fname = self.ElfTestFile('u_boot_ucode_ptr')
107 syms = elf.GetSymbols(fname, ['ucode'])
108 self.assertIn('_dt_ucode_base_size', syms)
109 syms = elf.GetSymbols(fname, ['missing'])
110 self.assertNotIn('_dt_ucode_base_size', syms)
111 syms = elf.GetSymbols(fname, ['missing', 'ucode'])
112 self.assertIn('_dt_ucode_base_size', syms)
114 def testMissingFile(self):
115 """Test that a missing file is detected"""
116 entry = FakeEntry(10)
117 section = FakeSection()
118 with self.assertRaises(ValueError) as e:
119 elf.LookupAndWriteSymbols('missing-file', entry, section)
120 self.assertIn("Filename 'missing-file' not found in input path",
123 def testOutsideFile(self):
124 """Test a symbol which extends outside the entry area is detected"""
125 entry = FakeEntry(10)
126 section = FakeSection()
127 elf_fname = self.ElfTestFile('u_boot_binman_syms')
128 with self.assertRaises(ValueError) as e:
129 elf.LookupAndWriteSymbols(elf_fname, entry, section)
130 self.assertIn('entry_path has offset 4 (size 8) but the contents size '
131 'is a', str(e.exception))
133 def testMissingImageStart(self):
134 """Test that we detect a missing __image_copy_start symbol
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.
139 entry = FakeEntry(10)
140 section = FakeSection()
141 elf_fname = self.ElfTestFile('u_boot_binman_syms_bad')
142 elf.LookupAndWriteSymbols(elf_fname, entry, section)
144 def testBadSymbolSize(self):
145 """Test that an attempt to use an 8-bit symbol are detected
147 Only 32 and 64 bits are supported, since we need to store an offset
150 entry = FakeEntry(10)
151 section = FakeSection()
152 elf_fname =self.ElfTestFile('u_boot_binman_syms_size')
153 with self.assertRaises(ValueError) as e:
154 elf.LookupAndWriteSymbols(elf_fname, entry, section)
155 self.assertIn('has size 1: only 4 and 8 are supported',
158 def testNoValue(self):
159 """Test the case where we have no value for the symbol
161 This should produce -1 values for all thress symbols, taking up the
162 first 16 bytes of the image.
164 entry = FakeEntry(24)
165 section = FakeSection(sym_value=None)
166 elf_fname = self.ElfTestFile('u_boot_binman_syms')
167 elf.LookupAndWriteSymbols(elf_fname, entry, section)
168 self.assertEqual(tools.get_bytes(255, 20) + tools.get_bytes(ord('a'), 4),
172 """Check that enabling debug in the elf module produced debug output"""
174 tout.init(tout.DEBUG)
175 entry = FakeEntry(20)
176 section = FakeSection()
177 elf_fname = self.ElfTestFile('u_boot_binman_syms')
178 with test_util.capture_sys_output() as (stdout, stderr):
179 elf.LookupAndWriteSymbols(elf_fname, entry, section)
180 self.assertTrue(len(stdout.getvalue()) > 0)
182 tout.init(tout.WARNING)
184 def testMakeElf(self):
185 """Test for the MakeElf function"""
186 outdir = tempfile.mkdtemp(prefix='elf.')
187 expected_text = b'1234'
188 expected_data = b'wxyz'
189 elf_fname = os.path.join(outdir, 'elf')
190 bin_fname = os.path.join(outdir, 'bin')
192 # Make an Elf file and then convert it to a fkat binary file. This
193 # should produce the original data.
194 elf.MakeElf(elf_fname, expected_text, expected_data)
195 objcopy, args = tools.get_target_compile_tool('objcopy')
196 args += ['-O', 'binary', elf_fname, bin_fname]
197 stdout = command.output(objcopy, *args)
198 with open(bin_fname, 'rb') as fd:
200 self.assertEqual(expected_text + expected_data, data)
201 shutil.rmtree(outdir)
203 def testDecodeElf(self):
204 """Test for the MakeElf function"""
205 if not elf.ELF_TOOLS:
206 self.skipTest('Python elftools not available')
207 outdir = tempfile.mkdtemp(prefix='elf.')
208 expected_text = b'1234'
209 expected_data = b'wxyz'
210 elf_fname = os.path.join(outdir, 'elf')
211 elf.MakeElf(elf_fname, expected_text, expected_data)
212 data = tools.read_file(elf_fname)
216 expected = expected_text + expected_data
217 self.assertEqual(elf.ElfInfo(expected, load, entry, len(expected)),
218 elf.DecodeElf(data, 0))
219 self.assertEqual(elf.ElfInfo(b'\0\0' + expected[2:],
220 load, entry, len(expected)),
221 elf.DecodeElf(data, load + 2))
222 shutil.rmtree(outdir)
224 def testEmbedData(self):
225 """Test for the GetSymbolFileOffset() function"""
226 if not elf.ELF_TOOLS:
227 self.skipTest('Python elftools not available')
229 fname = self.ElfTestFile('embed_data')
230 offset = elf.GetSymbolFileOffset(fname, ['embed_start', 'embed_end'])
231 start = offset['embed_start'].offset
232 end = offset['embed_end'].offset
233 data = tools.read_file(fname)
234 embed_data = data[start:end]
235 expect = struct.pack('<III', 0x1234, 0x5678, 0)
236 self.assertEqual(expect, embed_data)
238 def testEmbedFail(self):
239 """Test calling GetSymbolFileOffset() without elftools"""
241 old_val = elf.ELF_TOOLS
242 elf.ELF_TOOLS = False
243 fname = self.ElfTestFile('embed_data')
244 with self.assertRaises(ValueError) as e:
245 elf.GetSymbolFileOffset(fname, ['embed_start', 'embed_end'])
246 self.assertIn('Python elftools package is not available',
249 elf.ELF_TOOLS = old_val
251 def testEmbedDataNoSym(self):
252 """Test for GetSymbolFileOffset() getting no symbols"""
253 if not elf.ELF_TOOLS:
254 self.skipTest('Python elftools not available')
256 fname = self.ElfTestFile('embed_data')
257 offset = elf.GetSymbolFileOffset(fname, ['missing_sym'])
258 self.assertEqual({}, offset)
260 def test_read_segments(self):
261 """Test for read_segments()"""
262 if not elf.ELF_TOOLS:
263 self.skipTest('Python elftools not available')
264 fname = self.ElfTestFile('embed_data')
265 segments, entry = elf.read_segments(tools.read_file(fname))
267 def test_read_segments_fail(self):
268 """Test for read_segments() without elftools"""
270 old_val = elf.ELF_TOOLS
271 elf.ELF_TOOLS = False
272 fname = self.ElfTestFile('embed_data')
273 with self.assertRaises(ValueError) as e:
274 elf.read_segments(tools.read_file(fname))
275 self.assertIn('Python elftools package is not available',
278 elf.ELF_TOOLS = old_val
280 def test_read_segments_bad_data(self):
281 """Test for read_segments() with an invalid ELF file"""
282 fname = self.ElfTestFile('embed_data')
283 with self.assertRaises(ValueError) as e:
284 elf.read_segments(tools.get_bytes(100, 100))
285 self.assertIn('Magic number does not match', str(e.exception))
288 if __name__ == '__main__':