1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2016 Google, Inc
3 # Written by Simon Glass <sjg@chromium.org>
5 # Handle various things related to ELF images
8 from __future__ import print_function
10 from collections import namedtuple, OrderedDict
20 # This is enabled from control.py
23 Symbol = namedtuple('Symbol', ['section', 'address', 'size', 'weak'])
26 def GetSymbols(fname, patterns):
27 """Get the symbols from an ELF file
30 fname: Filename of the ELF file to read
31 patterns: List of regex patterns to search for, each a string
34 None, if the file does not exist, or Dict:
36 value: Hex value of symbol
38 stdout = command.Output('objdump', '-t', fname, raise_on_error=False)
39 lines = stdout.splitlines()
41 re_syms = re.compile('|'.join(patterns))
47 if not line or not syms_started:
48 if 'SYMBOL TABLE' in line:
50 line = None # Otherwise code coverage complains about 'continue'
52 if re_syms and not re_syms.search(line):
55 space_pos = line.find(' ')
56 value, rest = line[:space_pos], line[space_pos + 1:]
58 parts = rest[7:].split()
59 section, size = parts[:2]
62 syms[name] = Symbol(section, int(value, 16), int(size,16),
65 # Sort dict by address
66 return OrderedDict(sorted(syms.items(), key=lambda x: x[1].address))
68 def GetSymbolAddress(fname, sym_name):
69 """Get a value of a symbol from an ELF file
72 fname: Filename of the ELF file to read
73 patterns: List of regex patterns to search for, each a string
76 Symbol value (as an integer) or None if not found
78 syms = GetSymbols(fname, [sym_name])
79 sym = syms.get(sym_name)
84 def LookupAndWriteSymbols(elf_fname, entry, section):
85 """Replace all symbols in an entry with their correct values
87 The entry contents is updated so that values for referenced symbols will be
88 visible at run time. This is done by finding out the symbols offsets in the
89 entry (using the ELF file) and replacing them with values from binman's data
93 elf_fname: Filename of ELF image containing the symbol information for
95 entry: Entry to process
96 section: Section which can be used to lookup symbol values
98 fname = tools.GetInputFilename(elf_fname)
99 syms = GetSymbols(fname, ['image', 'binman'])
102 base = syms.get('__image_copy_start')
105 for name, sym in syms.items():
106 if name.startswith('_binman'):
107 msg = ("Section '%s': Symbol '%s'\n in entry '%s'" %
108 (section.GetPath(), name, entry.GetPath()))
109 offset = sym.address - base.address
110 if offset < 0 or offset + sym.size > entry.contents_size:
111 raise ValueError('%s has offset %x (size %x) but the contents '
112 'size is %x' % (entry.GetPath(), offset,
113 sym.size, entry.contents_size))
119 raise ValueError('%s has size %d: only 4 and 8 are supported' %
122 # Look up the symbol in our entry tables.
123 value = section.LookupSymbol(name, sym.weak, msg)
124 if value is not None:
125 value += base.address
128 pack_string = pack_string.lower()
129 value_bytes = struct.pack(pack_string, value)
131 print('%s:\n insert %s, offset %x, value %x, length %d' %
132 (msg, name, offset, value, len(value_bytes)))
133 entry.data = (entry.data[:offset] + value_bytes +
134 entry.data[offset + sym.size:])
136 def MakeElf(elf_fname, text, data):
137 """Make an elf file with the given data in a single section
139 The output file has a several section including '.text' and '.data',
140 containing the info provided in arguments.
143 elf_fname: Output filename
144 text: Text (code) to put in the file's .text section
145 data: Data to put in the file's .data section
147 outdir = tempfile.mkdtemp(prefix='binman.elf.')
148 s_file = os.path.join(outdir, 'elf.S')
150 # Spilt the text into two parts so that we can make the entry point two
151 # bytes after the start of the text section
152 text_bytes1 = ['\t.byte\t%#x' % tools.ToByte(byte) for byte in text[:2]]
153 text_bytes2 = ['\t.byte\t%#x' % tools.ToByte(byte) for byte in text[2:]]
154 data_bytes = ['\t.byte\t%#x' % tools.ToByte(byte) for byte in data]
155 with open(s_file, 'w') as fd:
156 print('''/* Auto-generated C program to produce an ELF file for testing */
161 .type _start, @function
180 ''' % ('\n'.join(text_bytes1), '\n'.join(text_bytes2), '\n'.join(data_bytes)),
182 lds_file = os.path.join(outdir, 'elf.lds')
184 # Use a linker script to set the alignment and text address.
185 with open(lds_file, 'w') as fd:
186 print('''/* Auto-generated linker script to produce an ELF file for testing */
192 empty PT_LOAD FLAGS ( 6 ) ;
200 .text . : SUBALIGN(0)
214 .bss _bss_start (OVERLAY) : {
219 # -static: Avoid requiring any shared libraries
220 # -nostdlib: Don't link with C library
221 # -Wl,--build-id=none: Don't generate a build ID, so that we just get the
222 # text section at the start
223 # -m32: Build for 32-bit x86
224 # -T...: Specifies the link script, which sets the start address
225 stdout = command.Output('cc', '-static', '-nostdlib', '-Wl,--build-id=none',
226 '-m32','-T', lds_file, '-o', elf_fname, s_file)
227 shutil.rmtree(outdir)