binman: Correct pylint errors
[platform/kernel/u-boot.git] / tools / binman / elf_test.py
1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2017 Google, Inc
3 # Written by Simon Glass <sjg@chromium.org>
4 #
5 # Test for the elf module
6
7 import os
8 import shutil
9 import struct
10 import sys
11 import tempfile
12 import unittest
13
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
19
20 binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
21
22
23 class FakeEntry:
24     """A fake Entry object, usedfor testing
25
26     This supports an entry with a given size.
27     """
28     def __init__(self, contents_size):
29         self.contents_size = contents_size
30         self.data = tools.get_bytes(ord('a'), contents_size)
31
32     def GetPath(self):
33         return 'entry_path'
34
35
36 class FakeSection:
37     """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     """
43     def __init__(self, sym_value=1):
44         self.sym_value = sym_value
45
46     def GetPath(self):
47         return 'section_path'
48
49     def LookupImageSymbol(self, name, weak, msg, base_addr):
50         """Fake implementation which returns the same value for all symbols"""
51         return self.sym_value
52
53     def GetImage(self):
54         return self
55
56 def 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 uses
60     the 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']
74     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)
80
81
82 class TestElf(unittest.TestCase):
83     @classmethod
84     def setUpClass(cls):
85         cls._indir = tempfile.mkdtemp(prefix='elf.')
86         tools.set_input_dirs(['.'])
87         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)
97
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)
103
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)
113
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",
121                       str(e.exception))
122
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))
132
133     def testMissingImageStart(self):
134         """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         """
139         entry = FakeEntry(10)
140         section = FakeSection()
141         elf_fname = self.ElfTestFile('u_boot_binman_syms_bad')
142         elf.LookupAndWriteSymbols(elf_fname, entry, section)
143
144     def testBadSymbolSize(self):
145         """Test that an attempt to use an 8-bit symbol are detected
146
147         Only 32 and 64 bits are supported, since we need to store an offset
148         into the image.
149         """
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',
156                       str(e.exception))
157
158     def testNoValue(self):
159         """Test the case where we have no value for the symbol
160
161         This should produce -1 values for all thress symbols, taking up the
162         first 16 bytes of the image.
163         """
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),
169                                                                   entry.data)
170
171     def testDebug(self):
172         """Check that enabling debug in the elf module produced debug output"""
173         try:
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)
181         finally:
182             tout.init(tout.WARNING)
183
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')
191
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:
199             data = fd.read()
200         self.assertEqual(expected_text + expected_data, data)
201         shutil.rmtree(outdir)
202
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)
213
214         load = 0xfef20000
215         entry = load + 2
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)
223
224     def testEmbedData(self):
225         """Test for the GetSymbolFileOffset() function"""
226         if not elf.ELF_TOOLS:
227             self.skipTest('Python elftools not available')
228
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)
237
238     def testEmbedFail(self):
239         """Test calling GetSymbolFileOffset() without elftools"""
240         try:
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',
247                       str(e.exception))
248         finally:
249             elf.ELF_TOOLS = old_val
250
251     def testEmbedDataNoSym(self):
252         """Test for GetSymbolFileOffset() getting no symbols"""
253         if not elf.ELF_TOOLS:
254             self.skipTest('Python elftools not available')
255
256         fname = self.ElfTestFile('embed_data')
257         offset = elf.GetSymbolFileOffset(fname, ['missing_sym'])
258         self.assertEqual({}, offset)
259
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))
266
267     def test_read_segments_fail(self):
268         """Test for read_segments() without elftools"""
269         try:
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',
276                           str(e.exception))
277         finally:
278             elf.ELF_TOOLS = old_val
279
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))
286
287
288 if __name__ == '__main__':
289     unittest.main()