# 应用程序镜像格式

[English]

## 应用程序镜像结构

应用程序镜像包含以下内容：

1. `esp_image_header_t` 结构体描述了 SPI flash 的模式和内存段的数量。

2. `esp_image_segment_header_t` 结构体描述了每个段、每个段的长度及其在 ESP32-C2 内存中的位置，此描述后接长度为 `data_len` 的数据。镜像中每个段的数据偏移量的计算方式如下：

   > - 0 段的偏移量 = sizeof(`esp_image_header_t`) + sizeof(`esp_image_segment_header_t`)

   > - 1 段的偏移量 = 0 段的偏移量 + 0 段长度 + sizeof(`esp_image_segment_header_t`)

   > - 2 段的偏移量 = 1 段的偏移量 + 1 段长度 + sizeof(`esp_image_segment_header_t`)

   > - ...

`segment_count` 字段定义了每个段的数量，存储在 `esp_image_header_t` 中。各段段数量不能超过 `ESP_IMAGE_MAX_SEGMENTS`。

运行以下命令，获取镜像段列表：

    esptool.py --chip esp32c2 image_info build/app.bin

    esptool.py v2.3.1
    Image version: 1
    Entry point: 40080ea4
    13 segments

    Segment 1: len 0x13ce0 load 0x3f400020 file_offs 0x00000018 SOC_DROM
    Segment 2: len 0x00000 load 0x3ff80000 file_offs 0x00013d00 SOC_RTC_DRAM
    Segment 3: len 0x00000 load 0x3ff80000 file_offs 0x00013d08 SOC_RTC_DRAM
    Segment 4: len 0x028e0 load 0x3ffb0000 file_offs 0x00013d10 DRAM
    Segment 5: len 0x00000 load 0x3ffb28e0 file_offs 0x000165f8 DRAM
    Segment 6: len 0x00400 load 0x40080000 file_offs 0x00016600 SOC_IRAM
    Segment 7: len 0x09600 load 0x40080400 file_offs 0x00016a08 SOC_IRAM
    Segment 8: len 0x62e4c load 0x400d0018 file_offs 0x00020010 SOC_IROM
    Segment 9: len 0x06cec load 0x40089a00 file_offs 0x00082e64 SOC_IROM
    Segment 10: len 0x00000 load 0x400c0000 file_offs 0x00089b58 SOC_RTC_IRAM
    Segment 11: len 0x00004 load 0x50000000 file_offs 0x00089b60 SOC_RTC_DATA
    Segment 12: len 0x00000 load 0x50000004 file_offs 0x00089b6c SOC_RTC_DATA
    Segment 13: len 0x00000 load 0x50000004 file_offs 0x00089b74 SOC_RTC_DATA
    Checksum: e8 (valid)
    Validation Hash: 407089ca0eae2bbf83b4120979d3354b1c938a49cb7a0c997f240474ef2ec76b (valid)

应用程序启动时，ESP-IDF 日志中也会包含段的相关信息：

    I (443) esp_image: segment 0: paddr=0x00020020 vaddr=0x3f400020 size=0x13ce0 ( 81120) map
    I (489) esp_image: segment 1: paddr=0x00033d08 vaddr=0x3ff80000 size=0x00000 ( 0) load
    I (530) esp_image: segment 2: paddr=0x00033d10 vaddr=0x3ff80000 size=0x00000 ( 0) load
    I (571) esp_image: segment 3: paddr=0x00033d18 vaddr=0x3ffb0000 size=0x028e0 ( 10464) load
    I (612) esp_image: segment 4: paddr=0x00036600 vaddr=0x3ffb28e0 size=0x00000 ( 0) load
    I (654) esp_image: segment 5: paddr=0x00036608 vaddr=0x40080000 size=0x00400 ( 1024) load
    I (695) esp_image: segment 6: paddr=0x00036a10 vaddr=0x40080400 size=0x09600 ( 38400) load
    I (737) esp_image: segment 7: paddr=0x00040018 vaddr=0x400d0018 size=0x62e4c (405068) map
    I (847) esp_image: segment 8: paddr=0x000a2e6c vaddr=0x40089a00 size=0x06cec ( 27884) load
    I (888) esp_image: segment 9: paddr=0x000a9b60 vaddr=0x400c0000 size=0x00000 ( 0) load
    I (929) esp_image: segment 10: paddr=0x000a9b68 vaddr=0x50000000 size=0x00004 ( 4) load
    I (971) esp_image: segment 11: paddr=0x000a9b74 vaddr=0x50000004 size=0x00000 ( 0) load
    I (1012) esp_image: segment 12: paddr=0x000a9b7c vaddr=0x50000004 size=0x00000 ( 0) load

有关内存段类型和地址范围的详细信息，请参阅 ESP32-C2 技术参考手册 > 系统和存储器 > 内部存储器 [PDF]。

3. 镜像有一个校验和字节，位于最后一个段之后。此字节写在一个十六字节填充边界上，因此应用程序镜像可能需要填充。

4. 如果在 `esp_image_header_t` 中设置了 `hash_appended` 字段，则会附加 SHA256 校验和字段。SHA256 哈希值的计算范围是从第一个字节开始，到这个字段为止。该字段长度为 32 字节。

5. 如果选项 CONFIG_SECURE_SIGNED_APPS_SCHEME 设置为 ECDSA，那么应用程序镜像将有额外的 68 字节用于 ECDSA 签名，其中包括：

   > - 版本号（4 字节）

   > - 签名数据（64 字节）

6. 如果选项 CONFIG_SECURE_SIGNED_APPS_SCHEME 设置为 RSA 或 ECDSA (V2)，则应用程序镜像将有一个额外的签名扇区，大小为 4K 字节。关于此签名扇区格式的更多内容，请参考 签名块格式。

## 应用程序描述

应用程序二进制文件的 `DROM` 段从 `esp_app_desc_t` 结构体开始，该结构体中包含了用于描述应用程序的特定字段，如下所示：

- `magic_word`：`esp_app_desc_t` 结构体的魔术词

- `secure_version`：参见 防回滚

- `version`：参见 应用程序版本 [1]

- `project_name`：通过 `PROJECT_NAME` 填充 [1]

- `time` 和 `date`：编译时间和日期

- `idf_ver`：ESP-IDF 的版本 [1]

- `app_elf_sha256`：包含应用程序 ELF 文件的 sha256 哈希

[1] (1,2,3)

最大长度为 32 个字符，其中包括 null 终止符。也就是说，如果 `PROJECT_NAME` 的长度超过 31 个字符，超出的字符将被忽略。

这个结构体有助于识别通过空中升级 (OTA) 上传的镜像，因为其中包含一个固定的偏移量，大小为 sizeof(`esp_image_header_t`) + sizeof(`esp_image_segment_header_t`)。一旦设备接收到包含此结构体的第一个段，就能根据其中的充分信息来确定是否应继续更新。

要获取当前运行的应用程序的 `esp_app_desc_t` 结构体，请调用 `esp_app_get_description()`。

要获取另一个 OTA 分区的 `esp_app_desc_t` 结构体，请调用 `esp_ota_get_partition_description()`。

## 向应用程序添加自定义结构体

也可以自定义类似的结构体，并使其相对于镜像起始位置有一个固定的偏移量。

采用以下方式向镜像添加自定义结构体：

    const __attribute__((section(".rodata_custom_desc"))) esp_custom_app_desc_t custom_app_desc = { ... }

自定义结构体的偏移量为 sizeof(`esp_image_header_t`) + sizeof(`esp_image_segment_header_t`) + sizeof(`esp_app_desc_t`)。

需在 `CMakeLists.txt` 中添加 `target_link_libraries(${COMPONENT_TARGET} "-u custom_app_desc")`，确保自定义结构体在未使用时也位于镜像中。

## API 参考

### Header File

- components/bootloader_support/include/esp_app_format.h

- This header file can be included with:

  >     #include "esp_app_format.h"

- This header file is a part of the API provided by the `bootloader_support` component. To declare that your component depends on `bootloader_support`, add the following to your CMakeLists.txt:

  >     REQUIRES bootloader_support

  > or

  >     PRIV_REQUIRES bootloader_support

### Structures

struct esp_image_header_t

  Main header of binary image.

  Public Members

  uint8_t magic

    Magic word ESP_IMAGE_HEADER_MAGIC

  uint8_t segment_count

    Count of memory segments

  uint8_t spi_mode

    flash read mode (esp_image_spi_mode_t as uint8_t)

  uint8_t spi_speed

    flash frequency (esp_image_spi_freq_t as uint8_t)

  uint8_t spi_size

    flash chip size (esp_image_flash_size_t as uint8_t)

  uint32_t entry_addr

    Entry address

  uint8_t wp_pin

    WP pin when SPI pins set via efuse (read by ROM bootloader, the IDF bootloader uses software to configure the WP pin and sets this field to 0xEE=disabled)

  uint8_t spi_pin_drv[3]

    Drive settings for the SPI flash pins (read by ROM bootloader)

  esp_chip_id_t chip_id

    Chip identification number

  uint8_t min_chip_rev

    Minimal chip revision supported by image After the Major and Minor revision eFuses were introduced into the chips, this field is no longer used. But for compatibility reasons, we keep this field and the data in it. Use min_chip_rev_full instead. The software interprets this as a Major version for most of the chips and as a Minor version for the ESP32-C3.

  uint16_t min_chip_rev_full

    Minimal chip revision supported by image, in format: major * 100 + minor

  uint16_t max_chip_rev_full

    Maximal chip revision supported by image, in format: major * 100 + minor

  uint8_t reserved[4]

    Reserved bytes in additional header space, currently unused

  uint8_t hash_appended

    If 1, a SHA256 digest "simple hash" (of the entire image) is appended after the checksum. Included in image length. This digest is separate to secure boot and only used for detecting corruption. For secure boot signed images, the signature is appended after this (and the simple hash is included in the signed data).

struct esp_image_segment_header_t

  Header of binary image segment.

  Public Members

  uint32_t load_addr

    Address of segment

  uint32_t data_len

    Length of data

### Macros

ESP_IMAGE_HEADER_MAGIC

  The magic word for the esp_image_header_t structure.

ESP_IMAGE_MAX_SEGMENTS

  Max count of segments in the image.

### Enumerations

enum esp_chip_id_t

  ESP chip ID.

  Values:

  enumerator ESP_CHIP_ID_ESP32

    chip ID: ESP32

  enumerator ESP_CHIP_ID_ESP32S2

    chip ID: ESP32-S2

  enumerator ESP_CHIP_ID_ESP32C3

    chip ID: ESP32-C3

  enumerator ESP_CHIP_ID_ESP32S3

    chip ID: ESP32-S3

  enumerator ESP_CHIP_ID_ESP32C2

    chip ID: ESP32-C2

  enumerator ESP_CHIP_ID_ESP32C6

    chip ID: ESP32-C6

  enumerator ESP_CHIP_ID_ESP32H2

    chip ID: ESP32-H2

  enumerator ESP_CHIP_ID_ESP32P4

    chip ID: ESP32-P4

  enumerator ESP_CHIP_ID_ESP32C5

    chip ID: ESP32-C5

  enumerator ESP_CHIP_ID_ESP32C61

    chip ID: ESP32-C61

  enumerator ESP_CHIP_ID_ESP32H21

    chip ID: ESP32-H21

  enumerator ESP_CHIP_ID_ESP32H4

    chip ID: ESP32-H4

  enumerator ESP_CHIP_ID_INVALID

    Invalid chip ID (we defined it to make sure the esp_chip_id_t is 2 bytes size)

enum esp_image_spi_mode_t

  SPI flash mode, used in esp_image_header_t.

  Values:

  enumerator ESP_IMAGE_SPI_MODE_QIO

    SPI mode QIO

  enumerator ESP_IMAGE_SPI_MODE_QOUT

    SPI mode QOUT

  enumerator ESP_IMAGE_SPI_MODE_DIO

    SPI mode DIO

  enumerator ESP_IMAGE_SPI_MODE_DOUT

    SPI mode DOUT

  enumerator ESP_IMAGE_SPI_MODE_FAST_READ

    SPI mode FAST_READ

  enumerator ESP_IMAGE_SPI_MODE_SLOW_READ

    SPI mode SLOW_READ

enum esp_image_spi_freq_t

  SPI flash clock division factor.

  Values:

  enumerator ESP_IMAGE_SPI_SPEED_DIV_2

    The SPI flash clock frequency is divided by 2 of the clock source

  enumerator ESP_IMAGE_SPI_SPEED_DIV_3

    The SPI flash clock frequency is divided by 3 of the clock source

  enumerator ESP_IMAGE_SPI_SPEED_DIV_4

    The SPI flash clock frequency is divided by 4 of the clock source

  enumerator ESP_IMAGE_SPI_SPEED_DIV_1

    The SPI flash clock frequency equals to the clock source

enum esp_image_flash_size_t

  Supported SPI flash sizes.

  Values:

  enumerator ESP_IMAGE_FLASH_SIZE_1MB

    SPI flash size 1 MB

  enumerator ESP_IMAGE_FLASH_SIZE_2MB

    SPI flash size 2 MB

  enumerator ESP_IMAGE_FLASH_SIZE_4MB

    SPI flash size 4 MB

  enumerator ESP_IMAGE_FLASH_SIZE_8MB

    SPI flash size 8 MB

  enumerator ESP_IMAGE_FLASH_SIZE_16MB

    SPI flash size 16 MB

  enumerator ESP_IMAGE_FLASH_SIZE_32MB

    SPI flash size 32 MB

  enumerator ESP_IMAGE_FLASH_SIZE_64MB

    SPI flash size 64 MB

  enumerator ESP_IMAGE_FLASH_SIZE_128MB

    SPI flash size 128 MB

  enumerator ESP_IMAGE_FLASH_SIZE_MAX

    SPI flash size MAX

此文档对您有帮助吗？

- 反馈已收到，谢谢！
- 如果您有其他意见，欢迎填写乐鑫文档反馈表。

- 我们重视您的反馈。
- 您可以填写乐鑫文档反馈表告诉我们如何改进该文档。