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