generation.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  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 AlignAtAddress, InputSectionDesc, SymbolAtAddress
  24. class Placement():
  25. """
  26. A Placement is an assignment of an entity's input sections to a target
  27. in the output linker script - a precursor to the input section description.
  28. A placement can be excluded from another placement. These are represented
  29. as contents of EXCLUDE_FILE in the input section description. Since the linker uses the
  30. first matching rule, these exclusions make sure that accidental matching
  31. of entities with higher specificity does not occur.
  32. The placement which a placement is excluded from is referred to as the
  33. 'basis' placement. It operates on the same input section of the entity on
  34. one of the parent (or parent's parent and so forth), but might have
  35. a different target (see is_significant() for the criteria).
  36. A placement is explicit if it was derived from an actual entry in one of
  37. the mapping fragments. Just as intermediate entity nodes are created in some cases,
  38. intermediate placements are created particularly for symbol placements.
  39. The reason is that EXCLUDE_FILE does not work on symbols (see ObjectNode
  40. for details).
  41. """
  42. def __init__(self, node, sections, target, flags, explicit, force=False, dryrun=False):
  43. self.node = node
  44. self.sections = sections
  45. self.target = target
  46. self.flags = flags
  47. self.exclusions = set()
  48. self.subplacements = set()
  49. # Force this placement to be output
  50. self.force = force
  51. # This placement was created from a mapping
  52. # fragment entry.
  53. self.explicit = explicit
  54. # Find basis placement.
  55. parent = node.parent
  56. candidate = None
  57. while parent:
  58. try:
  59. candidate = parent.placements[sections]
  60. except KeyError:
  61. pass
  62. if candidate and candidate.is_significant():
  63. break
  64. else:
  65. parent = parent.parent
  66. self.basis = candidate
  67. if self.is_significant() and not dryrun and self.basis:
  68. self.basis.add_exclusion(self)
  69. def is_significant(self):
  70. # Check if the placement is significant. Significant placements
  71. # are the end of a basis chain (not self.basis) or a change
  72. # in target (self.target != self.basis.target)
  73. #
  74. # Placement can also be a basis if it has flags
  75. # (self.flags) or its basis has flags (self.basis.flags)
  76. return (not self.basis or
  77. self.target != self.basis.target or
  78. (self.flags and not self.basis.flags) or
  79. (not self.flags and self.basis.flags) or
  80. self.force)
  81. def force_significant(self):
  82. if not self.is_significant():
  83. self.force = True
  84. if self.basis:
  85. self.basis.add_exclusion(self)
  86. def add_exclusion(self, exclusion):
  87. self.exclusions.add(exclusion)
  88. def add_subplacement(self, subplacement):
  89. self.subplacements.add(subplacement)
  90. class EntityNode():
  91. """
  92. Node in entity tree. An EntityNode
  93. is created from an Entity (see entity.py).
  94. The entity tree has a maximum depth of 3. Nodes at different
  95. depths are derived from this class for special behavior (see
  96. RootNode, ArchiveNode, ObjectNode, SymbolNode) depending
  97. on entity specificity.
  98. Nodes for entities are inserted at the appropriate depth, creating
  99. intermediate nodes along the path if necessary. For example, a node
  100. for entity `lib1.a:obj1:sym1` needs to be inserted. If the node for `lib1:obj1`
  101. does not exist, then it needs to be created.
  102. A node contains a dictionary of placements (see Placement).
  103. The key to this dictionary are contents of sections fragments,
  104. representing the input sections of an entity. For example,
  105. a node for entity `lib1.a` might have a placement entry for its `.text` input section
  106. in this dictionary. The placement will contain details about the
  107. target, the flags, etc.
  108. Generation of output commands to be written to the output linker script
  109. requires traversal of the tree, each node collecting the output commands
  110. from its children, so on and so forth.
  111. """
  112. def __init__(self, parent, name):
  113. self.children = []
  114. self.parent = parent
  115. self.name = name
  116. self.child_t = EntityNode
  117. self.entity = None
  118. self.placements = dict()
  119. def add_child(self, entity):
  120. child_specificity = self.entity.specificity.value + 1
  121. assert(child_specificity <= Entity.Specificity.SYMBOL.value)
  122. name = entity[Entity.Specificity(child_specificity)]
  123. assert(name and name != Entity.ALL)
  124. child = [c for c in self.children if c.name == name]
  125. assert(len(child) <= 1)
  126. if not child:
  127. child = self.child_t(self, name)
  128. self.children.append(child)
  129. else:
  130. child = child[0]
  131. return child
  132. def get_output_commands(self):
  133. commands = collections.defaultdict(list)
  134. def process_commands(cmds):
  135. for (target, commands_list) in cmds.items():
  136. commands[target].extend(commands_list)
  137. # Process the commands generated from this node
  138. node_commands = self.get_node_output_commands()
  139. process_commands(node_commands)
  140. # Process the commands generated from this node's children
  141. # recursively
  142. for child in sorted(self.children, key=lambda c: c.name):
  143. children_commands = child.get_output_commands()
  144. process_commands(children_commands)
  145. return commands
  146. def get_node_output_commands(self):
  147. commands = collections.defaultdict(list)
  148. for sections in self.get_output_sections():
  149. placement = self.placements[sections]
  150. if placement.is_significant():
  151. assert(placement.node == self)
  152. keep = False
  153. sort = None
  154. surround_type = []
  155. placement_flags = placement.flags if placement.flags is not None else []
  156. for flag in placement_flags:
  157. if isinstance(flag, Mapping.Keep):
  158. keep = True
  159. elif isinstance(flag, Mapping.Sort):
  160. sort = (flag.first, flag.second)
  161. else: # SURROUND or ALIGN
  162. surround_type.append(flag)
  163. for flag in surround_type:
  164. if flag.pre:
  165. if isinstance(flag, Mapping.Surround):
  166. commands[placement.target].append(SymbolAtAddress('_%s_start' % flag.symbol))
  167. else: # ALIGN
  168. commands[placement.target].append(AlignAtAddress(flag.alignment))
  169. # This is for expanded object node and symbol node placements without checking for
  170. # the type.
  171. placement_sections = frozenset(placement.sections)
  172. command_sections = sections if sections == placement_sections else placement_sections
  173. command = InputSectionDesc(placement.node.entity, command_sections, [e.node.entity for e in placement.exclusions], keep, sort)
  174. commands[placement.target].append(command)
  175. # Generate commands for intermediate, non-explicit exclusion placements here, so that they can be enclosed by
  176. # flags that affect the parent placement.
  177. for subplacement in placement.subplacements:
  178. if not subplacement.flags and not subplacement.explicit:
  179. command = InputSectionDesc(subplacement.node.entity, subplacement.sections,
  180. [e.node.entity for e in subplacement.exclusions], keep, sort)
  181. commands[placement.target].append(command)
  182. for flag in surround_type:
  183. if flag.post:
  184. if isinstance(flag, Mapping.Surround):
  185. commands[placement.target].append(SymbolAtAddress('_%s_end' % flag.symbol))
  186. else: # ALIGN
  187. commands[placement.target].append(AlignAtAddress(flag.alignment))
  188. return commands
  189. def self_placement(self, sections, target, flags, explicit=True, force=False):
  190. placement = Placement(self, sections, target, flags, explicit, force)
  191. self.placements[sections] = placement
  192. return placement
  193. def child_placement(self, entity, sections, target, flags, sections_db):
  194. child = self.add_child(entity)
  195. child.insert(entity, sections, target, flags, sections_db)
  196. def insert(self, entity, sections, target, flags, sections_db):
  197. if self.entity.specificity == entity.specificity:
  198. # Since specificities match, create the placement in this node.
  199. self.self_placement(sections, target, flags)
  200. else:
  201. # If not, create a child node and try to create the placement there.
  202. self.child_placement(entity, sections, target, flags, sections_db)
  203. def get_output_sections(self):
  204. return sorted(self.placements.keys(), key=' '.join)
  205. class SymbolNode(EntityNode):
  206. """
  207. Entities at depth=3. Represents entities with archive, object
  208. and symbol specified.
  209. """
  210. def __init__(self, parent, name):
  211. EntityNode.__init__(self, parent, name)
  212. self.entity = Entity(self.parent.parent.name, self.parent.name)
  213. class ObjectNode(EntityNode):
  214. """
  215. Entities at depth=2. Represents entities with archive
  216. and object specified.
  217. Creating a placement on a child node (SymbolNode) has a different behavior, since
  218. exclusions using EXCLUDE_FILE for symbols does not work.
  219. The sections of this entity has to be 'expanded'. That is, we must
  220. look into the actual input sections of this entity and remove
  221. the ones corresponding to the symbol. The remaining sections of an expanded
  222. object entity will be listed one-by-one in the corresponding
  223. input section description.
  224. An intermediate placement on this node is created, if one does not exist,
  225. and is the one excluded from its basis placement.
  226. """
  227. def __init__(self, parent, name):
  228. EntityNode.__init__(self, parent, name)
  229. self.child_t = SymbolNode
  230. self.entity = Entity(self.parent.name, self.name)
  231. self.subplacements = list()
  232. def child_placement(self, entity, sections, target, flags, sections_db):
  233. child = self.add_child(entity)
  234. sym_placement = Placement(child, sections, target, flags, True, dryrun=True)
  235. # The basis placement for sym_placement can either be
  236. # an existing placement on this node, or nonexistent.
  237. if sym_placement.is_significant():
  238. try:
  239. obj_sections = self.placements[sections].sections
  240. except KeyError:
  241. obj_sections = None
  242. if not obj_sections or obj_sections == sections:
  243. # Expand this section for the first time
  244. found_sections = sections_db.get_sections(self.parent.name, self.name)
  245. obj_sections = []
  246. for s in sections:
  247. obj_sections.extend(fnmatch.filter(found_sections, s))
  248. if obj_sections:
  249. symbol = entity.symbol
  250. remove_sections = [s.replace('.*', '.%s' % symbol) for s in sections if '.*' in s]
  251. filtered_sections = [s for s in obj_sections if s not in remove_sections]
  252. if set(filtered_sections) != set(obj_sections):
  253. if sym_placement.basis:
  254. subplace = False
  255. try:
  256. # If existing placement exists, make sure that
  257. # it is emitted.
  258. obj_placement = self.placements[sections]
  259. except KeyError:
  260. # Create intermediate placement.
  261. obj_placement = self.self_placement(sections, sym_placement.basis.target, None, False)
  262. if obj_placement.basis.flags:
  263. subplace = True
  264. if subplace:
  265. obj_placement.basis.add_subplacement(obj_placement)
  266. self.subplacements.append(sections)
  267. else:
  268. obj_placement.force_significant()
  269. obj_placement.sections = filtered_sections
  270. sym_placement.basis = obj_placement
  271. sym_placement.sections = remove_sections
  272. child.placements[sections] = sym_placement
  273. def get_output_sections(self):
  274. output_sections = [key for key in self.placements if key not in self.subplacements]
  275. return sorted(output_sections, key=' '.join)
  276. class ArchiveNode(EntityNode):
  277. """
  278. Entities at depth=1. Represents entities with archive specified.
  279. """
  280. def __init__(self, parent, name):
  281. EntityNode.__init__(self, parent, name)
  282. self.child_t = ObjectNode
  283. self.entity = Entity(self.name)
  284. class RootNode(EntityNode):
  285. """
  286. Single entity at depth=0. Represents entities with no specific members
  287. specified.
  288. """
  289. def __init__(self):
  290. EntityNode.__init__(self, None, Entity.ALL)
  291. self.child_t = ArchiveNode
  292. self.entity = Entity(Entity.ALL)
  293. class Generation:
  294. """
  295. Processes all fragments processed from fragment files included in the build.
  296. Generates output commands (see output_commands.py) that LinkerScript (see linker_script.py) can
  297. write to the output linker script.
  298. """
  299. # Processed mapping, scheme and section entries
  300. EntityMapping = namedtuple('EntityMapping', 'entity sections_group target flags')
  301. def __init__(self, check_mappings=False, check_mapping_exceptions=None):
  302. self.schemes = {}
  303. self.placements = {}
  304. self.mappings = {}
  305. self.check_mappings = check_mappings
  306. if check_mapping_exceptions:
  307. self.check_mapping_exceptions = check_mapping_exceptions
  308. else:
  309. self.check_mapping_exceptions = []
  310. def _prepare_scheme_dictionary(self):
  311. scheme_dictionary = collections.defaultdict(dict)
  312. # Collect sections into buckets based on target name
  313. for scheme in self.schemes.values():
  314. sections_bucket = collections.defaultdict(list)
  315. for (sections_name, target_name) in scheme.entries:
  316. # Get the sections under the bucket 'target_name'. If this bucket does not exist
  317. # is is created automatically
  318. sections_in_bucket = sections_bucket[target_name]
  319. try:
  320. sections = self.placements[sections_name]
  321. except KeyError:
  322. message = GenerationException.UNDEFINED_REFERENCE + " to sections '" + sections_name + "'."
  323. raise GenerationException(message, scheme)
  324. sections_in_bucket.append(sections)
  325. scheme_dictionary[scheme.name] = sections_bucket
  326. # Search for and raise exception on first instance of sections mapped to multiple targets
  327. for (scheme_name, sections_bucket) in scheme_dictionary.items():
  328. for sections_a, sections_b in itertools.combinations(sections_bucket.values(), 2):
  329. set_a = set()
  330. set_b = set()
  331. for sections in sections_a:
  332. set_a.update(sections.entries)
  333. for sections in sections_b:
  334. set_b.update(sections.entries)
  335. intersection = set_a.intersection(set_b)
  336. # If the intersection is a non-empty set, it means sections are mapped to multiple
  337. # targets. Raise exception.
  338. if intersection:
  339. scheme = self.schemes[scheme_name]
  340. message = 'Sections ' + str(intersection) + ' mapped to multiple targets.'
  341. raise GenerationException(message, scheme)
  342. return scheme_dictionary
  343. def _prepare_entity_mappings(self, scheme_dictionary, entities):
  344. # Prepare entity mappings processed from mapping fragment entries.
  345. def get_section_strs(section):
  346. s_list = [Sections.get_section_data_from_entry(s) for s in section.entries]
  347. return frozenset([item for sublist in s_list for item in sublist])
  348. entity_mappings = dict()
  349. for mapping in self.mappings.values():
  350. archive = mapping.archive
  351. for (obj, symbol, scheme_name) in mapping.entries:
  352. entity = Entity(archive, obj, symbol)
  353. # Check the entity exists
  354. if (self.check_mappings and
  355. entity.specificity.value > Entity.Specificity.ARCHIVE.value and
  356. mapping.name not in self.check_mapping_exceptions):
  357. if not entities.check_exists(entity):
  358. message = "'%s' not found" % str(entity)
  359. raise GenerationException(message, mapping)
  360. if (obj, symbol, scheme_name) in mapping.flags.keys():
  361. flags = mapping.flags[(obj, symbol, scheme_name)]
  362. # Check if all section->target defined in the current
  363. # scheme.
  364. for (s, t, f) in flags:
  365. if (t not in scheme_dictionary[scheme_name].keys() or
  366. s not in [_s.name for _s in scheme_dictionary[scheme_name][t]]):
  367. message = "%s->%s not defined in scheme '%s'" % (s, t, scheme_name)
  368. raise GenerationException(message, mapping)
  369. else:
  370. flags = None
  371. # Create placement for each 'section -> target' in the scheme.
  372. for (target, sections) in scheme_dictionary[scheme_name].items():
  373. for section in sections:
  374. # Find the applicable flags
  375. _flags = []
  376. if flags:
  377. for (s, t, f) in flags:
  378. if (s, t) == (section.name, target):
  379. _flags.extend(f)
  380. sections_str = get_section_strs(section)
  381. key = (entity, section.name)
  382. try:
  383. existing = entity_mappings[key]
  384. except KeyError:
  385. existing = None
  386. if not existing:
  387. entity_mappings[key] = Generation.EntityMapping(entity, sections_str, target, _flags)
  388. else:
  389. # Check for conflicts.
  390. if (target != existing.target):
  391. raise GenerationException('Sections mapped to multiple targets.', mapping)
  392. # Combine flags here if applicable, to simplify
  393. # insertion logic.
  394. if (_flags or existing.flags):
  395. if ((_flags and not existing.flags) or (not _flags and existing.flags)):
  396. _flags.extend(existing.flags)
  397. entity_mappings[key] = Generation.EntityMapping(entity,
  398. sections_str,
  399. target, _flags)
  400. elif (_flags == existing.flags):
  401. pass
  402. else:
  403. raise GenerationException('Conflicting flags specified.', mapping)
  404. # Sort the mappings by specificity, so as to simplify
  405. # insertion logic.
  406. res = list(entity_mappings.values())
  407. res.sort(key=lambda m: m.entity)
  408. return res
  409. def generate(self, entities):
  410. scheme_dictionary = self._prepare_scheme_dictionary()
  411. entity_mappings = self._prepare_entity_mappings(scheme_dictionary, entities)
  412. root_node = RootNode()
  413. for mapping in entity_mappings:
  414. (entity, sections, target, flags) = mapping
  415. try:
  416. root_node.insert(entity, sections, target, flags, entities)
  417. except ValueError as e:
  418. raise GenerationException(str(e))
  419. # Traverse the tree, creating the placements
  420. commands = root_node.get_output_commands()
  421. return commands
  422. def add_fragments_from_file(self, fragment_file):
  423. for fragment in fragment_file.fragments:
  424. dict_to_append_to = None
  425. if isinstance(fragment, Mapping) and fragment.deprecated and fragment.name in self.mappings.keys():
  426. self.mappings[fragment.name].entries |= fragment.entries
  427. else:
  428. if isinstance(fragment, Scheme):
  429. dict_to_append_to = self.schemes
  430. elif isinstance(fragment, Sections):
  431. dict_to_append_to = self.placements
  432. else:
  433. dict_to_append_to = self.mappings
  434. # Raise exception when the fragment of the same type is already in the stored fragments
  435. if fragment.name in dict_to_append_to.keys():
  436. stored = dict_to_append_to[fragment.name].path
  437. new = fragment.path
  438. message = "Duplicate definition of fragment '%s' found in %s and %s." % (fragment.name, stored, new)
  439. raise GenerationException(message)
  440. dict_to_append_to[fragment.name] = fragment
  441. class GenerationException(LdGenFailure):
  442. """
  443. Exception for linker script generation failures such as undefined references/ failure to
  444. evaluate conditions, duplicate mappings, etc.
  445. """
  446. UNDEFINED_REFERENCE = 'Undefined reference'
  447. def __init__(self, message, fragment=None):
  448. self.fragment = fragment
  449. self.message = message
  450. def __str__(self):
  451. if self.fragment:
  452. return "%s\nIn fragment '%s' defined in '%s'." % (self.message, self.fragment.name, self.fragment.path)
  453. else:
  454. return self.message