1#!/usr/bin/env python3 2# 3# Copyright (c) 2018 Intel Corporation. 4# 5# SPDX-License-Identifier: Apache-2.0 6# 7 8""" 9This script will relocate .text, .rodata, .data and .bss sections from required files 10and places it in the required memory region. This memory region and file 11are given to this python script in the form of a string. 12 13Example of such a string would be:: 14 15 SRAM2:/home/xyz/zephyr/samples/hello_world/src/main.c,\ 16 SRAM1:/home/xyz/zephyr/samples/hello_world/src/main2.c 17 18To invoke this script:: 19 20 python3 gen_relocate_app.py -i input_string -o generated_linker -c generated_code 21 22Configuration that needs to be sent to the python script. 23 24- If the memory is like SRAM1/SRAM2/CCD/AON then place full object in 25 the sections 26- If the memory type is appended with _DATA / _TEXT/ _RODATA/ _BSS only the 27 selected memory is placed in the required memory region. Others are 28 ignored. 29 30Multiple regions can be appended together like SRAM2_DATA_BSS 31this will place data and bss inside SRAM2. 32""" 33 34 35import sys 36import argparse 37import os 38import glob 39import warnings 40from elftools.elf.elffile import ELFFile 41 42# This script will create linker comands for text,rodata data, bss section relocation 43 44PRINT_TEMPLATE = """ 45 KEEP(*({0})) 46""" 47 48SECTION_LOAD_MEMORY_SEQ = """ 49 __{0}_{1}_rom_start = LOADADDR(_{2}_{3}_SECTION_NAME); 50""" 51 52LOAD_ADDRESS_LOCATION_FLASH = """ 53#ifdef CONFIG_XIP 54GROUP_DATA_LINK_IN({0}, FLASH) 55#else 56GROUP_DATA_LINK_IN({0}, {0}) 57#endif 58""" 59LOAD_ADDRESS_LOCATION_BSS = "GROUP_LINK_IN({0})" 60 61MPU_RO_REGION_START = """ 62 63 _{0}_mpu_ro_region_start = ORIGIN({1}); 64 65""" 66 67MPU_RO_REGION_END = """ 68 69 _{0}_mpu_ro_region_end = .; 70 71""" 72 73# generic section creation format 74LINKER_SECTION_SEQ = """ 75 76/* Linker section for memory region {2} for {3} section */ 77 78 SECTION_PROLOGUE(_{2}_{3}_SECTION_NAME,,) 79 {{ 80 . = ALIGN(4); 81 {4} 82 . = ALIGN(4); 83 }} {5} 84 __{0}_{1}_end = .; 85 __{0}_{1}_start = ADDR(_{2}_{3}_SECTION_NAME); 86 __{0}_{1}_size = SIZEOF(_{2}_{3}_SECTION_NAME); 87""" 88 89LINKER_SECTION_SEQ_MPU = """ 90 91/* Linker section for memory region {2} for {3} section */ 92 93 SECTION_PROLOGUE(_{2}_{3}_SECTION_NAME,,) 94 {{ 95 __{0}_{1}_start = .; 96 {4} 97#if {6} 98 . = ALIGN({6}); 99#else 100 MPU_ALIGN(__{0}_{1}_size); 101#endif 102 __{0}_{1}_end = .; 103 }} {5} 104 __{0}_{1}_size = __{0}_{1}_end - __{0}_{1}_start; 105""" 106 107SOURCE_CODE_INCLUDES = """ 108/* Auto generated code. Do not modify.*/ 109#include <zephyr.h> 110#include <linker/linker-defs.h> 111#include <kernel_structs.h> 112#include <string.h> 113""" 114 115EXTERN_LINKER_VAR_DECLARATION = """ 116extern char __{0}_{1}_start[]; 117extern char __{0}_{1}_rom_start[]; 118extern char __{0}_{1}_size[]; 119""" 120 121 122DATA_COPY_FUNCTION = """ 123void data_copy_xip_relocation(void) 124{{ 125{0} 126}} 127""" 128 129BSS_ZEROING_FUNCTION = """ 130void bss_zeroing_relocation(void) 131{{ 132{0} 133}} 134""" 135 136MEMCPY_TEMPLATE = """ 137 (void)memcpy(&__{0}_{1}_start, &__{0}_{1}_rom_start, 138 (uint32_t) &__{0}_{1}_size); 139 140""" 141 142MEMSET_TEMPLATE = """ 143 (void)memset(&__{0}_bss_start, 0, 144 (uint32_t) &__{0}_bss_size); 145""" 146 147 148def find_sections(filename, full_list_of_sections): 149 with open(filename, 'rb') as obj_file_desc: 150 full_lib = ELFFile(obj_file_desc) 151 if not full_lib: 152 sys.exit("Error parsing file: " + filename) 153 154 sections = [x for x in full_lib.iter_sections()] 155 156 for section in sections: 157 158 if ".text." in section.name: 159 full_list_of_sections["text"].append(section.name) 160 161 if ".rodata." in section.name: 162 full_list_of_sections["rodata"].append(section.name) 163 164 if ".data." in section.name: 165 full_list_of_sections["data"].append(section.name) 166 167 if ".bss." in section.name: 168 full_list_of_sections["bss"].append(section.name) 169 170 # Common variables will be placed in the .bss section 171 # only after linking in the final executable. This "if" finds 172 # common symbols and warns the user of the problem. 173 # The solution to which is simply assigning a 0 to 174 # bss variable and it will go to the required place. 175 if ".symtab" in section.name: 176 symbols = [x for x in section.iter_symbols()] 177 for symbol in symbols: 178 if symbol.entry["st_shndx"] == 'SHN_COMMON': 179 warnings.warn("Common variable found. Move "+ 180 symbol.name + " to bss by assigning it to 0/NULL") 181 182 return full_list_of_sections 183 184 185def assign_to_correct_mem_region(memory_type, 186 full_list_of_sections, complete_list_of_sections): 187 all_regions = False 188 iteration_sections = {"text": False, "rodata": False, "data": False, "bss": False} 189 if "_TEXT" in memory_type: 190 iteration_sections["text"] = True 191 memory_type = memory_type.replace("_TEXT", "") 192 if "_RODATA" in memory_type: 193 iteration_sections["rodata"] = True 194 memory_type = memory_type.replace("_RODATA", "") 195 if "_DATA" in memory_type: 196 iteration_sections["data"] = True 197 memory_type = memory_type.replace("_DATA", "") 198 if "_BSS" in memory_type: 199 iteration_sections["bss"] = True 200 memory_type = memory_type.replace("_BSS", "") 201 if not (iteration_sections["data"] or iteration_sections["bss"] or 202 iteration_sections["text"] or iteration_sections["rodata"]): 203 all_regions = True 204 205 pos = memory_type.find('_') 206 if pos in range(len(memory_type)): 207 align_size = int(memory_type[pos+1:]) 208 memory_type = memory_type[:pos] 209 mpu_align[memory_type] = align_size 210 211 if memory_type in complete_list_of_sections: 212 for iter_sec in ["text", "rodata", "data", "bss"]: 213 if ((iteration_sections[iter_sec] or all_regions) and 214 full_list_of_sections[iter_sec] != []): 215 complete_list_of_sections[memory_type][iter_sec] += ( 216 full_list_of_sections[iter_sec]) 217 else: 218 # new memory type was found. in which case just assign the 219 # full_list_of_sections to the memorytype dict 220 tmp_list = {"text": [], "rodata": [], "data": [], "bss": []} 221 for iter_sec in ["text", "rodata", "data", "bss"]: 222 if ((iteration_sections[iter_sec] or all_regions) and 223 full_list_of_sections[iter_sec] != []): 224 tmp_list[iter_sec] = full_list_of_sections[iter_sec] 225 226 complete_list_of_sections[memory_type] = tmp_list 227 228 return complete_list_of_sections 229 230 231def print_linker_sections(list_sections): 232 print_string = '' 233 for section in sorted(list_sections): 234 print_string += PRINT_TEMPLATE.format(section) 235 return print_string 236 237 238def string_create_helper(region, memory_type, 239 full_list_of_sections, load_address_in_flash): 240 linker_string = '' 241 if load_address_in_flash: 242 load_address_string = LOAD_ADDRESS_LOCATION_FLASH.format(memory_type) 243 else: 244 load_address_string = LOAD_ADDRESS_LOCATION_BSS.format(memory_type) 245 if full_list_of_sections[region]: 246 # Create a complete list of funcs/ variables that goes in for this 247 # memory type 248 tmp = print_linker_sections(full_list_of_sections[region]) 249 if memory_type == 'SRAM' and region in {'data', 'bss'}: 250 linker_string += tmp 251 else: 252 if memory_type != 'SRAM' and region == 'rodata': 253 align_size = 0 254 if memory_type in mpu_align.keys(): 255 align_size = mpu_align[memory_type] 256 257 linker_string += LINKER_SECTION_SEQ_MPU.format(memory_type.lower(), region, memory_type.upper(), 258 region.upper(), tmp, load_address_string, align_size) 259 else: 260 if memory_type == 'SRAM' and region == 'text': 261 align_size = 0 262 linker_string += LINKER_SECTION_SEQ_MPU.format(memory_type.lower(), region, memory_type.upper(), 263 region.upper(), tmp, load_address_string, align_size) 264 else: 265 linker_string += LINKER_SECTION_SEQ.format(memory_type.lower(), region, memory_type.upper(), 266 region.upper(), tmp, load_address_string) 267 if load_address_in_flash: 268 linker_string += SECTION_LOAD_MEMORY_SEQ.format(memory_type.lower(), region, memory_type.upper(), 269 region.upper()) 270 return linker_string 271 272 273def generate_linker_script(linker_file, sram_data_linker_file, sram_bss_linker_file, complete_list_of_sections): 274 gen_string = '' 275 gen_string_sram_data = '' 276 gen_string_sram_bss = '' 277 278 for memory_type, full_list_of_sections in \ 279 sorted(complete_list_of_sections.items()): 280 281 if memory_type != "SRAM": 282 gen_string += MPU_RO_REGION_START.format(memory_type.lower(), memory_type.upper()) 283 gen_string += string_create_helper("text", memory_type, full_list_of_sections, 1) 284 gen_string += string_create_helper("rodata", memory_type, full_list_of_sections, 1) 285 if memory_type != "SRAM": 286 gen_string += MPU_RO_REGION_END.format(memory_type.lower()) 287 288 if memory_type == 'SRAM': 289 gen_string_sram_data += string_create_helper("data", memory_type, full_list_of_sections, 1) 290 gen_string_sram_bss += string_create_helper("bss", memory_type, full_list_of_sections, 0) 291 else: 292 gen_string += string_create_helper("data", memory_type, full_list_of_sections, 1) 293 gen_string += string_create_helper("bss", memory_type, full_list_of_sections, 0) 294 295 # finally writing to the linker file 296 with open(linker_file, "w") as file_desc: 297 file_desc.write(gen_string) 298 299 with open(sram_data_linker_file, "w") as file_desc: 300 file_desc.write(gen_string_sram_data) 301 302 with open(sram_bss_linker_file, "w") as file_desc: 303 file_desc.write(gen_string_sram_bss) 304 305 306def generate_memcpy_code(memory_type, full_list_of_sections, code_generation): 307 all_sections = True 308 generate_section = {"text": False, "rodata": False, "data": False, "bss": False} 309 for section_name in ["_TEXT", "_RODATA", "_DATA", "_BSS"]: 310 if section_name in memory_type: 311 generate_section[section_name.lower()[1:]] = True 312 memory_type = memory_type.replace(section_name, "") 313 all_sections = False 314 315 if all_sections: 316 generate_section["text"] = True 317 generate_section["rodata"] = True 318 generate_section["data"] = True 319 generate_section["bss"] = True 320 321 # add all the regions that needs to be copied on boot up 322 for mtype in ["text", "rodata", "data"]: 323 if memory_type == "SRAM" and mtype == "data": 324 continue 325 326 if full_list_of_sections[mtype] and generate_section[mtype]: 327 code_generation["copy_code"] += MEMCPY_TEMPLATE.format(memory_type.lower(), mtype) 328 code_generation["extern"] += EXTERN_LINKER_VAR_DECLARATION.format( 329 memory_type.lower(), mtype) 330 331 # add for all the bss data that needs to be zeored on boot up 332 if full_list_of_sections["bss"] and generate_section["bss"] and memory_type != "SRAM": 333 code_generation["zero_code"] += MEMSET_TEMPLATE.format(memory_type.lower()) 334 code_generation["extern"] += EXTERN_LINKER_VAR_DECLARATION.format( 335 memory_type.lower(), "bss") 336 337 return code_generation 338 339 340def dump_header_file(header_file, code_generation): 341 code_string = '' 342 # create a dummy void function if there is no code to generate for 343 # bss/data/text regions 344 345 code_string += code_generation["extern"] 346 347 if code_generation["copy_code"]: 348 code_string += DATA_COPY_FUNCTION.format(code_generation["copy_code"]) 349 else: 350 code_string += DATA_COPY_FUNCTION.format("void;") 351 if code_generation["zero_code"]: 352 code_string += BSS_ZEROING_FUNCTION.format(code_generation["zero_code"]) 353 else: 354 code_string += BSS_ZEROING_FUNCTION.format("return;") 355 356 with open(header_file, "w") as header_file_desc: 357 header_file_desc.write(SOURCE_CODE_INCLUDES) 358 header_file_desc.write(code_string) 359 360 361def parse_args(): 362 global args 363 parser = argparse.ArgumentParser( 364 description=__doc__, 365 formatter_class=argparse.RawDescriptionHelpFormatter) 366 parser.add_argument("-d", "--directory", required=True, 367 help="obj file's directory") 368 parser.add_argument("-i", "--input_rel_dict", required=True, 369 help="input src:memory type(sram2 or ccm or aon etc) string") 370 parser.add_argument("-o", "--output", required=False, help="Output ld file") 371 parser.add_argument("-s", "--output_sram_data", required=False, 372 help="Output sram data ld file") 373 parser.add_argument("-b", "--output_sram_bss", required=False, 374 help="Output sram bss ld file") 375 parser.add_argument("-c", "--output_code", required=False, 376 help="Output relocation code header file") 377 parser.add_argument("-v", "--verbose", action="count", default=0, 378 help="Verbose Output") 379 args = parser.parse_args() 380 381 382# return the absolute path for the object file. 383def get_obj_filename(searchpath, filename): 384 # get the object file name which is almost always pended with .obj 385 obj_filename = filename.split("/")[-1] + ".obj" 386 387 for dirpath, _, files in os.walk(searchpath): 388 for filename1 in files: 389 if filename1 == obj_filename: 390 if filename.split("/")[-2] in dirpath.split("/")[-1]: 391 fullname = os.path.join(dirpath, filename1) 392 return fullname 393 394 395# Create a dict with key as memory type and files as a list of values. 396def create_dict_wrt_mem(): 397 # need to support wild card * 398 rel_dict = dict() 399 if args.input_rel_dict == '': 400 sys.exit("Disable CONFIG_CODE_DATA_RELOCATION if no file needs relocation") 401 for line in args.input_rel_dict.split(';'): 402 mem_region, file_name = line.split(':', 1) 403 404 file_name_list = glob.glob(file_name) 405 if not file_name_list: 406 warnings.warn("File: "+file_name+" Not found") 407 continue 408 if mem_region == '': 409 continue 410 if args.verbose: 411 print("Memory region ", mem_region, " Selected for file:", file_name_list) 412 if mem_region in rel_dict: 413 rel_dict[mem_region].extend(file_name_list) 414 else: 415 rel_dict[mem_region] = file_name_list 416 417 return rel_dict 418 419 420def main(): 421 global mpu_align 422 mpu_align = {} 423 parse_args() 424 searchpath = args.directory 425 linker_file = args.output 426 sram_data_linker_file = args.output_sram_data 427 sram_bss_linker_file = args.output_sram_bss 428 rel_dict = create_dict_wrt_mem() 429 complete_list_of_sections = {} 430 431 # Create/or trucate file contents if it already exists 432 # raw = open(linker_file, "w") 433 434 # for each memory_type, create text/rodata/data/bss sections for all obj files 435 for memory_type, files in rel_dict.items(): 436 full_list_of_sections = {"text": [], "rodata": [], "data": [], "bss": []} 437 438 for filename in files: 439 obj_filename = get_obj_filename(searchpath, filename) 440 # the obj file wasn't found. Probably not compiled. 441 if not obj_filename: 442 continue 443 444 full_list_of_sections = find_sections(obj_filename, full_list_of_sections) 445 446 # cleanup and attach the sections to the memory type after cleanup. 447 complete_list_of_sections = assign_to_correct_mem_region(memory_type, 448 full_list_of_sections, 449 complete_list_of_sections) 450 451 generate_linker_script(linker_file, sram_data_linker_file, 452 sram_bss_linker_file, complete_list_of_sections) 453 454 code_generation = {"copy_code": '', "zero_code": '', "extern": ''} 455 for mem_type, list_of_sections in sorted(complete_list_of_sections.items()): 456 code_generation = generate_memcpy_code(mem_type, 457 list_of_sections, code_generation) 458 459 dump_header_file(args.output_code, code_generation) 460 461 462if __name__ == '__main__': 463 main() 464