test_generation.py 69 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698
  1. #!/usr/bin/env python
  2. #
  3. # SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
  4. # SPDX-License-Identifier: Apache-2.0
  5. #
  6. import collections
  7. import fnmatch
  8. import os
  9. import sys
  10. import tempfile
  11. import unittest
  12. from io import StringIO
  13. try:
  14. from ldgen.entity import Entity, EntityDB
  15. from ldgen.fragments import parse_fragment_file
  16. from ldgen.generation import Generation, GenerationException
  17. from ldgen.linker_script import LinkerScript
  18. from ldgen.output_commands import AlignAtAddress, InputSectionDesc, SymbolAtAddress
  19. from ldgen.sdkconfig import SDKConfig
  20. except ImportError:
  21. sys.path.append(os.path.dirname(os.path.dirname(__file__)))
  22. from ldgen.entity import Entity, EntityDB
  23. from ldgen.fragments import parse_fragment_file
  24. from ldgen.generation import Generation, GenerationException
  25. from ldgen.linker_script import LinkerScript
  26. from ldgen.output_commands import AlignAtAddress, InputSectionDesc, SymbolAtAddress
  27. from ldgen.sdkconfig import SDKConfig
  28. ROOT = Entity('*')
  29. FREERTOS = Entity('libfreertos.a')
  30. CROUTINE = Entity('libfreertos.a', 'croutine')
  31. TIMERS = Entity('libfreertos.a', 'timers')
  32. FREERTOS2 = Entity('libfreertos2.a')
  33. class GenerationTest(unittest.TestCase):
  34. def setUp(self):
  35. self.generation = Generation()
  36. self.entities = None
  37. self.linker_script = None
  38. with tempfile.NamedTemporaryFile(delete=False) as f:
  39. self.kconfigs_source_file = os.path.join(tempfile.gettempdir(), f.name)
  40. self.addCleanup(os.remove, self.kconfigs_source_file)
  41. with tempfile.NamedTemporaryFile(delete=False) as f:
  42. self.kconfig_projbuilds_source_file = os.path.join(tempfile.gettempdir(), f.name)
  43. self.addCleanup(os.remove, self.kconfig_projbuilds_source_file)
  44. os.environ['COMPONENT_KCONFIGS_SOURCE_FILE'] = self.kconfigs_source_file
  45. os.environ['COMPONENT_KCONFIGS_PROJBUILD_SOURCE_FILE'] = self.kconfig_projbuilds_source_file
  46. os.environ['COMPONENT_KCONFIGS'] = ''
  47. os.environ['COMPONENT_KCONFIGS_PROJBUILD'] = ''
  48. # prepare_kconfig_files.py doesn't have to be called because COMPONENT_KCONFIGS and
  49. # COMPONENT_KCONFIGS_PROJBUILD are empty
  50. self.sdkconfig = SDKConfig('data/Kconfig', 'data/sdkconfig')
  51. fragment_file = parse_fragment_file('data/base.lf', self.sdkconfig)
  52. self.generation.add_fragments_from_file(fragment_file)
  53. self.entities = EntityDB()
  54. with open('data/libfreertos.a.txt') as objdump:
  55. self.entities.add_sections_info(objdump)
  56. with open('data/linker_script.ld') as linker_script:
  57. self.linker_script = LinkerScript(linker_script)
  58. @staticmethod
  59. def create_fragment_file(contents, name='test_fragment.lf'):
  60. f = StringIO(contents)
  61. f.name = name
  62. return f
  63. def add_fragments(self, text):
  64. fragment_file = self.create_fragment_file(text)
  65. fragment_file = parse_fragment_file(fragment_file, self.sdkconfig)
  66. self.generation.add_fragments_from_file(fragment_file)
  67. def write(self, expected, actual):
  68. self.linker_script.fill(expected)
  69. self.linker_script.write(open('expected.ld', 'w'))
  70. self.linker_script.fill(actual)
  71. self.linker_script.write(open('actual.ld', 'w'))
  72. def generate_default_rules(self):
  73. rules = collections.defaultdict(list)
  74. rules['dram0_bss'].append(InputSectionDesc(ROOT, ['.bss', '.bss.*'], []))
  75. rules['dram0_bss'].append(InputSectionDesc(ROOT, ['COMMON'], []))
  76. rules['dram0_data'].append(InputSectionDesc(ROOT, ['.data', '.data.*'], []))
  77. rules['dram0_data'].append(InputSectionDesc(ROOT, ['.dram', '.dram.*'], []))
  78. rules['flash_text'].append(InputSectionDesc(ROOT, ['.literal', '.literal.*', '.text', '.text.*'], []))
  79. rules['flash_rodata'].append(InputSectionDesc(ROOT, ['.rodata', '.rodata.*'], []))
  80. rules['iram0_text'].append(InputSectionDesc(ROOT, ['.iram', '.iram.*'], []))
  81. rules['rtc_bss'].append(InputSectionDesc(ROOT, ['.rtc.bss'], []))
  82. rules['rtc_data'].append(InputSectionDesc(ROOT, ['.rtc.data'], []))
  83. rules['rtc_data'].append(InputSectionDesc(ROOT, ['.rtc.rodata'], []))
  84. rules['rtc_text'].append(InputSectionDesc(ROOT, ['.rtc.text', '.rtc.literal'], []))
  85. return rules
  86. def compare_rules(self, expected, actual):
  87. self.assertEqual(expected, actual)
  88. def get_default(self, target, rules):
  89. return rules[target][0]
  90. class DefaultMappingTest(GenerationTest):
  91. def test_rule_generation_default(self):
  92. # Checks that default rules are generated from
  93. # the default scheme properly and even if no mappings
  94. # are defined.
  95. actual = self.generation.generate(self.entities)
  96. expected = self.generate_default_rules()
  97. self.compare_rules(expected, actual)
  98. def test_default_mapping_lib(self):
  99. # Mapping a library with default mapping. This should not emit additional rules,
  100. # other than the default ones.
  101. mapping = u"""
  102. [mapping:test]
  103. archive: libfreertos.a
  104. entries:
  105. * (default)
  106. """
  107. self.add_fragments(mapping)
  108. self.test_rule_generation_default()
  109. def test_default_mapping_obj(self):
  110. # Mapping an object with default mapping. This should not emit additional rules,
  111. # other than the default ones.
  112. mapping = u"""
  113. [mapping:test]
  114. archive: libfreertos.a
  115. entries:
  116. croutine (default)
  117. """
  118. self.add_fragments(mapping)
  119. self.test_rule_generation_default()
  120. def test_default_mapping_symbol(self):
  121. # Mapping a symbol with default mapping. This should not emit additional rules,
  122. # other than the default ones.
  123. mapping = u"""
  124. [mapping:test]
  125. archive: libfreertos.a
  126. entries:
  127. croutine:prvCheckPendingReadyList (default) #1
  128. """
  129. self.add_fragments(mapping)
  130. self.test_rule_generation_default()
  131. def test_default_mapping_all(self):
  132. # Mapping a library, object, and symbol with default mapping. This should not emit additional rules,
  133. # other than the default ones.
  134. mapping = u"""
  135. [mapping:test]
  136. archive: libfreertos.a
  137. entries:
  138. * (default) #1
  139. croutine (default) #2
  140. croutine:prvCheckPendingReadyList (default) #3
  141. """
  142. self.add_fragments(mapping)
  143. self.test_rule_generation_default()
  144. def test_default_mapping_lib_symbol(self):
  145. # Mapping a library, and symbol with default mapping. This should not emit additional rules,
  146. # other than the default ones.
  147. #
  148. # This is a check needed to make sure generation does not generate
  149. # intermediate commands due to presence of symbol mapping.
  150. mapping = u"""
  151. [mapping:test]
  152. archive: libfreertos.a
  153. entries:
  154. * (default) #1
  155. croutine:prvCheckPendingReadyList (default) #2
  156. """
  157. self.add_fragments(mapping)
  158. self.test_rule_generation_default()
  159. def test_default_mapping_obj_symbol(self):
  160. # Mapping a library, and symbol with default mapping. This should not emit additional rules,
  161. # other than the default ones.
  162. #
  163. # This is a check needed to make sure generation does not generate
  164. # intermediate commands due to presence of symbol mapping.
  165. mapping = u"""
  166. [mapping:test]
  167. archive: libfreertos.a
  168. entries:
  169. croutine (default) #1
  170. croutine:prvCheckPendingReadyList (default) #2
  171. """
  172. self.add_fragments(mapping)
  173. self.test_rule_generation_default()
  174. class BasicTest(GenerationTest):
  175. # Test basic and fundamental interactions between typical
  176. # entries.
  177. def test_nondefault_mapping_lib(self, alt=None):
  178. # Test mapping entry different from default for a library.
  179. # There should be exclusions in the default commands for flash_text and flash_rodata:
  180. #
  181. # flash_text
  182. # *((EXCLUDE_FILE(libfreertos.a)) .literal ...) A
  183. #
  184. # Commands placing the entire library in iram, dram should be generated:
  185. #
  186. # iram0_text
  187. # *(.iram ...)
  188. # *libfreertos.a(.literal ...) B
  189. mapping = u"""
  190. [mapping:test]
  191. archive: libfreertos.a
  192. entries:
  193. * (noflash) #1
  194. """
  195. self.add_fragments(alt if alt else mapping)
  196. actual = self.generation.generate(self.entities)
  197. expected = self.generate_default_rules()
  198. flash_text = expected['flash_text']
  199. flash_rodata = expected['flash_rodata']
  200. iram0_text = expected['iram0_text']
  201. dram0_data = expected['dram0_data']
  202. # Generate exclusions in flash_text and flash_rodata A
  203. flash_text[0].exclusions.add(FREERTOS)
  204. flash_rodata[0].exclusions.add(FREERTOS)
  205. # Input section commands in iram_text and dram0_data for #1 B
  206. iram0_text.append(InputSectionDesc(FREERTOS, flash_text[0].sections, []))
  207. dram0_data.append(InputSectionDesc(FREERTOS, flash_rodata[0].sections, []))
  208. self.compare_rules(expected, actual)
  209. def test_nondefault_mapping_obj(self, alt=None):
  210. # Test mapping entry different from default for an object.
  211. # There should be exclusions in the default commands for flash_text and flash_rodata:
  212. #
  213. # flash_text
  214. # *((EXCLUDE_FILE(libfreertos.a:croutine)) .literal ...) A
  215. #
  216. # Commands placing the entire library in iram, dram should be generated:
  217. #
  218. # iram0_text
  219. # *(.iram ...)
  220. # *libfreertos.a:croutine(.literal ...) B
  221. mapping = u"""
  222. [mapping:test]
  223. archive: libfreertos.a
  224. entries:
  225. croutine (noflash) #1
  226. """
  227. self.add_fragments(alt if alt else mapping)
  228. actual = self.generation.generate(self.entities)
  229. expected = self.generate_default_rules()
  230. flash_text = expected['flash_text']
  231. flash_rodata = expected['flash_rodata']
  232. iram0_text = expected['iram0_text']
  233. dram0_data = expected['dram0_data']
  234. # Generate exclusions in flash_text and flash_rodata A
  235. flash_text[0].exclusions.add(CROUTINE)
  236. flash_rodata[0].exclusions.add(CROUTINE)
  237. # Input section commands in iram_text and dram0_data for #1 B
  238. iram0_text.append(InputSectionDesc(CROUTINE, flash_text[0].sections, []))
  239. dram0_data.append(InputSectionDesc(CROUTINE, flash_rodata[0].sections, []))
  240. self.compare_rules(expected, actual)
  241. def test_nondefault_mapping_symbol(self):
  242. # Test mapping entry different from default for symbol.
  243. # There should be exclusions in the default commands for flash_text, as well as the implicit intermediate object command
  244. # with an exclusion from default:
  245. #
  246. # flash_text
  247. # *((EXCLUDE_FILE(libfreertos.a:croutine)) .literal ...) A
  248. # *libfreertos.a:croutine(.literal .literal.prvCheckDelayedList ...) B
  249. #
  250. # Commands placing the entire library in iram should be generated:
  251. #
  252. # iram0_text
  253. # *(.iram ...)
  254. # *libfreertos.a:croutine(.text.prvCheckPendingReadyList .literal.prvCheckPendingReadyList) C
  255. mapping = u"""
  256. [mapping:test]
  257. archive: libfreertos.a
  258. entries:
  259. croutine:prvCheckPendingReadyList (noflash) #1
  260. """
  261. self.add_fragments(mapping)
  262. actual = self.generation.generate(self.entities)
  263. expected = self.generate_default_rules()
  264. flash_text = expected['flash_text']
  265. iram0_text = expected['iram0_text']
  266. # Generate exclusion in flash_text A
  267. flash_text[0].exclusions.add(CROUTINE)
  268. # Generate intermediate command B
  269. # List all relevant sections except the symbol
  270. # being mapped
  271. croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine')
  272. filtered_sections = fnmatch.filter(croutine_sections, '.literal.*')
  273. filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*'))
  274. filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')]
  275. filtered_sections.append('.text')
  276. flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), []))
  277. # Input section commands in iram_text for #1 C
  278. iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []))
  279. self.compare_rules(expected, actual)
  280. def test_default_symbol_nondefault_lib(self):
  281. # Test default symbol mapping with different lib mapping. This should create an implicit intermediate object command.
  282. # The significant targets are flash_text, flash_rodata, iram0_text, dram0_data.
  283. #
  284. # flash_text
  285. # *(EXCLUDE_FILE(libfreertos.a) .text ...) A
  286. # libfreertos.a:croutine (.text.prvCheckPendingReadyList .literal.prvCheckPendingReadyList) B
  287. #
  288. # flash_rodata
  289. # *(EXCLUDE_FILE(libfreertos.a) .rodata ...) A
  290. #
  291. # iram0_text
  292. # * ( .iram ...)
  293. # libfreertos.a (EXCLUDE_FILE(libfreertos:croutine) .text ...) C.1
  294. # *libfreertos.a:croutine(.literal .literal.prvCheckDelayedList ...) D
  295. #
  296. # dram0_data
  297. # * ( .dram ...)
  298. # libfreertos.a ( .rodata ...) C.2
  299. #
  300. # Only default commands are in the other targets.
  301. mapping = u"""
  302. [mapping:test]
  303. archive: libfreertos.a
  304. entries:
  305. * (noflash) #1
  306. croutine:prvCheckPendingReadyList (default) #2
  307. """
  308. self.add_fragments(mapping)
  309. actual = self.generation.generate(self.entities)
  310. expected = self.generate_default_rules()
  311. flash_text = expected['flash_text']
  312. flash_rodata = expected['flash_rodata']
  313. iram0_text = expected['iram0_text']
  314. dram0_data = expected['dram0_data']
  315. # Exclusions for #1 A
  316. flash_text[0].exclusions.add(FREERTOS)
  317. flash_rodata[0].exclusions.add(FREERTOS)
  318. # Commands for #1 C.1 & C.2
  319. # C.1 excludes intermediate command for #2
  320. iram0_text.append(InputSectionDesc(FREERTOS, flash_text[0].sections, [CROUTINE]))
  321. dram0_data.append(InputSectionDesc(FREERTOS, flash_rodata[0].sections, []))
  322. # Intermediate command for excluding #2 D
  323. croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine')
  324. filtered_sections = fnmatch.filter(croutine_sections, '.literal.*')
  325. filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*'))
  326. filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')]
  327. filtered_sections.append('.text')
  328. iram0_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), []))
  329. # Command for #2 B
  330. flash_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []))
  331. self.compare_rules(expected, actual)
  332. def test_default_symbol_nondefault_obj(self):
  333. # Test default symbol mapping with different obj mapping. Since there is an explicit entry for the object,
  334. # the sections for that object should just be expanded and the symbol section subtracted, to be placed
  335. # using another command.
  336. #
  337. # flash_text
  338. # *(EXCLUDE_FILE(libfreertos.a:croutine) .text ...) A
  339. # libfreertos.a:croutine (.text.prvCheckPendingReadyList .literal.prvCheckPendingReadyList) B
  340. #
  341. # flash_rodata
  342. # *(EXCLUDE_FILE(libfreertos.a:croutine) .rodata ...) A
  343. #
  344. # iram0_text
  345. # *( .iram ...)
  346. # *libfreertos.a:croutine(.literal .literal.prvCheckDelayedList ...) C.1
  347. #
  348. # dram0_data
  349. # *(.data ..)
  350. # *libfreertos.a:croutine(.rodata ....) C.2
  351. #
  352. # Only default commands are in the other targets
  353. mapping = u"""
  354. [mapping:test]
  355. archive: libfreertos.a
  356. entries:
  357. croutine (noflash) #1
  358. croutine:prvCheckPendingReadyList (default) #2
  359. """
  360. self.add_fragments(mapping)
  361. actual = self.generation.generate(self.entities)
  362. expected = self.generate_default_rules()
  363. flash_text = expected['flash_text']
  364. flash_rodata = expected['flash_rodata']
  365. iram0_text = expected['iram0_text']
  366. dram0_data = expected['dram0_data']
  367. # Exclusions for #1 A
  368. flash_text[0].exclusions.add(CROUTINE)
  369. flash_rodata[0].exclusions.add(CROUTINE)
  370. # Commands for #1 C.1 & C.2
  371. # C.1 list relevant sections for libfreertos.a:croutine to
  372. # exclude symbol to map
  373. croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine')
  374. filtered_sections = fnmatch.filter(croutine_sections, '.literal.*')
  375. filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*'))
  376. filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')]
  377. filtered_sections.append('.text')
  378. iram0_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), []))
  379. dram0_data.append(InputSectionDesc(CROUTINE, flash_rodata[0].sections, []))
  380. # Command for #2 B
  381. flash_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []))
  382. self.compare_rules(expected, actual)
  383. def test_default_nondefault_alternating(self):
  384. # Here, each of the entries map sections to something different
  385. # than its one-level-up entry.
  386. #
  387. # * text -> flash, rodata -> flash
  388. # libfreertos.a text -> iram, rodata -> dram
  389. # libfreertos.a:croutine text -> flash, rodata -> flash
  390. # croutine:prvCheckPendingReadyList text -> iram
  391. #
  392. # The significant targets are flash_text, flash_rodata, iram0_text, and dram0_data.
  393. #
  394. # flash_text
  395. # *(EXCLUDE_FILE(libfreertos.a) .text ...) A
  396. # *libfreertos.a:croutine(.literal .literal.prvCheckDelayedList ...) B.1
  397. #
  398. # flash_rodata
  399. # *(EXCLUDE_FILE(libfreertos.a) .rodata ...) A
  400. # *libfreertos.a:croutine(.rodata .rodata.*) B.2
  401. #
  402. # iram0_text
  403. # * ( .iram ...)
  404. # libfreertos.a (EXCLUDE_FILE(libfreertos:croutine) .text ...) C
  405. # libfreertos.a:croutine (.text.prvCheckPendingReadyList .literal.prvCheckPendingReadyList) D
  406. #
  407. # dram0_data
  408. # * ( .dram ...)
  409. # libfreertos.a (EXCLUDE_FILE(libfreertos:croutine) .rodata ...) C
  410. #
  411. # For the other targets only the default commands should be present.
  412. mapping = u"""
  413. [mapping:test]
  414. archive: libfreertos.a
  415. entries:
  416. * (noflash) #1
  417. croutine (default) #2
  418. croutine:prvCheckPendingReadyList (noflash) #3
  419. """
  420. self.add_fragments(mapping)
  421. actual = self.generation.generate(self.entities)
  422. expected = self.generate_default_rules()
  423. flash_text = expected['flash_text']
  424. flash_rodata = expected['flash_rodata']
  425. iram0_text = expected['iram0_text']
  426. dram0_data = expected['dram0_data']
  427. # Exclusions for #1 A
  428. # Only for flash_text and flash_rodata
  429. flash_text[0].exclusions.add(FREERTOS)
  430. flash_rodata[0].exclusions.add(FREERTOS)
  431. # Commands for #1 C
  432. # with exclusions for #2
  433. iram0_text.append(InputSectionDesc(FREERTOS, flash_text[0].sections, [CROUTINE]))
  434. dram0_data.append(InputSectionDesc(FREERTOS, flash_rodata[0].sections, [CROUTINE]))
  435. # Commands for #2 B.1
  436. flash_rodata.append(InputSectionDesc(CROUTINE, flash_rodata[0].sections, []))
  437. # List all relevant sections in case of flash_text B.2
  438. # as exclusion for #3
  439. croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine')
  440. filtered_sections = fnmatch.filter(croutine_sections, '.literal.*')
  441. filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*'))
  442. filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')]
  443. filtered_sections.append('.text')
  444. flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), []))
  445. # Command for #3 D
  446. iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []))
  447. self.compare_rules(expected, actual)
  448. def test_nondefault_but_same_lib_and_obj(self):
  449. # Extension of DefaultMappingTest. Commands should not be generated for #2, since it does similar mapping
  450. # to #1. Output is similar to test_different_mapping_lib.
  451. mapping = u"""
  452. [mapping:test]
  453. archive: libfreertos.a
  454. entries:
  455. * (noflash) #1
  456. croutine (noflash) #2
  457. """
  458. self.test_nondefault_mapping_lib(mapping)
  459. def test_nondefault_but_same_lib_and_sym(self):
  460. # Extension of DefaultMappingTest. Commands should not be generated for #2, since it does similar mapping
  461. # to #1. Output is similar to test_different_mapping_lib.
  462. mapping = u"""
  463. [mapping:test]
  464. archive: libfreertos.a
  465. entries:
  466. * (noflash) #1
  467. croutine:prvCheckPendingReadyList (noflash) #2
  468. """
  469. self.test_nondefault_mapping_lib(mapping)
  470. def test_nondefault_but_same_obj_and_sym(self):
  471. # Commands should not be generated for #2, since it does similar mapping
  472. # to #1. Output is similar to test_different_mapping_obj.
  473. mapping = u"""
  474. [mapping:test]
  475. archive: libfreertos.a
  476. entries:
  477. croutine (noflash) #1
  478. croutine:prvCheckPendingReadyList (noflash) #2
  479. """
  480. self.test_nondefault_mapping_obj(mapping)
  481. def test_multiple_symbols_excluded_from_intermediate_command(self):
  482. # Test mapping multiple symbols from the same object.
  483. # All these symbols must be succesfully excluded from
  484. # the intermediate command.
  485. #
  486. # flash_text
  487. # * (EXCLUDE_FILE(libfreertos.a:croutine) .text ...) A
  488. # libfreertos:croutine(.text ...) B
  489. #
  490. # iram0_text
  491. #
  492. #
  493. mapping = u"""
  494. [mapping:test]
  495. archive: libfreertos.a
  496. entries:
  497. croutine:prvCheckPendingReadyList (noflash) #1
  498. croutine:prvCheckDelayedList (noflash) #2
  499. """
  500. self.add_fragments(mapping)
  501. actual = self.generation.generate(self.entities)
  502. expected = self.generate_default_rules()
  503. flash_text = expected['flash_text']
  504. iram0_text = expected['iram0_text']
  505. # Exclusions for #1 & #2 intermediate command A
  506. flash_text[0].exclusions.add(CROUTINE)
  507. # Intermediate command for #1 & #2 which lists B
  508. # all relevant sections in croutine except prvCheckPendingReadyList
  509. # and prvCheckDelayedList
  510. croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine')
  511. filtered_sections = fnmatch.filter(croutine_sections, '.literal.*')
  512. filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*'))
  513. filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')]
  514. filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckDelayedList')]
  515. filtered_sections.append('.text')
  516. flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), []))
  517. # Commands for #1 & 2
  518. iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckDelayedList', '.literal.prvCheckDelayedList']), []))
  519. iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []))
  520. self.compare_rules(expected, actual)
  521. def test_root_mapping_fragment(self):
  522. # Test creation of a mapping fragment that maps '*'.
  523. # This should generate another default command in iram0_text:
  524. #
  525. # iram0_text
  526. # * (.custom_section) A
  527. # * (.iram .iram.*)
  528. mapping = u"""
  529. [sections:custom_section]
  530. entries:
  531. .custom_section
  532. [scheme:custom_scheme]
  533. entries:
  534. custom_section -> iram0_text
  535. [mapping:default2]
  536. archive: *
  537. entries:
  538. * (custom_scheme) #1
  539. """
  540. self.add_fragments(mapping)
  541. actual = self.generation.generate(self.entities)
  542. expected = self.generate_default_rules()
  543. # Generate default command A
  544. # Since these are the same 'specificity', the commands
  545. # are arranged alphabetically.
  546. expected['iram0_text'].append(expected['iram0_text'][0])
  547. expected['iram0_text'][0] = InputSectionDesc(ROOT, ['.custom_section'], [])
  548. self.compare_rules(expected, actual)
  549. class AdvancedTest(GenerationTest):
  550. # Test valid but quirky cases, corner cases, failure cases, and
  551. # cases involving interaction between schemes, other mapping
  552. # fragments.
  553. def test_same_entity_no_scheme_common(self):
  554. # Test same entity being mapped by schemes that have nothing in common.
  555. #
  556. # noflash_data: rodata -> dram0_data
  557. # noflash_text: text -> iram0_text
  558. #
  559. # This operation should succeed with the following commands:
  560. #
  561. # flash_text
  562. # *(EXCLUDE_FILE(libfreertos.a:croutine) .text ...) A
  563. #
  564. # flash_rodata
  565. # *(EXCLUDE_FILE(libfreertos.a:croutine) .rodata ...) B
  566. #
  567. # iram0_text
  568. # *(.iram ...)
  569. # *libfreertos.a:croutine(.text .text.* ...) C
  570. #
  571. # dram0_data
  572. # *(.data ..)
  573. # *(.dram ...)
  574. # *libfreertos.a:croutine(.rodata .rodata.*) D
  575. mapping = u"""
  576. [mapping:test]
  577. archive: libfreertos.a
  578. entries:
  579. croutine (noflash_text) #1
  580. croutine (noflash_data) #2
  581. """
  582. self.add_fragments(mapping)
  583. actual = self.generation.generate(self.entities)
  584. expected = self.generate_default_rules()
  585. flash_text = expected['flash_text']
  586. flash_rodata = expected['flash_rodata']
  587. iram0_text = expected['iram0_text']
  588. dram0_data = expected['dram0_data']
  589. # Exclusions for #1 A
  590. flash_text[0].exclusions.add(CROUTINE)
  591. # Exclusions for #2 B
  592. flash_rodata[0].exclusions.add(CROUTINE)
  593. # Command for #1 C
  594. iram0_text.append(InputSectionDesc(CROUTINE, flash_text[0].sections, []))
  595. # Command for #2 D
  596. dram0_data.append(InputSectionDesc(CROUTINE, flash_rodata[0].sections, []))
  597. self.compare_rules(expected, actual)
  598. def test_same_entity_sub_scheme(self):
  599. # Test same entity being mapped by scheme that is a subset of the other.
  600. #
  601. # noflash: text -> iram0_text, rodata -> dram0_data
  602. # noflash_text: text -> iram0_text
  603. #
  604. # `text -> iram0_text` is common between the two schemes.
  605. #
  606. # This operation should succeed with the following commands:
  607. #
  608. # flash_text
  609. # *(EXCLUDE_FILE(libfreertos.a:croutine) .text ...) A
  610. #
  611. # flash_rodata
  612. # *(EXCLUDE_FILE(libfreertos.a:croutine) .rodata ...) B
  613. #
  614. # iram0_text
  615. # *(.iram ...)
  616. # *libfreertos.a:croutine(.text .text.* ...) C
  617. #
  618. # dram0_data
  619. # *(.data ..)
  620. # *(.dram ...)
  621. # *libfreertos.a:croutine(.rodata .rodata.*) D
  622. mapping = u"""
  623. [mapping:test]
  624. archive: libfreertos.a
  625. entries:
  626. croutine (noflash) #1
  627. croutine (noflash_data) #2
  628. """
  629. self.add_fragments(mapping)
  630. actual = self.generation.generate(self.entities)
  631. expected = self.generate_default_rules()
  632. flash_text = expected['flash_text']
  633. flash_rodata = expected['flash_rodata']
  634. iram0_text = expected['iram0_text']
  635. dram0_data = expected['dram0_data']
  636. # Exclusions for #1 A
  637. flash_text[0].exclusions.add(CROUTINE)
  638. # Exclusions for #1 & #2 B
  639. flash_rodata[0].exclusions.add(CROUTINE)
  640. # Command for #1 C
  641. iram0_text.append(InputSectionDesc(CROUTINE, flash_text[0].sections, []))
  642. # Command for #1 & #2 D
  643. dram0_data.append(InputSectionDesc(CROUTINE, flash_rodata[0].sections, []))
  644. self.compare_rules(expected, actual)
  645. def test_same_entity_conflicting_scheme(self, alt=None):
  646. # Test same entity being mapped by scheme conflicting with another.
  647. #
  648. # rtc = text -> rtc_text, rodata -> rtc_data
  649. # noflash = text -> iram0_text, rodata -> dram0_data
  650. #
  651. # This operation should fail.
  652. mapping = u"""
  653. [mapping:test]
  654. archive: libfreertos.a
  655. entries:
  656. croutine (noflash) #1
  657. croutine (rtc) #2
  658. """
  659. self.add_fragments(alt if alt else mapping)
  660. with self.assertRaises(GenerationException):
  661. self.generation.generate(self.entities)
  662. def test_complex_mapping_case(self, alt=None):
  663. # Test a complex case where an object is mapped using
  664. # one scheme, but a specific symbol in that object is mapped
  665. # using another. Another object and symbol is mapped the other way around.
  666. #
  667. # flash_text
  668. # *(EXCLUDE_FILE(libfreertos.a:croutine libfreertos.a:timers) .text ...) A, B
  669. #
  670. # flash_rodata
  671. # *(EXCLUDE_FILE(libfreertos.a:croutine libfreertos.a:timers) .rodata ...) A, B
  672. #
  673. # dram0_data
  674. # *(EXCLUDE_FILES(libfreertos.a:timers) .data ..) B
  675. # *(.dram ...)
  676. # *libfreertos.a:croutine(.rodata .rodata.*) C
  677. # *libfreertos.a:timers(.rodata.prvProcessReceivedCommands ...) E
  678. #
  679. # dram0_bss
  680. # *(EXCLUDE_FILE(libfreertos.a:timers) .bss .bss.* ...) B
  681. # *(EXCLUDE_FILE(libfreertos.a:timers) COMMON) B
  682. #
  683. # iram0_text
  684. # *(.iram ...)
  685. # *libfreertos.a:croutine(.literal .literal.prvCheckDelayedList ...) C
  686. # *libfreertos.a:timers(.literal .literal.prvProcessReceivedCommands ...) E
  687. #
  688. # rtc_text
  689. # *(rtc.text .rtc.literal)
  690. # libfreertos.a:croutine (.text.prvCheckPendingReadyList .literal.prvCheckPendingReadyList) F
  691. # libfreertos.a:timers (.text .text.prvCheckForValidListAndQueue ...) D.2
  692. #
  693. # rtc_data
  694. # *(rtc.data)
  695. # *(rtc.rodata)
  696. # libfreertos.a:timers (.data .data.*) D
  697. # libfreertos.a:timers (.rodata ...) D.2
  698. #
  699. # rtc_bss
  700. # *(rtc.bss .rtc.bss)
  701. # libfreertos.a:timers (.bss .bss.*) D
  702. # libfreertos.a:timers (COMMON) D
  703. mapping = u"""
  704. [mapping:test]
  705. archive: libfreertos.a
  706. entries:
  707. croutine (noflash) #1
  708. timers (rtc) #2
  709. timers:prvProcessReceivedCommands (noflash) #3
  710. croutine:prvCheckPendingReadyList (rtc) #4
  711. """
  712. self.add_fragments(alt if alt else mapping)
  713. actual = self.generation.generate(self.entities)
  714. expected = self.generate_default_rules()
  715. flash_text = expected['flash_text']
  716. flash_rodata = expected['flash_rodata']
  717. dram0_data = expected['dram0_data']
  718. iram0_text = expected['iram0_text']
  719. dram0_bss = expected['dram0_bss']
  720. rtc_text = expected['rtc_text']
  721. rtc_data = expected['rtc_data']
  722. rtc_bss = expected['rtc_bss']
  723. # Exclusions for #1 A
  724. flash_text[0].exclusions.add(CROUTINE)
  725. flash_rodata[0].exclusions.add(CROUTINE)
  726. # Exclusions for #2 B
  727. flash_text[0].exclusions.add(TIMERS)
  728. flash_rodata[0].exclusions.add(TIMERS)
  729. dram0_data[0].exclusions.add(TIMERS)
  730. dram0_bss[0].exclusions.add(TIMERS)
  731. dram0_bss[1].exclusions.add(TIMERS)
  732. # Commands for #1 C
  733. # List all relevant sections excluding #4 for text -> iram0_text
  734. croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine')
  735. filtered_sections = fnmatch.filter(croutine_sections, '.literal.*')
  736. filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*'))
  737. filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')]
  738. filtered_sections.append('.text')
  739. iram0_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), []))
  740. dram0_data.append(InputSectionDesc(CROUTINE, flash_rodata[0].sections, []))
  741. # Commands for #4 F
  742. # Processed first due to alphabetical ordering
  743. rtc_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []))
  744. # Commands for #2 D
  745. # List all relevant sections excluding #3 for text -> rtc_text and D.2
  746. # rodata -> rtc_data
  747. timers_sections = self.entities.get_sections('libfreertos.a', 'timers')
  748. filtered_sections = fnmatch.filter(timers_sections, '.literal.*')
  749. filtered_sections.extend(fnmatch.filter(timers_sections, '.text.*'))
  750. filtered_sections = [s for s in filtered_sections if not s.endswith('prvProcessReceivedCommands')]
  751. filtered_sections.append('.text')
  752. rtc_text.append(InputSectionDesc(TIMERS, set(filtered_sections), []))
  753. rtc_data.append(InputSectionDesc(TIMERS, dram0_data[0].sections, []))
  754. filtered_sections = fnmatch.filter(timers_sections, '.rodata.*')
  755. filtered_sections = [s for s in filtered_sections if not s.endswith('prvProcessReceivedCommands')]
  756. rtc_data.append(InputSectionDesc(TIMERS, set(filtered_sections), []))
  757. rtc_bss.append(InputSectionDesc(TIMERS, dram0_bss[0].sections, []))
  758. rtc_bss.append(InputSectionDesc(TIMERS, dram0_bss[1].sections, []))
  759. # Commands for #3 E
  760. iram0_text.append(InputSectionDesc(TIMERS, set(['.text.prvProcessReceivedCommands', '.literal.prvProcessReceivedCommands']), []))
  761. dram0_data.append(InputSectionDesc(TIMERS, set(['.rodata.prvProcessReceivedCommands']), []))
  762. self.compare_rules(expected, actual)
  763. def test_multiple_mapping_fragments(self):
  764. # Test mapping multiple fragments succeeds, particularly
  765. # generating exclusions from the default command of archive
  766. # and object specificity.
  767. #
  768. # flash_text
  769. # * (EXCLUDE_FILE(libfreertos.a libfreertos.a:croutine) .text ...)
  770. #
  771. # flash_rodata
  772. # * (EXCLUDE_FILE(libfreertos.a libfreertos.a:croutine) .text ...)
  773. #
  774. # iram0_text
  775. mapping = u"""
  776. [mapping:test_1]
  777. archive: libfreertos.a
  778. entries:
  779. croutine (noflash) #1
  780. [mapping:test_2]
  781. archive: libfreertos2.a
  782. entries:
  783. * (noflash) #2
  784. """
  785. self.add_fragments(mapping)
  786. actual = self.generation.generate(self.entities)
  787. expected = self.generate_default_rules()
  788. flash_text = expected['flash_text']
  789. flash_rodata = expected['flash_rodata']
  790. iram0_text = expected['iram0_text']
  791. dram0_data = expected['dram0_data']
  792. # Exclusions for #1 A
  793. flash_text[0].exclusions.add(CROUTINE)
  794. flash_rodata[0].exclusions.add(CROUTINE)
  795. # Exclusions for #1 & #2 B
  796. flash_text[0].exclusions.add(FREERTOS2)
  797. flash_rodata[0].exclusions.add(FREERTOS2)
  798. # Command for #1 C
  799. iram0_text.append(InputSectionDesc(CROUTINE, flash_text[0].sections, []))
  800. dram0_data.append(InputSectionDesc(CROUTINE, flash_rodata[0].sections, []))
  801. # Command for #1 & #2 D
  802. iram0_text.append(InputSectionDesc(FREERTOS2, flash_text[0].sections, []))
  803. dram0_data.append(InputSectionDesc(FREERTOS2, flash_rodata[0].sections, []))
  804. self.compare_rules(expected, actual)
  805. def test_mapping_same_lib_in_multiple_fragments_no_conflict(self):
  806. # Test mapping fragments operating on the same archive.
  807. # In these cases, the entries are taken together.
  808. #
  809. # Uses the same entries as C_05 but spreads them across
  810. # two fragments. The output should still be the same.
  811. mapping = u"""
  812. [mapping:test_1]
  813. archive: libfreertos.a
  814. entries:
  815. croutine (noflash) #1
  816. timers:prvProcessReceivedCommands (noflash) #3
  817. [mapping:test_2]
  818. archive: libfreertos.a
  819. entries:
  820. timers (rtc) #2
  821. croutine:prvCheckPendingReadyList (rtc) #4
  822. """
  823. self.test_complex_mapping_case(mapping)
  824. def test_mapping_same_lib_in_multiple_fragments_conflict(self):
  825. # Test mapping fragments operating on the same archive
  826. # with conflicting mappings.
  827. mapping = u"""
  828. [mapping:test_1]
  829. archive: libfreertos.a
  830. entries:
  831. croutine (noflash) #1
  832. [mapping:test_2]
  833. archive: libfreertos.a
  834. entries:
  835. croutine (rtc) #2
  836. """
  837. self.test_same_entity_conflicting_scheme(mapping)
  838. def test_command_order(self):
  839. # Test command order sorting: the commands should be sorted by specificity, then
  840. # alphabetically. This contributes to deterministic output given
  841. # the same input mapping entries.
  842. #
  843. # This ordering is also tested in other tests as a side-effect.
  844. #
  845. # flash_text
  846. # * (EXCLUDE_FILE(libfreertos.a:croutine libfreertos.a:croutine2)) A
  847. # libfreertos.a:croutine(.text ....) B
  848. #
  849. # iram0_text
  850. #
  851. # * (.iram .iram.*)
  852. # libfreertos:croutine(.text .literal ...) C
  853. # libfreertos:croutine(.text.prvCheckDelayedList .literal.prvCheckDelayedList) F
  854. # libfreertos:croutine(.text.prvCheckPendingReadyList .literal.prvCheckPendingReadyList) G
  855. # libfreertos2:croutine(.text .literal ...) D
  856. # libfreertos2:croutine2(.text .literal ...) E
  857. mapping = u"""
  858. [mapping:freertos2]
  859. archive: libfreertos2.a
  860. entries:
  861. croutine2 (noflash_text) #1
  862. croutine (noflash_text) #2
  863. [mapping:freertos]
  864. archive: libfreertos.a
  865. entries:
  866. croutine:prvCheckPendingReadyList (noflash_text) #3
  867. croutine:prvCheckDelayedList (noflash_text) #4
  868. """
  869. self.add_fragments(mapping)
  870. actual = self.generation.generate(self.entities)
  871. expected = self.generate_default_rules()
  872. flash_text = expected['flash_text']
  873. iram0_text = expected['iram0_text']
  874. # Exclusions for #1 A
  875. flash_text[0].exclusions.add(CROUTINE)
  876. flash_text[0].exclusions.add(Entity(FREERTOS2.archive, 'croutine2'))
  877. flash_text[0].exclusions.add(Entity(FREERTOS2.archive, 'croutine'))
  878. # Intermediate command for #3 and #4 B
  879. croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine')
  880. filtered_sections = fnmatch.filter(croutine_sections, '.literal.*')
  881. filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*'))
  882. filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')]
  883. filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckDelayedList')]
  884. filtered_sections.append('.text')
  885. flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), []))
  886. # Command for
  887. iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckDelayedList', '.literal.prvCheckDelayedList']), []))
  888. iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []))
  889. iram0_text.append(InputSectionDesc(Entity(FREERTOS2.archive, 'croutine'), flash_text[0].sections, []))
  890. iram0_text.append(InputSectionDesc(Entity(FREERTOS2.archive, 'croutine2'), flash_text[0].sections, []))
  891. self.compare_rules(expected, actual)
  892. def test_ambigious_obj(self):
  893. # Command generation for ambiguous entry should fail.
  894. mapping = u"""
  895. [mapping:test]
  896. archive: libfreertos.a
  897. entries:
  898. port:xPortGetTickRateHz (noflash) #1
  899. """
  900. self.add_fragments(mapping)
  901. with self.assertRaises(GenerationException):
  902. self.generation.generate(self.entities)
  903. def test_root_mapping_fragment_conflict(self):
  904. # Test that root mapping fragments are also checked for
  905. # conflicts.
  906. #
  907. # 'custom_scheme' entries conflict the 'default' scheme
  908. # entries.
  909. mapping = u"""
  910. [scheme:custom_scheme]
  911. entries:
  912. flash_text -> iram0_text
  913. [mapping:default2]
  914. archive: *
  915. entries:
  916. * (custom_scheme)
  917. """
  918. self.add_fragments(mapping)
  919. with self.assertRaises(GenerationException):
  920. self.generation.generate(self.entities)
  921. def test_root_mapping_fragment_duplicate(self):
  922. # Same root mappings have no effect.
  923. #
  924. # custom_scheme has the 'iram -> iram0_text' in common with
  925. # default scheme
  926. mapping = u"""
  927. [sections:custom_section]
  928. entries:
  929. .custom_section
  930. [scheme:custom_scheme]
  931. entries:
  932. iram -> iram0_text
  933. custom_section -> iram0_text
  934. [mapping:default2]
  935. archive: *
  936. entries:
  937. * (custom_scheme)
  938. """
  939. self.add_fragments(mapping)
  940. actual = self.generation.generate(self.entities)
  941. expected = self.generate_default_rules()
  942. # Generate default command A
  943. # Since these are the same 'specificity', the commands
  944. # are arranged alphabetically.
  945. expected['iram0_text'].append(expected['iram0_text'][0])
  946. expected['iram0_text'][0] = InputSectionDesc(ROOT, ['.custom_section'], [])
  947. self.compare_rules(expected, actual)
  948. class ConfigTest(GenerationTest):
  949. # Test command generation with conditions
  950. def _test_conditional_on_scheme(self, perf, alt=None):
  951. # Test that proper commands are generated if using
  952. # schemes with conditional entries.
  953. scheme = u"""
  954. [sections:cond_text_data]
  955. entries:
  956. if PERFORMANCE_LEVEL >= 1:
  957. .text+
  958. .literal+
  959. else:
  960. .rodata+
  961. [scheme:cond_noflash]
  962. entries:
  963. if PERFORMANCE_LEVEL >= 1:
  964. cond_text_data -> iram0_text
  965. else:
  966. cond_text_data -> dram0_data
  967. """
  968. mapping = u"""
  969. [mapping:test]
  970. archive: lib.a
  971. entries:
  972. * (cond_noflash)
  973. """
  974. self.sdkconfig.config.syms['PERFORMANCE_LEVEL'].set_value(str(perf))
  975. self.add_fragments(scheme)
  976. self.add_fragments(alt if alt else mapping)
  977. actual = self.generation.generate(self.entities)
  978. expected = self.generate_default_rules()
  979. if perf >= 1:
  980. flash_text = expected['flash_text']
  981. iram0_text = expected['iram0_text']
  982. flash_text[0].exclusions.add(Entity('lib.a'))
  983. iram0_text.append(InputSectionDesc(Entity('lib.a'), flash_text[0].sections, []))
  984. else:
  985. flash_rodata = expected['flash_rodata']
  986. dram0_data = expected['dram0_data']
  987. flash_rodata[0].exclusions.add(Entity('lib.a'))
  988. dram0_data.append(InputSectionDesc(Entity('lib.a'), flash_rodata[0].sections, []))
  989. self.compare_rules(expected, actual)
  990. def test_conditional_on_scheme_00(self):
  991. self._test_conditional_on_scheme(0)
  992. def test_conditional_on_scheme_01(self):
  993. self._test_conditional_on_scheme(1)
  994. def test_conditional_mapping(self, alt=None):
  995. # Test that proper commands are generated
  996. # in conditional mapping entries.
  997. mapping = u"""
  998. [mapping:default]
  999. archive: *
  1000. entries:
  1001. * (default)
  1002. [mapping:test]
  1003. archive: lib.a
  1004. entries:
  1005. if PERFORMANCE_LEVEL = 1:
  1006. obj1 (noflash)
  1007. elif PERFORMANCE_LEVEL = 2:
  1008. obj1 (noflash)
  1009. obj2 (noflash)
  1010. elif PERFORMANCE_LEVEL = 3:
  1011. obj1 (noflash)
  1012. obj2 (noflash)
  1013. obj3 (noflash)
  1014. """
  1015. for perf_level in range(0, 4):
  1016. self.sdkconfig.config.syms['PERFORMANCE_LEVEL'].set_value(str(perf_level))
  1017. self.generation.mappings = {}
  1018. self.add_fragments(alt if alt else mapping)
  1019. actual = self.generation.generate(self.entities)
  1020. expected = self.generate_default_rules()
  1021. if perf_level < 4 and perf_level > 0:
  1022. for append_no in range(1, perf_level + 1):
  1023. flash_text = expected['flash_text']
  1024. flash_rodata = expected['flash_rodata']
  1025. iram0_text = expected['iram0_text']
  1026. dram0_data = expected['dram0_data']
  1027. obj_str = 'obj' + str(append_no)
  1028. flash_text[0].exclusions.add(Entity('lib.a', obj_str))
  1029. flash_rodata[0].exclusions.add(Entity('lib.a', obj_str))
  1030. iram0_text.append(InputSectionDesc(Entity('lib.a', obj_str), flash_text[0].sections, []))
  1031. dram0_data.append(InputSectionDesc(Entity('lib.a', obj_str), flash_rodata[0].sections, []))
  1032. self.compare_rules(expected, actual)
  1033. def test_multiple_fragment_same_lib_conditional(self):
  1034. # Test conditional entries on new mapping fragment grammar.
  1035. # across multiple fragments.
  1036. mapping = u"""
  1037. [mapping:default]
  1038. archive: *
  1039. entries:
  1040. * (default)
  1041. [mapping:base]
  1042. archive: lib.a
  1043. entries:
  1044. if PERFORMANCE_LEVEL = 1:
  1045. obj1 (noflash)
  1046. elif PERFORMANCE_LEVEL = 2:
  1047. obj1 (noflash)
  1048. elif PERFORMANCE_LEVEL = 3:
  1049. obj1 (noflash)
  1050. [mapping:extra]
  1051. archive: lib.a
  1052. entries:
  1053. if PERFORMANCE_LEVEL = 1:
  1054. obj1 (noflash) # ignore duplicate definition
  1055. elif PERFORMANCE_LEVEL = 2:
  1056. obj2 (noflash)
  1057. elif PERFORMANCE_LEVEL = 3:
  1058. obj2 (noflash)
  1059. obj3 (noflash)
  1060. """
  1061. self.test_conditional_mapping(mapping)
  1062. class FlagTest(GenerationTest):
  1063. # Test correct generation of mapping fragment entries
  1064. # with flags.
  1065. def test_flags_basics(self):
  1066. # Test that input section commands additions are done (KEEP SORT).
  1067. # Test that order dependent commands are properly generated (ALIGN, SURROUND)
  1068. # Normally, if an entry has the same mapping as parent, commands.
  1069. # are not emitted for them. However, if there are flags, they should be -
  1070. # only for the scheme entries that have flags, though.
  1071. # Flag entries split across multiple entries work.
  1072. #
  1073. # flash_text
  1074. # *((EXCLUDE_FILE(libfreertos:timers libfreertos:croutine).text ...) A
  1075. # KEEP(* (SORT_BY_NAME(EXCLUDE_FILE(libfreertos:timers).text) ...) B
  1076. #
  1077. # flash_rodata
  1078. # *((EXCLUDE_FILE(libfreertos:timers) .rodata ...) C
  1079. # _sym2_start D.1
  1080. # . = ALIGN(4) E.1
  1081. # KEEP(* (EXCLUDE_FILE(libfreertos:timers) .rodata ...) F
  1082. # _sym2_end D.2
  1083. # . = ALIGN(4) E.2
  1084. #
  1085. # iram0_text
  1086. # *(.iram .iram.*)
  1087. # . = ALIGN(4) G.1
  1088. # _sym1_start H.1
  1089. # libfreertos.a:croutine(.text .literal ...) I
  1090. # . = ALIGN(4) G.2
  1091. # _sym1_end H.2
  1092. mapping = u"""
  1093. [mapping:test]
  1094. archive: libfreertos.a
  1095. entries:
  1096. croutine (noflash_text);
  1097. text->iram0_text ALIGN(4, pre, post) SURROUND(sym1) #1
  1098. timers (default);
  1099. text->flash_text KEEP() SORT(name) #2
  1100. timers (default);
  1101. rodata->flash_rodata SURROUND(sym2) ALIGN(4, pre, post) #3
  1102. """
  1103. self.add_fragments(mapping)
  1104. actual = self.generation.generate(self.entities)
  1105. expected = self.generate_default_rules()
  1106. flash_text = expected['flash_text']
  1107. iram0_text = expected['iram0_text']
  1108. flash_rodata = expected['flash_rodata']
  1109. # Exclusions in flash_text for timers and croutine A
  1110. flash_text[0].exclusions.add(CROUTINE)
  1111. flash_text[0].exclusions.add(TIMERS)
  1112. # Command for #3 B
  1113. flash_text.append(InputSectionDesc(TIMERS, flash_text[0].sections, [], keep=True, sort=('name', None)))
  1114. # Exclusions in flash_rodata for timers C
  1115. flash_rodata[0].exclusions.add(TIMERS)
  1116. # Commands for #3 D.1, E.1, F, D.2, E.2
  1117. flash_rodata.append(SymbolAtAddress('_sym2_start'))
  1118. flash_rodata.append(AlignAtAddress(4))
  1119. flash_rodata.append(InputSectionDesc(TIMERS, flash_rodata[0].sections, []))
  1120. flash_rodata.append(SymbolAtAddress('_sym2_end'))
  1121. flash_rodata.append(AlignAtAddress(4))
  1122. # Commands for # G.1, H.1, I, G.2, H.2
  1123. iram0_text.append(AlignAtAddress(4))
  1124. iram0_text.append(SymbolAtAddress('_sym1_start'))
  1125. iram0_text.append(InputSectionDesc(CROUTINE, flash_text[0].sections, []))
  1126. iram0_text.append(AlignAtAddress(4))
  1127. iram0_text.append(SymbolAtAddress('_sym1_end'))
  1128. self.compare_rules(expected, actual)
  1129. def test_flags_intermediate_exclusion_command_root(self):
  1130. # Test that intermediate exclusion commands from root-level commands
  1131. # are included in the flags.
  1132. #
  1133. # flash_text
  1134. # _sym1_start A.1
  1135. # KEEP(* (EXCLUDE_FILE(libfreertos:croutine).text ...) B
  1136. # KEEP(libfreertos.a:croutine(...))) C
  1137. # _sym1_end A.2
  1138. #
  1139. # iram0_text
  1140. # *(.iram .iram.*)
  1141. # libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) D
  1142. mapping = u"""
  1143. [mapping:default]
  1144. archive: *
  1145. entries:
  1146. # 1
  1147. * (default);
  1148. text->flash_text SURROUND(sym1) KEEP() #2
  1149. [mapping:test]
  1150. archive: libfreertos.a
  1151. entries:
  1152. croutine:prvCheckPendingReadyList (noflash_text) #3
  1153. """
  1154. self.generation.mappings = {}
  1155. self.add_fragments(mapping)
  1156. actual = self.generation.generate(self.entities)
  1157. expected = self.generate_default_rules()
  1158. flash_text = expected['flash_text']
  1159. iram0_text = expected['iram0_text']
  1160. # Command for #2, pre A.1
  1161. flash_text.insert(0, SymbolAtAddress('_sym1_start'))
  1162. # Command for #1 with KEEP() B
  1163. # and exclusion for #3
  1164. flash_text[1].keep = True
  1165. flash_text[1].exclusions.add(CROUTINE)
  1166. # Implicit exclusion command for #3 C
  1167. croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine')
  1168. filtered_sections = fnmatch.filter(croutine_sections, '.literal.*')
  1169. filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*'))
  1170. filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')]
  1171. filtered_sections.append('.text')
  1172. flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [], keep=True))
  1173. # Command for #2, post A.2
  1174. flash_text.append(SymbolAtAddress('_sym1_end'))
  1175. # Command for #3 D
  1176. iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []))
  1177. self.compare_rules(expected, actual)
  1178. def test_flags_intermediate_exclusion_command_lib(self):
  1179. # Test that intermediate exclusion commands from lib-level commands
  1180. # are included in the flags.
  1181. #
  1182. # flash_text
  1183. # *(EXCLUDE_FILE(libfreertos.a).text ...)
  1184. # _sym1_start A.1
  1185. # KEEP(libfreertos.a(EXCLUDE_FILE(libfreertos:croutine).text.* ...)) B
  1186. # KEEP(libfreertos.a:croutine(...))) C
  1187. # _sym1_end A.2
  1188. #
  1189. # iram0_text
  1190. # *(.iram .iram.*)
  1191. # libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) D
  1192. mapping = u"""
  1193. [mapping:test]
  1194. archive: libfreertos.a
  1195. entries:
  1196. # 1
  1197. * (default);
  1198. text->flash_text SURROUND(sym1) KEEP() #2
  1199. croutine:prvCheckPendingReadyList (noflash_text) #3
  1200. """
  1201. self.add_fragments(mapping)
  1202. actual = self.generation.generate(self.entities)
  1203. expected = self.generate_default_rules()
  1204. flash_text = expected['flash_text']
  1205. iram0_text = expected['iram0_text']
  1206. # Command for #2, pre A.1
  1207. flash_text.append(SymbolAtAddress('_sym1_start'))
  1208. flash_text[0].exclusions.add(FREERTOS)
  1209. # Command for #1 with KEEP() B
  1210. # and exclusion for #3
  1211. flash_text.append(InputSectionDesc(FREERTOS, flash_text[0].sections, [CROUTINE], keep=True))
  1212. # Implicit exclusion command for #3 C
  1213. croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine')
  1214. filtered_sections = fnmatch.filter(croutine_sections, '.literal.*')
  1215. filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*'))
  1216. filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')]
  1217. filtered_sections.append('.text')
  1218. flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [], keep=True))
  1219. # Command for #2, post A.2
  1220. flash_text.append(SymbolAtAddress('_sym1_end'))
  1221. # Command for #3 C
  1222. iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []))
  1223. self.compare_rules(expected, actual)
  1224. def test_flags_intermediate_exclusion_command_obj(self):
  1225. # Test that intermediate exclusion commands from obj-level commands
  1226. # are included in the flags.
  1227. #
  1228. # flash_text
  1229. # *(EXCLUDE_FILE(libfreertos.a).text ...)
  1230. # _sym1_start A.1
  1231. # KEEP(libfreertos.a:croutine(...))) B
  1232. # _sym1_end A.2
  1233. #
  1234. # iram0_text
  1235. # *(.iram .iram.*)
  1236. # libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) C
  1237. mapping = u"""
  1238. [mapping:test]
  1239. archive: libfreertos.a
  1240. entries:
  1241. # 1
  1242. croutine (default);
  1243. text->flash_text SURROUND(sym1) KEEP() #2
  1244. croutine:prvCheckPendingReadyList (noflash_text) #3
  1245. """
  1246. self.add_fragments(mapping)
  1247. actual = self.generation.generate(self.entities)
  1248. expected = self.generate_default_rules()
  1249. flash_text = expected['flash_text']
  1250. iram0_text = expected['iram0_text']
  1251. # Command for #2, pre A.1
  1252. flash_text.append(SymbolAtAddress('_sym1_start'))
  1253. flash_text[0].exclusions.add(CROUTINE)
  1254. # Implicit exclusion command for #3 B
  1255. croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine')
  1256. filtered_sections = fnmatch.filter(croutine_sections, '.literal.*')
  1257. filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*'))
  1258. filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')]
  1259. filtered_sections.append('.text')
  1260. flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [], keep=True))
  1261. # Command for #2, post A.2
  1262. flash_text.append(SymbolAtAddress('_sym1_end'))
  1263. # Command for #3 C
  1264. iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []))
  1265. self.compare_rules(expected, actual)
  1266. def test_flags_separate_exclusion_command_if_explicit_root(self):
  1267. # Explicit commands are separated from the parent's flags.
  1268. #
  1269. # flash_text
  1270. # _sym1_start A.1
  1271. # KEEP(* (EXCLUDE_FILE(libfreertos:croutine).text ...) B
  1272. # _sym1_end A.2
  1273. # KEEP(libfreertos.a:croutine(...))) C
  1274. #
  1275. # iram0_text
  1276. # *(.iram .iram.*)
  1277. # libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) D
  1278. mapping = u"""
  1279. [mapping:default]
  1280. archive: *
  1281. entries:
  1282. # 1
  1283. * (default);
  1284. text->flash_text SURROUND(sym1) KEEP() #2
  1285. [mapping:test]
  1286. archive: libfreertos.a
  1287. entries:
  1288. croutine (default) #3
  1289. croutine:prvCheckPendingReadyList (noflash_text) #4
  1290. """
  1291. self.generation.mappings = {}
  1292. self.add_fragments(mapping)
  1293. actual = self.generation.generate(self.entities)
  1294. expected = self.generate_default_rules()
  1295. flash_text = expected['flash_text']
  1296. iram0_text = expected['iram0_text']
  1297. # Command for #2, pre A.1
  1298. flash_text.insert(0, SymbolAtAddress('_sym1_start'))
  1299. # Command for #1 with KEEP() B
  1300. # and exclusion for #3
  1301. flash_text[1].keep = True
  1302. flash_text[1].exclusions.add(CROUTINE)
  1303. # Command for #2, post A.2
  1304. flash_text.append(SymbolAtAddress('_sym1_end'))
  1305. # Command for #3 C
  1306. croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine')
  1307. filtered_sections = fnmatch.filter(croutine_sections, '.literal.*')
  1308. filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*'))
  1309. filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')]
  1310. filtered_sections.append('.text')
  1311. flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), []))
  1312. # Command for #4 D
  1313. iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []))
  1314. self.compare_rules(expected, actual)
  1315. def test_flags_separate_exclusion_command_if_explicit_lib(self):
  1316. # Explicit commands are separated from the parent's flags.
  1317. #
  1318. # flash_text
  1319. # *(EXCLUDE_FILE(libfreertos.a).text ...)
  1320. # _sym1_start A.1
  1321. # KEEP(libfreertos.a(EXCLUDE_FILE(libfreertos:croutine).text.* ...)) B
  1322. # _sym1_end A.2
  1323. # KEEP(libfreertos.a:croutine(...))) C
  1324. #
  1325. # iram0_text
  1326. # *(.iram .iram.*)
  1327. # libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) D
  1328. mapping = u"""
  1329. [mapping:test]
  1330. archive: libfreertos.a
  1331. entries:
  1332. # 1
  1333. * (default);
  1334. text->flash_text SURROUND(sym1) KEEP()
  1335. croutine (default) #2
  1336. croutine:prvCheckPendingReadyList (noflash_text) #3
  1337. """
  1338. self.add_fragments(mapping)
  1339. actual = self.generation.generate(self.entities)
  1340. expected = self.generate_default_rules()
  1341. flash_text = expected['flash_text']
  1342. iram0_text = expected['iram0_text']
  1343. # Command for #2, pre A.1
  1344. flash_text.append(SymbolAtAddress('_sym1_start'))
  1345. flash_text[0].exclusions.add(FREERTOS)
  1346. # Command for #1 with KEEP() B
  1347. # and exclusion for #3
  1348. flash_text.append(InputSectionDesc(FREERTOS, flash_text[0].sections, [CROUTINE], keep=True))
  1349. # Command for #2, post A.2
  1350. flash_text.append(SymbolAtAddress('_sym1_end'))
  1351. # Implicit exclusion command for #3 C
  1352. croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine')
  1353. filtered_sections = fnmatch.filter(croutine_sections, '.literal.*')
  1354. filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*'))
  1355. filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')]
  1356. filtered_sections.append('.text')
  1357. flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), []))
  1358. # Command for #3 C
  1359. iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []))
  1360. self.compare_rules(expected, actual)
  1361. def test_flag_additions(self):
  1362. # Test ability to add flags as long as no other mapping fragments
  1363. # does the same thing.
  1364. mapping = u"""
  1365. [mapping:default_add_flag]
  1366. archive: *
  1367. entries:
  1368. * (default);
  1369. text->flash_text KEEP()
  1370. """
  1371. self.add_fragments(mapping)
  1372. actual = self.generation.generate(self.entities)
  1373. expected = self.generate_default_rules()
  1374. flash_text = expected['flash_text']
  1375. flash_text[0].keep = True
  1376. self.compare_rules(expected, actual)
  1377. def test_flags_flag_additions_duplicate(self):
  1378. # Test same flags added to same entity - these
  1379. # are ignored.
  1380. mapping = u"""
  1381. [mapping:default_add_flag_1]
  1382. archive: *
  1383. entries:
  1384. * (default);
  1385. text->flash_text KEEP()
  1386. [mapping:default_add_flag_2]
  1387. archive: *
  1388. entries:
  1389. * (default);
  1390. text->flash_text KEEP()
  1391. """
  1392. self.add_fragments(mapping)
  1393. actual = self.generation.generate(self.entities)
  1394. expected = self.generate_default_rules()
  1395. flash_text = expected['flash_text']
  1396. flash_text[0].keep = True
  1397. self.compare_rules(expected, actual)
  1398. def test_flags_flag_additions_conflict(self):
  1399. # Test condition where multiple fragments specifies flags
  1400. # to same entity - should generate exception.
  1401. mapping = u"""
  1402. [mapping:default_add_flag_1]
  1403. archive: *
  1404. entries:
  1405. * (default);
  1406. text->flash_text ALIGN(2)
  1407. [mapping:default_add_flag_2]
  1408. archive: *
  1409. entries:
  1410. * (default);
  1411. text->flash_text SURROUND(sym1)
  1412. """
  1413. self.add_fragments(mapping)
  1414. with self.assertRaises(GenerationException):
  1415. self.generation.generate(self.entities)
  1416. if __name__ == '__main__':
  1417. unittest.main()