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

ldgen: Check mappings (v4.2)

See merge request espressif/esp-idf!13994
This commit is contained in:
Angus Gratton 2021-06-21 23:20:59 +00:00
commit 0b45e84e2a
18 changed files with 145 additions and 49 deletions

View File

@ -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

View File

@ -3,4 +3,4 @@ archive: libesp_system.a
entries:
panic (noflash)
panic_handler (noflash)
system_api:esp_system_abort (noflash)
system_api:esp_system_abort (noflash)

View File

@ -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)

View File

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

View File

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

View File

@ -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)

View File

@ -7,7 +7,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)
@ -21,13 +20,14 @@ entries:
spi_flash_hal_iram (noflash)
ledc_hal_iram (noflash)
i2c_hal_iram (noflash)
spi_flash_hal_gpspi (noflash)
lldesc (noflash)
cpu_hal (noflash)
soc_hal (noflash)
wdt_hal_iram (noflash)
systimer_hal (noflash)
if TWAI_ISR_IN_IRAM = y:
twai_hal_iram (noflash)
else:
twai_hal_iram (default)
if IDF_TARGET_ESP32 = n:
spi_flash_hal_gpspi (noflash)
systimer_hal (noflash)

View File

@ -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"

View File

@ -1,7 +1,8 @@
[mapping:spi_flash]
archive: libspi_flash.a
entries:
spi_flash_rom_patch (noflash)
if IDF_TARGET_ESP32 = y:
spi_flash_rom_patch (noflash)
spi_flash_chip_generic (noflash)
spi_flash_chip_issi (noflash)
spi_flash_chip_mxic (noflash)

View File

@ -2,7 +2,8 @@
archive: libxtensa.a
entries:
eri (noflash_text)
stdatomic (noflash)
if IDF_TARGET_ESP32S2 = y:
stdatomic (noflash)
[mapping:hal]
archive: libhal.a

View File

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

View File

@ -59,6 +59,7 @@ build_ssc_esp32s2:
variables:
LOG_PATH: "$CI_PROJECT_DIR/log_ut_cmake"
PYTHON_VER: 3
LDGEN_CHECK_MAPPING: 1
script:
- export EXTRA_CFLAGS=${PEDANTIC_CFLAGS}
- export EXTRA_CXXFLAGS=${PEDANTIC_CXXFLAGS}
@ -81,6 +82,7 @@ build_esp_idf_tests_cmake_esp32s2:
extends: .build_template
variables:
PYTHON_VER: 3
LDGEN_CHECK_MAPPING: 1
parallel: 10
artifacts:
when: always

View File

@ -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}
)

View File

@ -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]
res = []
try:
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
# 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
for obj_key in stored.keys():
if obj_key == obj + ".o" or obj_key == obj + ".c.obj":
return stored[obj_key]
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()