test_generation.py 70 KB

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