generation.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. #
  2. # Copyright 2021 Espressif Systems (Shanghai) CO LTD
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. #
  16. import collections
  17. import fnmatch
  18. import itertools
  19. from collections import namedtuple
  20. from entity import Entity
  21. from fragments import Mapping, Scheme, Sections
  22. from ldgen_common import LdGenFailure
  23. from output_commands import InputSectionDesc
  24. class RuleNode():
  25. class Section():
  26. def __init__(self, target, exclusions, explicit=False):
  27. self.target = target
  28. self.exclusions = set(exclusions)
  29. # Indicate whether this node has been created explicitly from a mapping,
  30. # or simply just to create a path to the explicitly created node.
  31. #
  32. # For example,
  33. #
  34. # lib.a
  35. # obj:sym (scheme)
  36. #
  37. # Nodes for lib.a and obj will be created, but only the node for
  38. # sym will have been created explicitly.
  39. #
  40. # This is used in deciding whether or not an output command should
  41. # be emitted for this node, or for exclusion rule generation.
  42. self.explicit = explicit
  43. def __init__(self, parent, name, sections):
  44. self.children = []
  45. self.parent = parent
  46. self.name = name
  47. self.child_node = None
  48. self.entity = None
  49. self.sections = dict()
  50. # A node inherits the section -> target entries from
  51. # its parent. This is to simplify logic, avoiding
  52. # going up the parental chain to try a 'basis' rule
  53. # in creating exclusions. This relies on the fact that
  54. # the mappings must be inserted from least to most specific.
  55. # This sort is done in generate_rules().
  56. if sections:
  57. for (s, v) in sections.items():
  58. self.sections[s] = RuleNode.Section(v.target, [], [])
  59. def add_exclusion(self, sections, exclusion):
  60. self.sections[sections].exclusions.add(exclusion)
  61. # Recursively create exclusions in parents
  62. if self.parent:
  63. self.exclude_from_parent(sections)
  64. def add_sections(self, sections, target):
  65. try:
  66. _sections = self.sections[sections]
  67. if not _sections.explicit:
  68. _sections.target = target
  69. _sections.explicit = True
  70. else:
  71. if target != _sections.target:
  72. raise GenerationException('Sections mapped to multiple targets')
  73. except KeyError:
  74. self.sections[sections] = RuleNode.Section(target, [], True)
  75. def exclude_from_parent(self, sections):
  76. self.parent.add_exclusion(sections, self.entity)
  77. def add_child(self, entity):
  78. child_specificity = self.entity.specificity.value + 1
  79. assert(child_specificity <= Entity.Specificity.SYMBOL.value)
  80. name = entity[Entity.Specificity(child_specificity)]
  81. assert(name and name != Entity.ALL)
  82. child = [c for c in self.children if c.name == name]
  83. assert(len(child) <= 1)
  84. if not child:
  85. child = self.child_node(self, name, self.sections)
  86. self.children.append(child)
  87. else:
  88. child = child[0]
  89. return child
  90. def get_output_commands(self):
  91. commands = collections.defaultdict(list)
  92. def process_commands(cmds):
  93. for (target, commands_list) in cmds.items():
  94. commands[target].extend(commands_list)
  95. # Process the commands generated from this node
  96. node_commands = self.get_node_output_commands()
  97. process_commands(node_commands)
  98. # Process the commands generated from this node's children
  99. # recursively
  100. for child in sorted(self.children, key=lambda c: c.name):
  101. children_commands = child.get_output_commands()
  102. process_commands(children_commands)
  103. return commands
  104. def add_node_child(self, entity, sections, target, sections_db):
  105. child = self.add_child(entity)
  106. child.insert(entity, sections, target, sections_db)
  107. def get_node_output_commands(self):
  108. commands = collections.defaultdict(list)
  109. for sections in self.get_section_keys():
  110. info = self.sections[sections]
  111. if info.exclusions or info.explicit:
  112. command = InputSectionDesc(self.entity, sections, info.exclusions)
  113. commands[info.target].append(command)
  114. return commands
  115. def insert(self, entity, sections, target, sections_db):
  116. if self.entity.specificity == entity.specificity:
  117. if self.parent.sections[sections].target != target:
  118. self.add_sections(sections, target)
  119. self.exclude_from_parent(sections)
  120. else:
  121. self.add_node_child(entity, sections, target, sections_db)
  122. def get_section_keys(self):
  123. return sorted(self.sections.keys(), key=' '.join)
  124. class SymbolNode(RuleNode):
  125. def __init__(self, parent, name, sections):
  126. RuleNode.__init__(self, parent, name, sections)
  127. self.entity = Entity(self.parent.parent.name, self.parent.name, self.name)
  128. def insert(self, entity, sections, target, sections_db):
  129. self.add_sections(sections, target)
  130. def get_node_output_commands(self):
  131. commands = collections.defaultdict(list)
  132. for sections in self.get_section_keys():
  133. info = self.sections[sections]
  134. if info.explicit:
  135. command = InputSectionDesc(Entity(self.parent.parent.name, self.parent.name), sections, [])
  136. commands[info.target].append(command)
  137. return commands
  138. class ObjectNode(RuleNode):
  139. def __init__(self, parent, name, sections):
  140. RuleNode.__init__(self, parent, name, sections)
  141. self.child_node = SymbolNode
  142. self.expanded_sections = dict()
  143. self.entity = Entity(self.parent.name, self.name)
  144. def add_node_child(self, entity, sections, target, sections_db):
  145. if self.sections[sections].target != target:
  146. symbol = entity.symbol
  147. match_sections = None
  148. obj_sections = sections_db.get_sections(self.parent.name, self.name)
  149. try:
  150. match_sections = self.expanded_sections[sections]
  151. except KeyError:
  152. match_sections = []
  153. for s in sections:
  154. match_sections.extend(fnmatch.filter(obj_sections, s))
  155. if match_sections:
  156. remove_sections = [s.replace('.*', '.%s' % symbol) for s in sections if '.*' in s]
  157. filtered_sections = [s for s in match_sections if s not in remove_sections]
  158. if set(filtered_sections) != set(match_sections): # some sections removed
  159. child = self.add_child(entity)
  160. child.insert(entity, frozenset(remove_sections), target, obj_sections)
  161. # Remember the result for node command generation
  162. self.expanded_sections[sections] = filtered_sections
  163. self.exclude_from_parent(sections)
  164. def get_node_output_commands(self):
  165. commands = collections.defaultdict(list)
  166. for sections in self.get_section_keys():
  167. info = self.sections[sections]
  168. try:
  169. match_sections = self.expanded_sections[sections]
  170. except KeyError:
  171. match_sections = []
  172. if match_sections or info.explicit:
  173. command_sections = match_sections if match_sections else sections
  174. command = InputSectionDesc(self.entity, command_sections, [])
  175. commands[info.target].append(command)
  176. return commands
  177. def exclude_from_parent(self, sections):
  178. # Check if there is an explicit emmission for the parent node, which is an archive node.
  179. # If there is, make the exclusion there. If not, make the exclusion on the root node.
  180. # This is to avoid emitting unecessary command and exclusions for the archive node and
  181. # from the root node, respectively.
  182. if self.parent.sections[sections].explicit:
  183. self.parent.add_exclusion(sections, self.entity)
  184. else:
  185. self.parent.parent.add_exclusion(sections, self.entity)
  186. class ArchiveNode(RuleNode):
  187. def __init__(self, parent, name, sections):
  188. RuleNode.__init__(self, parent, name, sections)
  189. self.child_node = ObjectNode
  190. self.entity = Entity(self.name)
  191. class RootNode(RuleNode):
  192. def __init__(self):
  193. RuleNode.__init__(self, None, Entity.ALL, None)
  194. self.child_node = ArchiveNode
  195. self.entity = Entity('*')
  196. def insert(self, entity, sections, target, sections_db):
  197. if self.entity.specificity == entity.specificity:
  198. self.add_sections(sections, target)
  199. else:
  200. self.add_node_child(entity, sections, target, sections_db)
  201. class Generation:
  202. """
  203. Implements generation of placement rules based on collected sections, scheme and mapping fragment.
  204. """
  205. DEFAULT_SCHEME = 'default'
  206. # Processed mapping, scheme and section entries
  207. EntityMapping = namedtuple('EntityMapping', 'entity sections_group target')
  208. def __init__(self, check_mappings=False, check_mapping_exceptions=None):
  209. self.schemes = {}
  210. self.sections = {}
  211. self.mappings = {}
  212. self.check_mappings = check_mappings
  213. if check_mapping_exceptions:
  214. self.check_mapping_exceptions = check_mapping_exceptions
  215. else:
  216. self.check_mapping_exceptions = []
  217. def _build_scheme_dictionary(self):
  218. scheme_dictionary = collections.defaultdict(dict)
  219. # Collect sections into buckets based on target name
  220. for scheme in self.schemes.values():
  221. sections_bucket = collections.defaultdict(list)
  222. for (sections_name, target_name) in scheme.entries:
  223. # Get the sections under the bucket 'target_name'. If this bucket does not exist
  224. # is is created automatically
  225. sections_in_bucket = sections_bucket[target_name]
  226. try:
  227. sections = self.sections[sections_name]
  228. except KeyError:
  229. message = GenerationException.UNDEFINED_REFERENCE + " to sections '" + sections_name + "'."
  230. raise GenerationException(message, scheme)
  231. sections_in_bucket.append(sections)
  232. scheme_dictionary[scheme.name] = sections_bucket
  233. # Search for and raise exception on first instance of sections mapped to multiple targets
  234. for (scheme_name, sections_bucket) in scheme_dictionary.items():
  235. for sections_a, sections_b in itertools.combinations(sections_bucket.values(), 2):
  236. set_a = set()
  237. set_b = set()
  238. for sections in sections_a:
  239. set_a.update(sections.entries)
  240. for sections in sections_b:
  241. set_b.update(sections.entries)
  242. intersection = set_a.intersection(set_b)
  243. # If the intersection is a non-empty set, it means sections are mapped to multiple
  244. # targets. Raise exception.
  245. if intersection:
  246. scheme = self.schemes[scheme_name]
  247. message = 'Sections ' + str(intersection) + ' mapped to multiple targets.'
  248. raise GenerationException(message, scheme)
  249. return scheme_dictionary
  250. def get_section_strs(self, section):
  251. s_list = [Sections.get_section_data_from_entry(s) for s in section.entries]
  252. return frozenset([item for sublist in s_list for item in sublist])
  253. def _generate_entity_mappings(self, scheme_dictionary, entities):
  254. entity_mappings = []
  255. for mapping in self.mappings.values():
  256. archive = mapping.archive
  257. for (obj, symbol, scheme_name) in mapping.entries:
  258. entity = Entity(archive, obj, symbol)
  259. # Check the entity exists
  260. if (self.check_mappings and
  261. entity.specificity.value > Entity.Specificity.ARCHIVE.value and
  262. mapping.name not in self.check_mapping_exceptions):
  263. if not entities.check_exists(entity):
  264. message = "'%s' not found" % str(entity)
  265. raise GenerationException(message, mapping)
  266. # Create placement rule for each 'section -> target' in the scheme.
  267. #
  268. # For example. for the mapping entry:
  269. #
  270. # obj (scheme)
  271. #
  272. # The enumrated to:
  273. #
  274. # obj (section1 -> target1)
  275. # obj (section2 -> target2)
  276. # ...
  277. for (target, sections) in scheme_dictionary[scheme_name].items():
  278. for section in sections:
  279. entity_mappings.append(Generation.EntityMapping(entity, self.get_section_strs(section), target))
  280. return entity_mappings
  281. def generate_rules(self, entities):
  282. scheme_dictionary = self._build_scheme_dictionary()
  283. entity_mappings = self._generate_entity_mappings(scheme_dictionary, entities)
  284. entity_mappings.sort(key=lambda m: m.entity)
  285. # Create root nodes dictionary for the default scheme, whose
  286. # key is the target name and value is a list of the root nodes for that target.
  287. root_node = RootNode()
  288. for (target, sections) in scheme_dictionary['default'].items():
  289. for section in sections:
  290. root_node.insert(Entity(), self.get_section_strs(section), target, entities)
  291. for mapping in entity_mappings:
  292. (entity, sections, target) = mapping
  293. try:
  294. root_node.insert(entity, sections, target, entities)
  295. except ValueError as e:
  296. raise GenerationException(str(e))
  297. # Traverse the tree, creating the rules
  298. commands = root_node.get_output_commands()
  299. return commands
  300. def add_fragments_from_file(self, fragment_file):
  301. for fragment in fragment_file.fragments:
  302. dict_to_append_to = None
  303. if isinstance(fragment, Mapping) and fragment.deprecated and fragment.name in self.mappings.keys():
  304. self.mappings[fragment.name].entries |= fragment.entries
  305. else:
  306. if isinstance(fragment, Scheme):
  307. dict_to_append_to = self.schemes
  308. elif isinstance(fragment, Sections):
  309. dict_to_append_to = self.sections
  310. else:
  311. dict_to_append_to = self.mappings
  312. # Raise exception when the fragment of the same type is already in the stored fragments
  313. if fragment.name in dict_to_append_to.keys():
  314. stored = dict_to_append_to[fragment.name].path
  315. new = fragment.path
  316. message = "Duplicate definition of fragment '%s' found in %s and %s." % (fragment.name, stored, new)
  317. raise GenerationException(message)
  318. dict_to_append_to[fragment.name] = fragment
  319. class GenerationException(LdGenFailure):
  320. """
  321. Exception for linker script generation failures such as undefined references/ failure to
  322. evaluate conditions, duplicate mappings, etc.
  323. """
  324. UNDEFINED_REFERENCE = 'Undefined reference'
  325. def __init__(self, message, fragment=None):
  326. self.fragment = fragment
  327. self.message = message
  328. def __str__(self):
  329. if self.fragment:
  330. return "%s\nIn fragment '%s' defined in '%s'." % (self.message, self.fragment.name, self.fragment.path)
  331. else:
  332. return self.message