binman: Use the Makefile for u_boot_binman_syms_size
[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               'u_boot_binman_syms_size')
76
77
78 class TestElf(unittest.TestCase):
79     @classmethod
80     def setUpClass(cls):
81         cls._indir = tempfile.mkdtemp(prefix='elf.')
82         tools.SetInputDirs(['.'])
83         BuildElfTestFiles(cls._indir)
84
85     @classmethod
86     def tearDownClass(cls):
87         if cls._indir:
88             shutil.rmtree(cls._indir)
89
90     @classmethod
91     def ElfTestFile(cls, fname):
92         return os.path.join(cls._indir, fname)
93
94     def testAllSymbols(self):
95         """Test that we can obtain a symbol from the ELF file"""
96         fname = self.ElfTestFile('u_boot_ucode_ptr')
97         syms = elf.GetSymbols(fname, [])
98         self.assertIn('.ucode', syms)
99
100     def testRegexSymbols(self):
101         """Test that we can obtain from the ELF file by regular expression"""
102         fname = self.ElfTestFile('u_boot_ucode_ptr')
103         syms = elf.GetSymbols(fname, ['ucode'])
104         self.assertIn('.ucode', syms)
105         syms = elf.GetSymbols(fname, ['missing'])
106         self.assertNotIn('.ucode', syms)
107         syms = elf.GetSymbols(fname, ['missing', 'ucode'])
108         self.assertIn('.ucode', syms)
109
110     def testMissingFile(self):
111         """Test that a missing file is detected"""
112         entry = FakeEntry(10)
113         section = FakeSection()
114         with self.assertRaises(ValueError) as e:
115             syms = elf.LookupAndWriteSymbols('missing-file', entry, section)
116         self.assertIn("Filename 'missing-file' not found in input path",
117                       str(e.exception))
118
119     def testOutsideFile(self):
120         """Test a symbol which extends outside the entry area is detected"""
121         entry = FakeEntry(10)
122         section = FakeSection()
123         elf_fname = self.ElfTestFile('u_boot_binman_syms')
124         with self.assertRaises(ValueError) as e:
125             syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
126         self.assertIn('entry_path has offset 4 (size 8) but the contents size '
127                       'is a', str(e.exception))
128
129     def testMissingImageStart(self):
130         """Test that we detect a missing __image_copy_start symbol
131
132         This is needed to mark the start of the image. Without it we cannot
133         locate the offset of a binman symbol within the image.
134         """
135         entry = FakeEntry(10)
136         section = FakeSection()
137         elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_bad')
138         self.assertEqual(elf.LookupAndWriteSymbols(elf_fname, entry, section),
139                          None)
140
141     def testBadSymbolSize(self):
142         """Test that an attempt to use an 8-bit symbol are detected
143
144         Only 32 and 64 bits are supported, since we need to store an offset
145         into the image.
146         """
147         entry = FakeEntry(10)
148         section = FakeSection()
149         elf_fname =self.ElfTestFile('u_boot_binman_syms_size')
150         with self.assertRaises(ValueError) as e:
151             syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
152         self.assertIn('has size 1: only 4 and 8 are supported',
153                       str(e.exception))
154
155     def testNoValue(self):
156         """Test the case where we have no value for the symbol
157
158         This should produce -1 values for all thress symbols, taking up the
159         first 16 bytes of the image.
160         """
161         entry = FakeEntry(20)
162         section = FakeSection(sym_value=None)
163         elf_fname = self.ElfTestFile('u_boot_binman_syms')
164         syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
165         self.assertEqual(tools.GetBytes(255, 16) + tools.GetBytes(ord('a'), 4),
166                                                                   entry.data)
167
168     def testDebug(self):
169         """Check that enabling debug in the elf module produced debug output"""
170         try:
171             tout.Init(tout.DEBUG)
172             entry = FakeEntry(20)
173             section = FakeSection()
174             elf_fname = self.ElfTestFile('u_boot_binman_syms')
175             with test_util.capture_sys_output() as (stdout, stderr):
176                 syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
177             self.assertTrue(len(stdout.getvalue()) > 0)
178         finally:
179             tout.Init(tout.WARNING)
180
181     def testMakeElf(self):
182         """Test for the MakeElf function"""
183         outdir = tempfile.mkdtemp(prefix='elf.')
184         expected_text = b'1234'
185         expected_data = b'wxyz'
186         elf_fname = os.path.join(outdir, 'elf')
187         bin_fname = os.path.join(outdir, 'bin')
188
189         # Make an Elf file and then convert it to a fkat binary file. This
190         # should produce the original data.
191         elf.MakeElf(elf_fname, expected_text, expected_data)
192         stdout = command.Output('objcopy', '-O', 'binary', elf_fname, bin_fname)
193         with open(bin_fname, 'rb') as fd:
194             data = fd.read()
195         self.assertEqual(expected_text + expected_data, data)
196         shutil.rmtree(outdir)
197
198     def testDecodeElf(self):
199         """Test for the MakeElf function"""
200         if not elf.ELF_TOOLS:
201             self.skipTest('Python elftools not available')
202         outdir = tempfile.mkdtemp(prefix='elf.')
203         expected_text = b'1234'
204         expected_data = b'wxyz'
205         elf_fname = os.path.join(outdir, 'elf')
206         elf.MakeElf(elf_fname, expected_text, expected_data)
207         data = tools.ReadFile(elf_fname)
208
209         load = 0xfef20000
210         entry = load + 2
211         expected = expected_text + expected_data
212         self.assertEqual(elf.ElfInfo(expected, load, entry, len(expected)),
213                          elf.DecodeElf(data, 0))
214         self.assertEqual(elf.ElfInfo(b'\0\0' + expected[2:],
215                                      load, entry, len(expected)),
216                          elf.DecodeElf(data, load + 2))
217         shutil.rmtree(outdir)
218
219
220 if __name__ == '__main__':
221     unittest.main()