Ver Fonte

Merge branch 'bugfix/ldgen_ignore_nonexistent_archives_and_obj_v4.1' into 'release/v4.1'

ldgen: check mappings (v4.1)

See merge request espressif/esp-idf!14058
Angus Gratton há 4 anos atrás
pai
commit
1ea2b57b41

+ 5 - 4
components/app_trace/linker.lf

@@ -3,10 +3,11 @@ archive: libapp_trace.a
 entries: 
     app_trace (noflash)
     app_trace_util (noflash)
-    SEGGER_SYSVIEW (noflash)
-    SEGGER_RTT_esp32 (noflash)
-    SEGGER_SYSVIEW_Config_FreeRTOS (noflash)
-    SEGGER_SYSVIEW_FreeRTOS (noflash)
+    if SYSVIEW_ENABLE = y:
+      SEGGER_SYSVIEW (noflash)
+      SEGGER_RTT_esp32 (noflash)
+      SEGGER_SYSVIEW_Config_FreeRTOS (noflash)
+      SEGGER_SYSVIEW_FreeRTOS (noflash)
 
 [mapping:driver]
 archive: libdriver.a

+ 2 - 4
components/espcoredump/linker.lf

@@ -19,13 +19,11 @@ entries:
     else:
         * (default)
 
-[mapping:mbedtls]
-archive: libmbedtls.a
+[mapping:sha256_coredump]
+archive: libmbedcrypto.a
 entries:
     if ESP32_COREDUMP_CHECKSUM_SHA256 = y :
         if MBEDTLS_HARDWARE_SHA = n:
             sha256 (noflash_text)
         else:
             esp_sha256 (noflash_text)
-    else:
-        * (default)

+ 3 - 0
components/freertos/linker.lf

@@ -2,4 +2,7 @@
 archive: libfreertos.a
 entries:
     * (noflash_text)
+    if FREERTOS_SUPPORT_STATIC_ALLOCATION = y:
+        queue:xQueueGenericCreateStatic (default)
+
 

+ 3 - 1
components/heap/linker.lf

@@ -2,4 +2,6 @@
 archive: libheap.a
 entries:
     multi_heap (noflash)
-    multi_heap_poisoning (noflash)
+    if HEAP_POISONING_DISABLED = n:
+        multi_heap_poisoning (noflash)
+

+ 0 - 1
components/lwip/linker.lf

@@ -62,7 +62,6 @@ entries:
     pbuf:pbuf_header_impl (noflash_text)
     pbuf:pbuf_header (noflash_text)
     pbuf:pbuf_free (noflash_text)
-    timeouts:sys_timeouts_mbox_fetch (noflash_text)
     udp:udp_input_local_match (noflash_text)
     udp:udp_input (noflash_text)
     udp:udp_send (noflash_text)

+ 0 - 2
components/soc/esp32s2beta/rtc_periph.c

@@ -11,5 +11,3 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-
-#include "soc/rtc_periph.h"

+ 1 - 3
components/soc/linker.lf

@@ -5,7 +5,6 @@ entries:
     rtc_clk (noflash)
     rtc_clk_init (noflash_text)
     rtc_init (noflash_text)
-    rtc_periph (noflash_text)
     rtc_pm (noflash_text)
     rtc_sleep (noflash_text)
     rtc_time (noflash_text)
@@ -19,9 +18,8 @@ entries:
     spi_flash_hal_iram (noflash)
     ledc_hal_iram (noflash_text)
     i2c_hal_iram (noflash)
-    spi_flash_hal_gpspi (noflash)
     lldesc (noflash_text)
     if CAN_ISR_IN_IRAM = y:
         can_hal_iram (noflash)
     else:
-        can_hal_iram (default)
+        can_hal_iram (default)

+ 3 - 0
tools/ci/check_ldgen_mapping_exceptions.txt

@@ -0,0 +1,3 @@
+libc
+sha256_coredump
+gcc

+ 3 - 1
tools/ci/config/build.yml

@@ -57,7 +57,8 @@ build_ssc_esp32s2beta:
 build_esp_idf_tests_make:
   extends: .build_esp_idf_unit_test_template
   variables:
-      PYTHON_VER: 3
+    PYTHON_VER: 3
+    LDGEN_CHECK_MAPPING: 1
   script:
     - export EXTRA_CFLAGS=${PEDANTIC_CFLAGS}
     - export EXTRA_CXXFLAGS=${PEDANTIC_CXXFLAGS}
@@ -108,6 +109,7 @@ build_examples_make:
       - $LOG_PATH
     expire_in: 4 days
   variables:
+    LDGEN_CHECK_MAPPING: 1
     LOG_PATH: "$CI_PROJECT_DIR/log_examples_make"
   only:
     # Here both 'variables' and 'refs' conditions are given. They are combined with "AND" logic.

+ 7 - 0
tools/cmake/ldgen.cmake

@@ -56,6 +56,12 @@ function(__ldgen_process_template template output)
 
     idf_build_get_property(config_env_path CONFIG_ENV_PATH)
 
+    if($ENV{LDGEN_CHECK_MAPPING})
+        set(ldgen_check "--check-mapping"
+            "--check-mapping-exceptions=${idf_path}/tools/ci/check_ldgen_mapping_exceptions.txt")
+        message(STATUS "Mapping check enabled in ldgen")
+    endif()
+
     add_custom_command(
         OUTPUT ${output}
         COMMAND ${python} ${idf_path}/tools/ldgen/ldgen.py
@@ -67,6 +73,7 @@ function(__ldgen_process_template template output)
         --env-file  "${config_env_path}"
         --libraries-file ${build_dir}/ldgen_libraries
         --objdump   ${CMAKE_OBJDUMP}
+        ${ldgen_check}
         DEPENDS     ${template} ${ldgen_fragment_files} ${ldgen_depends} ${SDKCONFIG}
     )
 

+ 63 - 30
tools/ldgen/generation.py

@@ -21,7 +21,7 @@ import fnmatch
 
 from fragments import Sections, Scheme, Mapping, Fragment
 from pyparsing import Suppress, White, ParseException, Literal, Group, ZeroOrMore
-from pyparsing import Word, OneOrMore, nums, alphanums, alphas, Optional, restOfLine
+from pyparsing import Word, OneOrMore, nums, alphas, restOfLine, SkipTo
 from ldgen_common import LdGenFailure
 
 
@@ -80,7 +80,6 @@ class PlacementRule():
         def do_section_expansion(rule, section):
             if section in rule.get_section_names():
                 sections_in_obj = sections_infos.get_obj_sections(rule.archive, rule.obj)
-
                 expansions = fnmatch.filter(sections_in_obj, section)
                 return expansions
 
@@ -254,11 +253,18 @@ class GenerationModel:
 
     DEFAULT_SCHEME = "default"
 
-    def __init__(self):
+    def __init__(self, check_mappings=False, check_mapping_exceptions=None):
         self.schemes = {}
         self.sections = {}
         self.mappings = {}
 
+        self.check_mappings = check_mappings
+
+        if check_mapping_exceptions:
+            self.check_mapping_exceptions = check_mapping_exceptions
+        else:
+            self.check_mapping_exceptions = []
+
     def _add_mapping_rules(self, archive, obj, symbol, scheme_name, scheme_dict, rules):
         # Use an ordinary dictionary to raise exception on non-existing keys
         temp_dict = dict(scheme_dict)
@@ -338,6 +344,19 @@ class GenerationModel:
                 try:
                     if not (obj == Mapping.MAPPING_ALL_OBJECTS and symbol is None and
                             scheme_name == GenerationModel.DEFAULT_SCHEME):
+                        if self.check_mappings and mapping.name not in self.check_mapping_exceptions:
+                            if not obj == Mapping.MAPPING_ALL_OBJECTS:
+                                obj_sections = sections_infos.get_obj_sections(archive, obj)
+                                if not obj_sections:
+                                    message = "'%s:%s' not found" % (archive, obj)
+                                    raise GenerationException(message, mapping)
+
+                                if symbol:
+                                    obj_sym = fnmatch.filter(obj_sections, "*%s" % symbol)
+                                    if not obj_sym:
+                                        message = "'%s:%s %s' not found" % (archive, obj, symbol)
+                                        raise GenerationException(message, mapping)
+
                         self._add_mapping_rules(archive, obj, symbol, scheme_name, scheme_dictionary, mapping_rules)
                 except KeyError:
                     message = GenerationException.UNDEFINED_REFERENCE + " to scheme '" + scheme_name + "'."
@@ -589,7 +608,7 @@ class SectionsInfo(dict):
         results = None
 
         try:
-            results = parser.parseString(first_line)
+            results = parser.parseString(first_line, parseAll=True)
         except ParseException as p:
             raise ParseException("Parsing sections info for library " + sections_info_dump.name + " failed. " + p.message)
 
@@ -597,43 +616,57 @@ class SectionsInfo(dict):
         self.sections[archive] = SectionsInfo.__info(sections_info_dump.name, sections_info_dump.read())
 
     def _get_infos_from_file(self, info):
-        # Object file line: '{object}:  file format elf32-xtensa-le'
-        object = Fragment.ENTITY.setResultsName("object") + Literal(":").suppress() + Literal("file format elf32-xtensa-le").suppress()
+        # {object}:  file format elf32-xtensa-le
+        object_line = SkipTo(":").setResultsName("object") + Suppress(restOfLine)
 
-        # Sections table
-        header = Suppress(Literal("Sections:") + Literal("Idx") + Literal("Name") + Literal("Size") + Literal("VMA") +
-                          Literal("LMA") + Literal("File off") + Literal("Algn"))
-        entry = Word(nums).suppress() + Fragment.ENTITY + Suppress(OneOrMore(Word(alphanums, exact=8)) +
-                                                                   Word(nums + "*") + ZeroOrMore(Word(alphas.upper()) +
-                                                                   Optional(Literal(","))))
+        # Sections:
+        # Idx Name ...
+        section_start = Suppress(Literal("Sections:"))
+        section_header = Suppress(OneOrMore(Word(alphas)))
 
-        # Content is object file line + sections table
-        content = Group(object + header + Group(ZeroOrMore(entry)).setResultsName("sections"))
+        # 00 {section} 0000000 ...
+        #              CONTENTS, ALLOC, ....
+        section_entry = Suppress(Word(nums)) + SkipTo(' ') + Suppress(restOfLine) + \
+            Suppress(ZeroOrMore(Word(alphas) + Literal(",")) + Word(alphas))
 
+        content = Group(object_line + section_start + section_header + Group(OneOrMore(section_entry)).setResultsName("sections"))
         parser = Group(ZeroOrMore(content)).setResultsName("contents")
 
-        sections_info_text = info.content
         results = None
 
         try:
-            results = parser.parseString(sections_info_text)
+            results = parser.parseString(info.content, parseAll=True)
         except ParseException as p:
             raise ParseException("Unable to parse section info file " + info.filename + ". " + p.message)
 
         return results
 
     def get_obj_sections(self, archive, obj):
-        stored = self.sections[archive]
-
-        # Parse the contents of the sections file
-        if not isinstance(stored, dict):
-            parsed = self._get_infos_from_file(stored)
-            stored = dict()
-            for content in parsed.contents:
-                sections = list(map(lambda s: s, content.sections))
-                stored[content.object] = sections
-            self.sections[archive] = stored
-
-        for obj_key in stored.keys():
-            if obj_key == obj + ".o" or obj_key == obj + ".c.obj":
-                return stored[obj_key]
+        res = []
+        try:
+            stored = self.sections[archive]
+
+            # Parse the contents of the sections file on-demand,
+            # save the result for later
+            if not isinstance(stored, dict):
+                parsed = self._get_infos_from_file(stored)
+                stored = dict()
+                for content in parsed.contents:
+                    sections = list(map(lambda s: s, content.sections))
+                    stored[content.object] = sections
+                self.sections[archive] = stored
+
+            try:
+                res = stored[obj + ".o"]
+            except KeyError:
+                try:
+                    res = stored[obj + ".c.obj"]
+                except KeyError:
+                    try:
+                        res = stored[obj + ".cpp.obj"]
+                    except KeyError:
+                        res = stored[obj + ".S.obj"]
+        except KeyError:
+            pass
+
+        return res

+ 19 - 1
tools/ldgen/ldgen.py

@@ -83,6 +83,18 @@ def main():
         "--kconfig", "-k",
         help="IDF Kconfig file")
 
+    argparser.add_argument(
+        "--check-mapping",
+        help="Perform a check if a mapping (archive, obj, symbol) exists",
+        action='store_true'
+    )
+
+    argparser.add_argument(
+        "--check-mapping-exceptions",
+        help="Mappings exempted from check",
+        type=argparse.FileType("r")
+    )
+
     argparser.add_argument(
         "--env", "-e",
         action='append', default=[],
@@ -106,6 +118,12 @@ def main():
     kconfig_file = args.kconfig
     objdump = args.objdump
 
+    check_mapping = args.check_mapping
+    if args.check_mapping_exceptions:
+        check_mapping_exceptions = [line.strip() for line in args.check_mapping_exceptions]
+    else:
+        check_mapping_exceptions = None
+
     try:
         sections_infos = SectionsInfo()
         for library in libraries_file:
@@ -115,7 +133,7 @@ def main():
                 dump.name = library
                 sections_infos.add_sections_info(dump)
 
-        generation_model = GenerationModel()
+        generation_model = GenerationModel(check_mapping, check_mapping_exceptions)
 
         _update_environment(args)  # assign args.env and args.env_file to os.environ
 

+ 1 - 1
tools/ldgen/test/data/sections.info

@@ -1,4 +1,4 @@
-In archive /home/user/ãóç+ěščřžýáíé/build/esp-idf/freertos/libfreertos.a:
+In archive /home/user/build/esp-idf/freertos/libfreertos.a:
 
 FreeRTOS-openocd.c.obj:     file format elf32-xtensa-le
 

+ 19 - 0
tools/ldgen/test/data/sections_parse.info

@@ -0,0 +1,19 @@
+In archive /home/user/ãóç+ěščřžýáíé/build/esp-idf/freertos/libsections_parse.a:
+
+croutine.c.obj:     file format elf32-littleriscv
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .text         00000000  00000000  00000000  00000034  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  1 .data         00000000  00000000  00000000  00000034  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+  2 .bss          00000000  00000000  00000000  00000034  2**0
+                  ALLOC
+
+FreeRTOS-openocd.c.obj:     file format elf32-xtensa-le // 'F' should not get included in match for 'CONTENTS, ALLOC, LOAD ...' prior
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal.prvCheckPendingReadyList 00000018  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE

+ 13 - 0
tools/ldgen/test/test_generation.py

@@ -1422,6 +1422,19 @@ entries:
         self.assertListEqual(actual["flash_text"], expected["flash_text"])
         self.assertListEqual(actual["iram0_text"], expected["iram0_text"])
 
+    def test_sections_info_parsing(self):
+
+        self.sections_info = SectionsInfo()
+
+        with open("data/sections_parse.info") as sections_info_file_obj:
+            self.sections_info.add_sections_info(sections_info_file_obj)
+
+        sections = self.sections_info.get_obj_sections("libsections_parse.a", "croutine")
+        self.assertEqual(set(sections), set([".text", ".data", ".bss"]))
+
+        sections = self.sections_info.get_obj_sections("libsections_parse.a", "FreeRTOS-openocd")
+        self.assertEqual(set(sections), set([".literal.prvCheckPendingReadyList"]))
+
 
 if __name__ == "__main__":
     unittest.main()