blob: f05545bcb13b5e592b4f4708619fbf082ac7e533 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glass24ad3652017-11-13 18:54:54 -07002# Copyright (c) 2017 Google, Inc
3# Written by Simon Glass <sjg@chromium.org>
4#
Simon Glass24ad3652017-11-13 18:54:54 -07005# Test for the elf module
6
7import os
Simon Glass4f379ea2019-07-08 13:18:34 -06008import shutil
Simon Glass24ad3652017-11-13 18:54:54 -07009import sys
Simon Glass4f379ea2019-07-08 13:18:34 -060010import tempfile
Simon Glass24ad3652017-11-13 18:54:54 -070011import unittest
12
Simon Glass4f379ea2019-07-08 13:18:34 -060013import command
Simon Glass24ad3652017-11-13 18:54:54 -070014import elf
Simon Glass3609d5e2018-07-06 10:27:34 -060015import test_util
Simon Glass752e7552018-10-01 21:12:41 -060016import tools
Simon Glassb6dff4c2019-07-20 12:23:36 -060017import tout
Simon Glass24ad3652017-11-13 18:54:54 -070018
19binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
Simon Glass4ca8e042017-11-13 18:55:01 -070020
Simon Glass4ca8e042017-11-13 18:55:01 -070021
22class FakeEntry:
Simon Glass4114f972018-07-17 13:25:26 -060023 """A fake Entry object, usedfor testing
24
25 This supports an entry with a given size.
26 """
Simon Glass4ca8e042017-11-13 18:55:01 -070027 def __init__(self, contents_size):
28 self.contents_size = contents_size
Simon Glass303f62f2019-05-17 22:00:46 -060029 self.data = tools.GetBytes(ord('a'), contents_size)
Simon Glass4ca8e042017-11-13 18:55:01 -070030
31 def GetPath(self):
32 return 'entry_path'
33
Simon Glass4114f972018-07-17 13:25:26 -060034
Simon Glass8a6f56e2018-06-01 09:38:13 -060035class FakeSection:
Simon Glass4114f972018-07-17 13:25:26 -060036 """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 """
Simon Glass4ca8e042017-11-13 18:55:01 -070042 def __init__(self, sym_value=1):
43 self.sym_value = sym_value
44
45 def GetPath(self):
Simon Glass8a6f56e2018-06-01 09:38:13 -060046 return 'section_path'
Simon Glass4ca8e042017-11-13 18:55:01 -070047
48 def LookupSymbol(self, name, weak, msg):
Simon Glass4114f972018-07-17 13:25:26 -060049 """Fake implementation which returns the same value for all symbols"""
Simon Glass4ca8e042017-11-13 18:55:01 -070050 return self.sym_value
Simon Glass24ad3652017-11-13 18:54:54 -070051
Simon Glass4114f972018-07-17 13:25:26 -060052
Simon Glassf6290892019-08-24 07:22:53 -060053def 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',
Simon Glass93a806f2019-08-24 07:22:59 -060072 os.path.join(testdir, 'Makefile'), 'SRC=%s/' % testdir)
Simon Glassf6290892019-08-24 07:22:53 -060073
74
Simon Glass24ad3652017-11-13 18:54:54 -070075class TestElf(unittest.TestCase):
Simon Glass752e7552018-10-01 21:12:41 -060076 @classmethod
Simon Glass4affd4b2019-08-24 07:22:54 -060077 def setUpClass(cls):
78 cls._indir = tempfile.mkdtemp(prefix='elf.')
Simon Glass752e7552018-10-01 21:12:41 -060079 tools.SetInputDirs(['.'])
Simon Glass4affd4b2019-08-24 07:22:54 -060080 BuildElfTestFiles(cls._indir)
81
82 @classmethod
83 def tearDownClass(cls):
84 if cls._indir:
85 shutil.rmtree(cls._indir)
86
87 @classmethod
88 def ElfTestFile(cls, fname):
89 return os.path.join(cls._indir, fname)
Simon Glass752e7552018-10-01 21:12:41 -060090
Simon Glass24ad3652017-11-13 18:54:54 -070091 def testAllSymbols(self):
Simon Glass4114f972018-07-17 13:25:26 -060092 """Test that we can obtain a symbol from the ELF file"""
Simon Glass4affd4b2019-08-24 07:22:54 -060093 fname = self.ElfTestFile('u_boot_ucode_ptr')
Simon Glass24ad3652017-11-13 18:54:54 -070094 syms = elf.GetSymbols(fname, [])
95 self.assertIn('.ucode', syms)
96
97 def testRegexSymbols(self):
Simon Glass4114f972018-07-17 13:25:26 -060098 """Test that we can obtain from the ELF file by regular expression"""
Simon Glass4affd4b2019-08-24 07:22:54 -060099 fname = self.ElfTestFile('u_boot_ucode_ptr')
Simon Glass24ad3652017-11-13 18:54:54 -0700100 syms = elf.GetSymbols(fname, ['ucode'])
101 self.assertIn('.ucode', syms)
102 syms = elf.GetSymbols(fname, ['missing'])
103 self.assertNotIn('.ucode', syms)
104 syms = elf.GetSymbols(fname, ['missing', 'ucode'])
105 self.assertIn('.ucode', syms)
106
Simon Glass4ca8e042017-11-13 18:55:01 -0700107 def testMissingFile(self):
Simon Glass4114f972018-07-17 13:25:26 -0600108 """Test that a missing file is detected"""
Simon Glass4ca8e042017-11-13 18:55:01 -0700109 entry = FakeEntry(10)
Simon Glass8a6f56e2018-06-01 09:38:13 -0600110 section = FakeSection()
Simon Glass4ca8e042017-11-13 18:55:01 -0700111 with self.assertRaises(ValueError) as e:
Simon Glass8a6f56e2018-06-01 09:38:13 -0600112 syms = elf.LookupAndWriteSymbols('missing-file', entry, section)
Simon Glass4ca8e042017-11-13 18:55:01 -0700113 self.assertIn("Filename 'missing-file' not found in input path",
114 str(e.exception))
115
116 def testOutsideFile(self):
Simon Glass4114f972018-07-17 13:25:26 -0600117 """Test a symbol which extends outside the entry area is detected"""
Simon Glass4ca8e042017-11-13 18:55:01 -0700118 entry = FakeEntry(10)
Simon Glass8a6f56e2018-06-01 09:38:13 -0600119 section = FakeSection()
Simon Glass5d0c0262019-08-24 07:22:56 -0600120 elf_fname = self.ElfTestFile('u_boot_binman_syms')
Simon Glass4ca8e042017-11-13 18:55:01 -0700121 with self.assertRaises(ValueError) as e:
Simon Glass8a6f56e2018-06-01 09:38:13 -0600122 syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
Simon Glass4ca8e042017-11-13 18:55:01 -0700123 self.assertIn('entry_path has offset 4 (size 8) but the contents size '
124 'is a', str(e.exception))
125
126 def testMissingImageStart(self):
Simon Glass4114f972018-07-17 13:25:26 -0600127 """Test that we detect a missing __image_copy_start symbol
128
129 This is needed to mark the start of the image. Without it we cannot
130 locate the offset of a binman symbol within the image.
131 """
Simon Glass4ca8e042017-11-13 18:55:01 -0700132 entry = FakeEntry(10)
Simon Glass8a6f56e2018-06-01 09:38:13 -0600133 section = FakeSection()
Simon Glass46ea6912019-08-24 07:22:58 -0600134 elf_fname = self.ElfTestFile('u_boot_binman_syms_bad')
Simon Glass8a6f56e2018-06-01 09:38:13 -0600135 self.assertEqual(elf.LookupAndWriteSymbols(elf_fname, entry, section),
Simon Glass4ca8e042017-11-13 18:55:01 -0700136 None)
137
138 def testBadSymbolSize(self):
Simon Glass4114f972018-07-17 13:25:26 -0600139 """Test that an attempt to use an 8-bit symbol are detected
140
141 Only 32 and 64 bits are supported, since we need to store an offset
142 into the image.
143 """
Simon Glass4ca8e042017-11-13 18:55:01 -0700144 entry = FakeEntry(10)
Simon Glass8a6f56e2018-06-01 09:38:13 -0600145 section = FakeSection()
Simon Glassb8deb122019-08-24 07:22:57 -0600146 elf_fname =self.ElfTestFile('u_boot_binman_syms_size')
Simon Glass4ca8e042017-11-13 18:55:01 -0700147 with self.assertRaises(ValueError) as e:
Simon Glass8a6f56e2018-06-01 09:38:13 -0600148 syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
Simon Glass4ca8e042017-11-13 18:55:01 -0700149 self.assertIn('has size 1: only 4 and 8 are supported',
150 str(e.exception))
151
152 def testNoValue(self):
Simon Glass4114f972018-07-17 13:25:26 -0600153 """Test the case where we have no value for the symbol
154
155 This should produce -1 values for all thress symbols, taking up the
156 first 16 bytes of the image.
157 """
Simon Glass4ca8e042017-11-13 18:55:01 -0700158 entry = FakeEntry(20)
Simon Glass8a6f56e2018-06-01 09:38:13 -0600159 section = FakeSection(sym_value=None)
Simon Glass5d0c0262019-08-24 07:22:56 -0600160 elf_fname = self.ElfTestFile('u_boot_binman_syms')
Simon Glass8a6f56e2018-06-01 09:38:13 -0600161 syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
Simon Glass303f62f2019-05-17 22:00:46 -0600162 self.assertEqual(tools.GetBytes(255, 16) + tools.GetBytes(ord('a'), 4),
163 entry.data)
Simon Glass4ca8e042017-11-13 18:55:01 -0700164
165 def testDebug(self):
Simon Glass4114f972018-07-17 13:25:26 -0600166 """Check that enabling debug in the elf module produced debug output"""
Simon Glassb6dff4c2019-07-20 12:23:36 -0600167 try:
168 tout.Init(tout.DEBUG)
169 entry = FakeEntry(20)
170 section = FakeSection()
Simon Glass5d0c0262019-08-24 07:22:56 -0600171 elf_fname = self.ElfTestFile('u_boot_binman_syms')
Simon Glassb6dff4c2019-07-20 12:23:36 -0600172 with test_util.capture_sys_output() as (stdout, stderr):
173 syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
174 self.assertTrue(len(stdout.getvalue()) > 0)
175 finally:
176 tout.Init(tout.WARNING)
Simon Glass4ca8e042017-11-13 18:55:01 -0700177
Simon Glass4f379ea2019-07-08 13:18:34 -0600178 def testMakeElf(self):
179 """Test for the MakeElf function"""
180 outdir = tempfile.mkdtemp(prefix='elf.')
181 expected_text = b'1234'
182 expected_data = b'wxyz'
183 elf_fname = os.path.join(outdir, 'elf')
Simon Glassd349ada2019-08-24 07:22:45 -0600184 bin_fname = os.path.join(outdir, 'bin')
Simon Glass4f379ea2019-07-08 13:18:34 -0600185
186 # Make an Elf file and then convert it to a fkat binary file. This
187 # should produce the original data.
188 elf.MakeElf(elf_fname, expected_text, expected_data)
189 stdout = command.Output('objcopy', '-O', 'binary', elf_fname, bin_fname)
190 with open(bin_fname, 'rb') as fd:
191 data = fd.read()
192 self.assertEqual(expected_text + expected_data, data)
193 shutil.rmtree(outdir)
194
Simon Glass567b6822019-07-08 13:18:35 -0600195 def testDecodeElf(self):
196 """Test for the MakeElf function"""
197 if not elf.ELF_TOOLS:
198 self.skipTest('Python elftools not available')
199 outdir = tempfile.mkdtemp(prefix='elf.')
200 expected_text = b'1234'
201 expected_data = b'wxyz'
202 elf_fname = os.path.join(outdir, 'elf')
203 elf.MakeElf(elf_fname, expected_text, expected_data)
204 data = tools.ReadFile(elf_fname)
205
206 load = 0xfef20000
207 entry = load + 2
208 expected = expected_text + expected_data
209 self.assertEqual(elf.ElfInfo(expected, load, entry, len(expected)),
210 elf.DecodeElf(data, 0))
211 self.assertEqual(elf.ElfInfo(b'\0\0' + expected[2:],
212 load, entry, len(expected)),
213 elf.DecodeElf(data, load + 2))
Simon Glass4affd4b2019-08-24 07:22:54 -0600214 shutil.rmtree(outdir)
Simon Glass567b6822019-07-08 13:18:35 -0600215
Simon Glass4ca8e042017-11-13 18:55:01 -0700216
Simon Glass24ad3652017-11-13 18:54:54 -0700217if __name__ == '__main__':
218 unittest.main()