binman: Use the Makefile for u_boot_binman_syms
[oweals/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 sys
10 import tempfile
11 import unittest
12
13 import command
14 import elf
15 import test_util
16 import tools
17 import tout
18
19 binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
20
21
22 class FakeEntry:
23     """A fake Entry object, usedfor testing
24
25     This supports an entry with a given size.
26     """
27     def __init__(self, contents_size):
28         self.contents_size = contents_size
29         self.data = tools.GetBytes(ord('a'), contents_size)
30
31     def GetPath(self):
32         return 'entry_path'
33
34
35 class FakeSection:
36     """A fake Section object, used for testing
37
38     This has the minimum feature set needed to support testing elf functions.
39     A LookupSymbol() function is provided which returns a fake value for amu
40     symbol requested.
41     """
42     def __init__(self, sym_value=1):
43         self.sym_value = sym_value
44
45     def GetPath(self):
46         return 'section_path'
47
48     def LookupSymbol(self, name, weak, msg):
49         """Fake implementation which returns the same value for all symbols"""
50         return self.sym_value
51
52
53 def BuildElfTestFiles(target_dir):
54     """Build ELF files used for testing in binman
55
56     This compiles and links the test files into the specified directory. It the
57     Makefile and source files in the binman test/ directory.
58
59     Args:
60         target_dir: Directory to put the files into
61     """
62     if not os.path.exists(target_dir):
63         os.mkdir(target_dir)
64     testdir = os.path.join(binman_dir, 'test')
65
66     # If binman is involved from the main U-Boot Makefile the -r and -R
67     # flags are set in MAKEFLAGS. This prevents this Makefile from working
68     # correctly. So drop any make flags here.
69     if 'MAKEFLAGS' in os.environ:
70         del os.environ['MAKEFLAGS']
71     tools.Run('make', '-C', target_dir, '-f',
72               os.path.join(testdir, 'Makefile'), 'SRC=%s/' % testdir,
73               'bss_data', 'u_boot_ucode_ptr', 'u_boot_no_ucode_ptr',
74               'u_boot_binman_syms', 'u_boot_binman_syms.bin')
75
76
77 class TestElf(unittest.TestCase):
78     @classmethod
79     def setUpClass(cls):
80         cls._indir = tempfile.mkdtemp(prefix='elf.')
81         tools.SetInputDirs(['.'])
82         BuildElfTestFiles(cls._indir)
83
84     @classmethod
85     def tearDownClass(cls):
86         if cls._indir:
87             shutil.rmtree(cls._indir)
88
89     @classmethod
90     def ElfTestFile(cls, fname):
91         return os.path.join(cls._indir, fname)
92
93     def testAllSymbols(self):
94         """Test that we can obtain a symbol from the ELF file"""
95         fname = self.ElfTestFile('u_boot_ucode_ptr')
96         syms = elf.GetSymbols(fname, [])
97         self.assertIn('.ucode', syms)
98
99     def testRegexSymbols(self):
100         """Test that we can obtain from the ELF file by regular expression"""
101         fname = self.ElfTestFile('u_boot_ucode_ptr')
102         syms = elf.GetSymbols(fname, ['ucode'])
103         self.assertIn('.ucode', syms)
104         syms = elf.GetSymbols(fname, ['missing'])
105         self.assertNotIn('.ucode', syms)
106         syms = elf.GetSymbols(fname, ['missing', 'ucode'])
107         self.assertIn('.ucode', syms)
108
109     def testMissingFile(self):
110         """Test that a missing file is detected"""
111         entry = FakeEntry(10)
112         section = FakeSection()
113         with self.assertRaises(ValueError) as e:
114             syms = elf.LookupAndWriteSymbols('missing-file', entry, section)
115         self.assertIn("Filename 'missing-file' not found in input path",
116                       str(e.exception))
117
118     def testOutsideFile(self):
119         """Test a symbol which extends outside the entry area is detected"""
120         entry = FakeEntry(10)
121         section = FakeSection()
122         elf_fname = self.ElfTestFile('u_boot_binman_syms')
123         with self.assertRaises(ValueError) as e:
124             syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
125         self.assertIn('entry_path has offset 4 (size 8) but the contents size '
126                       'is a', str(e.exception))
127
128     def testMissingImageStart(self):
129         """Test that we detect a missing __image_copy_start symbol
130
131         This is needed to mark the start of the image. Without it we cannot
132         locate the offset of a binman symbol within the image.
133         """
134         entry = FakeEntry(10)
135         section = FakeSection()
136         elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_bad')
137         self.assertEqual(elf.LookupAndWriteSymbols(elf_fname, entry, section),
138                          None)
139
140     def testBadSymbolSize(self):
141         """Test that an attempt to use an 8-bit symbol are detected
142
143         Only 32 and 64 bits are supported, since we need to store an offset
144         into the image.
145         """
146         entry = FakeEntry(10)
147         section = FakeSection()
148         elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_size')
149         with self.assertRaises(ValueError) as e:
150             syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
151         self.assertIn('has size 1: only 4 and 8 are supported',
152                       str(e.exception))
153
154     def testNoValue(self):
155         """Test the case where we have no value for the symbol
156
157         This should produce -1 values for all thress symbols, taking up the
158         first 16 bytes of the image.
159         """
160         entry = FakeEntry(20)
161         section = FakeSection(sym_value=None)
162         elf_fname = self.ElfTestFile('u_boot_binman_syms')
163         syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
164         self.assertEqual(tools.GetBytes(255, 16) + tools.GetBytes(ord('a'), 4),
165                                                                   entry.data)
166
167     def testDebug(self):
168         """Check that enabling debug in the elf module produced debug output"""
169         try:
170             tout.Init(tout.DEBUG)
171             entry = FakeEntry(20)
172             section = FakeSection()
173             elf_fname = self.ElfTestFile('u_boot_binman_syms')
174             with test_util.capture_sys_output() as (stdout, stderr):
175                 syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
176             self.assertTrue(len(stdout.getvalue()) > 0)
177         finally:
178             tout.Init(tout.WARNING)
179
180     def testMakeElf(self):
181         """Test for the MakeElf function"""
182         outdir = tempfile.mkdtemp(prefix='elf.')
183         expected_text = b'1234'
184         expected_data = b'wxyz'
185         elf_fname = os.path.join(outdir, 'elf')
186         bin_fname = os.path.join(outdir, 'bin')
187
188         # Make an Elf file and then convert it to a fkat binary file. This
189         # should produce the original data.
190         elf.MakeElf(elf_fname, expected_text, expected_data)
191         stdout = command.Output('objcopy', '-O', 'binary', elf_fname, bin_fname)
192         with open(bin_fname, 'rb') as fd:
193             data = fd.read()
194         self.assertEqual(expected_text + expected_data, data)
195         shutil.rmtree(outdir)
196
197     def testDecodeElf(self):
198         """Test for the MakeElf function"""
199         if not elf.ELF_TOOLS:
200             self.skipTest('Python elftools not available')
201         outdir = tempfile.mkdtemp(prefix='elf.')
202         expected_text = b'1234'
203         expected_data = b'wxyz'
204         elf_fname = os.path.join(outdir, 'elf')
205         elf.MakeElf(elf_fname, expected_text, expected_data)
206         data = tools.ReadFile(elf_fname)
207
208         load = 0xfef20000
209         entry = load + 2
210         expected = expected_text + expected_data
211         self.assertEqual(elf.ElfInfo(expected, load, entry, len(expected)),
212                          elf.DecodeElf(data, 0))
213         self.assertEqual(elf.ElfInfo(b'\0\0' + expected[2:],
214                                      load, entry, len(expected)),
215                          elf.DecodeElf(data, load + 2))
216         shutil.rmtree(outdir)
217
218
219 if __name__ == '__main__':
220     unittest.main()