Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
__init__.py
00001 #!/usr/bin/env python 00002 """ 00003 mbed 00004 Copyright (c) 2017-2017 ARM Limited 00005 00006 Licensed under the Apache License, Version 2.0 (the "License"); 00007 you may not use this file except in compliance with the License. 00008 You may obtain a copy of the License at 00009 00010 http://www.apache.org/licenses/LICENSE-2.0 00011 00012 Unless required by applicable law or agreed to in writing, software 00013 distributed under the License is distributed on an "AS IS" BASIS, 00014 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00015 See the License for the specific language governing permissions and 00016 limitations under the License. 00017 """ 00018 00019 from __future__ import print_function 00020 import os 00021 import struct 00022 import binascii 00023 import argparse 00024 import logging 00025 try: 00026 from StringIO import StringIO 00027 except ImportError: 00028 from io import StringIO 00029 import jinja2 00030 from collections import namedtuple 00031 from itertools import count 00032 00033 from elftools.common.py3compat import bytes2str 00034 from elftools.elf.elffile import ELFFile 00035 from elftools.elf.sections import SymbolTableSection 00036 00037 logger = logging.getLogger(__name__) 00038 logger.addHandler(logging.NullHandler()) 00039 00040 00041 def main(): 00042 parser = argparse.ArgumentParser(description="Algo Extracter") 00043 parser.add_argument("input", help="File to extract flash algo from") 00044 parser.add_argument("template", default="py_blob.tmpl", 00045 help="Template to use") 00046 parser.add_argument("output", help="Output file") 00047 args = parser.parse_args() 00048 00049 with open(args.input, "rb") as file_handle: 00050 data = file_handle.read() 00051 algo = PackFlashAlgo(data) 00052 algo.process_template(args.template, args.output) 00053 00054 00055 class PackFlashAlgo (object): 00056 """ 00057 Class to wrap a flash algo 00058 00059 This class is intended to provide easy access to the information 00060 provided by a flash algorithm, such as symbols and the flash 00061 algorithm itself. 00062 """ 00063 00064 REQUIRED_SYMBOLS = set([ 00065 "Init", 00066 "UnInit", 00067 "EraseSector", 00068 "ProgramPage", 00069 ]) 00070 00071 EXTRA_SYMBOLS = set([ 00072 "BlankCheck", 00073 "EraseChip", 00074 "Verify", 00075 ]) 00076 00077 def __init__ (self, data): 00078 """Construct a PackFlashAlgorithm from an ElfFileSimple""" 00079 self.elf = ElfFileSimple(data) 00080 self.flash_info = PackFlashInfo(self.elf ) 00081 00082 self.flash_start = self.flash_info .start 00083 self.flash_size = self.flash_info .size 00084 self.page_size = self.flash_info .page_size 00085 self.sector_sizes = self.flash_info .sector_info_list 00086 00087 symbols = {} 00088 symbols.update(_extract_symbols(self.elf , self.REQUIRED_SYMBOLS )) 00089 symbols.update(_extract_symbols(self.elf , self.EXTRA_SYMBOLS , 00090 default=0xFFFFFFFF)) 00091 self.symbols = symbols 00092 00093 sections_to_find = ( 00094 ("PrgCode", "SHT_PROGBITS"), 00095 ("PrgData", "SHT_PROGBITS"), 00096 ("PrgData", "SHT_NOBITS"), 00097 ) 00098 00099 ro_rw_zi = _find_sections(self.elf , sections_to_find) 00100 ro_rw_zi = _algo_fill_zi_if_missing(ro_rw_zi) 00101 error_msg = _algo_check_for_section_problems(ro_rw_zi) 00102 if error_msg is not None: 00103 raise Exception(error_msg) 00104 00105 sect_ro, sect_rw, sect_zi = ro_rw_zi 00106 self.ro_start = sect_ro["sh_addr"] 00107 self.ro_size = sect_ro["sh_size"] 00108 self.rw_start = sect_rw["sh_addr"] 00109 self.rw_size = sect_rw["sh_size"] 00110 self.zi_start = sect_zi["sh_addr"] 00111 self.zi_size = sect_zi["sh_size"] 00112 00113 self.algo_data = _create_algo_bin(ro_rw_zi) 00114 00115 def format_algo_data (self, spaces, group_size, fmt): 00116 """" 00117 Return a string representing algo_data suitable for use in a template 00118 00119 The string is intended for use in a template. 00120 00121 :param spaces: The number of leading spaces for each line 00122 :param group_size: number of elements per line (element type 00123 depends of format) 00124 :param fmt: - format to create - can be either "hex" or "c" 00125 """ 00126 padding = " " * spaces 00127 if fmt == "hex": 00128 blob = binascii.b2a_hex(self.algo_data ) 00129 line_list = [] 00130 for i in range(0, len(blob), group_size): 00131 line_list.append('"' + blob[i:i + group_size] + '"') 00132 return ("\n" + padding).join(line_list) 00133 elif fmt == "c": 00134 blob = self.algo_data [:] 00135 pad_size = 0 if len(blob) % 4 == 0 else 4 - len(blob) % 4 00136 blob = blob + "\x00" * pad_size 00137 integer_list = struct.unpack("<" + "L" * (len(blob) / 4), blob) 00138 line_list = [] 00139 for pos in range(0, len(integer_list), group_size): 00140 group = ["0x%08x" % value for value in 00141 integer_list[pos:pos + group_size]] 00142 line_list.append(", ".join(group)) 00143 return (",\n" + padding).join(line_list) 00144 else: 00145 raise Exception("Unsupported format %s" % fmt) 00146 00147 def process_template (self, template_path, output_path, data_dict=None): 00148 """ 00149 Generate output from the supplied template 00150 00151 All the public methods and fields of this class can be accessed from 00152 the template via "algo". 00153 00154 :param template_path: Relative or absolute file path to the template 00155 :param output_path: Relative or absolute file path to create 00156 :param data_dict: Additional data to use when generating 00157 """ 00158 if data_dict is None: 00159 data_dict = {} 00160 else: 00161 assert isinstance(data_dict, dict) 00162 data_dict = dict(data_dict) 00163 assert "algo" not in data_dict, "algo already set by user data" 00164 data_dict["algo"] = self 00165 00166 with open(template_path) as file_handle: 00167 template_text = file_handle.read() 00168 00169 template = jinja2.Template(template_text) 00170 target_text = template.render(data_dict) 00171 00172 with open(output_path, "wb") as file_handle: 00173 file_handle.write(target_text) 00174 00175 00176 def _extract_symbols(simple_elf, symbols, default=None): 00177 """Fill 'symbols' field with required flash algo symbols""" 00178 to_ret = {} 00179 for symbol in symbols: 00180 if symbol not in simple_elf.symbols: 00181 if default is not None: 00182 to_ret[symbol] = default 00183 continue 00184 raise Exception("Missing symbol %s" % symbol) 00185 to_ret[symbol] = simple_elf.symbols[symbol].value 00186 return to_ret 00187 00188 00189 def _find_sections(elf, name_type_pairs): 00190 """Return a list of sections the same length and order of the input list""" 00191 sections = [None] * len(name_type_pairs) 00192 for section in elf.iter_sections(): 00193 section_name = bytes2str(section.name) 00194 section_type = section["sh_type"] 00195 for i, name_and_type in enumerate(name_type_pairs): 00196 if name_and_type != (section_name, section_type): 00197 continue 00198 if sections[i] is not None: 00199 raise Exception("Elf contains duplicate section %s attr %s" % 00200 (section_name, section_type)) 00201 sections[i] = section 00202 return sections 00203 00204 00205 def _algo_fill_zi_if_missing(ro_rw_zi): 00206 """Create an empty zi section if it is missing""" 00207 s_ro, s_rw, s_zi = ro_rw_zi 00208 if s_rw is None: 00209 return ro_rw_zi 00210 if s_zi is not None: 00211 return ro_rw_zi 00212 s_zi = { 00213 "sh_addr": s_rw["sh_addr"] + s_rw["sh_size"], 00214 "sh_size": 0 00215 } 00216 return s_ro, s_rw, s_zi 00217 00218 00219 def _algo_check_for_section_problems(ro_rw_zi): 00220 """Return a string describing any errors with the layout or None if good""" 00221 s_ro, s_rw, s_zi = ro_rw_zi 00222 if s_ro is None: 00223 return "RO section is missing" 00224 if s_rw is None: 00225 return "RW section is missing" 00226 if s_zi is None: 00227 return "ZI section is missing" 00228 if s_ro["sh_addr"] != 0: 00229 return "RO section does not start at address 0" 00230 if s_ro["sh_addr"] + s_ro["sh_size"] != s_rw["sh_addr"]: 00231 return "RW section does not follow RO section" 00232 if s_rw["sh_addr"] + s_rw["sh_size"] != s_zi["sh_addr"]: 00233 return "ZI section does not follow RW section" 00234 return None 00235 00236 00237 def _create_algo_bin(ro_rw_zi): 00238 """Create a binary blob of the flash algo which can execute from ram""" 00239 sect_ro, sect_rw, sect_zi = ro_rw_zi 00240 algo_size = sect_ro["sh_size"] + sect_rw["sh_size"] + sect_zi["sh_size"] 00241 algo_data = bytearray(algo_size) 00242 for section in (sect_ro, sect_rw): 00243 start = section["sh_addr"] 00244 size = section["sh_size"] 00245 data = section.data() 00246 assert len(data) == size 00247 algo_data[start:start + size] = data 00248 return algo_data 00249 00250 00251 class PackFlashInfo (object): 00252 """Wrapper class for the non-executable information in an FLM file""" 00253 00254 FLASH_DEVICE_STRUCT = "<H128sHLLLLBxxxLL" 00255 FLASH_SECTORS_STRUCT = "<LL" 00256 FLASH_SECTORS_STRUCT_SIZE = struct.calcsize(FLASH_SECTORS_STRUCT) 00257 SECTOR_END = 0xFFFFFFFF 00258 00259 def __init__(self, elf_simple): 00260 dev_info = elf_simple.symbols["FlashDevice"] 00261 info_start = dev_info.value 00262 info_size = struct.calcsize(self.FLASH_DEVICE_STRUCT ) 00263 data = elf_simple.read(info_start, info_size) 00264 values = struct.unpack(self.FLASH_DEVICE_STRUCT , data) 00265 00266 self.version = values[0] 00267 self.name = values[1].strip("\x00") 00268 self.type = values[2] 00269 self.start = values[3] 00270 self.size = values[4] 00271 self.page_size = values[5] 00272 self.value_empty = values[7] 00273 self.prog_timeout_ms = values[8] 00274 self.erase_timeout_ms = values[9] 00275 00276 sector_gen = self._sector_and_sz_itr (elf_simple, 00277 info_start + info_size) 00278 self.sector_info_list = list(sector_gen) 00279 00280 def __str__(self): 00281 desc = "" 00282 desc += "Flash Device:" + os.linesep 00283 desc += " name=%s" % self.name + os.linesep 00284 desc += " version=0x%x" % self.version + os.linesep 00285 desc += " type=%i" % self.type + os.linesep 00286 desc += " start=0x%x" % self.start + os.linesep 00287 desc += " size=0x%x" % self.size + os.linesep 00288 desc += " page_size=0x%x" % self.page_size + os.linesep 00289 desc += " value_empty=0x%x" % self.value_empty + os.linesep 00290 desc += " prog_timeout_ms=%i" % self.prog_timeout_ms + os.linesep 00291 desc += " erase_timeout_ms=%i" % self.erase_timeout_ms + os.linesep 00292 desc += " sectors:" + os.linesep 00293 for sector_start, sector_size in self.sector_info_list : 00294 desc += (" start=0x%x, size=0x%x" % 00295 (sector_start, sector_size) + os.linesep) 00296 return desc 00297 00298 def _sector_and_sz_itr(self, elf_simple, data_start): 00299 """Iterator which returns starting address and sector size""" 00300 for entry_start in count(data_start, self.FLASH_SECTORS_STRUCT_SIZE ): 00301 data = elf_simple.read(entry_start, self.FLASH_SECTORS_STRUCT_SIZE ) 00302 size, start = struct.unpack(self.FLASH_SECTORS_STRUCT , data) 00303 start_and_size = start, size 00304 if start_and_size == (self.SECTOR_END , self.SECTOR_END ): 00305 return 00306 yield start_and_size 00307 00308 00309 SymbolSimple = namedtuple("SymbolSimple", "name, value, size") 00310 00311 00312 class ElfFileSimple (ELFFile): 00313 """Wrapper for elf object which allows easy access to symbols and rom""" 00314 00315 def __init__ (self, data): 00316 """Construct a ElfFileSimple from bytes or a bytearray""" 00317 super(ElfFileSimple, self).__init__(StringIO(data)) 00318 self.symbols = self._read_symbol_table () 00319 00320 def _read_symbol_table(self): 00321 """Read the symbol table into the field "symbols" for easy use""" 00322 section = self.get_section_by_name(b".symtab") 00323 if not section: 00324 raise Exception("Missing symbol table") 00325 00326 if not isinstance(section, SymbolTableSection): 00327 raise Exception("Invalid symbol table section") 00328 00329 symbols = {} 00330 for symbol in section.iter_symbols(): 00331 name_str = bytes2str(symbol.name) 00332 if name_str in symbols: 00333 logging.debug("Duplicate symbol %s", name_str) 00334 symbols[name_str] = SymbolSimple(name_str, symbol["st_value"], 00335 symbol["st_size"]) 00336 return symbols 00337 00338 def read (self, addr, size): 00339 """Read program data from the elf file 00340 00341 :param addr: physical address (load address) to read from 00342 :param size: number of bytes to read 00343 :return: Requested data or None if address is unmapped 00344 """ 00345 for segment in self.iter_segments(): 00346 seg_addr = segment["p_paddr"] 00347 seg_size = min(segment["p_memsz"], segment["p_filesz"]) 00348 if addr >= seg_addr + seg_size: 00349 continue 00350 if addr + size <= seg_addr: 00351 continue 00352 # There is at least some overlap 00353 00354 if addr >= seg_addr and addr + size <= seg_addr + seg_size: 00355 # Region is fully contained 00356 data = segment.data() 00357 start = addr - seg_addr 00358 return data[start:start + size] 00359 00360 00361 if __name__ == '__main__': 00362 main()
Generated on Tue Jul 12 2022 12:21:33 by
