diff --git a/docs/en/api-guides/linker-script-generation.rst b/docs/en/api-guides/linker-script-generation.rst index 0ef4543448..415ace4025 100644 --- a/docs/en/api-guides/linker-script-generation.rst +++ b/docs/en/api-guides/linker-script-generation.rst @@ -495,6 +495,95 @@ Example: entries: * (noflash) +Aside from the entity and scheme, flags can also be specified in an entry. The following +flags are supported (note: <> = argument name, [] = optional): + +1. ALIGN([, pre, post]) + Align the placement by the amount specified in ``alignment``. Generates + +.. code-block::none + + . = ALIGN() + + before and/or after (depending whether ``pre``, ``post`` or both are specified) + the input section description generated from the mapping + fragment entry. If neither 'pre' or 'post' is specified, the alignment command is + generated before the input section description. Order sensitive. + +2. SORT([, ]) + Emits ``SORT_BY_NAME``, ``SORT_BY_ALIGNMENT``, + ``SORT_BY_INIT_PRIORITY`` or ``SORT`` in the input section description. + Possible values for ``sort_by_first`` and ``sort_by_second`` are: + ``name``, ``alignment``, ``init_priority``. + + If both ``sort_by_first`` and ``sort_by_second`` are not specified, the input + sections are sorted by name. If both are specified, then the nested + sorting follows the same rules discussed in + https://sourceware.org/binutils/docs/ld/Input-Section-Wildcards.html. + +3. KEEP() + Prevent the linker from discarding the placement by + surrounding the input section description with KEEP command. + See https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html + for more details. + +4.SURROUND() + Generate symbols before and after the placement. The generated symbols + follow the naming ``__start`` and ``__end``. For example, if + ``name`` == sym1, + +.. code-block::none + + _sym1_start = ABSOLUTE(.) + ... + _sym2_end = ABSOLUTE(.) + + These symbols can then be referenced from C/C++ code. Order sensitive. + +When adding flags, the specific ``section -> target`` in the scheme needs to be specified. +For multiple ``section -> target``, use a comma as a separator. For example, + +.. code-block:: none + + # Notes: + # A. semicolon after entity-scheme + # B. comma before section2 -> target2 + # C. section1 -> target1 and section2 -> target2 should be defined in entries of scheme1 + entity1 (scheme1); + section1 -> target1 KEEP() ALIGN(4, pre, post), + section2 -> target2 SURROUND(sym) ALIGN(4, post) SORT() + +Putting it all together, the following mapping fragment, for example, + +.. code-block:: none + + [mapping:name] + archive: lib1.a + entries: + obj1 (noflash); + rodata -> dram0_data KEEP() SORT() ALIGN(8) SURROUND(my_sym) + +generates an output on the linker script: + +.. code-block:: none + + . = ALIGN(8) + __my_sym_start = ABSOLUTE(.) + KEEP(lib1.a:obj1.*( SORT(.rodata) SORT(.rodata.*) )) + __my_sym_end = ABSOLUTE(.) + +Note that ALIGN and SURROUND, as mentioned in the flag descriptions, are order sensitive. +Therefore, if for the same mapping fragment these two are switched, the following +is generated instead: + +.. code-block:: none + + __my_sym_start = ABSOLUTE(.) + . = ALIGN(8) + KEEP(lib1.a:obj1.*( SORT(.rodata) SORT(.rodata.*) )) + __my_sym_end = ABSOLUTE(.) + + .. _ldgen-symbol-granularity-placements : On Symbol-Granularity Placements diff --git a/tools/ldgen/README.md b/tools/ldgen/README.md new file mode 100644 index 0000000000..de40eb9fdd --- /dev/null +++ b/tools/ldgen/README.md @@ -0,0 +1,42 @@ +## Linker Script Generator + +Contains code that implements linker script generation, `ldgen`. For more information about the feature, +see `docs/en/api-guides/linker-script-generation.rst`. + +### Source Files + +The following are the source files in the directory: + +- `ldgen.py` - Python executable that gets called during build. +- `entity.py` - contains classes related to entities (library, object, symbol or combination of the above) with mappable input sections. +- `fragments.py` - contains classes for parsing the different types of fragments in linker fragment files. +- `generation.py` - contains bulk of the logic used to process fragments into output commands. +- `sdkconfig.py` - used for evaluating conditionals in fragment files. +- `linker_script.py` - augments the input linker script template with output commands from generation process to produce the output linker script. +- `output_commands.py` - contains classes that represent the output commands in the output linker script. +- `ldgen_common.py` - contains miscellaneous utilities/definitions that can be used in the files mentioned above. + +### Tests + +Unit tests are in the `test` directory. These tests are run as part of CI in the job `test_ldgen_on_host`. + +There is also a test app for `ldgen` in `tools/test_apps/build_system/ldgen_test`. + +### Build System + +Linker script generation is a part of the build process. The build scripts `tools/cmake/ldgen.cmake` +and `make/ldgen.mk` contain the build-system-side implementation for CMake and Make, respectively. + +### Basic Flow + +The build system invokes `ldgen.py`, passing some information from the build. + +The linker fragment files are parsed by `fragments.py`, evaluating conditional expressions +with `sdkconfig.py`. + +From the parsed fragments, `generation.py` generates output commands defined in `output_commands.py`, +with some help from `entity.py`. + +`linker_script.py` writes the output linker script, replacing markers with output commands generated. + +More details about the implementation are in the respective source files. \ No newline at end of file diff --git a/tools/ldgen/entity.py b/tools/ldgen/entity.py index fde5b47d7d..065f5828be 100644 --- a/tools/ldgen/entity.py +++ b/tools/ldgen/entity.py @@ -27,8 +27,13 @@ from pyparsing import (Group, Literal, OneOrMore, ParseException, SkipTo, Suppre @total_ordering class Entity(): """ - Definition of an entity which can be placed or excluded - from placement. + An entity refers to a library, object, symbol whose input + sections can be placed or excluded from placement. + + An important property of an entity is its specificity - the granularity + of the the entity to be placed. Specificity increases in the following + order: library, object, symbol. An entity with no specificity refers + to all entities. """ ALL = '*' @@ -105,8 +110,10 @@ class Entity(): class EntityDB(): """ - Encapsulates an output of objdump. Contains information about the static library sections - and names + Collection of entities extracted from libraries known in the build. + Allows retrieving a list of archives, a list of object files in an archive + or a list of symbols in an archive; as well as allows for checking if an + entity exists in the collection. """ __info = collections.namedtuple('__info', 'filename content') diff --git a/tools/ldgen/fragments.py b/tools/ldgen/fragments.py index d3e2a2e761..09f070b91b 100644 --- a/tools/ldgen/fragments.py +++ b/tools/ldgen/fragments.py @@ -25,13 +25,12 @@ from pyparsing import (Combine, Forward, Group, Keyword, Literal, OneOrMore, Opt originalTextFor, restOfLine) from sdkconfig import SDKConfig -KeyGrammar = namedtuple('KeyGrammar', 'grammar min max required') - class FragmentFile(): """ - Fragment file internal representation. Parses and stores instances of the fragment definitions - contained within the file. + Processes a fragment file and stores all parsed fragments. For + more information on how this class interacts with classes for the different fragment types, + see description of Fragment. """ def __init__(self, fragment_file, sdkconfig): @@ -186,11 +185,36 @@ class FragmentFile(): class Fragment(): + """ + Base class for a fragment that can be parsed from a fragment file. All fragments + share the common grammar: + + [type:name] + key1:value1 + key2:value2 + ... + + Supporting a new fragment type means deriving a concrete class which specifies + key-value pairs that the fragment supports and what to do with the parsed key-value pairs. + + The new fragment must also be appended to FRAGMENT_TYPES, specifying the + keyword for the type and the derived class. + + The key of the key-value pair is a simple keyword string. Other parameters + that describe the key-value pair is specified in Fragment.KeyValue: + 1. grammar - pyparsing grammar to parse the value of key-value pair + 2. min - the minimum number of value in the key entry, None means no minimum + 3. max - the maximum number of value in the key entry, None means no maximum + 4. required - if the key-value pair is required in the fragment + + Setting min=max=1 means that the key has a single value. + + FragmentFile provides conditional expression evaluation, enforcing + the parameters for Fragment.Keyvalue. + """ __metaclass__ = abc.ABCMeta - """ - Encapsulates a fragment as defined in the generator syntax. Sets values common to all fragment and performs processing - such as checking the validity of the fragment name and getting the entry values. - """ + + KeyValue = namedtuple('KeyValue', 'grammar min max required') IDENTIFIER = Word(alphas + '_', alphanums + '_') ENTITY = Word(alphanums + '.-_$') @@ -205,6 +229,15 @@ class Fragment(): class Sections(Fragment): + """ + Fragment which contains list of input sections. + + [sections:] + entries: + .section1 + .section2 + ... + """ # Unless quoted, symbol names start with a letter, underscore, or point # and may include any letters, underscores, digits, points, and hyphens. @@ -213,7 +246,7 @@ class Sections(Fragment): entries_grammar = Combine(GNU_LD_SYMBOLS + Optional('+')) grammars = { - 'entries': KeyGrammar(entries_grammar.setResultsName('section'), 1, None, True) + 'entries': Fragment.KeyValue(entries_grammar.setResultsName('section'), 1, None, True) } """ @@ -247,12 +280,19 @@ class Sections(Fragment): class Scheme(Fragment): """ - Encapsulates a scheme fragment, which defines what target input sections are placed under. + Fragment which defines where the input sections defined in a Sections fragment + is going to end up, the target. The targets are markers in a linker script template + (see LinkerScript in linker_script.py). + + [scheme:] + entries: + sections1 -> target1 + ... """ grammars = { - 'entries': KeyGrammar(Fragment.IDENTIFIER.setResultsName('sections') + Suppress('->') + - Fragment.IDENTIFIER.setResultsName('target'), 1, None, True) + 'entries': Fragment.KeyValue(Fragment.IDENTIFIER.setResultsName('sections') + Suppress('->') + + Fragment.IDENTIFIER.setResultsName('target'), 1, None, True) } def set_key_value(self, key, parse_results): @@ -267,7 +307,23 @@ class Scheme(Fragment): class Mapping(Fragment): """ - Encapsulates a mapping fragment, which defines what targets the input sections of mappable entties are placed under. + Fragment which attaches a scheme to entities (see Entity in entity.py), specifying where the input + sections of the entity will end up. + + [mapping:] + archive: lib1.a + entries: + obj1:symbol1 (scheme1); section1 -> target1 KEEP SURROUND(sym1) ... + obj2 (scheme2) + ... + + Ultimately, an `entity (scheme)` entry generates an + input section description (see https://sourceware.org/binutils/docs/ld/Input-Section.html) + in the output linker script. It is possible to attach 'flags' to the + `entity (scheme)` to generate different output commands or to + emit additional keywords in the generated input section description. The + input section description, as well as other output commands, is defined in + output_commands.py. """ class Flag(): @@ -275,7 +331,6 @@ class Mapping(Fragment): Optional(Suppress(',') + Suppress('post').setParseAction(lambda: True).setResultsName('post'))) class Surround(Flag): - def __init__(self, symbol): self.symbol = symbol self.pre = True @@ -285,7 +340,7 @@ class Mapping(Fragment): def get_grammar(): # SURROUND(symbol) # - # __symbol_start, __symbol_end is generated before and after + # '__symbol_start', '__symbol_end' is generated before and after # the corresponding input section description, respectively. grammar = (Keyword('SURROUND').suppress() + Suppress('(') + @@ -308,7 +363,11 @@ class Mapping(Fragment): @staticmethod def get_grammar(): - # ALIGN(alignment, [, pre, post]) + # ALIGN(alignment, [, pre, post]). + # + # Generates alignment command before and/or after the corresponding + # input section description, depending whether pre, post or + # both are specified. grammar = (Keyword('ALIGN').suppress() + Suppress('(') + Word(nums).setResultsName('alignment') + @@ -343,7 +402,10 @@ class Mapping(Fragment): @staticmethod def get_grammar(): - grammar = Keyword('KEEP').setParseAction(Mapping.Keep) + # KEEP() + # + # Surrounds input section description with KEEP command. + grammar = Keyword('KEEP()').setParseAction(Mapping.Keep) return grammar def __eq__(self, other): @@ -361,7 +423,12 @@ class Mapping(Fragment): @staticmethod def get_grammar(): - # SORT(sort_by_first [, sort_by_second]) + # SORT([sort_by_first, sort_by_second]) + # + # where sort_by_first, sort_by_second = {name, alignment, init_priority} + # + # Emits SORT_BY_NAME, SORT_BY_ALIGNMENT or SORT_BY_INIT_PRIORITY + # depending on arguments. Nested sort follows linker script rules. keywords = Keyword('name') | Keyword('alignment') | Keyword('init_priority') grammar = (Keyword('SORT').suppress() + Suppress('(') + keywords.setResultsName('first') + @@ -448,8 +515,8 @@ class Mapping(Fragment): Optional(Suppress(';') + delimitedList(section_target_flags).setResultsName('sections_target_flags'))) grammars = { - 'archive': KeyGrammar(Or([Fragment.ENTITY, Word(Entity.ALL)]).setResultsName('archive'), 1, 1, True), - 'entries': KeyGrammar(entry, 0, None, True) + 'archive': Fragment.KeyValue(Or([Fragment.ENTITY, Word(Entity.ALL)]).setResultsName('archive'), 1, 1, True), + 'entries': Fragment.KeyValue(entry, 0, None, True) } return grammars @@ -457,7 +524,9 @@ class Mapping(Fragment): class DeprecatedMapping(): """ - Encapsulates a mapping fragment, which defines what targets the input sections of mappable entties are placed under. + Mapping fragment with old grammar in versions older than ESP-IDF v4.0. Does not conform to + requirements of the Fragment class and thus is limited when it comes to conditional expression + evaluation. """ # Name of the default condition entry diff --git a/tools/ldgen/generation.py b/tools/ldgen/generation.py index 71fbf48339..910969dd60 100644 --- a/tools/ldgen/generation.py +++ b/tools/ldgen/generation.py @@ -26,6 +26,26 @@ from output_commands import AlignAtAddress, InputSectionDesc, SymbolAtAddress class Placement(): + """ + A Placement is an assignment of an entity's input sections to a target + in the output linker script - a precursor to the input section description. + + A placement can be excluded from another placement. These are represented + as contents of EXCLUDE_FILE in the input section description. Since the linker uses the + first matching rule, these exclusions make sure that accidental matching + of entities with higher specificity does not occur. + + The placement which a placement is excluded from is referred to as the + 'basis' placement. It operates on the same input section of the entity on + one of the parent (or parent's parent and so forth), but might have + a different target (see is_significant() for the criteria). + + A placement is explicit if it was derived from an actual entry in one of + the mapping fragments. Just as intermediate entity nodes are created in some cases, + intermediate placements are created particularly for symbol placements. + The reason is that EXCLUDE_FILE does not work on symbols (see ObjectNode + for details). + """ def __init__(self, node, sections, target, flags, explicit, force=False, dryrun=False): self.node = node @@ -43,9 +63,7 @@ class Placement(): # fragment entry. self.explicit = explicit - # Find basis placement. A basis placement is a placement - # on the parent (or parent's parent and so on and so forth) - # that operates on the same section as this one. + # Find basis placement. parent = node.parent candidate = None while parent: @@ -91,6 +109,31 @@ class Placement(): class EntityNode(): + """ + Node in entity tree. An EntityNode + is created from an Entity (see entity.py). + + The entity tree has a maximum depth of 3. Nodes at different + depths are derived from this class for special behavior (see + RootNode, ArchiveNode, ObjectNode, SymbolNode) depending + on entity specificity. + + Nodes for entities are inserted at the appropriate depth, creating + intermediate nodes along the path if necessary. For example, a node + for entity `lib1.a:obj1:sym1` needs to be inserted. If the node for `lib1:obj1` + does not exist, then it needs to be created. + + A node contains a dictionary of placements (see Placement). + The key to this dictionary are contents of sections fragments, + representing the input sections of an entity. For example, + a node for entity `lib1.a` might have a placement entry for its `.text` input section + in this dictionary. The placement will contain details about the + target, the flags, etc. + + Generation of output commands to be written to the output linker script + requires traversal of the tree, each node collecting the output commands + from its children, so on and so forth. + """ def __init__(self, parent, name): self.children = [] @@ -212,14 +255,32 @@ class EntityNode(): class SymbolNode(EntityNode): - + """ + Entities at depth=3. Represents entities with archive, object + and symbol specified. + """ def __init__(self, parent, name): EntityNode.__init__(self, parent, name) self.entity = Entity(self.parent.parent.name, self.parent.name) class ObjectNode(EntityNode): + """ + Entities at depth=2. Represents entities with archive + and object specified. + Creating a placement on a child node (SymbolNode) has a different behavior, since + exclusions using EXCLUDE_FILE for symbols does not work. + + The sections of this entity has to be 'expanded'. That is, we must + look into the actual input sections of this entity and remove + the ones corresponding to the symbol. The remaining sections of an expanded + object entity will be listed one-by-one in the corresponding + input section description. + + An intermediate placement on this node is created, if one does not exist, + and is the one excluded from its basis placement. + """ def __init__(self, parent, name): EntityNode.__init__(self, parent, name) self.child_t = SymbolNode @@ -281,7 +342,9 @@ class ObjectNode(EntityNode): class ArchiveNode(EntityNode): - + """ + Entities at depth=1. Represents entities with archive specified. + """ def __init__(self, parent, name): EntityNode.__init__(self, parent, name) self.child_t = ObjectNode @@ -289,15 +352,21 @@ class ArchiveNode(EntityNode): class RootNode(EntityNode): + """ + Single entity at depth=0. Represents entities with no specific members + specified. + """ def __init__(self): EntityNode.__init__(self, None, Entity.ALL) self.child_t = ArchiveNode - self.entity = Entity('*') + self.entity = Entity(Entity.ALL) class Generation: """ - Implements generation of placement based on collected sections, scheme and mapping fragment. + Processes all fragments processed from fragment files included in the build. + Generates output commands (see output_commands.py) that LinkerScript (see linker_script.py) can + write to the output linker script. """ # Processed mapping, scheme and section entries diff --git a/tools/ldgen/linker_script.py b/tools/ldgen/linker_script.py index f82747bed4..c15b397a24 100644 --- a/tools/ldgen/linker_script.py +++ b/tools/ldgen/linker_script.py @@ -24,8 +24,11 @@ from pyparsing import ParseException, Suppress, White class LinkerScript: """ - Encapsulates a linker script template file. Finds marker syntax and handles replacement to generate the - final output. + Process a linker script template, which contains markers with grammar: + + [] + + The is where output commands (see output_commands.py) are placed. """ Marker = collections.namedtuple('Marker', 'target indent rules') diff --git a/tools/ldgen/output_commands.py b/tools/ldgen/output_commands.py index 4fb07485e3..05726b8d5d 100644 --- a/tools/ldgen/output_commands.py +++ b/tools/ldgen/output_commands.py @@ -16,8 +16,20 @@ from entity import Entity +# Contains classes for output section commands referred to in +# https://www.acrc.bris.ac.uk/acrc/RedHat/rhel-ld-en-4/sections.html#OUTPUT-SECTION-DESCRIPTION. + class AlignAtAddress(): + """ + Outputs assignment of builtin function ALIGN to current + position: + + . = ALIGN() + + Mapping fragment flag ALIGN causes this output section + command to be emitted. + """ def __init__(self, alignment): self.alignment = alignment @@ -31,6 +43,16 @@ class AlignAtAddress(): class SymbolAtAddress(): + """ + Outputs assignment of builtin function ABSOLUTE to a symbol + for current position: + + = ABSOLUTE(.) + + Mapping fragment flag SURROUND causes this + output section command to be emitted before and after + an InputSectionDesc. + """ def __init__(self, symbol): self.symbol = symbol @@ -44,6 +66,14 @@ class SymbolAtAddress(): class InputSectionDesc(): + """ + Outputs an input section description as described in + https://www.acrc.bris.ac.uk/acrc/RedHat/rhel-ld-en-4/sections.html#INPUT-SECTION. + + These commands are emmited from mapping fragment entries, specifically attaching + a scheme onto an entity. Mapping fragment flags KEEP, SORT will also affect + the emitted input section description. + """ def __init__(self, entity, sections, exclusions=None, keep=False, sort=None): assert(entity.specificity != Entity.Specificity.SYMBOL) diff --git a/tools/ldgen/sdkconfig.py b/tools/ldgen/sdkconfig.py index b05a6e3b83..f6e8fa520a 100644 --- a/tools/ldgen/sdkconfig.py +++ b/tools/ldgen/sdkconfig.py @@ -21,8 +21,8 @@ from pyparsing import (Combine, Group, Literal, Optional, Word, alphanums, hexnu class SDKConfig: """ - Encapsulates an sdkconfig file. Defines grammar of a configuration entry, and enables - evaluation of logical expressions involving those entries. + Evaluates conditional expressions based on the build's sdkconfig and Kconfig files. + This also defines the grammar of conditional expressions. """ # A configuration entry is in the form CONFIG=VALUE. Definitions of components of that grammar diff --git a/tools/ldgen/test/test_fragments.py b/tools/ldgen/test/test_fragments.py index d844ae5fb4..1c5878cc4c 100755 --- a/tools/ldgen/test/test_fragments.py +++ b/tools/ldgen/test/test_fragments.py @@ -23,20 +23,20 @@ from io import StringIO from pyparsing import ParseException, ParseFatalException, Word, alphanums try: - from fragments import FRAGMENT_TYPES, Fragment, FragmentFile, KeyGrammar, Mapping + from fragments import FRAGMENT_TYPES, Fragment, FragmentFile, Mapping from sdkconfig import SDKConfig except ImportError: sys.path.append('../') - from fragments import FRAGMENT_TYPES, Fragment, FragmentFile, KeyGrammar, Mapping + from fragments import FRAGMENT_TYPES, Fragment, FragmentFile, Mapping from sdkconfig import SDKConfig class SampleFragment(Fragment): grammars = { - 'key_1': KeyGrammar(Word(alphanums + '_').setResultsName('value'), 0, None, True), - 'key_2': KeyGrammar(Word(alphanums + '_').setResultsName('value'), 0, None, False), - 'key_3': KeyGrammar(Word(alphanums + '_').setResultsName('value'), 3, 5, False) + 'key_1': Fragment.KeyValue(Word(alphanums + '_').setResultsName('value'), 0, None, True), + 'key_2': Fragment.KeyValue(Word(alphanums + '_').setResultsName('value'), 0, None, False), + 'key_3': Fragment.KeyValue(Word(alphanums + '_').setResultsName('value'), 3, 5, False) } def set_key_value(self, key, parse_results): @@ -818,8 +818,8 @@ entries: archive: libmain.a entries: obj1 (default); - text->flash_text KEEP, - rodata->flash_rodata KEEP KEEP + text->flash_text KEEP(), + rodata->flash_rodata KEEP() KEEP() """) fragment_file = FragmentFile(test_fragment, self.sdkconfig) @@ -930,8 +930,8 @@ entries: archive: libmain.a entries: obj1 (default); - text->flash_text ALIGN(4) KEEP SURROUND(sym1) ALIGN(8) SORT(name), - rodata->flash_rodata KEEP ALIGN(4) KEEP SURROUND(sym1) ALIGN(8) ALIGN(4) SORT(name) + text->flash_text ALIGN(4) KEEP() SURROUND(sym1) ALIGN(8) SORT(name), + rodata->flash_rodata KEEP() ALIGN(4) KEEP() SURROUND(sym1) ALIGN(8) ALIGN(4) SORT(name) """) fragment_file = FragmentFile(test_fragment, self.sdkconfig) fragment = fragment_file.fragments[0] @@ -960,8 +960,8 @@ entries: archive: libmain.a entries: obj1 (default); - text->flash_text ALIGN(4) KEEP SURROUND(sym1) SORT(name), - text->flash_text ALIGN(4) KEEP SURROUND(sym1) SORT(name) + text->flash_text ALIGN(4) KEEP() SURROUND(sym1) SORT(name), + text->flash_text ALIGN(4) KEEP() SURROUND(sym1) SORT(name) """) fragment_file = FragmentFile(test_fragment, self.sdkconfig) fragment = fragment_file.fragments[0] @@ -987,9 +987,9 @@ entries: archive: libmain.a entries: obj1 (default); - text->flash_text ALIGN(4) KEEP SURROUND(sym1) SORT(name) + text->flash_text ALIGN(4) KEEP() SURROUND(sym1) SORT(name) obj1 (default); - text->flash_text ALIGN(4) KEEP SURROUND(sym1) SORT(name) + text->flash_text ALIGN(4) KEEP() SURROUND(sym1) SORT(name) """) fragment_file = FragmentFile(test_fragment, self.sdkconfig) fragment = fragment_file.fragments[0] diff --git a/tools/ldgen/test/test_generation.py b/tools/ldgen/test/test_generation.py index 5a2fd2e920..28aac1c974 100755 --- a/tools/ldgen/test/test_generation.py +++ b/tools/ldgen/test/test_generation.py @@ -1397,7 +1397,7 @@ class FlagTest(GenerationTest): # with flags. def test_flags_basics(self): - # Test that input section commands additions are done (KEEP, SORT). + # Test that input section commands additions are done (KEEP SORT). # Test that order dependent commands are properly generated (ALIGN, SURROUND) # Normally, if an entry has the same mapping as parent, commands. # are not emitted for them. However, if there are flags, they should be - @@ -1430,7 +1430,7 @@ entries: croutine (noflash_text); text->iram0_text ALIGN(4, pre, post) SURROUND(sym1) #1 timers (default); - text->flash_text KEEP SORT(name) #2 + text->flash_text KEEP() SORT(name) #2 timers (default); rodata->flash_rodata SURROUND(sym2) ALIGN(4, pre, post) #3 """ @@ -1489,7 +1489,7 @@ archive: * entries: # 1 * (default); - text->flash_text SURROUND(sym1) KEEP #2 + text->flash_text SURROUND(sym1) KEEP() #2 [mapping:test] archive: libfreertos.a @@ -1509,7 +1509,7 @@ entries: # Command for #2, pre A.1 flash_text.insert(0, SymbolAtAddress('_sym1_start')) - # Command for #1 with KEEP B + # Command for #1 with KEEP() B # and exclusion for #3 flash_text[1].keep = True flash_text[1].exclusions.add(CROUTINE) @@ -1551,7 +1551,7 @@ archive: libfreertos.a entries: # 1 * (default); - text->flash_text SURROUND(sym1) KEEP #2 + text->flash_text SURROUND(sym1) KEEP() #2 croutine:prvCheckPendingReadyList (noflash_text) #3 """ @@ -1567,7 +1567,7 @@ entries: flash_text.append(SymbolAtAddress('_sym1_start')) flash_text[0].exclusions.add(FREERTOS) - # Command for #1 with KEEP B + # Command for #1 with KEEP() B # and exclusion for #3 flash_text.append(InputSectionDesc(FREERTOS, flash_text[0].sections, [CROUTINE], keep=True)) @@ -1607,7 +1607,7 @@ archive: libfreertos.a entries: # 1 croutine (default); - text->flash_text SURROUND(sym1) KEEP #2 + text->flash_text SURROUND(sym1) KEEP() #2 croutine:prvCheckPendingReadyList (noflash_text) #3 """ @@ -1658,7 +1658,7 @@ archive: * entries: # 1 * (default); - text->flash_text SURROUND(sym1) KEEP #2 + text->flash_text SURROUND(sym1) KEEP() #2 [mapping:test] archive: libfreertos.a @@ -1679,7 +1679,7 @@ entries: # Command for #2, pre A.1 flash_text.insert(0, SymbolAtAddress('_sym1_start')) - # Command for #1 with KEEP B + # Command for #1 with KEEP() B # and exclusion for #3 flash_text[1].keep = True flash_text[1].exclusions.add(CROUTINE) @@ -1720,7 +1720,7 @@ archive: libfreertos.a entries: # 1 * (default); - text->flash_text SURROUND(sym1) KEEP + text->flash_text SURROUND(sym1) KEEP() croutine (default) #2 croutine:prvCheckPendingReadyList (noflash_text) #3 """ @@ -1737,7 +1737,7 @@ entries: flash_text.append(SymbolAtAddress('_sym1_start')) flash_text[0].exclusions.add(FREERTOS) - # Command for #1 with KEEP B + # Command for #1 with KEEP() B # and exclusion for #3 flash_text.append(InputSectionDesc(FREERTOS, flash_text[0].sections, [CROUTINE], keep=True)) @@ -1766,7 +1766,7 @@ entries: archive: * entries: * (default); - text->flash_text KEEP + text->flash_text KEEP() """ self.add_fragments(mapping) @@ -1787,13 +1787,13 @@ entries: archive: * entries: * (default); - text->flash_text KEEP + text->flash_text KEEP() [mapping:default_add_flag_2] archive: * entries: * (default); - text->flash_text KEEP + text->flash_text KEEP() """ self.add_fragments(mapping)