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 the
60 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 syms = 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 syms = 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 self.assertEqual(elf.LookupAndWriteSymbols(elf_fname, entry, section),
145 def testBadSymbolSize(self):
146 """Test that an attempt to use an 8-bit symbol are detected
148 Only 32 and 64 bits are supported, since we need to store an offset
151 entry = FakeEntry(10)
152 section = FakeSection()
153 elf_fname =self.ElfTestFile('u_boot_binman_syms_size')
154 with self.assertRaises(ValueError) as e:
155 syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
156 self.assertIn('has size 1: only 4 and 8 are supported',
159 def testNoValue(self):
160 """Test the case where we have no value for the symbol
162 This should produce -1 values for all thress symbols, taking up the
163 first 16 bytes of the image.
165 entry = FakeEntry(24)
166 section = FakeSection(sym_value=None)
167 elf_fname = self.ElfTestFile('u_boot_binman_syms')
168 syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
169 self.assertEqual(tools.get_bytes(255, 20) + tools.get_bytes(ord('a'), 4),
173 """Check that enabling debug in the elf module produced debug output"""
175 tout.init(tout.DEBUG)
176 entry = FakeEntry(20)
177 section = FakeSection()
178 elf_fname = self.ElfTestFile('u_boot_binman_syms')
179 with test_util.capture_sys_output() as (stdout, stderr):
180 syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
181 self.assertTrue(len(stdout.getvalue()) > 0)
183 tout.init(tout.WARNING)
185 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')
191 bin_fname = os.path.join(outdir, 'bin')
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)
196 objcopy, args = tools.get_target_compile_tool('objcopy')
197 args += ['-O', 'binary', elf_fname, bin_fname]
198 stdout = command.output(objcopy, *args)
199 with open(bin_fname, 'rb') as fd:
201 self.assertEqual(expected_text + expected_data, data)
202 shutil.rmtree(outdir)
204 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.read_file(elf_fname)
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))
223 shutil.rmtree(outdir)
225 def testEmbedData(self):
226 """Test for the GetSymbolFileOffset() function"""
227 if not elf.ELF_TOOLS:
228 self.skipTest('Python elftools not available')
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.read_file(fname)
235 embed_data = data[start:end]
236 expect = struct.pack('<III', 0x1234, 0x5678, 0)
237 self.assertEqual(expect, embed_data)
239 def testEmbedFail(self):
240 """Test calling GetSymbolFileOffset() without elftools"""
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',
250 elf.ELF_TOOLS = old_val
252 def testEmbedDataNoSym(self):
253 """Test for GetSymbolFileOffset() getting no symbols"""
254 if not elf.ELF_TOOLS:
255 self.skipTest('Python elftools not available')
257 fname = self.ElfTestFile('embed_data')
258 offset = elf.GetSymbolFileOffset(fname, ['missing_sym'])
259 self.assertEqual({}, offset)
262 if __name__ == '__main__':