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 return syms 61 62def GetSymbolAddress(fname, sym_name): 63 """Get a value of a symbol from an ELF file 64 65 Args: 66 fname: Filename of the ELF file to read 67 patterns: List of regex patterns to search for, each a string 68 69 Returns: 70 Symbol value (as an integer) or None if not found 71 """ 72 syms = GetSymbols(fname, [sym_name]) 73 sym = syms.get(sym_name) 74 if not sym: 75 return None 76 return sym.address 77 78def LookupAndWriteSymbols(elf_fname, entry, image): 79 """Replace all symbols in an entry with their correct values 80 81 The entry contents is updated so that values for referenced symbols will be 82 visible at run time. This is done by finding out the symbols positions in 83 the entry (using the ELF file) and replacing them with values from binman's 84 data structures. 85 86 Args: 87 elf_fname: Filename of ELF image containing the symbol information for 88 entry 89 entry: Entry to process 90 image: Image which can be used to lookup symbol values 91 """ 92 fname = tools.GetInputFilename(elf_fname) 93 syms = GetSymbols(fname, ['image', 'binman']) 94 if not syms: 95 return 96 base = syms.get('__image_copy_start') 97 if not base: 98 return 99 for name, sym in syms.iteritems(): 100 if name.startswith('_binman'): 101 msg = ("Image '%s': Symbol '%s'\n in entry '%s'" % 102 (image.GetPath(), name, entry.GetPath())) 103 offset = sym.address - base.address 104 if offset < 0 or offset + sym.size > entry.contents_size: 105 raise ValueError('%s has offset %x (size %x) but the contents ' 106 'size is %x' % (entry.GetPath(), offset, 107 sym.size, entry.contents_size)) 108 if sym.size == 4: 109 pack_string = '<I' 110 elif sym.size == 8: 111 pack_string = '<Q' 112 else: 113 raise ValueError('%s has size %d: only 4 and 8 are supported' % 114 (msg, sym.size)) 115 116 # Look up the symbol in our entry tables. 117 value = image.LookupSymbol(name, sym.weak, msg) 118 if value is not None: 119 value += base.address 120 else: 121 value = -1 122 pack_string = pack_string.lower() 123 value_bytes = struct.pack(pack_string, value) 124 if debug: 125 print('%s:\n insert %s, offset %x, value %x, length %d' % 126 (msg, name, offset, value, len(value_bytes))) 127 entry.data = (entry.data[:offset] + value_bytes + 128 entry.data[offset + sym.size:]) 129