diff --git a/CMakeLists.txt b/CMakeLists.txt index 91c1399341..d7630462e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,6 +86,10 @@ elseif(CONFIG_COMPILER_STACK_CHECK_MODE_ALL) list(APPEND compile_options "-fstack-protector-all") endif() +if(CONFIG_COMPILER_DUMP_RTL_FILES) + list(APPEND compile_options "-fdump-rtl-expand") +endif() + list(APPEND link_options "-fno-lto") idf_build_set_property(COMPILE_OPTIONS "${compile_options}" APPEND) diff --git a/Kconfig b/Kconfig index bb0690157f..8bda616ea6 100644 --- a/Kconfig +++ b/Kconfig @@ -335,6 +335,12 @@ mainmenu "Espressif IoT Development Framework Configuration" Enable this option if using GCC 6 or newer, and wanting to disable warnings which don't appear with GCC 5. + config COMPILER_DUMP_RTL_FILES + bool "Dump RTL files during compilation" + help + If enabled, RTL files will be produced during compilation. These files + can be used by other tools, for example to calculate call graphs. + endmenu # Compiler Options diff --git a/components/bootloader/subproject/main/ld/esp32/bootloader.ld b/components/bootloader/subproject/main/ld/esp32/bootloader.ld index 22a872ea37..b9538c75c9 100644 --- a/components/bootloader/subproject/main/ld/esp32/bootloader.ld +++ b/components/bootloader/subproject/main/ld/esp32/bootloader.ld @@ -38,14 +38,15 @@ SECTIONS *(.iram1 .iram1.*) /* catch stray IRAM_ATTR */ *liblog.a:(.literal .text .literal.* .text.*) *libgcc.a:(.literal .text .literal.* .text.*) - *libbootloader_support.a:bootloader_clock.*(.literal .text .literal.* .text.*) - *libbootloader_support.a:bootloader_common.*(.literal .text .literal.* .text.*) + *libbootloader_support.a:bootloader_clock_loader.*(.literal .text .literal.* .text.*) + *libbootloader_support.a:bootloader_common_loader.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_flash.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_random.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_efuse_esp32.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_utility.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_sha.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_console_loader.*(.literal .text .literal.* .text.*) + *libbootloader_support.a:bootloader_panic.*(.literal .text .literal.* .text.*) *libbootloader_support.a:esp_image_format.*(.literal .text .literal.* .text.*) *libbootloader_support.a:flash_encrypt.*(.literal .text .literal.* .text.*) *libbootloader_support.a:flash_partitions.*(.literal .text .literal.* .text.*) @@ -53,8 +54,9 @@ SECTIONS *libbootloader_support.a:secure_boot_signatures.*(.literal .text .literal.* .text.*) *libmicro-ecc.a:*.*(.literal .text .literal.* .text.*) *libspi_flash.a:*.*(.literal .text .literal.* .text.*) - *libsoc.a:wdt_hal_iram.*(.literal .text .literal.* .text.*) + *libhal.a:wdt_hal_iram.*(.literal .text .literal.* .text.*) *libsoc.a:rtc_clk.*(.literal .text .literal.* .text.*) + *libsoc.a:rtc_time.*(.literal .text .literal.* .text.*) *libefuse.a:*.*(.literal .text .literal.* .text.*) *(.fini.literal) *(.fini) diff --git a/components/bootloader/subproject/main/ld/esp32s2/bootloader.ld b/components/bootloader/subproject/main/ld/esp32s2/bootloader.ld index c23258298b..eb4de05b7c 100644 --- a/components/bootloader/subproject/main/ld/esp32s2/bootloader.ld +++ b/components/bootloader/subproject/main/ld/esp32s2/bootloader.ld @@ -25,13 +25,15 @@ SECTIONS *(.iram1 .iram1.*) /* catch stray IRAM_ATTR */ *liblog.a:(.literal .text .literal.* .text.*) *libgcc.a:(.literal .text .literal.* .text.*) - *libbootloader_support.a:bootloader_common.*(.literal .text .literal.* .text.*) + *libbootloader_support.a:bootloader_clock_loader.*(.literal .text .literal.* .text.*) + *libbootloader_support.a:bootloader_common_loader.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_flash.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_random.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_efuse_esp32s2.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_utility.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_sha.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_console_loader.*(.literal .text .literal.* .text.*) + *libbootloader_support.a:bootloader_panic.*(.literal .text .literal.* .text.*) *libbootloader_support.a:esp_image_format.*(.literal .text .literal.* .text.*) *libbootloader_support.a:flash_encrypt.*(.literal .text .literal.* .text.*) *libbootloader_support.a:flash_partitions.*(.literal .text .literal.* .text.*) @@ -39,7 +41,9 @@ SECTIONS *libbootloader_support.a:secure_boot_signatures.*(.literal .text .literal.* .text.*) *libmicro-ecc.a:*.*(.literal .text .literal.* .text.*) *libspi_flash.a:*.*(.literal .text .literal.* .text.*) - *libsoc.a:wdt_hal_iram.*(.literal .text .literal.* .text.*) + *libhal.a:wdt_hal_iram.*(.literal .text .literal.* .text.*) + *libsoc.a:rtc_clk.*(.literal .text .literal.* .text.*) + *libsoc.a:rtc_time.*(.literal .text .literal.* .text.*) *libefuse.a:*.*(.literal .text .literal.* .text.*) *(.fini.literal) *(.fini) diff --git a/components/bootloader/subproject/main/ld/esp32s3/bootloader.ld b/components/bootloader/subproject/main/ld/esp32s3/bootloader.ld index 74463f1d9f..9822ee9a02 100644 --- a/components/bootloader/subproject/main/ld/esp32s3/bootloader.ld +++ b/components/bootloader/subproject/main/ld/esp32s3/bootloader.ld @@ -40,7 +40,7 @@ SECTIONS *libbootloader_support.a:secure_boot_signatures.*(.literal .text .literal.* .text.*) *libmicro-ecc.a:*.*(.literal .text .literal.* .text.*) *libspi_flash.a:*.*(.literal .text .literal.* .text.*) - *libsoc.a:wdt_hal_iram.*(.literal .text .literal.* .text.*) + *libhal.a:wdt_hal_iram.*(.literal .text .literal.* .text.*) *libsoc.a:rtc_clk.*(.literal .text .literal.* .text.*) *libefuse.a:*.*(.literal .text .literal.* .text.*) *(.fini.literal) diff --git a/components/bootloader_support/CMakeLists.txt b/components/bootloader_support/CMakeLists.txt index 1f16c3152e..f61c6081e6 100644 --- a/components/bootloader_support/CMakeLists.txt +++ b/components/bootloader_support/CMakeLists.txt @@ -1,6 +1,7 @@ set(srcs - "src/bootloader_clock.c" "src/bootloader_common.c" + "src/bootloader_common_loader.c" + "src/bootloader_clock_init.c" "src/bootloader_flash.c" "src/bootloader_mem.c" "src/bootloader_random.c" @@ -18,8 +19,10 @@ if(BOOTLOADER_BUILD) set(priv_requires micro-ecc spi_flash efuse) list(APPEND srcs "src/bootloader_init.c" + "src/bootloader_clock_loader.c" "src/bootloader_console.c" "src/bootloader_console_loader.c" + "src/bootloader_panic.c" "src/${IDF_TARGET}/bootloader_sha.c" "src/${IDF_TARGET}/flash_encrypt.c" "src/${IDF_TARGET}/bootloader_${IDF_TARGET}.c" @@ -114,3 +117,7 @@ if(CONFIG_SECURE_SIGNED_APPS AND (CONFIG_SECURE_BOOT_V1_ENABLED OR CONFIG_SECURE APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${secure_boot_verification_key}") endif() + +if(BOOTLOADER_BUILD) + target_link_libraries(${COMPONENT_LIB} INTERFACE "-u abort") +endif() diff --git a/components/bootloader_support/component.mk b/components/bootloader_support/component.mk index aab6890ec2..dd1c16ceb0 100644 --- a/components/bootloader_support/component.mk +++ b/components/bootloader_support/component.mk @@ -16,7 +16,11 @@ COMPONENT_SRCDIRS += src/$(IDF_TARGET) # one sub-dir per chip endif ifndef IS_BOOTLOADER_BUILD -COMPONENT_OBJEXCLUDE := src/bootloader_init.o +COMPONENT_OBJEXCLUDE := src/bootloader_init.o \ + src/bootloader_panic.o \ + src/bootloader_clock_loader.o \ + src/bootloader_console.o \ + src/bootloader_console_loader.o endif COMPONENT_OBJEXCLUDE += src/bootloader_flash_config_esp32s2.o \ diff --git a/components/bootloader_support/src/bootloader_clock.c b/components/bootloader_support/src/bootloader_clock_init.c similarity index 96% rename from components/bootloader_support/src/bootloader_clock.c rename to components/bootloader_support/src/bootloader_clock_init.c index 44ccff8ce0..9d641ddd63 100644 --- a/components/bootloader_support/src/bootloader_clock.c +++ b/components/bootloader_support/src/bootloader_clock_init.c @@ -75,11 +75,4 @@ void bootloader_clock_configure(void) #endif // CONFIG_ESP_SYSTEM_RTC_EXT_XTAL } -#ifdef BOOTLOADER_BUILD -int esp_clk_apb_freq(void) -{ - return rtc_clk_apb_freq_get(); -} - -#endif // BOOTLOADER_BUILD diff --git a/components/bootloader_support/src/bootloader_clock_loader.c b/components/bootloader_support/src/bootloader_clock_loader.c new file mode 100644 index 0000000000..f8082a95b2 --- /dev/null +++ b/components/bootloader_support/src/bootloader_clock_loader.c @@ -0,0 +1,24 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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.h" + +#ifdef BOOTLOADER_BUILD + +int esp_clk_apb_freq(void) +{ + return rtc_clk_apb_freq_get(); +} + +#endif // BOOTLOADER_BUILD diff --git a/components/bootloader_support/src/bootloader_common.c b/components/bootloader_support/src/bootloader_common.c index 78bad8dd12..641d07b3c3 100644 --- a/components/bootloader_support/src/bootloader_common.c +++ b/components/bootloader_support/src/bootloader_common.c @@ -41,21 +41,6 @@ static const char* TAG = "boot_comm"; -uint32_t bootloader_common_ota_select_crc(const esp_ota_select_entry_t *s) -{ - return esp_rom_crc32_le(UINT32_MAX, (uint8_t*)&s->ota_seq, 4); -} - -bool bootloader_common_ota_select_invalid(const esp_ota_select_entry_t *s) -{ - return s->ota_seq == UINT32_MAX || s->ota_state == ESP_OTA_IMG_INVALID || s->ota_state == ESP_OTA_IMG_ABORTED; -} - -bool bootloader_common_ota_select_valid(const esp_ota_select_entry_t *s) -{ - return bootloader_common_ota_select_invalid(s) == false && s->crc == bootloader_common_ota_select_crc(s); -} - esp_comm_gpio_hold_t bootloader_common_check_long_hold_gpio(uint32_t num_pin, uint32_t delay_sec) { esp_rom_gpio_pad_select_gpio(num_pin); @@ -190,43 +175,6 @@ esp_err_t bootloader_common_get_sha256_of_partition (uint32_t address, uint32_t return bootloader_sha256_flash_contents(address, size, out_sha_256); } -int bootloader_common_select_otadata(const esp_ota_select_entry_t *two_otadata, bool *valid_two_otadata, bool max) -{ - if (two_otadata == NULL || valid_two_otadata == NULL) { - return -1; - } - int active_otadata = -1; - if (valid_two_otadata[0] && valid_two_otadata[1]) { - int condition = (max == true) ? MAX(two_otadata[0].ota_seq, two_otadata[1].ota_seq) : MIN(two_otadata[0].ota_seq, two_otadata[1].ota_seq); - if (condition == two_otadata[0].ota_seq) { - active_otadata = 0; - } else { - active_otadata = 1; - } - ESP_LOGD(TAG, "Both OTA copies are valid"); - } else { - for (int i = 0; i < 2; ++i) { - if (valid_two_otadata[i]) { - active_otadata = i; - ESP_LOGD(TAG, "Only otadata[%d] is valid", i); - break; - } - } - } - return active_otadata; -} - -int bootloader_common_get_active_otadata(esp_ota_select_entry_t *two_otadata) -{ - if (two_otadata == NULL) { - return -1; - } - bool valid_two_otadata[2]; - valid_two_otadata[0] = bootloader_common_ota_select_valid(&two_otadata[0]); - valid_two_otadata[1] = bootloader_common_ota_select_valid(&two_otadata[1]); - return bootloader_common_select_otadata(two_otadata, valid_two_otadata, true); -} - esp_err_t bootloader_common_get_partition_description(const esp_partition_pos_t *partition, esp_app_desc_t *app_desc) { if (partition == NULL || app_desc == NULL || partition->offset == 0) { @@ -265,26 +213,6 @@ void bootloader_common_vddsdio_configure(void) } -esp_err_t bootloader_common_check_chip_validity(const esp_image_header_t* img_hdr, esp_image_type type) -{ - esp_err_t err = ESP_OK; - esp_chip_id_t chip_id = CONFIG_IDF_FIRMWARE_CHIP_ID; - if (chip_id != img_hdr->chip_id) { - ESP_LOGE(TAG, "mismatch chip ID, expected %d, found %d", chip_id, img_hdr->chip_id); - err = ESP_FAIL; - } - uint8_t revision = bootloader_common_get_chip_revision(); - if (revision < img_hdr->min_chip_rev) { - ESP_LOGE(TAG, "can't run on lower chip revision, expected %d, found %d", revision, img_hdr->min_chip_rev); - err = ESP_FAIL; - } else if (revision != img_hdr->min_chip_rev) { -#ifdef BOOTLOADER_BUILD - ESP_LOGI(TAG, "chip revision: %d, min. %s chip revision: %d", revision, type == ESP_IMAGE_BOOTLOADER ? "bootloader" : "application", img_hdr->min_chip_rev); -#endif - } - return err; -} - RESET_REASON bootloader_common_get_reset_reason(int cpu_no) { return rtc_get_reset_reason(cpu_no); diff --git a/components/bootloader_support/src/bootloader_common_loader.c b/components/bootloader_support/src/bootloader_common_loader.c new file mode 100644 index 0000000000..ea5f406895 --- /dev/null +++ b/components/bootloader_support/src/bootloader_common_loader.c @@ -0,0 +1,99 @@ +#include "string.h" +#include "sdkconfig.h" +#include "esp_err.h" +#include "esp_log.h" +#if CONFIG_IDF_TARGET_ESP32 +#include "esp32/rom/spi_flash.h" +#elif CONFIG_IDF_TARGET_ESP32S2 +#include "esp32s2/rom/spi_flash.h" +#include "esp32s2/rom/ets_sys.h" +#endif +#include "esp_rom_crc.h" +#include "esp_rom_gpio.h" +#include "esp_flash_partitions.h" +#include "bootloader_flash.h" +#include "bootloader_common.h" +#include "soc/gpio_periph.h" +#include "soc/rtc.h" +#include "soc/efuse_reg.h" +#include "hal/gpio_ll.h" +#include "esp_image_format.h" +#include "bootloader_sha.h" +#include "sys/param.h" + +#define ESP_PARTITION_HASH_LEN 32 /* SHA-256 digest length */ + +static const char* TAG = "boot_comm"; + +uint32_t bootloader_common_ota_select_crc(const esp_ota_select_entry_t *s) +{ + return esp_rom_crc32_le(UINT32_MAX, (uint8_t*)&s->ota_seq, 4); +} + +bool bootloader_common_ota_select_invalid(const esp_ota_select_entry_t *s) +{ + return s->ota_seq == UINT32_MAX || s->ota_state == ESP_OTA_IMG_INVALID || s->ota_state == ESP_OTA_IMG_ABORTED; +} + +bool bootloader_common_ota_select_valid(const esp_ota_select_entry_t *s) +{ + return bootloader_common_ota_select_invalid(s) == false && s->crc == bootloader_common_ota_select_crc(s); +} + +int bootloader_common_get_active_otadata(esp_ota_select_entry_t *two_otadata) +{ + if (two_otadata == NULL) { + return -1; + } + bool valid_two_otadata[2]; + valid_two_otadata[0] = bootloader_common_ota_select_valid(&two_otadata[0]); + valid_two_otadata[1] = bootloader_common_ota_select_valid(&two_otadata[1]); + return bootloader_common_select_otadata(two_otadata, valid_two_otadata, true); +} + +esp_err_t bootloader_common_check_chip_validity(const esp_image_header_t* img_hdr, esp_image_type type) +{ + esp_err_t err = ESP_OK; + esp_chip_id_t chip_id = CONFIG_IDF_FIRMWARE_CHIP_ID; + if (chip_id != img_hdr->chip_id) { + ESP_LOGE(TAG, "mismatch chip ID, expected %d, found %d", chip_id, img_hdr->chip_id); + err = ESP_FAIL; + } + uint8_t revision = bootloader_common_get_chip_revision(); + if (revision < img_hdr->min_chip_rev) { + ESP_LOGE(TAG, "can't run on lower chip revision, expected %d, found %d", revision, img_hdr->min_chip_rev); + err = ESP_FAIL; + } else if (revision != img_hdr->min_chip_rev) { +#ifdef BOOTLOADER_BUILD + ESP_LOGI(TAG, "chip revision: %d, min. %s chip revision: %d", revision, type == ESP_IMAGE_BOOTLOADER ? "bootloader" : "application", img_hdr->min_chip_rev); +#endif + } + return err; +} + +int bootloader_common_select_otadata(const esp_ota_select_entry_t *two_otadata, bool *valid_two_otadata, bool max) +{ + if (two_otadata == NULL || valid_two_otadata == NULL) { + return -1; + } + int active_otadata = -1; + if (valid_two_otadata[0] && valid_two_otadata[1]) { + int condition = (max == true) ? MAX(two_otadata[0].ota_seq, two_otadata[1].ota_seq) : MIN(two_otadata[0].ota_seq, two_otadata[1].ota_seq); + if (condition == two_otadata[0].ota_seq) { + active_otadata = 0; + } else { + active_otadata = 1; + } + ESP_LOGD(TAG, "Both OTA copies are valid"); + } else { + for (int i = 0; i < 2; ++i) { + if (valid_two_otadata[i]) { + active_otadata = i; + ESP_LOGD(TAG, "Only otadata[%d] is valid", i); + break; + } + } + } + return active_otadata; +} + diff --git a/components/bootloader_support/src/bootloader_init.c b/components/bootloader_support/src/bootloader_init.c index 4c57e848ea..8938dc7571 100644 --- a/components/bootloader_support/src/bootloader_init.c +++ b/components/bootloader_support/src/bootloader_init.c @@ -101,10 +101,3 @@ void bootloader_print_banner(void) ESP_LOGI(TAG, "ESP-IDF %s 2nd stage bootloader", IDF_VER); ESP_LOGI(TAG, "compile time " __TIME__); } - -void __assert_func(const char *file, int line, const char *func, const char *expr) -{ - ESP_LOGE(TAG, "Assert failed in %s, %s:%d (%s)", func, file, line, expr); - while (1) { - } -} diff --git a/components/bootloader_support/src/bootloader_panic.c b/components/bootloader_support/src/bootloader_panic.c new file mode 100644 index 0000000000..637f2c968c --- /dev/null +++ b/components/bootloader_support/src/bootloader_panic.c @@ -0,0 +1,38 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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 "esp_log.h" +#include "bootloader_common.h" +#include "soc/cpu.h" +#include "esp_rom_sys.h" + + +void __assert_func(const char *file, int line, const char *func, const char *expr) +{ + esp_rom_printf("Assert failed in %s, %s:%d (%s)\r\n", func, file, line, expr); + while (1) { + } +} + +void abort(void) +{ +#if !CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT + esp_rom_printf("abort() was called at PC 0x%08x\r\n", (intptr_t)__builtin_return_address(0) - 3); +#endif + if (esp_cpu_in_ocd_debug_mode()) { + __asm__("break 0,0"); + } + while (1) { + } +} diff --git a/components/bootloader_support/src/esp32/bootloader_esp32.c b/components/bootloader_support/src/esp32/bootloader_esp32.c index abf2b52956..4a2b501cd7 100644 --- a/components/bootloader_support/src/esp32/bootloader_esp32.c +++ b/components/bootloader_support/src/esp32/bootloader_esp32.c @@ -351,18 +351,6 @@ static void bootloader_check_wdt_reset(void) wdt_reset_cpu0_info_enable(); } -void abort(void) -{ -#if !CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT - esp_rom_printf("abort() was called at PC 0x%08x\r\n", (intptr_t)__builtin_return_address(0) - 3); -#endif - if (esp_cpu_in_ocd_debug_mode()) { - __asm__("break 0,0"); - } - while (1) { - } -} - esp_err_t bootloader_init(void) { esp_err_t ret = ESP_OK; diff --git a/components/bootloader_support/src/esp32s2/bootloader_esp32s2.c b/components/bootloader_support/src/esp32s2/bootloader_esp32s2.c index 9b08921d41..29f6d2f82b 100644 --- a/components/bootloader_support/src/esp32s2/bootloader_esp32s2.c +++ b/components/bootloader_support/src/esp32s2/bootloader_esp32s2.c @@ -46,7 +46,7 @@ #include static const char *TAG = "boot.esp32s2"; -void bootloader_configure_spi_pins(int drv) +void IRAM_ATTR bootloader_configure_spi_pins(int drv) { const uint32_t spiconfig = esp_rom_efuse_get_flash_gpio_info(); uint8_t wp_pin = esp_rom_efuse_get_flash_wp_gpio(); @@ -276,18 +276,6 @@ static void bootloader_check_wdt_reset(void) wdt_reset_cpu0_info_enable(); } -void abort(void) -{ -#if !CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT - esp_rom_printf("abort() was called at PC 0x%08x\r\n", (intptr_t)__builtin_return_address(0) - 3); -#endif - if (esp_cpu_in_ocd_debug_mode()) { - __asm__("break 0,0"); - } - while (1) { - } -} - static void bootloader_super_wdt_auto_feed(void) { REG_SET_BIT(RTC_CNTL_SWD_CONF_REG, RTC_CNTL_SWD_AUTO_FEED_EN); diff --git a/tools/ci/check_callgraph.py b/tools/ci/check_callgraph.py new file mode 100755 index 0000000000..a5eab370cd --- /dev/null +++ b/tools/ci/check_callgraph.py @@ -0,0 +1,418 @@ +#!/usr/bin/env python +# +# Based on cally.py (https://github.com/chaudron/cally/), Copyright 2018, Eelco Chaudron +# Copyright 2020 Espressif Systems (Shanghai) PTE LTD +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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. + +import argparse +from functools import partial +import os +import re + +import elftools +from elftools.elf import elffile + +try: + from typing import List, Optional, BinaryIO, Tuple, Generator, Dict, Callable +except ImportError: + pass + +FUNCTION_REGEX = re.compile( + r"^;; Function (?P.*)\s+\((?P\S+)(,.*)?\).*$" +) +CALL_REGEX = re.compile(r'^.*\(call.*"(?P.*)".*$') +SYMBOL_REF_REGEX = re.compile(r'^.*\(symbol_ref[^()]*\("(?P.*)"\).*$') + + +class RtlFunction(object): + def __init__(self, name, rtl_filename, tu_filename): + self.name = name + self.rtl_filename = rtl_filename + self.tu_filename = tu_filename + self.calls = list() # type: List[str] + self.refs = list() # type: List[str] + self.sym = None + + +class SectionAddressRange(object): + def __init__(self, name, addr, size): # type: (str, int, int) -> None + self.name = name + self.low = addr + self.high = addr + size + + def __str__(self): + return "{}: 0x{:08x} - 0x{:08x}".format(self.name, self.low, self.high) + + def contains_address(self, addr): + return self.low <= addr < self.high + + +TARGET_SECTIONS = { + "esp32": [ + SectionAddressRange(".rom.text", 0x40000000, 0x70000), + SectionAddressRange(".rom.rodata", 0x3ff96000, 0x9018) + ], + "esp32s2": [ + SectionAddressRange(".rom.text", 0x40000000, 0x1bed0), + SectionAddressRange(".rom.rodata", 0x3ffac600, 0x392c) + ], + "esp32s3": [ + SectionAddressRange(".rom.text", 0x40000000, 0x568d0), + SectionAddressRange(".rom.rodata", 0x3ff071c0, 0x8e30) + ] +} # type: Dict[str, List[SectionAddressRange]] + + +class Symbol(object): + def __init__(self, name, addr, local, filename, section): # type: (str, int, bool, Optional[str], Optional[str]) -> None + self.name = name + self.addr = addr + self.local = local + self.filename = filename + self.section = section + self.refers_to = list() # type: List[Symbol] + self.referred_from = list() # type: List[Symbol] + + def __str__(self): + return "{} @0x{:08x} [{}]{} {}".format( + self.name, + self.addr, + self.section or "unknown", + " (local)" if self.local else "", + self.filename + ) + + +class Reference(object): + def __init__(self, from_sym, to_sym): # type: (Symbol, Symbol) -> None + self.from_sym = from_sym + self.to_sym = to_sym + + def __str__(self): + return "{} @0x{:08x} ({}) -> {} @0x{:08x} ({})".format( + self.from_sym.name, + self.from_sym.addr, + self.from_sym.section, + self.to_sym.name, + self.to_sym.addr, + self.to_sym.section + ) + + +class ElfInfo(object): + def __init__(self, elf_file): # type: (BinaryIO) -> None + self.elf_file = elf_file + self.elf_obj = elffile.ELFFile(self.elf_file) + self.section_ranges = self._load_sections() + self.symbols = self._load_symbols() + + def _load_symbols(self): # type: () -> List[Symbol] + symbols = [] + for s in self.elf_obj.iter_sections(): + if not isinstance(s, elftools.elf.sections.SymbolTableSection): + continue + filename = None + for sym in s.iter_symbols(): + sym_type = sym.entry["st_info"]["type"] + if sym_type == "STT_FILE": + filename = sym.name + if sym_type in ["STT_NOTYPE", "STT_FUNC", "STT_OBJECT"]: + local = sym.entry["st_info"]["bind"] == "STB_LOCAL" + addr = sym.entry["st_value"] + symbols.append( + Symbol( + sym.name, + addr, + local, + filename if local else None, + self.section_for_addr(addr), + ) + ) + return symbols + + def _load_sections(self): # type: () -> List[SectionAddressRange] + result = [] + for segment in self.elf_obj.iter_segments(): + if segment["p_type"] == "PT_LOAD": + for section in self.elf_obj.iter_sections(): + if not segment.section_in_segment(section): + continue + result.append( + SectionAddressRange( + section.name, section["sh_addr"], section["sh_size"] + ) + ) + + target = os.environ.get("IDF_TARGET") + if target in TARGET_SECTIONS: + result += TARGET_SECTIONS[target] + + return result + + def symbols_by_name(self, name): # type: (str) -> List[Symbol] + res = [] + for sym in self.symbols: + if sym.name == name: + res.append(sym) + return res + + def section_for_addr(self, sym_addr): # type: (int) -> Optional[str] + for sar in self.section_ranges: + if sar.contains_address(sym_addr): + return sar.name + return None + + +def load_rtl_file(rtl_filename, tu_filename, functions): # type: (str, str, List[RtlFunction]) -> None + last_function = None # type: Optional[RtlFunction] + for line in open(rtl_filename): + # Find function definition + match = re.match(FUNCTION_REGEX, line) + if match: + function_name = match.group("function") + last_function = RtlFunction(function_name, rtl_filename, tu_filename) + functions.append(last_function) + continue + + if last_function: + # Find direct function calls + match = re.match(CALL_REGEX, line) + if match: + target = match.group("target") + if target not in last_function.calls: + last_function.calls.append(target) + continue + + # Find symbol references + match = re.match(SYMBOL_REF_REGEX, line) + if match: + target = match.group("target") + if target not in last_function.refs: + last_function.refs.append(target) + continue + + +def rtl_filename_matches_sym_filename(rtl_filename, symbol_filename): # type: (str, str) -> bool + # Symbol file names (from ELF debug info) are short source file names, without path: "cpu_start.c". + # RTL file names are paths relative to the build directory, e.g.: + # "build/esp-idf/esp_system/CMakeFiles/__idf_esp_system.dir/port/cpu_start.c.234r.expand" + # + # The check below may give a false positive if there are two files with the same name in + # different directories. This doesn't seem to happen in IDF now, but if it does happen, + # an assert in find_symbol_by_rtl_func should catch this. + # + # If this becomes and issue, consider also loading the .map file and using it to figure out + # which object file was used as the source of each symbol. Names of the object files and RTL files + # should be much easier to match. + return os.path.basename(rtl_filename).startswith(symbol_filename) + + +class SymbolNotFound(RuntimeError): + pass + + +def find_symbol_by_name(name, elfinfo, local_func_matcher): # type: (str, ElfInfo, Callable[[Symbol], bool]) -> Optional[Symbol] + """ + Find an ELF symbol for the given name. + local_func_matcher is a callback function which checks is the candidate local symbol is suitable. + """ + syms = elfinfo.symbols_by_name(name) + if not syms: + return None + if len(syms) == 1: + return syms[0] + else: + # There are multiple symbols with a given name. Find the best fit. + local_candidate = None + global_candidate = None + for sym in syms: + if not sym.local: + assert not global_candidate # can't have two global symbols with the same name + global_candidate = sym + elif local_func_matcher(sym): + assert not local_candidate # can't have two symbols with the same name in a single file + local_candidate = sym + + # If two symbols with the same name are defined, a global and a local one, + # prefer the local symbol as the reference target. + return local_candidate or global_candidate + + +def match_local_source_func(rtl_filename, sym): # type: (str, Symbol) -> bool + """ + Helper for match_rtl_funcs_to_symbols, checks if local symbol sym is a good candidate for the + reference source (caller), based on the RTL file name. + """ + assert sym.filename # should be set for local functions + return rtl_filename_matches_sym_filename(rtl_filename, sym.filename) + + +def match_local_target_func(rtl_filename, sym_from, sym): # type: (str, Symbol, Symbol) -> bool + """ + Helper for match_rtl_funcs_to_symbols, checks if local symbol sym is a good candidate for the + reference target (callee or referenced data), based on RTL filename of the source symbol + and the source symbol itself. + """ + assert sym.filename # should be set for local functions + if sym_from.local: + # local symbol referencing another local symbol + return sym_from.filename == sym.filename + else: + # global symbol referencing a local symbol; + # source filename is not known, use RTL filename as a hint + return rtl_filename_matches_sym_filename(rtl_filename, sym.filename) + + +def match_rtl_funcs_to_symbols(rtl_functions, elfinfo): # type: (List[RtlFunction], ElfInfo) -> Tuple[List[Symbol], List[Reference]] + symbols = [] # type: List[Symbol] + refs = [] # type: List[Reference] + + # General idea: + # - iterate over RTL functions. + # - for each RTL function, find the corresponding symbol + # - iterate over the functions and variables referenced from this RTL function + # - find symbols corresponding to the references + # - record every pair (sym_from, sym_to) as a Reference object + + for source_rtl_func in rtl_functions: + maybe_sym_from = find_symbol_by_name(source_rtl_func.name, elfinfo, partial(match_local_source_func, source_rtl_func.rtl_filename)) + if maybe_sym_from is None: + # RTL references a symbol, but the symbol is not defined in the generated object file. + # This means that the symbol was likely removed (or not included) at link time. + # There is nothing we can do to check section placement in this case. + continue + sym_from = maybe_sym_from + + if sym_from not in symbols: + symbols.append(sym_from) + + for target_rtl_func_name in source_rtl_func.calls + source_rtl_func.refs: + if "*.LC" in target_rtl_func_name: # skip local labels + continue + + maybe_sym_to = find_symbol_by_name(target_rtl_func_name, elfinfo, partial(match_local_target_func, source_rtl_func.rtl_filename, sym_from)) + if not maybe_sym_to: + # This may happen for a extern reference in the RTL file, if the reference was later removed + # by one of the optimization passes, and the external definition got garbage-collected. + # TODO: consider adding some sanity check that we are here not because of some bug in + # find_symbol_by_name?.. + continue + sym_to = maybe_sym_to + + sym_from.refers_to.append(sym_to) + sym_to.referred_from.append(sym_from) + refs.append(Reference(sym_from, sym_to)) + if sym_to not in symbols: + symbols.append(sym_to) + + return symbols, refs + + +def get_symbols_and_refs(rtl_list, elf_file): # type: (List[str], BinaryIO) -> Tuple[List[Symbol], List[Reference]] + elfinfo = ElfInfo(elf_file) + + rtl_functions = [] # type: List[RtlFunction] + for file_name in rtl_list: + load_rtl_file(file_name, file_name, rtl_functions) + + return match_rtl_funcs_to_symbols(rtl_functions, elfinfo) + + +def list_refs_from_to_sections(refs, from_sections, to_sections): # type: (List[Reference], List[str], List[str]) -> int + found = 0 + for ref in refs: + if (not from_sections or ref.from_sym.section in from_sections) and \ + (not to_sections or ref.to_sym.section in to_sections): + print(str(ref)) + found += 1 + return found + + +def find_files_recursive(root_path, ext): # type: (str, str) -> Generator[str, None, None] + for root, _, files in os.walk(root_path): + for basename in files: + if basename.endswith(ext): + filename = os.path.join(root, basename) + yield filename + + +def main(): + parser = argparse.ArgumentParser() + + parser.add_argument( + "--rtl-list", + help="File with the list of RTL files", + type=argparse.FileType("r"), + ) + parser.add_argument( + "--rtl-dir", help="Directory where to look for RTL files, recursively" + ) + parser.add_argument( + "--elf-file", + required=True, + help="Program ELF file", + type=argparse.FileType("rb"), + ) + action_sub = parser.add_subparsers(dest="action") + find_refs_parser = action_sub.add_parser( + "find-refs", + help="List the references coming from a given list of source sections" + "to a given list of target sections.", + ) + find_refs_parser.add_argument( + "--from-sections", help="comma-separated list of source sections" + ) + find_refs_parser.add_argument( + "--to-sections", help="comma-separated list of target sections" + ) + find_refs_parser.add_argument( + "--exit-code", + action="store_true", + help="If set, exits with non-zero code when any references found", + ) + action_sub.add_parser( + "all-refs", + help="Print the list of all references", + ) + + parser.parse_args() + args = parser.parse_args() + if args.rtl_list: + with open(args.rtl_list, "r") as rtl_list_file: + rtl_list = [line.strip for line in rtl_list_file] + else: + if not args.rtl_dir: + raise RuntimeError("Either --rtl-list or --rtl-dir must be specified") + rtl_list = list(find_files_recursive(args.rtl_dir, ".expand")) + + if not rtl_list: + raise RuntimeError("No RTL files specified") + + _, refs = get_symbols_and_refs(rtl_list, args.elf_file) + + if args.action == "find-refs": + from_sections = args.from_sections.split(",") if args.from_sections else [] + to_sections = args.to_sections.split(",") if args.to_sections else [] + found = list_refs_from_to_sections( + refs, from_sections, to_sections + ) + if args.exit_code and found: + raise SystemExit(1) + elif args.action == "all-refs": + for r in refs: + print(str(r)) + + +if __name__ == "__main__": + main() diff --git a/tools/ci/executable-list.txt b/tools/ci/executable-list.txt index 7008bab8ef..fee8e606e8 100644 --- a/tools/ci/executable-list.txt +++ b/tools/ci/executable-list.txt @@ -39,6 +39,7 @@ tools/ci/build_template_app.sh tools/ci/check-executable.sh tools/ci/check-line-endings.sh tools/ci/check_build_warnings.py +tools/ci/check_callgraph.py tools/ci/check_deprecated_kconfigs.py tools/ci/check_examples_cmake_make.py tools/ci/check_examples_rom_header.sh diff --git a/tools/test_apps/system/bootloader_sections/CMakeLists.txt b/tools/test_apps/system/bootloader_sections/CMakeLists.txt new file mode 100644 index 0000000000..3d5945c3b2 --- /dev/null +++ b/tools/test_apps/system/bootloader_sections/CMakeLists.txt @@ -0,0 +1,17 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(test_build) + +add_custom_target(check_bootloader_sections ALL + COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py + --rtl-dir ${CMAKE_BINARY_DIR}/bootloader + --elf-file ${CMAKE_BINARY_DIR}/bootloader/bootloader.elf + find-refs + --from-sections=.iram_loader.text + --to-sections=.iram.text + --exit-code + DEPENDS bootloader + ) diff --git a/tools/test_apps/system/bootloader_sections/main/CMakeLists.txt b/tools/test_apps/system/bootloader_sections/main/CMakeLists.txt new file mode 100644 index 0000000000..95cd206309 --- /dev/null +++ b/tools/test_apps/system/bootloader_sections/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "test_main.c" + INCLUDE_DIRS ".") + + diff --git a/tools/test_apps/system/bootloader_sections/main/test_main.c b/tools/test_apps/system/bootloader_sections/main/test_main.c new file mode 100644 index 0000000000..cb05851571 --- /dev/null +++ b/tools/test_apps/system/bootloader_sections/main/test_main.c @@ -0,0 +1,3 @@ +void app_main(void) +{ +} diff --git a/tools/test_apps/system/bootloader_sections/sdkconfig.ci.default b/tools/test_apps/system/bootloader_sections/sdkconfig.ci.default new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/test_apps/system/bootloader_sections/sdkconfig.ci.flash_encryption b/tools/test_apps/system/bootloader_sections/sdkconfig.ci.flash_encryption new file mode 100644 index 0000000000..47e4a657c6 --- /dev/null +++ b/tools/test_apps/system/bootloader_sections/sdkconfig.ci.flash_encryption @@ -0,0 +1,2 @@ +CONFIG_SECURE_FLASH_ENC_ENABLED=y +CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE=y diff --git a/tools/test_apps/system/bootloader_sections/sdkconfig.defaults b/tools/test_apps/system/bootloader_sections/sdkconfig.defaults new file mode 100644 index 0000000000..b1a27d76d2 --- /dev/null +++ b/tools/test_apps/system/bootloader_sections/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_COMPILER_DUMP_RTL_FILES=y