blob: 82ea8c3857f8d98f50f3610d725586ffb03c49eb [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) 2016 Google, Inc
3# Written by Simon Glass <sjg@chromium.org>
4#
Simon Glass24ad3652017-11-13 18:54:54 -07005# Handle various things related to ELF images
6#
7
Simon Glass4f379ea2019-07-08 13:18:34 -06008from __future__ import print_function
9
Simon Glass24ad3652017-11-13 18:54:54 -070010from collections import namedtuple, OrderedDict
11import command
12import os
13import re
Simon Glass4f379ea2019-07-08 13:18:34 -060014import shutil
Simon Glass24ad3652017-11-13 18:54:54 -070015import struct
Simon Glass4f379ea2019-07-08 13:18:34 -060016import tempfile
Simon Glass24ad3652017-11-13 18:54:54 -070017
18import tools
19
Simon Glass075a45c2017-11-13 18:55:00 -070020# This is enabled from control.py
21debug = False
22
Simon Glass24ad3652017-11-13 18:54:54 -070023Symbol = namedtuple('Symbol', ['section', 'address', 'size', 'weak'])
24
Simon Glass24ad3652017-11-13 18:54:54 -070025
26def GetSymbols(fname, patterns):
27 """Get the symbols from an ELF file
28
29 Args:
30 fname: Filename of the ELF file to read
31 patterns: List of regex patterns to search for, each a string
32
33 Returns:
34 None, if the file does not exist, or Dict:
35 key: Name of symbol
36 value: Hex value of symbol
37 """
38 stdout = command.Output('objdump', '-t', fname, raise_on_error=False)
39 lines = stdout.splitlines()
40 if patterns:
41 re_syms = re.compile('|'.join(patterns))
42 else:
43 re_syms = None
44 syms = {}
45 syms_started = False
46 for line in lines:
47 if not line or not syms_started:
48 if 'SYMBOL TABLE' in line:
49 syms_started = True
50 line = None # Otherwise code coverage complains about 'continue'
51 continue
52 if re_syms and not re_syms.search(line):
53 continue
54
55 space_pos = line.find(' ')
56 value, rest = line[:space_pos], line[space_pos + 1:]
57 flags = rest[:7]
58 parts = rest[7:].split()
59 section, size = parts[:2]
60 if len(parts) > 2:
61 name = parts[2]
62 syms[name] = Symbol(section, int(value, 16), int(size,16),
63 flags[1] == 'w')
Simon Glasse6854aa2018-07-17 13:25:24 -060064
65 # Sort dict by address
Simon Glass5f3645b2019-05-14 15:53:41 -060066 return OrderedDict(sorted(syms.items(), key=lambda x: x[1].address))
Simon Glass24ad3652017-11-13 18:54:54 -070067
68def GetSymbolAddress(fname, sym_name):
69 """Get a value of a symbol from an ELF file
70
71 Args:
72 fname: Filename of the ELF file to read
73 patterns: List of regex patterns to search for, each a string
74
75 Returns:
76 Symbol value (as an integer) or None if not found
77 """
78 syms = GetSymbols(fname, [sym_name])
79 sym = syms.get(sym_name)
80 if not sym:
81 return None
82 return sym.address
Simon Glass4ca8e042017-11-13 18:55:01 -070083
Simon Glass8a6f56e2018-06-01 09:38:13 -060084def LookupAndWriteSymbols(elf_fname, entry, section):
Simon Glass4ca8e042017-11-13 18:55:01 -070085 """Replace all symbols in an entry with their correct values
86
87 The entry contents is updated so that values for referenced symbols will be
Simon Glasse8561af2018-08-01 15:22:37 -060088 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
90 structures.
Simon Glass4ca8e042017-11-13 18:55:01 -070091
92 Args:
93 elf_fname: Filename of ELF image containing the symbol information for
94 entry
95 entry: Entry to process
Simon Glass8a6f56e2018-06-01 09:38:13 -060096 section: Section which can be used to lookup symbol values
Simon Glass4ca8e042017-11-13 18:55:01 -070097 """
98 fname = tools.GetInputFilename(elf_fname)
99 syms = GetSymbols(fname, ['image', 'binman'])
100 if not syms:
101 return
102 base = syms.get('__image_copy_start')
103 if not base:
104 return
Simon Glass5f3645b2019-05-14 15:53:41 -0600105 for name, sym in syms.items():
Simon Glass4ca8e042017-11-13 18:55:01 -0700106 if name.startswith('_binman'):
Simon Glass8a6f56e2018-06-01 09:38:13 -0600107 msg = ("Section '%s': Symbol '%s'\n in entry '%s'" %
108 (section.GetPath(), name, entry.GetPath()))
Simon Glass4ca8e042017-11-13 18:55:01 -0700109 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))
114 if sym.size == 4:
115 pack_string = '<I'
116 elif sym.size == 8:
117 pack_string = '<Q'
118 else:
119 raise ValueError('%s has size %d: only 4 and 8 are supported' %
120 (msg, sym.size))
121
122 # Look up the symbol in our entry tables.
Simon Glass8a6f56e2018-06-01 09:38:13 -0600123 value = section.LookupSymbol(name, sym.weak, msg)
Simon Glass4ca8e042017-11-13 18:55:01 -0700124 if value is not None:
125 value += base.address
126 else:
127 value = -1
128 pack_string = pack_string.lower()
129 value_bytes = struct.pack(pack_string, value)
130 if debug:
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:])
Simon Glass4f379ea2019-07-08 13:18:34 -0600135
136def MakeElf(elf_fname, text, data):
137 """Make an elf file with the given data in a single section
138
139 The output file has a several section including '.text' and '.data',
140 containing the info provided in arguments.
141
142 Args:
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
146 """
147 outdir = tempfile.mkdtemp(prefix='binman.elf.')
148 s_file = os.path.join(outdir, 'elf.S')
149
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 */
157
158.section .text
159.code32
160.globl _start
161.type _start, @function
162%s
163_start:
164%s
165.ident "comment"
166
167.comm fred,8,4
168
169.section .empty
170.globl _empty
171_empty:
172.byte 1
173
174.globl ernie
175.data
176.type ernie, @object
177.size ernie, 4
178ernie:
179%s
180''' % ('\n'.join(text_bytes1), '\n'.join(text_bytes2), '\n'.join(data_bytes)),
181 file=fd)
182 lds_file = os.path.join(outdir, 'elf.lds')
183
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 */
187
188PHDRS
189{
190 text PT_LOAD ;
191 data PT_LOAD ;
192 empty PT_LOAD FLAGS ( 6 ) ;
193 note PT_NOTE ;
194}
195
196SECTIONS
197{
198 . = 0xfef20000;
199 ENTRY(_start)
200 .text . : SUBALIGN(0)
201 {
202 *(.text)
203 } :text
204 .data : {
205 *(.data)
206 } :data
207 _bss_start = .;
208 .empty : {
209 *(.empty)
210 } :empty
211 .note : {
212 *(.comment)
213 } :note
214 .bss _bss_start (OVERLAY) : {
215 *(.bss)
216 }
217}
218''', file=fd)
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)