entity.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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 os
  19. from enum import Enum
  20. from functools import total_ordering
  21. from pyparsing import (Group, Literal, OneOrMore, ParseException, SkipTo, Suppress, White, Word, ZeroOrMore, alphas,
  22. nums, restOfLine)
  23. @total_ordering
  24. class Entity():
  25. """
  26. Definition of an entity which can be placed or excluded
  27. from placement.
  28. """
  29. ALL = '*'
  30. class Specificity(Enum):
  31. NONE = 0
  32. ARCHIVE = 1
  33. OBJ = 2
  34. SYMBOL = 3
  35. def __init__(self, archive=None, obj=None, symbol=None):
  36. archive_spec = archive and archive != Entity.ALL
  37. obj_spec = obj and obj != Entity.ALL
  38. symbol_spec = symbol and symbol != Entity.ALL
  39. if not archive_spec and not obj_spec and not symbol_spec:
  40. self.specificity = Entity.Specificity.NONE
  41. elif archive_spec and not obj_spec and not symbol_spec:
  42. self.specificity = Entity.Specificity.ARCHIVE
  43. elif archive_spec and obj_spec and not symbol_spec:
  44. self.specificity = Entity.Specificity.OBJ
  45. elif archive_spec and obj_spec and symbol_spec:
  46. self.specificity = Entity.Specificity.SYMBOL
  47. else:
  48. raise ValueError("Invalid arguments '(%s, %s, %s)'" % (archive, obj, symbol))
  49. self.archive = archive
  50. self.obj = obj
  51. self.symbol = symbol
  52. def __eq__(self, other):
  53. return (self.specificity.value == other.specificity.value and
  54. self.archive == other.archive and
  55. self.obj == other.obj and
  56. self.symbol == other.symbol)
  57. def __lt__(self, other):
  58. res = False
  59. if self.specificity.value < other.specificity.value:
  60. res = True
  61. elif self.specificity == other.specificity:
  62. for s in Entity.Specificity:
  63. a = self[s] if self[s] else ''
  64. b = other[s] if other[s] else ''
  65. if a != b:
  66. res = a < b
  67. break
  68. else:
  69. res = False
  70. return res
  71. def __hash__(self):
  72. return hash(self.__repr__())
  73. def __str__(self):
  74. return '%s:%s %s' % self.__repr__()
  75. def __repr__(self):
  76. return (self.archive, self.obj, self.symbol)
  77. def __getitem__(self, spec):
  78. res = None
  79. if spec == Entity.Specificity.ARCHIVE:
  80. res = self.archive
  81. elif spec == Entity.Specificity.OBJ:
  82. res = self.obj
  83. elif spec == Entity.Specificity.SYMBOL:
  84. res = self.symbol
  85. else:
  86. res = None
  87. return res
  88. class EntityDB():
  89. """
  90. Encapsulates an output of objdump. Contains information about the static library sections
  91. and names
  92. """
  93. __info = collections.namedtuple('__info', 'filename content')
  94. def __init__(self):
  95. self.sections = dict()
  96. def add_sections_info(self, sections_info_dump):
  97. first_line = sections_info_dump.readline()
  98. archive_path = (Literal('In archive').suppress() +
  99. White().suppress() +
  100. # trim the colon and line ending characters from archive_path
  101. restOfLine.setResultsName('archive_path').setParseAction(lambda s, loc, toks: s.rstrip(':\n\r ')))
  102. parser = archive_path
  103. results = None
  104. try:
  105. results = parser.parseString(first_line, parseAll=True)
  106. except ParseException as p:
  107. raise ParseException('Parsing sections info for library ' + sections_info_dump.name + ' failed. ' + p.msg)
  108. archive = os.path.basename(results.archive_path)
  109. self.sections[archive] = EntityDB.__info(sections_info_dump.name, sections_info_dump.read())
  110. def _get_infos_from_file(self, info):
  111. # {object}: file format elf32-xtensa-le
  112. object_line = SkipTo(':').setResultsName('object') + Suppress(restOfLine)
  113. # Sections:
  114. # Idx Name ...
  115. section_start = Suppress(Literal('Sections:'))
  116. section_header = Suppress(OneOrMore(Word(alphas)))
  117. # 00 {section} 0000000 ...
  118. # CONTENTS, ALLOC, ....
  119. section_entry = Suppress(Word(nums)) + SkipTo(' ') + Suppress(restOfLine) + \
  120. Suppress(ZeroOrMore(Word(alphas) + Literal(',')) + Word(alphas))
  121. content = Group(object_line + section_start + section_header + Group(OneOrMore(section_entry)).setResultsName('sections'))
  122. parser = Group(ZeroOrMore(content)).setResultsName('contents')
  123. results = None
  124. try:
  125. results = parser.parseString(info.content, parseAll=True)
  126. except ParseException as p:
  127. raise ParseException('Unable to parse section info file ' + info.filename + '. ' + p.msg)
  128. return results
  129. def _process_archive(self, archive):
  130. stored = self.sections[archive]
  131. # Parse the contents of the sections file on-demand,
  132. # save the result for later
  133. if not isinstance(stored, dict):
  134. parsed = self._get_infos_from_file(stored)
  135. stored = dict()
  136. for content in parsed.contents:
  137. sections = list(map(lambda s: s, content.sections))
  138. stored[content.object] = sections
  139. self.sections[archive] = stored
  140. def get_archives(self):
  141. return self.sections.keys()
  142. def get_objects(self, archive):
  143. try:
  144. self._process_archive(archive)
  145. except KeyError:
  146. return []
  147. return self.sections[archive].keys()
  148. def _match_obj(self, archive, obj):
  149. objs = self.get_objects(archive)
  150. match_objs = fnmatch.filter(objs, obj + '.o') + fnmatch.filter(objs, obj + '.*.obj') + fnmatch.filter(objs, obj + '.obj')
  151. if len(match_objs) > 1:
  152. raise ValueError("Multiple matches for object: '%s: %s': %s" % (archive, obj, str(match_objs)))
  153. try:
  154. return match_objs[0]
  155. except IndexError:
  156. return None
  157. def get_sections(self, archive, obj):
  158. obj = self._match_obj(archive, obj)
  159. res = []
  160. if obj:
  161. res = self.sections[archive][obj]
  162. return res
  163. def _match_symbol(self, archive, obj, symbol):
  164. sections = self.get_sections(archive, obj)
  165. return [s for s in sections if s.endswith(symbol)]
  166. def check_exists(self, entity):
  167. res = True
  168. if entity.specificity != Entity.Specificity.NONE:
  169. if entity.specificity == Entity.Specificity.ARCHIVE:
  170. res = entity.archive in self.get_archives()
  171. elif entity.specificity == Entity.Specificity.OBJ:
  172. res = self._match_obj(entity.archive, entity.obj) is not None
  173. elif entity.specificity == Entity.Specificity.SYMBOL:
  174. res = len(self._match_symbol(entity.archive, entity.obj, entity.symbol)) > 0
  175. else:
  176. res = False
  177. return res