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 7import os 8import shutil 9import sys 10import tempfile 11import unittest 12 13from binman import elf 14from patman import command 15from patman import test_util 16from patman import tools 17from patman import tout 18 19binman_dir = os.path.dirname(os.path.realpath(sys.argv[0])) 20 21 22class 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 35class 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 LookupImageSymbol(self, name, weak, msg, base_addr): 49 """Fake implementation which returns the same value for all symbols""" 50 return self.sym_value 51 52 def GetImage(self): 53 return self 54 55def BuildElfTestFiles(target_dir): 56 """Build ELF files used for testing in binman 57 58 This compiles and links the test files into the specified directory. It the 59 Makefile and source files in the binman test/ directory. 60 61 Args: 62 target_dir: Directory to put the files into 63 """ 64 if not os.path.exists(target_dir): 65 os.mkdir(target_dir) 66 testdir = os.path.join(binman_dir, 'test') 67 68 # If binman is involved from the main U-Boot Makefile the -r and -R 69 # flags are set in MAKEFLAGS. This prevents this Makefile from working 70 # correctly. So drop any make flags here. 71 if 'MAKEFLAGS' in os.environ: 72 del os.environ['MAKEFLAGS'] 73 tools.Run('make', '-C', target_dir, '-f', 74 os.path.join(testdir, 'Makefile'), 'SRC=%s/' % testdir) 75 76 77class 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 = self.ElfTestFile('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 =self.ElfTestFile('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(24) 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, 20) + 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 objcopy, args = tools.GetTargetCompileTool('objcopy') 192 args += ['-O', 'binary', elf_fname, bin_fname] 193 stdout = command.Output(objcopy, *args) 194 with open(bin_fname, 'rb') as fd: 195 data = fd.read() 196 self.assertEqual(expected_text + expected_data, data) 197 shutil.rmtree(outdir) 198 199 def testDecodeElf(self): 200 """Test for the MakeElf function""" 201 if not elf.ELF_TOOLS: 202 self.skipTest('Python elftools not available') 203 outdir = tempfile.mkdtemp(prefix='elf.') 204 expected_text = b'1234' 205 expected_data = b'wxyz' 206 elf_fname = os.path.join(outdir, 'elf') 207 elf.MakeElf(elf_fname, expected_text, expected_data) 208 data = tools.ReadFile(elf_fname) 209 210 load = 0xfef20000 211 entry = load + 2 212 expected = expected_text + expected_data 213 self.assertEqual(elf.ElfInfo(expected, load, entry, len(expected)), 214 elf.DecodeElf(data, 0)) 215 self.assertEqual(elf.ElfInfo(b'\0\0' + expected[2:], 216 load, entry, len(expected)), 217 elf.DecodeElf(data, load + 2)) 218 shutil.rmtree(outdir) 219 220 221if __name__ == '__main__': 222 unittest.main() 223