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