relinker.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. #!/usr/bin/env python3
  2. #
  3. # SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  4. # SPDX-License-Identifier: Apache-2.0
  5. import logging
  6. import argparse
  7. import csv
  8. import os
  9. import subprocess
  10. import sys
  11. import re
  12. from io import StringIO
  13. import configuration
  14. sys.path.append(os.environ['IDF_PATH'] + '/tools/ldgen')
  15. sys.path.append(os.environ['IDF_PATH'] + '/tools/ldgen/ldgen')
  16. from entity import EntityDB
  17. espidf_objdump = None
  18. def lib_secs(lib, file, lib_path):
  19. new_env = os.environ.copy()
  20. new_env['LC_ALL'] = 'C'
  21. dump = StringIO(subprocess.check_output([espidf_objdump, '-h', lib_path], env=new_env).decode())
  22. dump.name = lib
  23. sections_infos = EntityDB()
  24. sections_infos.add_sections_info(dump)
  25. secs = sections_infos.get_sections(lib, file.split('.')[0] + '.c')
  26. if len(secs) == 0:
  27. secs = sections_infos.get_sections(lib, file.split('.')[0])
  28. if len(secs) == 0:
  29. raise ValueError('Failed to get sections from lib %s'%(lib_path))
  30. return secs
  31. def filter_secs(secs_a, secs_b):
  32. new_secs = list()
  33. for s_a in secs_a:
  34. for s_b in secs_b:
  35. if s_b in s_a:
  36. new_secs.append(s_a)
  37. return new_secs
  38. def strip_secs(secs_a, secs_b):
  39. secs = list(set(secs_a) - set(secs_b))
  40. secs.sort()
  41. return secs
  42. def func2sect(func):
  43. if ' ' in func:
  44. func_l = func.split(' ')
  45. else:
  46. func_l = list()
  47. func_l.append(func)
  48. secs = list()
  49. for l in func_l:
  50. if '.iram1.' not in l:
  51. secs.append('.literal.%s'%(l,))
  52. secs.append('.text.%s'%(l, ))
  53. else:
  54. secs.append(l)
  55. return secs
  56. class filter_c:
  57. def __init__(self, file):
  58. lines = open(file).read().splitlines()
  59. self.libs_desc = ''
  60. self.libs = ''
  61. for l in lines:
  62. if ') .iram1 EXCLUDE_FILE(*' in l and ') .iram1.*)' in l:
  63. desc = '\(EXCLUDE_FILE\((.*)\) .iram1 '
  64. self.libs_desc = re.search(desc, l)[1]
  65. self.libs = self.libs_desc.replace('*', '')
  66. return
  67. def match(self, lib):
  68. if lib in self.libs:
  69. print('Remove lib %s'%(lib))
  70. return True
  71. return False
  72. def add(self):
  73. return self.libs_desc
  74. class target_c:
  75. def __init__(self, lib, lib_path, file, fsecs):
  76. self.lib = lib
  77. self.file = file
  78. self.lib_path = lib_path
  79. self.fsecs = func2sect(fsecs)
  80. self.desc = '*%s:%s.*'%(lib, file.split('.')[0])
  81. secs = lib_secs(lib, file, lib_path)
  82. if '.iram1.' in self.fsecs[0]:
  83. self.secs = filter_secs(secs, ('.iram1.', ))
  84. else:
  85. self.secs = filter_secs(secs, ('.iram1.', '.text.', '.literal.'))
  86. self.isecs = strip_secs(self.secs, self.fsecs)
  87. def __str__(self):
  88. s = 'lib=%s\nfile=%s\lib_path=%s\ndesc=%s\nsecs=%s\nfsecs=%s\nisecs=%s\n'%(\
  89. self.lib, self.file, self.lib_path, self.desc, self.secs, self.fsecs,\
  90. self.isecs)
  91. return s
  92. class relink_c:
  93. def __init__(self, input, library_file, object_file, function_file, sdkconfig_file, missing_function_info):
  94. self.filter = filter_c(input)
  95. libraries = configuration.generator(library_file, object_file, function_file, sdkconfig_file, missing_function_info, espidf_objdump)
  96. self.targets = list()
  97. for i in libraries.libs:
  98. lib = libraries.libs[i]
  99. if self.filter.match(lib.name):
  100. continue
  101. for j in lib.objs:
  102. obj = lib.objs[j]
  103. self.targets.append(target_c(lib.name, lib.path, obj.name,
  104. ' '.join(obj.sections())))
  105. # for i in self.targets:
  106. # print(i)
  107. self.__transform__()
  108. def __transform__(self):
  109. iram1_exclude = list()
  110. iram1_include = list()
  111. flash_include = list()
  112. for t in self.targets:
  113. secs = filter_secs(t.fsecs, ('.iram1.', ))
  114. if len(secs) > 0:
  115. iram1_exclude.append(t.desc)
  116. secs = filter_secs(t.isecs, ('.iram1.', ))
  117. if len(secs) > 0:
  118. iram1_include.append(' %s(%s)'%(t.desc, ' '.join(secs)))
  119. secs = t.fsecs
  120. if len(secs) > 0:
  121. flash_include.append(' %s(%s)'%(t.desc, ' '.join(secs)))
  122. self.iram1_exclude = ' *(EXCLUDE_FILE(%s %s) .iram1.*) *(EXCLUDE_FILE(%s %s) .iram1)' % \
  123. (self.filter.add(), ' '.join(iram1_exclude), \
  124. self.filter.add(), ' '.join(iram1_exclude))
  125. self.iram1_include = '\n'.join(iram1_include)
  126. self.flash_include = '\n'.join(flash_include)
  127. logging.debug('IRAM1 Exclude: %s'%(self.iram1_exclude))
  128. logging.debug('IRAM1 Include: %s'%(self.iram1_include))
  129. logging.debug('Flash Include: %s'%(self.flash_include))
  130. def __replace__(self, lines):
  131. def is_iram_desc(l):
  132. if '*(.iram1 .iram1.*)' in l or (') .iram1 EXCLUDE_FILE(*' in l and ') .iram1.*)' in l):
  133. return True
  134. return False
  135. iram_start = False
  136. flash_done = False
  137. for i in range(0, len(lines) - 1):
  138. l = lines[i]
  139. if '.iram0.text :' in l:
  140. logging.debug('start to process .iram0.text')
  141. iram_start = True
  142. elif '.dram0.data :' in l:
  143. logging.debug('end to process .iram0.text')
  144. iram_start = False
  145. elif is_iram_desc(l):
  146. if iram_start:
  147. lines[i] = '%s\n%s\n'%(self.iram1_exclude, self.iram1_include)
  148. elif '(.stub .gnu.warning' in l:
  149. if not flash_done:
  150. lines[i] = '%s\n\n%s'%(self.flash_include, l)
  151. elif self.flash_include in l:
  152. flash_done = True
  153. else:
  154. if iram_start:
  155. new_l = self._replace_func(l)
  156. if new_l:
  157. lines[i] = new_l
  158. return lines
  159. def _replace_func(self, l):
  160. for t in self.targets:
  161. if t.desc in l:
  162. S = '.literal .literal.* .text .text.*'
  163. if S in l:
  164. if len(t.isecs) > 0:
  165. return l.replace(S, ' '.join(t.isecs))
  166. else:
  167. return ' '
  168. S = '%s(%s)'%(t.desc, ' '.join(t.fsecs))
  169. if S in l:
  170. return ' '
  171. replaced = False
  172. for s in t.fsecs:
  173. s2 = s + ' '
  174. if s2 in l:
  175. l = l.replace(s2, '')
  176. replaced = True
  177. s2 = s + ')'
  178. if s2 in l:
  179. l = l.replace(s2, ')')
  180. replaced = True
  181. if '( )' in l or '()' in l:
  182. return ' '
  183. if replaced:
  184. return l
  185. else:
  186. index = '*%s:(EXCLUDE_FILE'%(t.lib)
  187. if index in l and t.file.split('.')[0] not in l:
  188. for m in self.targets:
  189. index = '*%s:(EXCLUDE_FILE'%(m.lib)
  190. if index in l and m.file.split('.')[0] not in l:
  191. l = l.replace('EXCLUDE_FILE(', 'EXCLUDE_FILE(%s '%(m.desc))
  192. if len(m.isecs) > 0:
  193. l += '\n %s(%s)'%(m.desc, ' '.join(m.isecs))
  194. return l
  195. return False
  196. def save(self, input, output):
  197. lines = open(input).read().splitlines()
  198. lines = self.__replace__(lines)
  199. open(output, 'w+').write('\n'.join(lines))
  200. def main():
  201. argparser = argparse.ArgumentParser(description='Relinker script generator')
  202. argparser.add_argument(
  203. '--input', '-i',
  204. help='Linker template file',
  205. type=str)
  206. argparser.add_argument(
  207. '--output', '-o',
  208. help='Output linker script',
  209. type=str)
  210. argparser.add_argument(
  211. '--library', '-l',
  212. help='Library description directory',
  213. type=str)
  214. argparser.add_argument(
  215. '--object', '-b',
  216. help='Object description file',
  217. type=str)
  218. argparser.add_argument(
  219. '--function', '-f',
  220. help='Function description file',
  221. type=str)
  222. argparser.add_argument(
  223. '--sdkconfig', '-s',
  224. help='sdkconfig file',
  225. type=str)
  226. argparser.add_argument(
  227. '--objdump', '-g',
  228. help='GCC objdump command',
  229. type=str)
  230. argparser.add_argument(
  231. '--debug', '-d',
  232. help='Debug level(option is \'debug\')',
  233. default='no',
  234. type=str)
  235. argparser.add_argument(
  236. '--missing_function_info',
  237. help='Print error information instead of throwing exception when missing function',
  238. default=False,
  239. type=bool)
  240. args = argparser.parse_args()
  241. if args.debug == 'debug':
  242. logging.basicConfig(level=logging.DEBUG)
  243. logging.debug('input: %s'%(args.input))
  244. logging.debug('output: %s'%(args.output))
  245. logging.debug('library: %s'%(args.library))
  246. logging.debug('object: %s'%(args.object))
  247. logging.debug('function: %s'%(args.function))
  248. logging.debug('sdkconfig:%s'%(args.sdkconfig))
  249. logging.debug('objdump: %s'%(args.objdump))
  250. logging.debug('debug: %s'%(args.debug))
  251. logging.debug('missing_function_info: %s'%(args.missing_function_info))
  252. global espidf_objdump
  253. espidf_objdump = args.objdump
  254. relink = relink_c(args.input, args.library, args.object, args.function, args.sdkconfig, args.missing_function_info)
  255. relink.save(args.input, args.output)
  256. if __name__ == '__main__':
  257. main()