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