patman: Convert camel case in command.py
[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 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']
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             syms = 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             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))
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         self.assertEqual(elf.LookupAndWriteSymbols(elf_fname, entry, section),
143                          None)
144
145     def testBadSymbolSize(self):
146         """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         """
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',
157                       str(e.exception))
158
159     def testNoValue(self):
160         """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         """
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),
170                                                                   entry.data)
171
172     def testDebug(self):
173         """Check that enabling debug in the elf module produced debug output"""
174         try:
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)
182         finally:
183             tout.Init(tout.WARNING)
184
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')
192
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:
200             data = fd.read()
201         self.assertEqual(expected_text + expected_data, data)
202         shutil.rmtree(outdir)
203
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)
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))
223         shutil.rmtree(outdir)
224
225     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.read_file(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
261
262 if __name__ == '__main__':
263     unittest.main()