1# SPDX-License-Identifier: GPL-2.0+ 2# Copyright (c) 2016 Google, Inc 3# Written by Simon Glass <sjg@chromium.org> 4# 5# Handle various things related to ELF images 6# 7 8from collections import namedtuple, OrderedDict 9import command 10import os 11import re 12import struct 13 14import tools 15 16# This is enabled from control.py 17debug = False 18 19Symbol = namedtuple('Symbol', ['section', 'address', 'size', 'weak']) 20 21 22def GetSymbols(fname, patterns): 23 """Get the symbols from an ELF file 24 25 Args: 26 fname: Filename of the ELF file to read 27 patterns: List of regex patterns to search for, each a string 28 29 Returns: 30 None, if the file does not exist, or Dict: 31 key: Name of symbol 32 value: Hex value of symbol 33 """ 34 stdout = command.Output('objdump', '-t', fname, raise_on_error=False) 35 lines = stdout.splitlines() 36 if patterns: 37 re_syms = re.compile('|'.join(patterns)) 38 else: 39 re_syms = None 40 syms = {} 41 syms_started = False 42 for line in lines: 43 if not line or not syms_started: 44 if 'SYMBOL TABLE' in line: 45 syms_started = True 46 line = None # Otherwise code coverage complains about 'continue' 47 continue 48 if re_syms and not re_syms.search(line): 49 continue 50 51 space_pos = line.find(' ') 52 value, rest = line[:space_pos], line[space_pos + 1:] 53 flags = rest[:7] 54 parts = rest[7:].split() 55 section, size = parts[:2] 56 if len(parts) > 2: 57 name = parts[2] 58 syms[name] = Symbol(section, int(value, 16), int(size,16), 59 flags[1] == 'w') 60 61 # Sort dict by address 62 return OrderedDict(sorted(syms.iteritems(), key=lambda x: x[1].address)) 63 64def GetSymbolAddress(fname, sym_name): 65 """Get a value of a symbol from an ELF file 66 67 Args: 68 fname: Filename of the ELF file to read 69 patterns: List of regex patterns to search for, each a string 70 71 Returns: 72 Symbol value (as an integer) or None if not found 73 """ 74 syms = GetSymbols(fname, [sym_name]) 75 sym = syms.get(sym_name) 76 if not sym: 77 return None 78 return sym.address 79 80def LookupAndWriteSymbols(elf_fname, entry, section): 81 """Replace all symbols in an entry with their correct values 82 83 The entry contents is updated so that values for referenced symbols will be 84 visible at run time. This is done by finding out the symbols offsets in the 85 entry (using the ELF file) and replacing them with values from binman's data 86 structures. 87 88 Args: 89 elf_fname: Filename of ELF image containing the symbol information for 90 entry 91 entry: Entry to process 92 section: Section which can be used to lookup symbol values 93 """ 94 fname = tools.GetInputFilename(elf_fname) 95 syms = GetSymbols(fname, ['image', 'binman']) 96 if not syms: 97 return 98 base = syms.get('__image_copy_start') 99 if not base: 100 return 101 for name, sym in syms.iteritems(): 102 if name.startswith('_binman'): 103 msg = ("Section '%s': Symbol '%s'\n in entry '%s'" % 104 (section.GetPath(), name, entry.GetPath())) 105 offset = sym.address - base.address 106 if offset < 0 or offset + sym.size > entry.contents_size: 107 raise ValueError('%s has offset %x (size %x) but the contents ' 108 'size is %x' % (entry.GetPath(), offset, 109 sym.size, entry.contents_size)) 110 if sym.size == 4: 111 pack_string = '<I' 112 elif sym.size == 8: 113 pack_string = '<Q' 114 else: 115 raise ValueError('%s has size %d: only 4 and 8 are supported' % 116 (msg, sym.size)) 117 118 # Look up the symbol in our entry tables. 119 value = section.LookupSymbol(name, sym.weak, msg) 120 if value is not None: 121 value += base.address 122 else: 123 value = -1 124 pack_string = pack_string.lower() 125 value_bytes = struct.pack(pack_string, value) 126 if debug: 127 print('%s:\n insert %s, offset %x, value %x, length %d' % 128 (msg, name, offset, value, len(value_bytes))) 129 entry.data = (entry.data[:offset] + value_bytes + 130 entry.data[offset + sym.size:]) 131