|  | #!/usr/bin/env python3 | 
|  | # | 
|  | # Copyright (c) 2018 Intel Corporation. | 
|  | # | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  | # | 
|  |  | 
|  | """ | 
|  | This script will relocate .text, .rodata, .data and .bss sections from required files | 
|  | and places it in the required memory region. This memory region and file | 
|  | are given to this python script in the form of a string. | 
|  |  | 
|  | Example of such a string would be:: | 
|  |  | 
|  | SRAM2:/home/xyz/zephyr/samples/hello_world/src/main.c,\ | 
|  | SRAM1:/home/xyz/zephyr/samples/hello_world/src/main2.c | 
|  |  | 
|  | To invoke this script:: | 
|  |  | 
|  | python3 gen_relocate_app.py -i input_string -o generated_linker -c generated_code | 
|  |  | 
|  | Configuration that needs to be sent to the python script. | 
|  |  | 
|  | - If the memory is like SRAM1/SRAM2/CCD/AON then place full object in | 
|  | the sections | 
|  | - If the memory type is appended with _DATA / _TEXT/ _RODATA/ _BSS only the | 
|  | selected memory is placed in the required memory region. Others are | 
|  | ignored. | 
|  |  | 
|  | Multiple regions can be appended together like SRAM2_DATA_BSS | 
|  | this will place data and bss inside SRAM2. | 
|  | """ | 
|  |  | 
|  |  | 
|  | import sys | 
|  | import argparse | 
|  | import os | 
|  | import glob | 
|  | import warnings | 
|  | from elftools.elf.elffile import ELFFile | 
|  |  | 
|  | # This script will create linker comands for text,rodata data, bss section relocation | 
|  |  | 
|  | PRINT_TEMPLATE = """ | 
|  | KEEP(*({0})) | 
|  | """ | 
|  |  | 
|  | SECTION_LOAD_MEMORY_SEQ = """ | 
|  | __{0}_{1}_rom_start = LOADADDR(_{2}_{3}_SECTION_NAME); | 
|  | """ | 
|  |  | 
|  | LOAD_ADDRESS_LOCATION_FLASH = """ | 
|  | #ifdef CONFIG_XIP | 
|  | GROUP_DATA_LINK_IN({0}, FLASH) | 
|  | #else | 
|  | GROUP_DATA_LINK_IN({0}, {0}) | 
|  | #endif | 
|  | """ | 
|  | LOAD_ADDRESS_LOCATION_BSS = "GROUP_LINK_IN({0})" | 
|  |  | 
|  | MPU_RO_REGION_START = """ | 
|  |  | 
|  | _{0}_mpu_ro_region_start = ORIGIN({1}); | 
|  |  | 
|  | """ | 
|  |  | 
|  | MPU_RO_REGION_END = """ | 
|  |  | 
|  | _{0}_mpu_ro_region_end = .; | 
|  |  | 
|  | """ | 
|  |  | 
|  | # generic section creation format | 
|  | LINKER_SECTION_SEQ = """ | 
|  |  | 
|  | /* Linker section for memory region {2} for  {3} section  */ | 
|  |  | 
|  | SECTION_PROLOGUE(_{2}_{3}_SECTION_NAME,,) | 
|  | {{ | 
|  | . = ALIGN(4); | 
|  | {4} | 
|  | . = ALIGN(4); | 
|  | }} {5} | 
|  | __{0}_{1}_end = .; | 
|  | __{0}_{1}_start = ADDR(_{2}_{3}_SECTION_NAME); | 
|  | __{0}_{1}_size = SIZEOF(_{2}_{3}_SECTION_NAME); | 
|  | """ | 
|  |  | 
|  | LINKER_SECTION_SEQ_MPU = """ | 
|  |  | 
|  | /* Linker section for memory region {2} for {3} section  */ | 
|  |  | 
|  | SECTION_PROLOGUE(_{2}_{3}_SECTION_NAME,,) | 
|  | {{ | 
|  | __{0}_{1}_start = .; | 
|  | {4} | 
|  | #if {6} | 
|  | . = ALIGN({6}); | 
|  | #else | 
|  | MPU_ALIGN(__{0}_{1}_size); | 
|  | #endif | 
|  | __{0}_{1}_end = .; | 
|  | }} {5} | 
|  | __{0}_{1}_size = __{0}_{1}_end - __{0}_{1}_start; | 
|  | """ | 
|  |  | 
|  | SOURCE_CODE_INCLUDES = """ | 
|  | /* Auto generated code. Do not modify.*/ | 
|  | #include <zephyr.h> | 
|  | #include <linker/linker-defs.h> | 
|  | #include <kernel_structs.h> | 
|  | #include <string.h> | 
|  | """ | 
|  |  | 
|  | EXTERN_LINKER_VAR_DECLARATION = """ | 
|  | extern char __{0}_{1}_start[]; | 
|  | extern char __{0}_{1}_rom_start[]; | 
|  | extern char __{0}_{1}_size[]; | 
|  | """ | 
|  |  | 
|  |  | 
|  | DATA_COPY_FUNCTION = """ | 
|  | void data_copy_xip_relocation(void) | 
|  | {{ | 
|  | {0} | 
|  | }} | 
|  | """ | 
|  |  | 
|  | BSS_ZEROING_FUNCTION = """ | 
|  | void bss_zeroing_relocation(void) | 
|  | {{ | 
|  | {0} | 
|  | }} | 
|  | """ | 
|  |  | 
|  | MEMCPY_TEMPLATE = """ | 
|  | (void)memcpy(&__{0}_{1}_start, &__{0}_{1}_rom_start, | 
|  | (uint32_t) &__{0}_{1}_size); | 
|  |  | 
|  | """ | 
|  |  | 
|  | MEMSET_TEMPLATE = """ | 
|  | (void)memset(&__{0}_bss_start, 0, | 
|  | (uint32_t) &__{0}_bss_size); | 
|  | """ | 
|  |  | 
|  |  | 
|  | def find_sections(filename, full_list_of_sections): | 
|  | with open(filename, 'rb') as obj_file_desc: | 
|  | full_lib = ELFFile(obj_file_desc) | 
|  | if not full_lib: | 
|  | sys.exit("Error parsing file: " + filename) | 
|  |  | 
|  | sections = [x for x in full_lib.iter_sections()] | 
|  |  | 
|  | for section in sections: | 
|  |  | 
|  | if ".text." in section.name: | 
|  | full_list_of_sections["text"].append(section.name) | 
|  |  | 
|  | if ".rodata." in section.name: | 
|  | full_list_of_sections["rodata"].append(section.name) | 
|  |  | 
|  | if ".data." in section.name: | 
|  | full_list_of_sections["data"].append(section.name) | 
|  |  | 
|  | if ".bss." in section.name: | 
|  | full_list_of_sections["bss"].append(section.name) | 
|  |  | 
|  | # Common variables will be placed in the .bss section | 
|  | # only after linking in the final executable. This "if" finds | 
|  | # common symbols and warns the user of the problem. | 
|  | # The solution to which is simply assigning a 0 to | 
|  | # bss variable and it will go to the required place. | 
|  | if ".symtab" in section.name: | 
|  | symbols = [x for x in section.iter_symbols()] | 
|  | for symbol in symbols: | 
|  | if symbol.entry["st_shndx"] == 'SHN_COMMON': | 
|  | warnings.warn("Common variable found. Move "+ | 
|  | symbol.name + " to bss by assigning it to 0/NULL") | 
|  |  | 
|  | return full_list_of_sections | 
|  |  | 
|  |  | 
|  | def assign_to_correct_mem_region(memory_type, | 
|  | full_list_of_sections, complete_list_of_sections): | 
|  | all_regions = False | 
|  | iteration_sections = {"text": False, "rodata": False, "data": False, "bss": False} | 
|  | if "_TEXT" in memory_type: | 
|  | iteration_sections["text"] = True | 
|  | memory_type = memory_type.replace("_TEXT", "") | 
|  | if "_RODATA" in memory_type: | 
|  | iteration_sections["rodata"] = True | 
|  | memory_type = memory_type.replace("_RODATA", "") | 
|  | if "_DATA" in memory_type: | 
|  | iteration_sections["data"] = True | 
|  | memory_type = memory_type.replace("_DATA", "") | 
|  | if "_BSS" in memory_type: | 
|  | iteration_sections["bss"] = True | 
|  | memory_type = memory_type.replace("_BSS", "") | 
|  | if not (iteration_sections["data"] or iteration_sections["bss"] or | 
|  | iteration_sections["text"] or iteration_sections["rodata"]): | 
|  | all_regions = True | 
|  |  | 
|  | pos = memory_type.find('_') | 
|  | if pos in range(len(memory_type)): | 
|  | align_size = int(memory_type[pos+1:]) | 
|  | memory_type = memory_type[:pos] | 
|  | mpu_align[memory_type] = align_size | 
|  |  | 
|  | if memory_type in complete_list_of_sections: | 
|  | for iter_sec in ["text", "rodata", "data", "bss"]: | 
|  | if ((iteration_sections[iter_sec] or all_regions) and | 
|  | full_list_of_sections[iter_sec] != []): | 
|  | complete_list_of_sections[memory_type][iter_sec] += ( | 
|  | full_list_of_sections[iter_sec]) | 
|  | else: | 
|  | # new memory type was found. in which case just assign the | 
|  | # full_list_of_sections to the memorytype dict | 
|  | tmp_list = {"text": [], "rodata": [], "data": [], "bss": []} | 
|  | for iter_sec in ["text", "rodata", "data", "bss"]: | 
|  | if ((iteration_sections[iter_sec] or all_regions) and | 
|  | full_list_of_sections[iter_sec] != []): | 
|  | tmp_list[iter_sec] = full_list_of_sections[iter_sec] | 
|  |  | 
|  | complete_list_of_sections[memory_type] = tmp_list | 
|  |  | 
|  | return complete_list_of_sections | 
|  |  | 
|  |  | 
|  | def print_linker_sections(list_sections): | 
|  | print_string = '' | 
|  | for section in sorted(list_sections): | 
|  | print_string += PRINT_TEMPLATE.format(section) | 
|  | return print_string | 
|  |  | 
|  |  | 
|  | def string_create_helper(region, memory_type, | 
|  | full_list_of_sections, load_address_in_flash): | 
|  | linker_string = '' | 
|  | if load_address_in_flash: | 
|  | load_address_string = LOAD_ADDRESS_LOCATION_FLASH.format(memory_type) | 
|  | else: | 
|  | load_address_string = LOAD_ADDRESS_LOCATION_BSS.format(memory_type) | 
|  | if full_list_of_sections[region]: | 
|  | # Create a complete list of funcs/ variables that goes in for this | 
|  | # memory type | 
|  | tmp = print_linker_sections(full_list_of_sections[region]) | 
|  | if memory_type == 'SRAM' and region in {'data', 'bss'}: | 
|  | linker_string += tmp | 
|  | else: | 
|  | if memory_type != 'SRAM' and region == 'rodata': | 
|  | align_size = 0 | 
|  | if memory_type in mpu_align.keys(): | 
|  | align_size = mpu_align[memory_type] | 
|  |  | 
|  | linker_string += LINKER_SECTION_SEQ_MPU.format(memory_type.lower(), region, memory_type.upper(), | 
|  | region.upper(), tmp, load_address_string, align_size) | 
|  | else: | 
|  | if memory_type == 'SRAM' and region == 'text': | 
|  | align_size = 0 | 
|  | linker_string += LINKER_SECTION_SEQ_MPU.format(memory_type.lower(), region, memory_type.upper(), | 
|  | region.upper(), tmp, load_address_string, align_size) | 
|  | else: | 
|  | linker_string += LINKER_SECTION_SEQ.format(memory_type.lower(), region, memory_type.upper(), | 
|  | region.upper(), tmp, load_address_string) | 
|  | if load_address_in_flash: | 
|  | linker_string += SECTION_LOAD_MEMORY_SEQ.format(memory_type.lower(), region, memory_type.upper(), | 
|  | region.upper()) | 
|  | return linker_string | 
|  |  | 
|  |  | 
|  | def generate_linker_script(linker_file, sram_data_linker_file, sram_bss_linker_file, complete_list_of_sections): | 
|  | gen_string = '' | 
|  | gen_string_sram_data = '' | 
|  | gen_string_sram_bss = '' | 
|  |  | 
|  | for memory_type, full_list_of_sections in \ | 
|  | sorted(complete_list_of_sections.items()): | 
|  |  | 
|  | if memory_type != "SRAM": | 
|  | gen_string += MPU_RO_REGION_START.format(memory_type.lower(), memory_type.upper()) | 
|  | gen_string += string_create_helper("text", memory_type, full_list_of_sections, 1) | 
|  | gen_string += string_create_helper("rodata", memory_type, full_list_of_sections, 1) | 
|  | if memory_type != "SRAM": | 
|  | gen_string += MPU_RO_REGION_END.format(memory_type.lower()) | 
|  |  | 
|  | if memory_type == 'SRAM': | 
|  | gen_string_sram_data += string_create_helper("data", memory_type, full_list_of_sections, 1) | 
|  | gen_string_sram_bss += string_create_helper("bss", memory_type, full_list_of_sections, 0) | 
|  | else: | 
|  | gen_string += string_create_helper("data", memory_type, full_list_of_sections, 1) | 
|  | gen_string += string_create_helper("bss", memory_type, full_list_of_sections, 0) | 
|  |  | 
|  | # finally writing to the linker file | 
|  | with open(linker_file, "a+") as file_desc: | 
|  | file_desc.write(gen_string) | 
|  |  | 
|  | with open(sram_data_linker_file, "a+") as file_desc: | 
|  | file_desc.write(gen_string_sram_data) | 
|  |  | 
|  | with open(sram_bss_linker_file, "a+") as file_desc: | 
|  | file_desc.write(gen_string_sram_bss) | 
|  |  | 
|  |  | 
|  | def generate_memcpy_code(memory_type, full_list_of_sections, code_generation): | 
|  | all_sections = True | 
|  | generate_section = {"text": False, "rodata": False, "data": False, "bss": False} | 
|  | for section_name in ["_TEXT", "_RODATA", "_DATA", "_BSS"]: | 
|  | if section_name in memory_type: | 
|  | generate_section[section_name.lower()[1:]] = True | 
|  | memory_type = memory_type.replace(section_name, "") | 
|  | all_sections = False | 
|  |  | 
|  | if all_sections: | 
|  | generate_section["text"] = True | 
|  | generate_section["rodata"] = True | 
|  | generate_section["data"] = True | 
|  | generate_section["bss"] = True | 
|  |  | 
|  | # add all the regions that needs to be copied on boot up | 
|  | for mtype in ["text", "rodata", "data"]: | 
|  | if memory_type == "SRAM" and mtype == "data": | 
|  | continue | 
|  |  | 
|  | if full_list_of_sections[mtype] and generate_section[mtype]: | 
|  | code_generation["copy_code"] += MEMCPY_TEMPLATE.format(memory_type.lower(), mtype) | 
|  | code_generation["extern"] += EXTERN_LINKER_VAR_DECLARATION.format( | 
|  | memory_type.lower(), mtype) | 
|  |  | 
|  | # add for all the bss data that needs to be zeored on boot up | 
|  | if full_list_of_sections["bss"] and generate_section["bss"] and memory_type != "SRAM": | 
|  | code_generation["zero_code"] += MEMSET_TEMPLATE.format(memory_type.lower()) | 
|  | code_generation["extern"] += EXTERN_LINKER_VAR_DECLARATION.format( | 
|  | memory_type.lower(), "bss") | 
|  |  | 
|  | return code_generation | 
|  |  | 
|  |  | 
|  | def dump_header_file(header_file, code_generation): | 
|  | code_string = '' | 
|  | # create a dummy void function if there is no code to generate for | 
|  | # bss/data/text regions | 
|  |  | 
|  | code_string += code_generation["extern"] | 
|  |  | 
|  | if code_generation["copy_code"]: | 
|  | code_string += DATA_COPY_FUNCTION.format(code_generation["copy_code"]) | 
|  | else: | 
|  | code_string += DATA_COPY_FUNCTION.format("void;") | 
|  | if code_generation["zero_code"]: | 
|  | code_string += BSS_ZEROING_FUNCTION.format(code_generation["zero_code"]) | 
|  | else: | 
|  | code_string += BSS_ZEROING_FUNCTION.format("return;") | 
|  |  | 
|  | with open(header_file, "w") as header_file_desc: | 
|  | header_file_desc.write(SOURCE_CODE_INCLUDES) | 
|  | header_file_desc.write(code_string) | 
|  |  | 
|  |  | 
|  | def parse_args(): | 
|  | global args | 
|  | parser = argparse.ArgumentParser( | 
|  | description=__doc__, | 
|  | formatter_class=argparse.RawDescriptionHelpFormatter) | 
|  | parser.add_argument("-d", "--directory", required=True, | 
|  | help="obj file's directory") | 
|  | parser.add_argument("-i", "--input_rel_dict", required=True, | 
|  | help="input src:memory type(sram2 or ccm or aon etc) string") | 
|  | parser.add_argument("-o", "--output", required=False, help="Output ld file") | 
|  | parser.add_argument("-s", "--output_sram_data", required=False, | 
|  | help="Output sram data ld file") | 
|  | parser.add_argument("-b", "--output_sram_bss", required=False, | 
|  | help="Output sram bss ld file") | 
|  | parser.add_argument("-c", "--output_code", required=False, | 
|  | help="Output relocation code header file") | 
|  | parser.add_argument("-v", "--verbose", action="count", default=0, | 
|  | help="Verbose Output") | 
|  | args = parser.parse_args() | 
|  |  | 
|  |  | 
|  | # return the absolute path for the object file. | 
|  | def get_obj_filename(searchpath, filename): | 
|  | # get the object file name which is almost always pended with .obj | 
|  | obj_filename = filename.split("/")[-1] + ".obj" | 
|  |  | 
|  | for dirpath, _, files in os.walk(searchpath): | 
|  | for filename1 in files: | 
|  | if filename1 == obj_filename: | 
|  | if filename.split("/")[-2] in dirpath.split("/")[-1]: | 
|  | fullname = os.path.join(dirpath, filename1) | 
|  | return fullname | 
|  |  | 
|  |  | 
|  | # Create a dict with key as memory type and files as a list of values. | 
|  | def create_dict_wrt_mem(): | 
|  | # need to support wild card * | 
|  | rel_dict = dict() | 
|  | if args.input_rel_dict == '': | 
|  | sys.exit("Disable CONFIG_CODE_DATA_RELOCATION if no file needs relocation") | 
|  | for line in args.input_rel_dict.split(';'): | 
|  | mem_region, file_name = line.split(':', 1) | 
|  |  | 
|  | file_name_list = glob.glob(file_name) | 
|  | if not file_name_list: | 
|  | warnings.warn("File: "+file_name+" Not found") | 
|  | continue | 
|  | if mem_region == '': | 
|  | continue | 
|  | if args.verbose: | 
|  | print("Memory region ", mem_region, " Selected for file:", file_name_list) | 
|  | if mem_region in rel_dict: | 
|  | rel_dict[mem_region].extend(file_name_list) | 
|  | else: | 
|  | rel_dict[mem_region] = file_name_list | 
|  |  | 
|  | return rel_dict | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | global mpu_align | 
|  | mpu_align = {} | 
|  | parse_args() | 
|  | searchpath = args.directory | 
|  | linker_file = args.output | 
|  | sram_data_linker_file = args.output_sram_data | 
|  | sram_bss_linker_file = args.output_sram_bss | 
|  | rel_dict = create_dict_wrt_mem() | 
|  | complete_list_of_sections = {} | 
|  |  | 
|  | # Create/or trucate file contents if it already exists | 
|  | # raw = open(linker_file, "w") | 
|  |  | 
|  | # for each memory_type, create text/rodata/data/bss sections for all obj files | 
|  | for memory_type, files in rel_dict.items(): | 
|  | full_list_of_sections = {"text": [], "rodata": [], "data": [], "bss": []} | 
|  |  | 
|  | for filename in files: | 
|  | obj_filename = get_obj_filename(searchpath, filename) | 
|  | # the obj file wasn't found. Probably not compiled. | 
|  | if not obj_filename: | 
|  | continue | 
|  |  | 
|  | full_list_of_sections = find_sections(obj_filename, full_list_of_sections) | 
|  |  | 
|  | # cleanup and attach the sections to the memory type after cleanup. | 
|  | complete_list_of_sections = assign_to_correct_mem_region(memory_type, | 
|  | full_list_of_sections, | 
|  | complete_list_of_sections) | 
|  |  | 
|  | generate_linker_script(linker_file, sram_data_linker_file, | 
|  | sram_bss_linker_file, complete_list_of_sections) | 
|  |  | 
|  | code_generation = {"copy_code": '', "zero_code": '', "extern": ''} | 
|  | for mem_type, list_of_sections in sorted(complete_list_of_sections.items()): | 
|  | code_generation = generate_memcpy_code(mem_type, | 
|  | list_of_sections, code_generation) | 
|  |  | 
|  | dump_header_file(args.output_code, code_generation) | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | main() |