1#!/usr/bin/env python 2# 3# Copyright 2021 Espressif Systems (Shanghai) CO LTD 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18import collections 19import fnmatch 20import os 21import sys 22import tempfile 23import unittest 24 25try: 26 from generation import Generation, GenerationException 27except ImportError: 28 sys.path.append('../') 29 from generation import Generation, GenerationException 30 31from io import StringIO 32 33from entity import Entity, EntityDB 34from fragments import FragmentFile 35from linker_script import LinkerScript 36from output_commands import AlignAtAddress, InputSectionDesc, SymbolAtAddress 37from sdkconfig import SDKConfig 38 39ROOT = Entity('*') 40 41FREERTOS = Entity('libfreertos.a') 42CROUTINE = Entity('libfreertos.a', 'croutine') 43TIMERS = Entity('libfreertos.a', 'timers') 44 45FREERTOS2 = Entity('libfreertos2.a') 46 47 48class GenerationTest(unittest.TestCase): 49 50 def setUp(self): 51 self.generation = Generation() 52 self.entities = None 53 self.linker_script = None 54 55 with tempfile.NamedTemporaryFile(delete=False) as f: 56 self.kconfigs_source_file = os.path.join(tempfile.gettempdir(), f.name) 57 self.addCleanup(os.remove, self.kconfigs_source_file) 58 with tempfile.NamedTemporaryFile(delete=False) as f: 59 self.kconfig_projbuilds_source_file = os.path.join(tempfile.gettempdir(), f.name) 60 self.addCleanup(os.remove, self.kconfig_projbuilds_source_file) 61 62 os.environ['COMPONENT_KCONFIGS_SOURCE_FILE'] = self.kconfigs_source_file 63 os.environ['COMPONENT_KCONFIGS_PROJBUILD_SOURCE_FILE'] = self.kconfig_projbuilds_source_file 64 os.environ['COMPONENT_KCONFIGS'] = '' 65 os.environ['COMPONENT_KCONFIGS_PROJBUILD'] = '' 66 67 # prepare_kconfig_files.py doesn't have to be called because COMPONENT_KCONFIGS and 68 # COMPONENT_KCONFIGS_PROJBUILD are empty 69 70 self.sdkconfig = SDKConfig('data/Kconfig', 'data/sdkconfig') 71 72 with open('data/base.lf') as fragment_file_obj: 73 fragment_file = FragmentFile(fragment_file_obj, self.sdkconfig) 74 self.generation.add_fragments_from_file(fragment_file) 75 76 self.entities = EntityDB() 77 78 with open('data/libfreertos.a.txt') as objdump: 79 self.entities.add_sections_info(objdump) 80 81 with open('data/linker_script.ld') as linker_script: 82 self.linker_script = LinkerScript(linker_script) 83 84 @staticmethod 85 def create_fragment_file(contents, name='test_fragment.lf'): 86 f = StringIO(contents) 87 f.name = name 88 return f 89 90 def add_fragments(self, text): 91 fragment_file = self.create_fragment_file(text) 92 fragment_file = FragmentFile(fragment_file, self.sdkconfig) 93 self.generation.add_fragments_from_file(fragment_file) 94 95 def write(self, expected, actual): 96 self.linker_script.fill(expected) 97 self.linker_script.write(open('expected.ld', 'w')) 98 99 self.linker_script.fill(actual) 100 self.linker_script.write(open('actual.ld', 'w')) 101 102 def generate_default_rules(self): 103 rules = collections.defaultdict(list) 104 105 rules['flash_text'].append(InputSectionDesc(ROOT, ['.literal', '.literal.*', '.text', '.text.*'], [])) 106 rules['flash_rodata'].append(InputSectionDesc(ROOT, ['.rodata', '.rodata.*'], [])) 107 rules['dram0_data'].append(InputSectionDesc(ROOT, ['.data', '.data.*'], [])) 108 rules['dram0_data'].append(InputSectionDesc(ROOT, ['.dram', '.dram.*'], [])) 109 rules['dram0_bss'].append(InputSectionDesc(ROOT, ['.bss', '.bss.*'], [])) 110 rules['dram0_bss'].append(InputSectionDesc(ROOT, ['COMMON'], [])) 111 rules['iram0_text'].append(InputSectionDesc(ROOT, ['.iram', '.iram.*'], [])) 112 rules['rtc_text'].append(InputSectionDesc(ROOT, ['.rtc.text', '.rtc.literal'], [])) 113 rules['rtc_data'].append(InputSectionDesc(ROOT, ['.rtc.data'], [])) 114 rules['rtc_data'].append(InputSectionDesc(ROOT, ['.rtc.rodata'], [])) 115 rules['rtc_bss'].append(InputSectionDesc(ROOT, ['.rtc.bss'], [])) 116 117 return rules 118 119 def compare_rules(self, expected, actual): 120 self.assertEqual(set(expected.keys()), set(actual.keys())) 121 122 for target in sorted(actual.keys()): 123 message = 'failed target %s' % target 124 a_cmds = actual[target] 125 e_cmds = expected[target] 126 127 self.assertEqual(len(a_cmds), len(e_cmds), message) 128 129 for a, e in zip(a_cmds, e_cmds): 130 self.assertEqual(a, e, message) 131 132 def get_default(self, target, rules): 133 return rules[target][0] 134 135 136class DefaultMappingTest(GenerationTest): 137 138 def test_rule_generation_default(self): 139 # Checks that default rules are generated from 140 # the default scheme properly and even if no mappings 141 # are defined. 142 actual = self.generation.generate(self.entities) 143 expected = self.generate_default_rules() 144 145 self.compare_rules(expected, actual) 146 147 def test_default_mapping_lib(self): 148 # Mapping a library with default mapping. This should not emit additional rules, 149 # other than the default ones. 150 mapping = u""" 151[mapping:test] 152archive: libfreertos.a 153entries: 154 * (default) 155""" 156 self.add_fragments(mapping) 157 self.test_rule_generation_default() 158 159 def test_default_mapping_obj(self): 160 # Mapping an object with default mapping. This should not emit additional rules, 161 # other than the default ones. 162 mapping = u""" 163[mapping:test] 164archive: libfreertos.a 165entries: 166 croutine (default) 167""" 168 self.add_fragments(mapping) 169 self.test_rule_generation_default() 170 171 def test_default_mapping_symbol(self): 172 # Mapping a symbol with default mapping. This should not emit additional rules, 173 # other than the default ones. 174 mapping = u""" 175[mapping:test] 176archive: libfreertos.a 177entries: 178 croutine:prvCheckPendingReadyList (default) #1 179""" 180 self.add_fragments(mapping) 181 self.test_rule_generation_default() 182 183 def test_default_mapping_all(self): 184 # Mapping a library, object, and symbol with default mapping. This should not emit additional rules, 185 # other than the default ones. 186 mapping = u""" 187[mapping:test] 188archive: libfreertos.a 189entries: 190 * (default) #1 191 croutine (default) #2 192 croutine:prvCheckPendingReadyList (default) #3 193""" 194 self.add_fragments(mapping) 195 self.test_rule_generation_default() 196 197 def test_default_mapping_lib_symbol(self): 198 # Mapping a library, and symbol with default mapping. This should not emit additional rules, 199 # other than the default ones. 200 # 201 # This is a check needed to make sure generation does not generate 202 # intermediate commands due to presence of symbol mapping. 203 mapping = u""" 204[mapping:test] 205archive: libfreertos.a 206entries: 207 * (default) #1 208 croutine:prvCheckPendingReadyList (default) #2 209""" 210 self.add_fragments(mapping) 211 self.test_rule_generation_default() 212 213 def test_default_mapping_obj_symbol(self): 214 # Mapping a library, and symbol with default mapping. This should not emit additional rules, 215 # other than the default ones. 216 # 217 # This is a check needed to make sure generation does not generate 218 # intermediate commands due to presence of symbol mapping. 219 mapping = u""" 220[mapping:test] 221archive: libfreertos.a 222entries: 223 croutine (default) #1 224 croutine:prvCheckPendingReadyList (default) #2 225""" 226 self.add_fragments(mapping) 227 self.test_rule_generation_default() 228 229 230class BasicTest(GenerationTest): 231 # Test basic and fundamental interactions between typical 232 # entries. 233 234 def test_nondefault_mapping_lib(self, alt=None): 235 # Test mapping entry different from default for a library. 236 # There should be exclusions in the default commands for flash_text and flash_rodata: 237 # 238 # flash_text 239 # *((EXCLUDE_FILE(libfreertos.a)) .literal ...) A 240 # 241 # Commands placing the entire library in iram, dram should be generated: 242 # 243 # iram0_text 244 # *(.iram ...) 245 # *libfreertos.a(.literal ...) B 246 mapping = u""" 247[mapping:test] 248archive: libfreertos.a 249entries: 250 * (noflash) #1 251""" 252 self.add_fragments(alt if alt else mapping) 253 actual = self.generation.generate(self.entities) 254 expected = self.generate_default_rules() 255 256 flash_text = expected['flash_text'] 257 flash_rodata = expected['flash_rodata'] 258 iram0_text = expected['iram0_text'] 259 dram0_data = expected['dram0_data'] 260 261 # Generate exclusions in flash_text and flash_rodata A 262 flash_text[0].exclusions.add(FREERTOS) 263 flash_rodata[0].exclusions.add(FREERTOS) 264 265 # Input section commands in iram_text and dram0_data for #1 B 266 iram0_text.append(InputSectionDesc(FREERTOS, flash_text[0].sections, [])) 267 dram0_data.append(InputSectionDesc(FREERTOS, flash_rodata[0].sections, [])) 268 269 self.compare_rules(expected, actual) 270 271 def test_nondefault_mapping_obj(self, alt=None): 272 # Test mapping entry different from default for an object. 273 # There should be exclusions in the default commands for flash_text and flash_rodata: 274 # 275 # flash_text 276 # *((EXCLUDE_FILE(libfreertos.a:croutine)) .literal ...) A 277 # 278 # Commands placing the entire library in iram, dram should be generated: 279 # 280 # iram0_text 281 # *(.iram ...) 282 # *libfreertos.a:croutine(.literal ...) B 283 mapping = u""" 284[mapping:test] 285archive: libfreertos.a 286entries: 287 croutine (noflash) #1 288""" 289 290 self.add_fragments(alt if alt else mapping) 291 actual = self.generation.generate(self.entities) 292 expected = self.generate_default_rules() 293 294 flash_text = expected['flash_text'] 295 flash_rodata = expected['flash_rodata'] 296 iram0_text = expected['iram0_text'] 297 dram0_data = expected['dram0_data'] 298 299 # Generate exclusions in flash_text and flash_rodata A 300 flash_text[0].exclusions.add(CROUTINE) 301 flash_rodata[0].exclusions.add(CROUTINE) 302 303 # Input section commands in iram_text and dram0_data for #1 B 304 iram0_text.append(InputSectionDesc(CROUTINE, flash_text[0].sections, [])) 305 dram0_data.append(InputSectionDesc(CROUTINE, flash_rodata[0].sections, [])) 306 307 self.compare_rules(expected, actual) 308 309 def test_nondefault_mapping_symbol(self): 310 # Test mapping entry different from default for symbol. 311 # There should be exclusions in the default commands for flash_text, as well as the implicit intermediate object command 312 # with an exclusion from default: 313 # 314 # flash_text 315 # *((EXCLUDE_FILE(libfreertos.a:croutine)) .literal ...) A 316 # *libfreertos.a:croutine(.literal .literal.prvCheckDelayedList ...) B 317 # 318 # Commands placing the entire library in iram should be generated: 319 # 320 # iram0_text 321 # *(.iram ...) 322 # *libfreertos.a:croutine(.text.prvCheckPendingReadyList .literal.prvCheckPendingReadyList) C 323 mapping = u""" 324[mapping:test] 325archive: libfreertos.a 326entries: 327 croutine:prvCheckPendingReadyList (noflash) #1 328""" 329 self.add_fragments(mapping) 330 actual = self.generation.generate(self.entities) 331 expected = self.generate_default_rules() 332 333 flash_text = expected['flash_text'] 334 iram0_text = expected['iram0_text'] 335 336 # Generate exclusion in flash_text A 337 flash_text[0].exclusions.add(CROUTINE) 338 339 # Generate intermediate command B 340 # List all relevant sections except the symbol 341 # being mapped 342 croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine') 343 filtered_sections = fnmatch.filter(croutine_sections, '.literal.*') 344 filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*')) 345 346 filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')] 347 filtered_sections.append('.text') 348 349 flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [])) 350 351 # Input section commands in iram_text for #1 C 352 iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) 353 354 self.compare_rules(expected, actual) 355 356 def test_default_symbol_nondefault_lib(self): 357 # Test default symbol mapping with different lib mapping. This should create an implicit intermediate object command. 358 # The significant targets are flash_text, flash_rodata, iram0_text, dram0_data. 359 # 360 # flash_text 361 # *(EXCLUDE_FILE(libfreertos.a) .text ...) A 362 # libfreertos.a:croutine (.text.prvCheckPendingReadyList .literal.prvCheckPendingReadyList) B 363 # 364 # flash_rodata 365 # *(EXCLUDE_FILE(libfreertos.a) .rodata ...) A 366 # 367 # iram0_text 368 # * ( .iram ...) 369 # libfreertos.a (EXCLUDE_FILE(libfreertos:croutine) .text ...) C.1 370 # *libfreertos.a:croutine(.literal .literal.prvCheckDelayedList ...) D 371 # 372 # dram0_data 373 # * ( .dram ...) 374 # libfreertos.a ( .rodata ...) C.2 375 # 376 # Only default commands are in the other targets. 377 mapping = u""" 378[mapping:test] 379archive: libfreertos.a 380entries: 381 * (noflash) #1 382 croutine:prvCheckPendingReadyList (default) #2 383""" 384 385 self.add_fragments(mapping) 386 actual = self.generation.generate(self.entities) 387 expected = self.generate_default_rules() 388 389 flash_text = expected['flash_text'] 390 flash_rodata = expected['flash_rodata'] 391 iram0_text = expected['iram0_text'] 392 dram0_data = expected['dram0_data'] 393 394 # Exclusions for #1 A 395 flash_text[0].exclusions.add(FREERTOS) 396 flash_rodata[0].exclusions.add(FREERTOS) 397 398 # Commands for #1 C.1 & C.2 399 # C.1 excludes intermediate command for #2 400 iram0_text.append(InputSectionDesc(FREERTOS, flash_text[0].sections, [CROUTINE])) 401 dram0_data.append(InputSectionDesc(FREERTOS, flash_rodata[0].sections, [])) 402 403 # Intermediate command for excluding #2 D 404 croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine') 405 filtered_sections = fnmatch.filter(croutine_sections, '.literal.*') 406 filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*')) 407 408 filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')] 409 filtered_sections.append('.text') 410 411 iram0_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [])) 412 413 # Command for #2 B 414 flash_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) 415 416 self.compare_rules(expected, actual) 417 418 def test_default_symbol_nondefault_obj(self): 419 # Test default symbol mapping with different obj mapping. Since there is an explicit entry for the object, 420 # the sections for that object should just be expanded and the symbol section subtracted, to be placed 421 # using another command. 422 # 423 # flash_text 424 # *(EXCLUDE_FILE(libfreertos.a:croutine) .text ...) A 425 # libfreertos.a:croutine (.text.prvCheckPendingReadyList .literal.prvCheckPendingReadyList) B 426 # 427 # flash_rodata 428 # *(EXCLUDE_FILE(libfreertos.a:croutine) .rodata ...) A 429 # 430 # iram0_text 431 # *( .iram ...) 432 # *libfreertos.a:croutine(.literal .literal.prvCheckDelayedList ...) C.1 433 # 434 # dram0_data 435 # *(.data ..) 436 # *libfreertos.a:croutine(.rodata ....) C.2 437 # 438 # Only default commands are in the other targets 439 mapping = u""" 440[mapping:test] 441archive: libfreertos.a 442entries: 443 croutine (noflash) #1 444 croutine:prvCheckPendingReadyList (default) #2 445""" 446 self.add_fragments(mapping) 447 actual = self.generation.generate(self.entities) 448 expected = self.generate_default_rules() 449 450 flash_text = expected['flash_text'] 451 flash_rodata = expected['flash_rodata'] 452 iram0_text = expected['iram0_text'] 453 dram0_data = expected['dram0_data'] 454 455 # Exclusions for #1 A 456 flash_text[0].exclusions.add(CROUTINE) 457 flash_rodata[0].exclusions.add(CROUTINE) 458 459 # Commands for #1 C.1 & C.2 460 # C.1 list relevant sections for libfreertos.a:croutine to 461 # exclude symbol to map 462 croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine') 463 filtered_sections = fnmatch.filter(croutine_sections, '.literal.*') 464 filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*')) 465 466 filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')] 467 filtered_sections.append('.text') 468 469 iram0_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [])) 470 dram0_data.append(InputSectionDesc(CROUTINE, flash_rodata[0].sections, [])) 471 472 # Command for #2 B 473 flash_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) 474 475 self.compare_rules(expected, actual) 476 477 def test_default_nondefault_alternating(self): 478 # Here, each of the entries map sections to something different 479 # than its one-level-up entry. 480 # 481 # * text -> flash, rodata -> flash 482 # libfreertos.a text -> iram, rodata -> dram 483 # libfreertos.a:croutine text -> flash, rodata -> flash 484 # croutine:prvCheckPendingReadyList text -> iram 485 # 486 # The significant targets are flash_text, flash_rodata, iram0_text, and dram0_data. 487 # 488 # flash_text 489 # *(EXCLUDE_FILE(libfreertos.a) .text ...) A 490 # *libfreertos.a:croutine(.literal .literal.prvCheckDelayedList ...) B.1 491 # 492 # flash_rodata 493 # *(EXCLUDE_FILE(libfreertos.a) .rodata ...) A 494 # *libfreertos.a:croutine(.rodata .rodata.*) B.2 495 # 496 # iram0_text 497 # * ( .iram ...) 498 # libfreertos.a (EXCLUDE_FILE(libfreertos:croutine) .text ...) C 499 # libfreertos.a:croutine (.text.prvCheckPendingReadyList .literal.prvCheckPendingReadyList) D 500 # 501 # dram0_data 502 # * ( .dram ...) 503 # libfreertos.a (EXCLUDE_FILE(libfreertos:croutine) .rodata ...) C 504 # 505 # For the other targets only the default commands should be present. 506 mapping = u""" 507[mapping:test] 508archive: libfreertos.a 509entries: 510 * (noflash) #1 511 croutine (default) #2 512 croutine:prvCheckPendingReadyList (noflash) #3 513""" 514 515 self.add_fragments(mapping) 516 actual = self.generation.generate(self.entities) 517 expected = self.generate_default_rules() 518 519 flash_text = expected['flash_text'] 520 flash_rodata = expected['flash_rodata'] 521 iram0_text = expected['iram0_text'] 522 dram0_data = expected['dram0_data'] 523 524 # Exclusions for #1 A 525 # Only for flash_text and flash_rodata 526 flash_text[0].exclusions.add(FREERTOS) 527 flash_rodata[0].exclusions.add(FREERTOS) 528 529 # Commands for #1 C 530 # with exclusions for #2 531 iram0_text.append(InputSectionDesc(FREERTOS, flash_text[0].sections, [CROUTINE])) 532 dram0_data.append(InputSectionDesc(FREERTOS, flash_rodata[0].sections, [CROUTINE])) 533 534 # Commands for #2 B.1 535 flash_rodata.append(InputSectionDesc(CROUTINE, flash_rodata[0].sections, [])) 536 537 # List all relevant sections in case of flash_text B.2 538 # as exclusion for #3 539 croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine') 540 filtered_sections = fnmatch.filter(croutine_sections, '.literal.*') 541 filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*')) 542 543 filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')] 544 filtered_sections.append('.text') 545 546 flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [])) 547 548 # Command for #3 D 549 iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) 550 551 self.compare_rules(expected, actual) 552 553 def test_nondefault_but_same_lib_and_obj(self): 554 # Extension of DefaultMappingTest. Commands should not be generated for #2, since it does similar mapping 555 # to #1. Output is similar to test_different_mapping_lib. 556 mapping = u""" 557[mapping:test] 558archive: libfreertos.a 559entries: 560 * (noflash) #1 561 croutine (noflash) #2 562""" 563 self.test_nondefault_mapping_lib(mapping) 564 565 def test_nondefault_but_same_lib_and_sym(self): 566 # Extension of DefaultMappingTest. Commands should not be generated for #2, since it does similar mapping 567 # to #1. Output is similar to test_different_mapping_lib. 568 mapping = u""" 569[mapping:test] 570archive: libfreertos.a 571entries: 572 * (noflash) #1 573 croutine:prvCheckPendingReadyList (noflash) #2 574""" 575 self.test_nondefault_mapping_lib(mapping) 576 577 def test_nondefault_but_same_obj_and_sym(self): 578 # Commands should not be generated for #2, since it does similar mapping 579 # to #1. Output is similar to test_different_mapping_obj. 580 mapping = u""" 581[mapping:test] 582archive: libfreertos.a 583entries: 584 croutine (noflash) #1 585 croutine:prvCheckPendingReadyList (noflash) #2 586""" 587 self.test_nondefault_mapping_obj(mapping) 588 589 def test_multiple_symbols_excluded_from_intermediate_command(self): 590 # Test mapping multiple symbols from the same object. 591 # All these symbols must be succesfully excluded from 592 # the intermediate command. 593 # 594 # flash_text 595 # * (EXCLUDE_FILE(libfreertos.a:croutine) .text ...) A 596 # libfreertos:croutine(.text ...) B 597 # 598 # iram0_text 599 # 600 # 601 mapping = u""" 602[mapping:test] 603archive: libfreertos.a 604entries: 605 croutine:prvCheckPendingReadyList (noflash) #1 606 croutine:prvCheckDelayedList (noflash) #2 607""" 608 609 self.add_fragments(mapping) 610 actual = self.generation.generate(self.entities) 611 expected = self.generate_default_rules() 612 613 flash_text = expected['flash_text'] 614 iram0_text = expected['iram0_text'] 615 616 # Exclusions for #1 & #2 intermediate command A 617 flash_text[0].exclusions.add(CROUTINE) 618 619 # Intermediate command for #1 & #2 which lists B 620 # all relevant sections in croutine except prvCheckPendingReadyList 621 # and prvCheckDelayedList 622 croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine') 623 filtered_sections = fnmatch.filter(croutine_sections, '.literal.*') 624 filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*')) 625 626 filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')] 627 filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckDelayedList')] 628 filtered_sections.append('.text') 629 630 flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [])) 631 632 # Commands for #1 & 2 633 iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckDelayedList', '.literal.prvCheckDelayedList']), [])) 634 iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) 635 636 self.compare_rules(expected, actual) 637 638 def test_root_mapping_fragment(self): 639 # Test creation of a mapping fragment that maps '*'. 640 # This should generate another default command in iram0_text: 641 # 642 # iram0_text 643 # * (.custom_section) A 644 # * (.iram .iram.*) 645 mapping = u""" 646[sections:custom_section] 647entries: 648 .custom_section 649 650[scheme:custom_scheme] 651entries: 652 custom_section -> iram0_text 653 654[mapping:default2] 655archive: * 656entries: 657 * (custom_scheme) #1 658""" 659 660 self.add_fragments(mapping) 661 actual = self.generation.generate(self.entities) 662 expected = self.generate_default_rules() 663 664 # Generate default command A 665 # Since these are the same 'specificity', the commands 666 # are arranged alphabetically. 667 expected['iram0_text'].append(expected['iram0_text'][0]) 668 expected['iram0_text'][0] = InputSectionDesc(ROOT, ['.custom_section'], []) 669 670 self.compare_rules(expected, actual) 671 672 673class AdvancedTest(GenerationTest): 674 675 # Test valid but quirky cases, corner cases, failure cases, and 676 # cases involving interaction between schemes, other mapping 677 # fragments. 678 679 def test_same_entity_no_scheme_common(self): 680 # Test same entity being mapped by schemes that have nothing in common. 681 # 682 # noflash_data: rodata -> dram0_data 683 # noflash_text: text -> iram0_text 684 # 685 # This operation should succeed with the following commands: 686 # 687 # flash_text 688 # *(EXCLUDE_FILE(libfreertos.a:croutine) .text ...) A 689 # 690 # flash_rodata 691 # *(EXCLUDE_FILE(libfreertos.a:croutine) .rodata ...) B 692 # 693 # iram0_text 694 # *(.iram ...) 695 # *libfreertos.a:croutine(.text .text.* ...) C 696 # 697 # dram0_data 698 # *(.data ..) 699 # *(.dram ...) 700 # *libfreertos.a:croutine(.rodata .rodata.*) D 701 mapping = u""" 702[mapping:test] 703archive: libfreertos.a 704entries: 705 croutine (noflash_text) #1 706 croutine (noflash_data) #2 707""" 708 self.add_fragments(mapping) 709 actual = self.generation.generate(self.entities) 710 expected = self.generate_default_rules() 711 712 flash_text = expected['flash_text'] 713 flash_rodata = expected['flash_rodata'] 714 iram0_text = expected['iram0_text'] 715 dram0_data = expected['dram0_data'] 716 717 # Exclusions for #1 A 718 flash_text[0].exclusions.add(CROUTINE) 719 720 # Exclusions for #2 B 721 flash_rodata[0].exclusions.add(CROUTINE) 722 723 # Command for #1 C 724 iram0_text.append(InputSectionDesc(CROUTINE, flash_text[0].sections, [])) 725 726 # Command for #2 D 727 dram0_data.append(InputSectionDesc(CROUTINE, flash_rodata[0].sections, [])) 728 729 self.compare_rules(expected, actual) 730 731 def test_same_entity_sub_scheme(self): 732 # Test same entity being mapped by scheme that is a subset of the other. 733 # 734 # noflash: text -> iram0_text, rodata -> dram0_data 735 # noflash_text: text -> iram0_text 736 # 737 # `text -> iram0_text` is common between the two schemes. 738 # 739 # This operation should succeed with the following commands: 740 # 741 # flash_text 742 # *(EXCLUDE_FILE(libfreertos.a:croutine) .text ...) A 743 # 744 # flash_rodata 745 # *(EXCLUDE_FILE(libfreertos.a:croutine) .rodata ...) B 746 # 747 # iram0_text 748 # *(.iram ...) 749 # *libfreertos.a:croutine(.text .text.* ...) C 750 # 751 # dram0_data 752 # *(.data ..) 753 # *(.dram ...) 754 # *libfreertos.a:croutine(.rodata .rodata.*) D 755 mapping = u""" 756[mapping:test] 757archive: libfreertos.a 758entries: 759 croutine (noflash) #1 760 croutine (noflash_data) #2 761""" 762 self.add_fragments(mapping) 763 actual = self.generation.generate(self.entities) 764 expected = self.generate_default_rules() 765 766 flash_text = expected['flash_text'] 767 flash_rodata = expected['flash_rodata'] 768 iram0_text = expected['iram0_text'] 769 dram0_data = expected['dram0_data'] 770 771 # Exclusions for #1 A 772 flash_text[0].exclusions.add(CROUTINE) 773 774 # Exclusions for #1 & #2 B 775 flash_rodata[0].exclusions.add(CROUTINE) 776 777 # Command for #1 C 778 iram0_text.append(InputSectionDesc(CROUTINE, flash_text[0].sections, [])) 779 780 # Command for #1 & #2 D 781 dram0_data.append(InputSectionDesc(CROUTINE, flash_rodata[0].sections, [])) 782 783 self.compare_rules(expected, actual) 784 785 def test_same_entity_conflicting_scheme(self, alt=None): 786 # Test same entity being mapped by scheme conflicting with another. 787 # 788 # rtc = text -> rtc_text, rodata -> rtc_data 789 # noflash = text -> iram0_text, rodata -> dram0_data 790 # 791 # This operation should fail. 792 mapping = u""" 793[mapping:test] 794archive: libfreertos.a 795entries: 796 croutine (noflash) #1 797 croutine (rtc) #2 798""" 799 self.add_fragments(alt if alt else mapping) 800 801 with self.assertRaises(GenerationException): 802 self.generation.generate(self.entities) 803 804 def test_complex_mapping_case(self, alt=None): 805 # Test a complex case where an object is mapped using 806 # one scheme, but a specific symbol in that object is mapped 807 # using another. Another object and symbol is mapped the other way around. 808 # 809 # flash_text 810 # *(EXCLUDE_FILE(libfreertos.a:croutine libfreertos.a:timers) .text ...) A, B 811 # 812 # flash_rodata 813 # *(EXCLUDE_FILE(libfreertos.a:croutine libfreertos.a:timers) .rodata ...) A, B 814 # 815 # dram0_data 816 # *(EXCLUDE_FILES(libfreertos.a:timers) .data ..) B 817 # *(.dram ...) 818 # *libfreertos.a:croutine(.rodata .rodata.*) C 819 # *libfreertos.a:timers(.rodata.prvProcessReceivedCommands ...) E 820 # 821 # dram0_bss 822 # *(EXCLUDE_FILE(libfreertos.a:timers) .bss .bss.* ...) B 823 # *(EXCLUDE_FILE(libfreertos.a:timers) COMMON) B 824 # 825 # iram0_text 826 # *(.iram ...) 827 # *libfreertos.a:croutine(.literal .literal.prvCheckDelayedList ...) C 828 # *libfreertos.a:timers(.literal .literal.prvProcessReceivedCommands ...) E 829 # 830 # rtc_text 831 # *(rtc.text .rtc.literal) 832 # libfreertos.a:croutine (.text.prvCheckPendingReadyList .literal.prvCheckPendingReadyList) F 833 # libfreertos.a:timers (.text .text.prvCheckForValidListAndQueue ...) D.2 834 # 835 # rtc_data 836 # *(rtc.data) 837 # *(rtc.rodata) 838 # libfreertos.a:timers (.data .data.*) D 839 # libfreertos.a:timers (.rodata ...) D.2 840 # 841 # rtc_bss 842 # *(rtc.bss .rtc.bss) 843 # libfreertos.a:timers (.bss .bss.*) D 844 # libfreertos.a:timers (COMMON) D 845 mapping = u""" 846[mapping:test] 847archive: libfreertos.a 848entries: 849 croutine (noflash) #1 850 timers (rtc) #2 851 timers:prvProcessReceivedCommands (noflash) #3 852 croutine:prvCheckPendingReadyList (rtc) #4 853""" 854 855 self.add_fragments(alt if alt else mapping) 856 actual = self.generation.generate(self.entities) 857 expected = self.generate_default_rules() 858 859 flash_text = expected['flash_text'] 860 flash_rodata = expected['flash_rodata'] 861 dram0_data = expected['dram0_data'] 862 iram0_text = expected['iram0_text'] 863 dram0_bss = expected['dram0_bss'] 864 rtc_text = expected['rtc_text'] 865 rtc_data = expected['rtc_data'] 866 rtc_bss = expected['rtc_bss'] 867 868 # Exclusions for #1 A 869 flash_text[0].exclusions.add(CROUTINE) 870 flash_rodata[0].exclusions.add(CROUTINE) 871 872 # Exclusions for #2 B 873 flash_text[0].exclusions.add(TIMERS) 874 flash_rodata[0].exclusions.add(TIMERS) 875 dram0_data[0].exclusions.add(TIMERS) 876 dram0_bss[0].exclusions.add(TIMERS) 877 dram0_bss[1].exclusions.add(TIMERS) 878 879 # Commands for #1 C 880 # List all relevant sections excluding #4 for text -> iram0_text 881 croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine') 882 filtered_sections = fnmatch.filter(croutine_sections, '.literal.*') 883 filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*')) 884 885 filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')] 886 filtered_sections.append('.text') 887 888 iram0_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [])) 889 dram0_data.append(InputSectionDesc(CROUTINE, flash_rodata[0].sections, [])) 890 891 # Commands for #4 F 892 # Processed first due to alphabetical ordering 893 rtc_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) 894 895 # Commands for #2 D 896 # List all relevant sections excluding #3 for text -> rtc_text and D.2 897 # rodata -> rtc_data 898 timers_sections = self.entities.get_sections('libfreertos.a', 'timers') 899 filtered_sections = fnmatch.filter(timers_sections, '.literal.*') 900 filtered_sections.extend(fnmatch.filter(timers_sections, '.text.*')) 901 902 filtered_sections = [s for s in filtered_sections if not s.endswith('prvProcessReceivedCommands')] 903 filtered_sections.append('.text') 904 rtc_text.append(InputSectionDesc(TIMERS, set(filtered_sections), [])) 905 906 rtc_data.append(InputSectionDesc(TIMERS, dram0_data[0].sections, [])) 907 filtered_sections = fnmatch.filter(timers_sections, '.rodata.*') 908 filtered_sections = [s for s in filtered_sections if not s.endswith('prvProcessReceivedCommands')] 909 rtc_data.append(InputSectionDesc(TIMERS, set(filtered_sections), [])) 910 911 rtc_bss.append(InputSectionDesc(TIMERS, dram0_bss[0].sections, [])) 912 rtc_bss.append(InputSectionDesc(TIMERS, dram0_bss[1].sections, [])) 913 914 # Commands for #3 E 915 iram0_text.append(InputSectionDesc(TIMERS, set(['.text.prvProcessReceivedCommands', '.literal.prvProcessReceivedCommands']), [])) 916 dram0_data.append(InputSectionDesc(TIMERS, set(['.rodata.prvProcessReceivedCommands']), [])) 917 918 self.compare_rules(expected, actual) 919 920 def test_multiple_mapping_fragments(self): 921 # Test mapping multiple fragments succeeds, particularly 922 # generating exclusions from the default command of archive 923 # and object specificity. 924 # 925 # flash_text 926 # * (EXCLUDE_FILE(libfreertos.a libfreertos.a:croutine) .text ...) 927 # 928 # flash_rodata 929 # * (EXCLUDE_FILE(libfreertos.a libfreertos.a:croutine) .text ...) 930 # 931 # iram0_text 932 mapping = u""" 933[mapping:test_1] 934archive: libfreertos.a 935entries: 936 croutine (noflash) #1 937 938[mapping:test_2] 939archive: libfreertos2.a 940entries: 941 * (noflash) #2 942""" 943 944 self.add_fragments(mapping) 945 actual = self.generation.generate(self.entities) 946 expected = self.generate_default_rules() 947 948 flash_text = expected['flash_text'] 949 flash_rodata = expected['flash_rodata'] 950 iram0_text = expected['iram0_text'] 951 dram0_data = expected['dram0_data'] 952 953 # Exclusions for #1 A 954 flash_text[0].exclusions.add(CROUTINE) 955 flash_rodata[0].exclusions.add(CROUTINE) 956 957 # Exclusions for #1 & #2 B 958 flash_text[0].exclusions.add(FREERTOS2) 959 flash_rodata[0].exclusions.add(FREERTOS2) 960 961 # Command for #1 C 962 iram0_text.append(InputSectionDesc(CROUTINE, flash_text[0].sections, [])) 963 dram0_data.append(InputSectionDesc(CROUTINE, flash_rodata[0].sections, [])) 964 965 # Command for #1 & #2 D 966 iram0_text.append(InputSectionDesc(FREERTOS2, flash_text[0].sections, [])) 967 dram0_data.append(InputSectionDesc(FREERTOS2, flash_rodata[0].sections, [])) 968 969 self.compare_rules(expected, actual) 970 971 def test_mapping_same_lib_in_multiple_fragments_no_conflict(self): 972 # Test mapping fragments operating on the same archive. 973 # In these cases, the entries are taken together. 974 # 975 # Uses the same entries as C_05 but spreads them across 976 # two fragments. The output should still be the same. 977 mapping = u""" 978[mapping:test_1] 979archive: libfreertos.a 980entries: 981 croutine (noflash) #1 982 timers:prvProcessReceivedCommands (noflash) #3 983 984[mapping:test_2] 985archive: libfreertos.a 986entries: 987 timers (rtc) #2 988 croutine:prvCheckPendingReadyList (rtc) #4 989""" 990 self.test_complex_mapping_case(mapping) 991 992 def test_mapping_same_lib_in_multiple_fragments_conflict(self): 993 # Test mapping fragments operating on the same archive 994 # with conflicting mappings. 995 mapping = u""" 996[mapping:test_1] 997archive: libfreertos.a 998entries: 999 croutine (noflash) #1 1000 1001[mapping:test_2] 1002archive: libfreertos.a 1003entries: 1004 croutine (rtc) #2 1005""" 1006 self.test_same_entity_conflicting_scheme(mapping) 1007 1008 def test_command_order(self): 1009 # Test command order sorting: the commands should be sorted by specificity, then 1010 # alphabetically. This contributes to deterministic output given 1011 # the same input mapping entries. 1012 # 1013 # This ordering is also tested in other tests as a side-effect. 1014 # 1015 # flash_text 1016 # * (EXCLUDE_FILE(libfreertos.a:croutine libfreertos.a:croutine2)) A 1017 # libfreertos.a:croutine(.text ....) B 1018 # 1019 # iram0_text 1020 # 1021 # * (.iram .iram.*) 1022 # libfreertos:croutine(.text .literal ...) C 1023 # libfreertos:croutine(.text.prvCheckDelayedList .literal.prvCheckDelayedList) F 1024 # libfreertos:croutine(.text.prvCheckPendingReadyList .literal.prvCheckPendingReadyList) G 1025 # libfreertos2:croutine(.text .literal ...) D 1026 # libfreertos2:croutine2(.text .literal ...) E 1027 mapping = u""" 1028[mapping:freertos2] 1029archive: libfreertos2.a 1030entries: 1031 croutine2 (noflash_text) #1 1032 croutine (noflash_text) #2 1033 1034[mapping:freertos] 1035archive: libfreertos.a 1036entries: 1037 croutine:prvCheckPendingReadyList (noflash_text) #3 1038 croutine:prvCheckDelayedList (noflash_text) #4 1039""" 1040 1041 self.add_fragments(mapping) 1042 actual = self.generation.generate(self.entities) 1043 expected = self.generate_default_rules() 1044 1045 flash_text = expected['flash_text'] 1046 iram0_text = expected['iram0_text'] 1047 1048 # Exclusions for #1 A 1049 flash_text[0].exclusions.add(CROUTINE) 1050 flash_text[0].exclusions.add(Entity(FREERTOS2.archive, 'croutine2')) 1051 flash_text[0].exclusions.add(Entity(FREERTOS2.archive, 'croutine')) 1052 1053 # Intermediate command for #3 and #4 B 1054 croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine') 1055 1056 filtered_sections = fnmatch.filter(croutine_sections, '.literal.*') 1057 filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*')) 1058 1059 filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')] 1060 filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckDelayedList')] 1061 filtered_sections.append('.text') 1062 flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [])) 1063 1064 # Command for 1065 iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckDelayedList', '.literal.prvCheckDelayedList']), [])) 1066 iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) 1067 1068 iram0_text.append(InputSectionDesc(Entity(FREERTOS2.archive, 'croutine'), flash_text[0].sections, [])) 1069 iram0_text.append(InputSectionDesc(Entity(FREERTOS2.archive, 'croutine2'), flash_text[0].sections, [])) 1070 1071 self.compare_rules(expected, actual) 1072 1073 def test_ambigious_obj(self): 1074 # Command generation for ambiguous entry should fail. 1075 mapping = u""" 1076[mapping:test] 1077archive: libfreertos.a 1078entries: 1079 port:xPortGetTickRateHz (noflash) #1 1080""" 1081 self.add_fragments(mapping) 1082 1083 with self.assertRaises(GenerationException): 1084 self.generation.generate(self.entities) 1085 1086 def test_disambiguated_obj(self): 1087 # Test command generation for disambiguated entry. Should produce similar 1088 # results to test_nondefault_mapping_symbol. 1089 mapping = u""" 1090[mapping:test] 1091archive: libfreertos.a 1092entries: 1093 port.c:xPortGetTickRateHz (noflash) #1 1094""" 1095 port = Entity('libfreertos.a', 'port.c') 1096 self.add_fragments(mapping) 1097 actual = self.generation.generate(self.entities) 1098 expected = self.generate_default_rules() 1099 1100 flash_text = expected['flash_text'] 1101 iram0_text = expected['iram0_text'] 1102 1103 # Generate exclusion in flash_text A 1104 flash_text[0].exclusions.add(port) 1105 1106 # Generate intermediate command B 1107 # List all relevant sections except the symbol 1108 # being mapped 1109 port_sections = self.entities.get_sections('libfreertos.a', 'port.c') 1110 filtered_sections = fnmatch.filter(port_sections, '.literal.*') 1111 filtered_sections.extend(fnmatch.filter(port_sections, '.text.*')) 1112 1113 filtered_sections = [s for s in filtered_sections if not s.endswith('xPortGetTickRateHz')] 1114 filtered_sections.append('.text') 1115 1116 flash_text.append(InputSectionDesc(port, set(filtered_sections), [])) 1117 1118 # Input section commands in iram_text for #1 C 1119 iram0_text.append(InputSectionDesc(port, set(['.text.xPortGetTickRateHz', '.literal.xPortGetTickRateHz']), [])) 1120 1121 self.compare_rules(expected, actual) 1122 1123 def test_root_mapping_fragment_conflict(self): 1124 # Test that root mapping fragments are also checked for 1125 # conflicts. 1126 # 1127 # 'custom_scheme' entries conflict the 'default' scheme 1128 # entries. 1129 mapping = u""" 1130[scheme:custom_scheme] 1131entries: 1132 flash_text -> iram0_text 1133 1134[mapping:default2] 1135archive: * 1136entries: 1137 * (custom_scheme) 1138""" 1139 1140 self.add_fragments(mapping) 1141 with self.assertRaises(GenerationException): 1142 self.generation.generate(self.entities) 1143 1144 def test_root_mapping_fragment_duplicate(self): 1145 # Same root mappings have no effect. 1146 # 1147 # custom_scheme has the 'iram -> iram0_text' in common with 1148 # default scheme 1149 mapping = u""" 1150[sections:custom_section] 1151entries: 1152 .custom_section 1153 1154[scheme:custom_scheme] 1155entries: 1156 iram -> iram0_text 1157 custom_section -> iram0_text 1158 1159[mapping:default2] 1160archive: * 1161entries: 1162 * (custom_scheme) 1163""" 1164 1165 self.add_fragments(mapping) 1166 actual = self.generation.generate(self.entities) 1167 expected = self.generate_default_rules() 1168 1169 # Generate default command A 1170 # Since these are the same 'specificity', the commands 1171 # are arranged alphabetically. 1172 expected['iram0_text'].append(expected['iram0_text'][0]) 1173 expected['iram0_text'][0] = InputSectionDesc(ROOT, ['.custom_section'], []) 1174 1175 self.compare_rules(expected, actual) 1176 1177 1178class ConfigTest(GenerationTest): 1179 # Test command generation with conditions 1180 1181 def _test_conditional_on_scheme(self, perf, alt=None): 1182 # Test that proper commands are generated if using 1183 # schemes with conditional entries. 1184 scheme = u""" 1185[sections:cond_text_data] 1186entries: 1187 if PERFORMANCE_LEVEL >= 1: 1188 .text+ 1189 .literal+ 1190 else: 1191 .rodata+ 1192 1193[scheme:cond_noflash] 1194entries: 1195 if PERFORMANCE_LEVEL >= 1: 1196 cond_text_data -> iram0_text 1197 else: 1198 cond_text_data -> dram0_data 1199""" 1200 1201 mapping = u""" 1202[mapping:test] 1203archive: lib.a 1204entries: 1205 * (cond_noflash) 1206""" 1207 self.sdkconfig.config.syms['PERFORMANCE_LEVEL'].set_value(str(perf)) 1208 self.add_fragments(scheme) 1209 self.add_fragments(alt if alt else mapping) 1210 1211 actual = self.generation.generate(self.entities) 1212 expected = self.generate_default_rules() 1213 1214 if perf >= 1: 1215 flash_text = expected['flash_text'] 1216 iram0_text = expected['iram0_text'] 1217 flash_text[0].exclusions.add(Entity('lib.a')) 1218 iram0_text.append(InputSectionDesc(Entity('lib.a'), flash_text[0].sections, [])) 1219 else: 1220 flash_rodata = expected['flash_rodata'] 1221 dram0_data = expected['dram0_data'] 1222 flash_rodata[0].exclusions.add(Entity('lib.a')) 1223 dram0_data.append(InputSectionDesc(Entity('lib.a'), flash_rodata[0].sections, [])) 1224 1225 self.compare_rules(expected, actual) 1226 1227 def test_conditional_on_scheme_00(self): 1228 self._test_conditional_on_scheme(0) 1229 1230 def test_conditional_on_scheme_01(self): 1231 self._test_conditional_on_scheme(1) 1232 1233 def test_conditional_mapping(self, alt=None): 1234 # Test that proper commands are generated 1235 # in conditional mapping entries. 1236 mapping = u""" 1237[mapping:default] 1238archive: * 1239entries: 1240 * (default) 1241 1242[mapping:test] 1243archive: lib.a 1244entries: 1245 if PERFORMANCE_LEVEL = 1: 1246 obj1 (noflash) 1247 elif PERFORMANCE_LEVEL = 2: 1248 obj1 (noflash) 1249 obj2 (noflash) 1250 elif PERFORMANCE_LEVEL = 3: 1251 obj1 (noflash) 1252 obj2 (noflash) 1253 obj3 (noflash) 1254""" 1255 1256 for perf_level in range(0, 4): 1257 self.sdkconfig.config.syms['PERFORMANCE_LEVEL'].set_value(str(perf_level)) 1258 1259 self.generation.mappings = {} 1260 self.add_fragments(alt if alt else mapping) 1261 1262 actual = self.generation.generate(self.entities) 1263 expected = self.generate_default_rules() 1264 1265 if perf_level < 4 and perf_level > 0: 1266 for append_no in range(1, perf_level + 1): 1267 flash_text = expected['flash_text'] 1268 flash_rodata = expected['flash_rodata'] 1269 iram0_text = expected['iram0_text'] 1270 dram0_data = expected['dram0_data'] 1271 1272 obj_str = 'obj' + str(append_no) 1273 1274 flash_text[0].exclusions.add(Entity('lib.a', obj_str)) 1275 flash_rodata[0].exclusions.add(Entity('lib.a', obj_str)) 1276 1277 iram0_text.append(InputSectionDesc(Entity('lib.a', obj_str), flash_text[0].sections, [])) 1278 dram0_data.append(InputSectionDesc(Entity('lib.a', obj_str), flash_rodata[0].sections, [])) 1279 1280 self.compare_rules(expected, actual) 1281 1282 def test_conditional_on_scheme_legacy_mapping_00(self): 1283 # Test use of conditional scheme on legacy mapping fragment grammar. 1284 mapping = u""" 1285[mapping] 1286archive: lib.a 1287entries: 1288 * (cond_noflash) 1289""" 1290 self._test_conditional_on_scheme(0, mapping) 1291 1292 def test_conditional_on_scheme_legacy_mapping_01(self): 1293 # Test use of conditional scheme on legacy mapping fragment grammar. 1294 mapping = u""" 1295[mapping] 1296archive: lib.a 1297entries: 1298 * (cond_noflash) 1299""" 1300 self._test_conditional_on_scheme(0, mapping) 1301 1302 def test_conditional_entries_legacy_mapping_fragment(self): 1303 # Test conditional entries on legacy mapping fragment grammar. 1304 mapping = u""" 1305[mapping:default] 1306archive: * 1307entries: 1308 * (default) 1309 1310[mapping] 1311archive: lib.a 1312entries: 1313 : PERFORMANCE_LEVEL = 0 1314 : PERFORMANCE_LEVEL = 1 1315 obj1 (noflash) 1316 : PERFORMANCE_LEVEL = 2 1317 obj1 (noflash) 1318 obj2 (noflash) 1319 : PERFORMANCE_LEVEL = 3 1320 obj1 (noflash) 1321 obj2 (noflash) 1322 obj3 (noflash) 1323""" 1324 self.test_conditional_mapping(mapping) 1325 1326 def test_multiple_fragment_same_lib_conditional_legacy(self): 1327 # Test conditional entries on legacy mapping fragment grammar 1328 # across multiple fragments. 1329 mapping = u""" 1330[mapping:default] 1331archive: * 1332entries: 1333 * (default) 1334 1335[mapping] 1336archive: lib.a 1337entries: 1338 : PERFORMANCE_LEVEL = 0 1339 : PERFORMANCE_LEVEL = 1 1340 obj1 (noflash) 1341 : PERFORMANCE_LEVEL = 2 1342 obj1 (noflash) 1343 : PERFORMANCE_LEVEL = 3 1344 obj1 (noflash) 1345 1346[mapping] 1347archive: lib.a 1348entries: 1349 : PERFORMANCE_LEVEL = 1 1350 obj1 (noflash) # ignore duplicate definition 1351 : PERFORMANCE_LEVEL = 2 1352 obj2 (noflash) 1353 : PERFORMANCE_LEVEL = 3 1354 obj2 (noflash) 1355 obj3 (noflash) 1356""" 1357 1358 self.test_conditional_mapping(mapping) 1359 1360 def test_multiple_fragment_same_lib_conditional(self): 1361 # Test conditional entries on new mapping fragment grammar. 1362 # across multiple fragments. 1363 mapping = u""" 1364[mapping:default] 1365archive: * 1366entries: 1367 * (default) 1368 1369[mapping:base] 1370archive: lib.a 1371entries: 1372 if PERFORMANCE_LEVEL = 1: 1373 obj1 (noflash) 1374 elif PERFORMANCE_LEVEL = 2: 1375 obj1 (noflash) 1376 elif PERFORMANCE_LEVEL = 3: 1377 obj1 (noflash) 1378 1379[mapping:extra] 1380archive: lib.a 1381entries: 1382 if PERFORMANCE_LEVEL = 1: 1383 obj1 (noflash) # ignore duplicate definition 1384 elif PERFORMANCE_LEVEL = 2: 1385 obj2 (noflash) 1386 elif PERFORMANCE_LEVEL = 3: 1387 obj2 (noflash) 1388 obj3 (noflash) 1389""" 1390 1391 self.test_conditional_mapping(mapping) 1392 1393 1394class FlagTest(GenerationTest): 1395 1396 # Test correct generation of mapping fragment entries 1397 # with flags. 1398 1399 def test_flags_basics(self): 1400 # Test that input section commands additions are done (KEEP SORT). 1401 # Test that order dependent commands are properly generated (ALIGN, SURROUND) 1402 # Normally, if an entry has the same mapping as parent, commands. 1403 # are not emitted for them. However, if there are flags, they should be - 1404 # only for the scheme entries that have flags, though. 1405 # Flag entries split across multiple entries work. 1406 # 1407 # flash_text 1408 # *((EXCLUDE_FILE(libfreertos:timers libfreertos:croutine).text ...) A 1409 # KEEP(* (SORT_BY_NAME(EXCLUDE_FILE(libfreertos:timers).text) ...) B 1410 # 1411 # flash_rodata 1412 # *((EXCLUDE_FILE(libfreertos:timers) .rodata ...) C 1413 # _sym2_start D.1 1414 # . = ALIGN(4) E.1 1415 # KEEP(* (EXCLUDE_FILE(libfreertos:timers) .rodata ...) F 1416 # _sym2_end D.2 1417 # . = ALIGN(4) E.2 1418 # 1419 # iram0_text 1420 # *(.iram .iram.*) 1421 # . = ALIGN(4) G.1 1422 # _sym1_start H.1 1423 # libfreertos.a:croutine(.text .literal ...) I 1424 # . = ALIGN(4) G.2 1425 # _sym1_end H.2 1426 mapping = u""" 1427[mapping:test] 1428archive: libfreertos.a 1429entries: 1430 croutine (noflash_text); 1431 text->iram0_text ALIGN(4, pre, post) SURROUND(sym1) #1 1432 timers (default); 1433 text->flash_text KEEP() SORT(name) #2 1434 timers (default); 1435 rodata->flash_rodata SURROUND(sym2) ALIGN(4, pre, post) #3 1436""" 1437 1438 self.add_fragments(mapping) 1439 1440 actual = self.generation.generate(self.entities) 1441 expected = self.generate_default_rules() 1442 1443 flash_text = expected['flash_text'] 1444 iram0_text = expected['iram0_text'] 1445 flash_rodata = expected['flash_rodata'] 1446 1447 # Exclusions in flash_text for timers and croutine A 1448 flash_text[0].exclusions.add(CROUTINE) 1449 flash_text[0].exclusions.add(TIMERS) 1450 1451 # Command for #3 B 1452 flash_text.append(InputSectionDesc(TIMERS, flash_text[0].sections, [], keep=True, sort=('name', None))) 1453 1454 # Exclusions in flash_rodata for timers C 1455 flash_rodata[0].exclusions.add(TIMERS) 1456 1457 # Commands for #3 D.1, E.1, F, D.2, E.2 1458 flash_rodata.append(SymbolAtAddress('_sym2_start')) 1459 flash_rodata.append(AlignAtAddress(4)) 1460 flash_rodata.append(InputSectionDesc(TIMERS, flash_rodata[0].sections, [])) 1461 flash_rodata.append(SymbolAtAddress('_sym2_end')) 1462 flash_rodata.append(AlignAtAddress(4)) 1463 1464 # Commands for # G.1, H.1, I, G.2, H.2 1465 iram0_text.append(AlignAtAddress(4)) 1466 iram0_text.append(SymbolAtAddress('_sym1_start')) 1467 iram0_text.append(InputSectionDesc(CROUTINE, flash_text[0].sections, [])) 1468 iram0_text.append(AlignAtAddress(4)) 1469 iram0_text.append(SymbolAtAddress('_sym1_end')) 1470 1471 self.compare_rules(expected, actual) 1472 1473 def test_flags_intermediate_exclusion_command_root(self): 1474 # Test that intermediate exclusion commands from root-level commands 1475 # are included in the flags. 1476 # 1477 # flash_text 1478 # _sym1_start A.1 1479 # KEEP(* (EXCLUDE_FILE(libfreertos:croutine).text ...) B 1480 # KEEP(libfreertos.a:croutine(...))) C 1481 # _sym1_end A.2 1482 # 1483 # iram0_text 1484 # *(.iram .iram.*) 1485 # libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) D 1486 mapping = u""" 1487[mapping:default] 1488archive: * 1489entries: 1490 # 1 1491 * (default); 1492 text->flash_text SURROUND(sym1) KEEP() #2 1493 1494[mapping:test] 1495archive: libfreertos.a 1496entries: 1497 croutine:prvCheckPendingReadyList (noflash_text) #3 1498""" 1499 1500 self.generation.mappings = {} 1501 self.add_fragments(mapping) 1502 1503 actual = self.generation.generate(self.entities) 1504 expected = self.generate_default_rules() 1505 1506 flash_text = expected['flash_text'] 1507 iram0_text = expected['iram0_text'] 1508 1509 # Command for #2, pre A.1 1510 flash_text.insert(0, SymbolAtAddress('_sym1_start')) 1511 1512 # Command for #1 with KEEP() B 1513 # and exclusion for #3 1514 flash_text[1].keep = True 1515 flash_text[1].exclusions.add(CROUTINE) 1516 1517 # Implicit exclusion command for #3 C 1518 croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine') 1519 filtered_sections = fnmatch.filter(croutine_sections, '.literal.*') 1520 filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*')) 1521 1522 filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')] 1523 filtered_sections.append('.text') 1524 flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [], keep=True)) 1525 1526 # Command for #2, post A.2 1527 flash_text.append(SymbolAtAddress('_sym1_end')) 1528 1529 # Command for #3 D 1530 iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) 1531 1532 self.compare_rules(expected, actual) 1533 1534 def test_flags_intermediate_exclusion_command_lib(self): 1535 # Test that intermediate exclusion commands from lib-level commands 1536 # are included in the flags. 1537 # 1538 # flash_text 1539 # *(EXCLUDE_FILE(libfreertos.a).text ...) 1540 # _sym1_start A.1 1541 # KEEP(libfreertos.a(EXCLUDE_FILE(libfreertos:croutine).text.* ...)) B 1542 # KEEP(libfreertos.a:croutine(...))) C 1543 # _sym1_end A.2 1544 # 1545 # iram0_text 1546 # *(.iram .iram.*) 1547 # libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) D 1548 mapping = u""" 1549[mapping:test] 1550archive: libfreertos.a 1551entries: 1552 # 1 1553 * (default); 1554 text->flash_text SURROUND(sym1) KEEP() #2 1555 croutine:prvCheckPendingReadyList (noflash_text) #3 1556""" 1557 1558 self.add_fragments(mapping) 1559 1560 actual = self.generation.generate(self.entities) 1561 expected = self.generate_default_rules() 1562 1563 flash_text = expected['flash_text'] 1564 iram0_text = expected['iram0_text'] 1565 1566 # Command for #2, pre A.1 1567 flash_text.append(SymbolAtAddress('_sym1_start')) 1568 flash_text[0].exclusions.add(FREERTOS) 1569 1570 # Command for #1 with KEEP() B 1571 # and exclusion for #3 1572 flash_text.append(InputSectionDesc(FREERTOS, flash_text[0].sections, [CROUTINE], keep=True)) 1573 1574 # Implicit exclusion command for #3 C 1575 croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine') 1576 filtered_sections = fnmatch.filter(croutine_sections, '.literal.*') 1577 filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*')) 1578 1579 filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')] 1580 filtered_sections.append('.text') 1581 flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [], keep=True)) 1582 1583 # Command for #2, post A.2 1584 flash_text.append(SymbolAtAddress('_sym1_end')) 1585 1586 # Command for #3 C 1587 iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) 1588 1589 self.compare_rules(expected, actual) 1590 1591 def test_flags_intermediate_exclusion_command_obj(self): 1592 # Test that intermediate exclusion commands from obj-level commands 1593 # are included in the flags. 1594 # 1595 # flash_text 1596 # *(EXCLUDE_FILE(libfreertos.a).text ...) 1597 # _sym1_start A.1 1598 # KEEP(libfreertos.a:croutine(...))) B 1599 # _sym1_end A.2 1600 # 1601 # iram0_text 1602 # *(.iram .iram.*) 1603 # libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) C 1604 mapping = u""" 1605[mapping:test] 1606archive: libfreertos.a 1607entries: 1608 # 1 1609 croutine (default); 1610 text->flash_text SURROUND(sym1) KEEP() #2 1611 croutine:prvCheckPendingReadyList (noflash_text) #3 1612""" 1613 1614 self.add_fragments(mapping) 1615 1616 actual = self.generation.generate(self.entities) 1617 expected = self.generate_default_rules() 1618 1619 flash_text = expected['flash_text'] 1620 iram0_text = expected['iram0_text'] 1621 1622 # Command for #2, pre A.1 1623 flash_text.append(SymbolAtAddress('_sym1_start')) 1624 flash_text[0].exclusions.add(CROUTINE) 1625 1626 # Implicit exclusion command for #3 B 1627 croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine') 1628 filtered_sections = fnmatch.filter(croutine_sections, '.literal.*') 1629 filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*')) 1630 1631 filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')] 1632 filtered_sections.append('.text') 1633 flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [], keep=True)) 1634 1635 # Command for #2, post A.2 1636 flash_text.append(SymbolAtAddress('_sym1_end')) 1637 1638 # Command for #3 C 1639 iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) 1640 1641 self.compare_rules(expected, actual) 1642 1643 def test_flags_separate_exclusion_command_if_explicit_root(self): 1644 # Explicit commands are separated from the parent's flags. 1645 # 1646 # flash_text 1647 # _sym1_start A.1 1648 # KEEP(* (EXCLUDE_FILE(libfreertos:croutine).text ...) B 1649 # _sym1_end A.2 1650 # KEEP(libfreertos.a:croutine(...))) C 1651 # 1652 # iram0_text 1653 # *(.iram .iram.*) 1654 # libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) D 1655 mapping = u""" 1656[mapping:default] 1657archive: * 1658entries: 1659 # 1 1660 * (default); 1661 text->flash_text SURROUND(sym1) KEEP() #2 1662 1663[mapping:test] 1664archive: libfreertos.a 1665entries: 1666 croutine (default) #3 1667 croutine:prvCheckPendingReadyList (noflash_text) #4 1668""" 1669 1670 self.generation.mappings = {} 1671 self.add_fragments(mapping) 1672 1673 actual = self.generation.generate(self.entities) 1674 expected = self.generate_default_rules() 1675 1676 flash_text = expected['flash_text'] 1677 iram0_text = expected['iram0_text'] 1678 1679 # Command for #2, pre A.1 1680 flash_text.insert(0, SymbolAtAddress('_sym1_start')) 1681 1682 # Command for #1 with KEEP() B 1683 # and exclusion for #3 1684 flash_text[1].keep = True 1685 flash_text[1].exclusions.add(CROUTINE) 1686 1687 # Command for #2, post A.2 1688 flash_text.append(SymbolAtAddress('_sym1_end')) 1689 1690 # Command for #3 C 1691 croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine') 1692 filtered_sections = fnmatch.filter(croutine_sections, '.literal.*') 1693 filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*')) 1694 1695 filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')] 1696 filtered_sections.append('.text') 1697 flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [])) 1698 1699 # Command for #4 D 1700 iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) 1701 1702 self.compare_rules(expected, actual) 1703 1704 def test_flags_separate_exclusion_command_if_explicit_lib(self): 1705 # Explicit commands are separated from the parent's flags. 1706 # 1707 # flash_text 1708 # *(EXCLUDE_FILE(libfreertos.a).text ...) 1709 # _sym1_start A.1 1710 # KEEP(libfreertos.a(EXCLUDE_FILE(libfreertos:croutine).text.* ...)) B 1711 # _sym1_end A.2 1712 # KEEP(libfreertos.a:croutine(...))) C 1713 # 1714 # iram0_text 1715 # *(.iram .iram.*) 1716 # libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) D 1717 mapping = u""" 1718[mapping:test] 1719archive: libfreertos.a 1720entries: 1721 # 1 1722 * (default); 1723 text->flash_text SURROUND(sym1) KEEP() 1724 croutine (default) #2 1725 croutine:prvCheckPendingReadyList (noflash_text) #3 1726""" 1727 1728 self.add_fragments(mapping) 1729 1730 actual = self.generation.generate(self.entities) 1731 expected = self.generate_default_rules() 1732 1733 flash_text = expected['flash_text'] 1734 iram0_text = expected['iram0_text'] 1735 1736 # Command for #2, pre A.1 1737 flash_text.append(SymbolAtAddress('_sym1_start')) 1738 flash_text[0].exclusions.add(FREERTOS) 1739 1740 # Command for #1 with KEEP() B 1741 # and exclusion for #3 1742 flash_text.append(InputSectionDesc(FREERTOS, flash_text[0].sections, [CROUTINE], keep=True)) 1743 1744 # Command for #2, post A.2 1745 flash_text.append(SymbolAtAddress('_sym1_end')) 1746 1747 # Implicit exclusion command for #3 C 1748 croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine') 1749 filtered_sections = fnmatch.filter(croutine_sections, '.literal.*') 1750 filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*')) 1751 1752 filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')] 1753 filtered_sections.append('.text') 1754 flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [])) 1755 1756 # Command for #3 C 1757 iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), [])) 1758 1759 self.compare_rules(expected, actual) 1760 1761 def test_flag_additions(self): 1762 # Test ability to add flags as long as no other mapping fragments 1763 # does the same thing. 1764 mapping = u""" 1765[mapping:default_add_flag] 1766archive: * 1767entries: 1768 * (default); 1769 text->flash_text KEEP() 1770""" 1771 1772 self.add_fragments(mapping) 1773 1774 actual = self.generation.generate(self.entities) 1775 expected = self.generate_default_rules() 1776 1777 flash_text = expected['flash_text'] 1778 flash_text[0].keep = True 1779 1780 self.compare_rules(expected, actual) 1781 1782 def test_flags_flag_additions_duplicate(self): 1783 # Test same flags added to same entity - these 1784 # are ignored. 1785 mapping = u""" 1786[mapping:default_add_flag_1] 1787archive: * 1788entries: 1789 * (default); 1790 text->flash_text KEEP() 1791 1792[mapping:default_add_flag_2] 1793archive: * 1794entries: 1795 * (default); 1796 text->flash_text KEEP() 1797""" 1798 1799 self.add_fragments(mapping) 1800 1801 actual = self.generation.generate(self.entities) 1802 expected = self.generate_default_rules() 1803 1804 flash_text = expected['flash_text'] 1805 flash_text[0].keep = True 1806 1807 self.compare_rules(expected, actual) 1808 1809 def test_flags_flag_additions_conflict(self): 1810 # Test condition where multiple fragments specifies flags 1811 # to same entity - should generate exception. 1812 mapping = u""" 1813[mapping:default_add_flag_1] 1814archive: * 1815entries: 1816 * (default); 1817 text->flash_text ALIGN(2) 1818 1819[mapping:default_add_flag_2] 1820archive: * 1821entries: 1822 * (default); 1823 text->flash_text SURROUND(sym1) 1824""" 1825 self.add_fragments(mapping) 1826 1827 with self.assertRaises(GenerationException): 1828 self.generation.generate(self.entities) 1829 1830 1831if __name__ == '__main__': 1832 unittest.main() 1833