Merge pull request #569 from bigbrett/update-flash-elf-tc3xx-integration

ELF scatter-loading fixes and TC3xx support
pull/572/merge
David Garske 2025-05-12 13:11:33 -07:00 committed by GitHub
commit 2b996f8280
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 5784 additions and 564 deletions

9
.gitignore vendored
View File

@ -118,7 +118,13 @@ tools/tpm/policy_sign
config/*.ld
test-lib
# Generated configuration file
# Elf preprocessing tools
tools/squashelf/**
!tools/squashelf/squashelf.c
!tools/squashelf/Makefile
!tools/squashelf/README.md
# Generated configuration files
.config
.vs
*.filters
@ -247,3 +253,4 @@ lib/r_tsip_rx
Debug/
Release/
language.settings.xml

View File

@ -8,9 +8,12 @@ This example demonstrates using wolfBoot on the Infineon AURIX TC3xx family of m
- [Overview](#overview)
- [Important notes](#important-notes)
- [Flash Partitioning](#flash-partitioning)
- [Standard wolfBoot images](#standard-wolfboot-images)
- [ELF files](#elf-files)
- [Configuration and the wolfBoot AURIX tool (wbaurixtool.sh)](#configuration-and-the-wolfboot-aurix-tool-wbaurixtoolsh)
- [Building and running the wolfBoot demo](#building-and-running-the-wolfboot-demo)
- [Prerequisites](#prerequisites)
- [Important notes](#important-notes-1)
- [Clone wolfBoot](#clone-wolfboot)
- [Build wolfBoot keytools and generate keys](#build-wolfboot-keytools-and-generate-keys)
- [Install the Infineon TC3xx SDK into the wolfBoot project](#install-the-infineon-tc3xx-sdk-into-the-wolfboot-project)
@ -26,7 +29,9 @@ This example demonstrates using wolfBoot on the Infineon AURIX TC3xx family of m
- [Post Quantum: ML-DSA](#post-quantum-ml-dsa)
- [ML-DSA Keytools](#ml-dsa-keytools)
The example contains two projects: `wolfBoot-tc3xx` and `test-app`. The `wolfBoot-tc3xx` project contains the wolfBoot bootloader, and the `test-app` project contains a simple firmware application that will be loaded and executed by wolfBoot. The `test-app` project is a simple blinky application that blinks LED2 on the TC375 Lite-Kit V2 once per second when running the base image, and rapidly (~3x/sec) when running the update image. The test app determines if it is a base or update image by inspecting the firmware version (obtained through the wolfBoot API). The firmware version is set in the image header by the wolfBoot keytools when signing the test app binaries. The same test app binary is used for both the base and update images, with the only difference being the firmware version set by the keytools.
The example contains two projects: `wolfBoot-tc3xx` and `test-app`. The `wolfBoot-tc3xx` project contains the wolfBoot bootloader, and the `test-app` project contains a simple firmware application that will be loaded and executed by wolfBoot. The `test-app` project is a simple blinky application that blinks LED1 on the TC375 Lite-Kit V2 once per second when running the base image, and rapidly (~3x/sec) when running the update image. The test app determines if it is a base or update image by inspecting the firmware version (obtained through the wolfBoot API). The firmware version is set in the image header by the wolfBoot keytools when signing the test app binaries. The same test app binary is used for both the base and update images, with the only difference being the firmware version set by the keytools.
This example supports loading the test application firmware as standard wolfBoot images or as an ELF file. Refer to the [firmware_update.md](../docs/firmware_update.md) documentation for more information on how the wolfBoot flash loader supports loading ELF files.
## Important notes
@ -34,10 +39,15 @@ The example contains two projects: `wolfBoot-tc3xx` and `test-app`. The `wolfBoo
- Because TC3xx PFLASH ECC prevents reading from erased flash, the `EXT_FLASH` option is used to redirect flash reads to the `ext_flash_read()` HAL API, where the flash pages requested to be read can be blank-checked by hardware before reading.
- TC3xx PFLASH is write-once (`NVM_FLASH_WRITEONCE`), however wolfBoot `NVM_FLASH_WRITEONCE` does not support `EXT_FLASH`. Therefore the write-once functionality is re-implemented in the `HAL` layer.
- This demo app is only compatible with the GCC toolchain build configurations shipped with the AURIX IDE. The TASKING compiler build configurations are not yet supported.
- When detailing commands to be run, square brackets `[]` are used to indicate optional arguments. Do not include the square brackets when running the commands.
## Flash Partitioning
The TC3xx AURIX port of wolfBoot places all images in PFLASH, and uses both PFLASH0 and PFLASH1 banks. The wolfBoot executable code and the image swap sector are located in PFLASH0, with the remainder available for use. PFLASH1 is divided in half, with the first half holding the BOOT partition and the second half holding the UPDATE partition. User firmware images are directly executed in place from the BOOT partition in PFLASH1, and so must be linked to execute within this address space, with an offset of `IMAGE_HEADER_SIZE` to account for the wolfBoot image header.
The TC3xx AURIX port of wolfBoot places all images in PFLASH, and uses both PFLASH0 and PFLASH1 banks. The wolfBoot executable code and the image swap sector are located in PFLASH0, with the remainder available for use. The layout of PFLASH1 depends on whether the test application is being loaded as a standard wolfBoot image or an ELF file.
### Standard wolfBoot images
When configured to load standard wolfBoot images, the demo application divides PFLASH1 approximately in half, with the first half holding the BOOT partition and the second half holding the UPDATE partition. User firmware images are directly executed in place from the BOOT partition in PFLASH1, and so must be linked to execute within this address space, with an offset of `IMAGE_HEADER_SIZE` to account for the wolfBoot image header. The last sector of PFLASH1 is reserved for the SWAP area.
```
+==========+
@ -61,11 +71,33 @@ The TC3xx AURIX port of wolfBoot places all images in PFLASH, and uses both PFLA
+----------+ <-- 0x8060_0000
```
Please refer to the [wolfBoot](wolfBoot-tc3xx/Lcf_Gnu_Tricore_Tc.lsl.in) and [test-app](test-app/Lcf_Gnu_Tricore_Tc.lsl.in) linker script templates for the exact memory configuration.
### ELF files
When loading the test app as an ELF file, PFLASH1 is divided into three sections. Approximately the first half of PFLASH1 is reserved for application use. This is the region that the application should be linked to execute from, and should contain all loadable segments in the ELF file. The second half of PFLASH1 is divided equally between the BOOT and UPDATE partitions as with standard wolfBoot images, with the last sector reserved for SWAP.
```
+==========+
| PFLASH1 |
+==========+ <-- 0x8030_0000
| APP | ~1.5M (0x17_C000)
+----------+ <-- 0x8047_C000
| BOOT | ~0.75M (0xC_0000)
+----------+ <-- 0x8053_C000
| UPDATE | ~0.75M (0xC_0000)
+----------+ <-- 0x805F_C000
| SWAP | 16K (0x4000)
+----------+ <-- 0x8060_0000
```
Different linker script templates are used to configure the memory layout via `wbaurixtool.sh` (see next section) depending on whether standard wolfBoot images or ELF files are being loaded. Please refer to the following linker script templates for the exact memory configuration:
- [wolfBoot](wolfBoot-tc3xx/Lcf_Gnu_Tricore_Tc.lsl.in)
- [test-app (standard wolfBoot images)](test-app/Lcf_Gnu_Tricore_Tc.lsl.in)
- [test-app (ELF files)](test-app/Lcf_Gnu_Tricore_elf.lsl.in)
## Configuration and the wolfBoot AURIX tool (wbaurixtool.sh)
wolfBoot relies extensively on its build system in order to properly set configuration macros, linker addresses, and other target-specific items. Because wolfBoot on AURIX uses the AURIX studio IDE to build, changing things like the signature algorithm would require manually editing IDE settings, linker scripts, etc. which is error prone and tedious. The `wbaurixtool.sh` script provides a single tool that automates the generation of all configurable items required for building wolfBoot and the test application on aurix given the chosen signature and hashing algorithms, including managing all configuration macros, linker scripts, as well as handling the actual key generation and image signing process. `wbaurixtool.sh` can also generate wolfHSM NVM images containing the generated image signing key when used in conjunction with wolfHSM.
wolfBoot relies extensively on its build system in order to properly set configuration macros, linker addresses, and other target-specific items. Because wolfBoot on AURIX uses the AURIX studio IDE to build, changing things like the signature algorithm would require manually editing IDE settings, linker scripts, etc. which is error prone and tedious. The `wbaurixtool.sh` script provides a single tool that automates the generation of all configurable items required for building wolfBoot and the test application on AURIX given the chosen signature and hashing algorithms, including managing all configuration macros, linker scripts, as well as handling the actual key generation and image signing process. `wbaurixtool.sh` can also generate wolfHSM NVM images containing the generated image signing key when used in conjunction with wolfHSM.
The general usage of `wbaurixtool.sh` is as follows:
@ -73,7 +105,12 @@ The general usage of `wbaurixtool.sh` is as follows:
$ ./wbaurixtool.sh [global options] <subcommand> [subcommand options]
```
where `<subcommand>` is one of the following:
where `[global options]` are:
- `--hsm`: Use the wolfHSM AURIX projects instead of the standard projects (e.g. `wolfBoot-tc3xx-wolfHSM` and `test-app-wolfHSM`)
- `--elf`: loads the test application as an ELF file instead of the standard wolfBoot image. If you wish to load the test application as an ELF file, all invocations of `wbaurixtool.sh` must use the `--elf` option.
and where `<subcommand>` is one of the following:
- `keygen`: Generate a new signing key pair
- `sign`: Sign a firmware image
@ -99,6 +136,10 @@ For more information on the `wbaurixtool.sh` script, run `./wbaurixtool.sh --hel
- A WSL2 distro (tested on Ubuntu 22.04) with the `build-essential` package installed (`sudo apt install build-essential`)
- A TC375 AURIX Lite-Kit V2
### Important notes
- If you wish to load the test application as an ELF file, all invocations of `wbaurixtool.sh` must use the global `--elf` option before any subcommand. Otherwise, the invocation to `wbaurixtool.sh` will execute its commands as if the test application was a standard wolfBoot image. Mixing and matching `--elf` invocations with non-`--elf` invocations to `wbaurixtool.sh` will result in difficult to diagnose build or runtime errors. When in doubt, clean all build artifacts and start fresh.
### Clone wolfBoot
1. Clone the wolfBoot repository and initialize the repository submodules (`git submodule update --init`)
@ -109,10 +150,10 @@ For more information on the `wbaurixtool.sh` script, run `./wbaurixtool.sh --hel
2. Compile the keytools by running `make keytools`
3. Use the helper script to generate a new signing key pair using ECC 256
1. Navigate to `wolfBoot/tools/scripts/tc3xx`
2. Run `./wbaurixtool.sh keygen --sign-algo ecc256 macros lcf`. This:
2. Run `./wbaurixtool.sh [--elf] keygen --sign-algo ecc256 macros lcf`. This:
- Generates the signing private key `wolfBoot/priv.der` and adds the public key to the wolfBoot keystore (see [keygen](https://github.com/wolfSSL/wolfBoot/blob/aurix-tc3xx-support/docs/Signing.md) for more information)
- Generates the `wolfBoot_macros.txt` file from the `wolfBoot_macros.in` template in the `wolfBoot-tc3xx` directory, which sets the appropriate wolfBoot preprocessor macros based on the hash and signature algorithms. The `wolfBoot_macros.txt` file is then passed to the compiler in the AURIX project
- Generates the `Lcf_Gnu_Tricore_Tc.lsl` file from the `Lcf_Gnu_Tricore_Tc.lcf.in` template in the `test-app` directory, which sets appropriate values for Linker addresses (e.g. wolfBoot header size)based on the selected signature algorithm. The `Lcf_Gnu_Tricore_Tc.lsl` file is then passed to the linker in the AURIX project
- Generates the `Lcf_Gnu_Tricore_Tc.lsl` file from either the `Lcf_Gnu_Tricore_Tc.lcf.in` template in the `test-app` directory or the `Lcf_Gnu_Tricore_elf.lcf.in` template in the `test-app` directory, which sets appropriate values for Linker addresses (e.g. wolfBoot header size) based on the selected signature algorithm. The `Lcf_Gnu_Tricore_Tc.lsl` file is then passed to the linker in the AURIX project
- Note that if you already have generated keys, you can use `./wbaurixtool.sh clean` to remove them first
```
@ -164,7 +205,7 @@ wolfBoot/IDE/AURIX/Configurations/
### Build wolfBoot
1. Generate the 'target.h` header file for the tc375 flash configuration
1. Open a WSL terminal and navigate to `wolfBoot/tools/scripts/tc3xx`
2. Run `./wbaurixtool.sh target`
2. Run `./wbaurixtool.sh [--elf] target`
2. Open the AURIX IDE and create a new workspace directory, if you do not already have a workspace you wish to use
3. Import the wolfBoot project
1. Click "File" -> Open Projects From File System"
@ -176,9 +217,10 @@ wolfBoot/IDE/AURIX/Configurations/
3. Click the hammer icon to build the active project. This will compile wolfBoot.
5. Import the test-app project using the same procedure as in step (3), except using `wolfBoot/IDE/AURIX/test-app` as the directory
6. Build the test-app project using the same procedure as in step (4), except choosing the `test-app` eclipse project. Note that the build process contains a custom post-build step that converts the application `elf` file into a `.bin` file using `tricore-elf-objcopy`, which can then be signed by the wolfBoot key tools in the following step
7. Sign the generated test-app binary using the wolfBoot keytools
7. If intending to build and load elf files, compile the squashelf tool by running `make` in the `tools/squashelf` directory. This step can be skipped if you only wish to build and load standard wolfBoot images.
8. Sign the generated test-app binary using the wolfBoot keytools
1. Open a WSL terminal and navigate to `wolfBoot/tools/scripts/tc3xx`
2. Run `./wbaurixtool.sh sign --debug` or `./wbaurixtool.sh sign` to sign either the debug or release build, respectively. This creates the signed image files `test-app_v1_signed.bin` and `test-app_v2_signed.bin` in the test-app output build directory. The v1 image is the initial image that will be loaded to the `BOOT` partition, and the v2 image is the update image that will be loaded to the `UPDATE` partition.
2. Run `./wbaurixtool.sh [--elf] sign --debug` or `./wbaurixtool.sh [--elf] sign` to sign either the debug or release build, respectively. This creates the signed image files `test-app_v1_signed.bin` and `test-app_v2_signed.bin` in the test-app output build directory. The v1 image is the initial image that will be loaded to the `BOOT` partition, and the v2 image is the update image that will be loaded to the `UPDATE` partition. If `--elf` is specified, `wbaurixtool.sh` will first preprocess the test-app ELF file with [squashelf](../../tools/squashelf) and then sign the resulting ELF file which contains only loadable segments.
```
$ ./wbaurixtool.sh sign
@ -236,7 +278,7 @@ The device is now configured to boot from `0xA00A0000` out of reset.
We can now load wolfBoot and the firmware application images to the tc3xx device using Trace32 and a Lauterbach probe
1. Click "File" -> "ChangeDir and Run Script" and choose the `wolfBoot/tools/scripts/tc3xx/wolfBoot-loadAll-$BUILD.cmm` script, where $BUILD should be either "debug" or "release" depending on your build type in (4) and (6).
1. Click "File" -> "ChangeDir and Run Script" and choose the `wolfBoot/tools/scripts/tc3xx/wolfBoot-loadAll-$BUILD.cmm` script, where $BUILD should be either "debug" or "release" depending on your build type from earlier. There are also corresponding ELF load scripts for loading the test app as an ELF image.
wolfBoot and the demo applications are now loaded into flash, and core0 will be halted at the wolfBoot entry point (`core0_main()`).
@ -275,8 +317,8 @@ IDE/AURIX/wolfHSM-infineon-tc3xx/
4. Follow all of the steps in [Building and Running the wolfBoot Demo](#building-and-running-the-wolfboot-demo) for the non-HSM enabled case, but with the following key differences:
1. The [wolfBoot-tc3xx-wolfHSM](./wolfBoot-tc3xx-wolfHSM/) AURIX Studio project should be used instead of `wolfBoot-tc3xx`
2. Use the `wolfBoot-wolfHSM-loadAll-XXX.cmm` lauterbach scripts instead of `wolfBoot-loadAll-XXX.cmm` to load the wolfBoot and test-app images in the TRACE32 GUI
3. Provide the `--hsm` global option to the `wbaurixtool.sh` script when invoking it, so the wolfHSM projects are used instead of the standard wolfBoot projects
4. If using the default build options in [wolfBoot-tc3xx-wolfHSM](./wolfBoot-tc3xx-wolfHSM/), wolfBoot will expect the public key for image verification to be stored at a specific keyId for the wolfBoot client ID. You can use [whnvmtool](https://github.com/wolfSSL/wolfHSM/tree/main/tools/whnvmtool) to generate a loadable NVM image that contains the required keys automatically via `wbaurixtool.sh` through the `nvm` subcommand. This generates an NVM image containing the generated image signing key based on the [wolfBoot-wolfHSM-keys.nvminit](../../tools/scripts/tc3xx/wolfBoot-wolfHSM-keys.nvminit) configuration file, which can then be loaded to the device via a flash programming tool. See the `whnvmtool` documentation and the documentation included in your wolfHSM AURIX release for more details. Note: if you want to use the standard wolfBoot keystore functionality in conjunction with wolfHSM for testing purposes (doesn't require pre-loading keys on the HSM) you can configure wolfBoot to send the keys to the HSM on-the-fly as ephemeral keys. To do this, ensure `WOLFBOOT_USE_WOLFHSM_PUBKEY_ID` is **NOT** defined, and add the `--localkeys` argument to then `./wbaurixtool.sh keygen` command, which invokes the `keygen` tool without the default `--nolocalkeys` option.
3. Provide the `--hsm` global option to the `wbaurixtool.sh` script when invoking it so that the wolfHSM projects are used instead of the standard wolfBoot projects
4. If using the default build options in [wolfBoot-tc3xx-wolfHSM](./wolfBoot-tc3xx-wolfHSM/), wolfBoot will expect the public key for image verification to be stored at a specific keyId for the wolfBoot client ID. You can use [whnvmtool](https://github.com/wolfSSL/wolfHSM/tree/main/tools/whnvmtool) to generate a loadable NVM image that contains the required keys automatically via `wbaurixtool.sh` through the `nvm` subcommand. This generates an NVM image containing the generated image signing key based on the [wolfBoot-wolfHSM-keys.nvminit](../../tools/scripts/tc3xx/wolfBoot-wolfHSM-keys.nvminit) configuration file, which can then be loaded to the device via a flash programming tool. Before using the `nvm` subcommand of `wbaurixtool.sh`, first compile `whnvmtool` by running `make` in the `lib/wolfHSM/tools/whnvmtool` directory. See the `whnvmtool` documentation and the documentation included in your wolfHSM AURIX release for more details. Note: if you want to use the standard wolfBoot keystore functionality in conjunction with wolfHSM for testing purposes (doesn't require pre-loading keys on the HSM) you can configure wolfBoot to send the keys to the HSM on-the-fly as ephemeral keys. To do this, ensure `WOLFBOOT_USE_WOLFHSM_PUBKEY_ID` is **NOT** defined, and add the `--localkeys` argument to then `./wbaurixtool.sh keygen` command, which invokes the `keygen` tool without the default `--nolocalkeys` option.
## Building: Command Sequence
@ -309,7 +351,7 @@ cd $SCRIPTS_DIR
# Generate keys, as well as configuration macros and linker script based on the selected signature algorithm
./wbaurixtool.sh [--hsm] keygen --sign-algo ecc256 --hash-algo sha256 macros lcf
# If using wolfHSM, generate key NVM image
# If using wolfHSM, generate key NVM image (make sure you have compiled whnvmtool before running this command)
./wbaurixtool.sh nvm
# Load NVM image hexfile to the device
# ...

View File

@ -453,6 +453,9 @@
<option id="com.infineon.aurix.buildsystem.managed.tool.c.link.option.ldflags.708051953" name="Linker flags" superClass="com.infineon.aurix.buildsystem.managed.tool.c.link.option.ldflags" useByScannerDiscovery="false" value="-mtc162 -Wl,--gc-sections -Wl,-Map,output.map" valueType="string"/>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="com.infineon.aurix.buildsystem.managed.tool.c.link.option.userobjs.1601921229" name="Other objects" superClass="com.infineon.aurix.buildsystem.managed.tool.c.link.option.userobjs" useByScannerDiscovery="false" valueType="userObjs">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx-wolfHSM/TriCore Debug (GCC)/wolfBoot/hal/aurix_tc3xx.o}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx-wolfHSM/TriCore Debug (GCC)/Libraries/iLLD/TC37A/Tricore/Asclin/Asc/IfxAsclin_Asc.o}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx-wolfHSM/TriCore Debug (GCC)/Libraries/iLLD/TC37A/Tricore/Asclin/Std/IfxAsclin.o}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx-wolfHSM/TriCore Debug (GCC)/wolfBoot/src/string.o}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx-wolfHSM/TriCore Debug (GCC)/wolfBoot/src/libwolfboot.o}&quot;"/>
</option>
<inputType id="com.infineon.aurix.buildsystem.managed.c.linker.inputType.1147881076" superClass="com.infineon.aurix.buildsystem.managed.c.linker.inputType"/>
@ -543,7 +546,7 @@
<listOptionValue builtIn="false" value="PART_SWAP_EXT"/>
<listOptionValue builtIn="false" value="RAM_CODE"/>
</option>
<option id="com.infineon.aurix.buildsystem.managed.tool.c.compiler.option.misc.other.1199404591" superClass="com.infineon.aurix.buildsystem.managed.tool.c.compiler.option.misc.other" value="-mtc162 -c -fmessage-length=0" valueType="string"/>
<option id="com.infineon.aurix.buildsystem.managed.tool.c.compiler.option.misc.other.1199404591" name="Other flags" superClass="com.infineon.aurix.buildsystem.managed.tool.c.compiler.option.misc.other" useByScannerDiscovery="false" value="-mtc162 -c -fmessage-length=0" valueType="string"/>
<inputType id="com.infineon.aurix.buildsystem.managed.tool.c.compiler.inputType.1990995811" superClass="com.infineon.aurix.buildsystem.managed.tool.c.compiler.inputType"/>
</tool>
<tool id="com.infineon.aurix.buildsystem.managed.tool.cpp.compiler.669528986" name="AURIX G++ Compiler" superClass="com.infineon.aurix.buildsystem.managed.tool.cpp.compiler">

View File

@ -121,10 +121,10 @@ MEMORY
pfls1_hdr (rx!p): org = LCF_WOLFBOOT_BOOT_PART_BASEADDR, len = LCF_WOLFBOOT_HEADER_OFFSET
/* pfls1 is the remainder of the wolfBoot BOOT partition. Everything goes here */
pfls1 (rx!p): org = LCF_CODE_BASE_ADDR, len = (0x17E000 - LCF_WOLFBOOT_HEADER_OFFSET)
pfls1 (rx!p): org = LCF_CODE_BASE_ADDR, len = (0x17C000 - LCF_WOLFBOOT_HEADER_OFFSET)
/* reserved for wolfBoot UPDATE partition */
pfls1_update (rwx!p): org = 0xA047E000, len = 0x17E000 /* ~1.5MiB */
pfls1_update (rwx!p): org = 0xA047C000, len = 0x17C000 /* ~1.5MiB */
/* SWAP sector for wolfBoot image update */
pfls1_swap (rwx!p): org = 0xA05FC000, len = 16K /* last sector of PFLASH1 */

File diff suppressed because it is too large Load Diff

View File

@ -440,6 +440,9 @@
<listOptionValue builtIn="false" value="PART_SWAP_EXT"/>
</option>
<option id="com.infineon.aurix.buildsystem.managed.tool.c.compiler.option.misc.other.739113076" name="Other flags" superClass="com.infineon.aurix.buildsystem.managed.tool.c.compiler.option.misc.other" useByScannerDiscovery="false" value="-c -fmessage-length=0 -mtc162" valueType="string"/>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="com.infineon.aurix.buildsystem.managed.tool.c.compiler.option.include.files.884786319" name="Include files (-include)" superClass="com.infineon.aurix.buildsystem.managed.tool.c.compiler.option.include.files" useByScannerDiscovery="false" valueType="includeFiles">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx/wolfBoot/include/image.h}&quot;"/>
</option>
<inputType id="com.infineon.aurix.buildsystem.managed.tool.c.compiler.inputType.1480422611" superClass="com.infineon.aurix.buildsystem.managed.tool.c.compiler.inputType"/>
</tool>
<tool id="com.infineon.aurix.buildsystem.managed.tool.cpp.compiler.366043271" name="AURIX G++ Compiler" superClass="com.infineon.aurix.buildsystem.managed.tool.cpp.compiler">
@ -453,6 +456,9 @@
<option id="com.infineon.aurix.buildsystem.managed.tool.c.link.option.ldflags.708051953" name="Linker flags" superClass="com.infineon.aurix.buildsystem.managed.tool.c.link.option.ldflags" useByScannerDiscovery="false" value="-mtc162 -Wl,--gc-sections -Wl,-Map,output.map" valueType="string"/>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="com.infineon.aurix.buildsystem.managed.tool.c.link.option.userobjs.1601921229" name="Other objects" superClass="com.infineon.aurix.buildsystem.managed.tool.c.link.option.userobjs" useByScannerDiscovery="false" valueType="userObjs">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx/TriCore Debug (GCC)/wolfBoot/hal/aurix_tc3xx.o}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx/TriCore Debug (GCC)/Libraries/iLLD/TC37A/Tricore/Asclin/Std/IfxAsclin.o}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx/TriCore Debug (GCC)/Libraries/iLLD/TC37A/Tricore/Asclin/Asc/IfxAsclin_Asc.o}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx/TriCore Debug (GCC)/wolfBoot/src/string.o}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx/TriCore Debug (GCC)/wolfBoot/src/libwolfboot.o}&quot;"/>
</option>
<inputType id="com.infineon.aurix.buildsystem.managed.c.linker.inputType.1147881076" superClass="com.infineon.aurix.buildsystem.managed.c.linker.inputType"/>
@ -469,6 +475,7 @@
</configuration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
<storageModule moduleId="com.infineon.aurix.buildsystem.build.autodiscovery.settings"/>
</cconfiguration>
<cconfiguration id="com.infineon.aurix.buildsystem.managed.external.gcc.builtin.configuration.release.856945873">
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="com.infineon.aurix.buildsystem.managed.external.gcc.builtin.configuration.release.856945873" moduleId="org.eclipse.cdt.core.settings" name="TriCore Release (GCC)">

View File

@ -1,41 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project>
<configuration id="com.infineon.aurix.buildsystem.managed.configuration.binary.1914762785" name="TriCore Debug (TASKING)">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="com.infineon.aurix.buildsystem.managed.TaskingBuiltintSpecsDetector" ref="shared-provider"/>
</extension>
</configuration>
<configuration id="com.infineon.aurix.buildsystem.managed.configuration.binary.697039770" name="TriCore Release (TASKING)">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="com.infineon.aurix.buildsystem.managed.TaskingBuiltintSpecsDetector" ref="shared-provider"/>
</extension>
</configuration>
<configuration id="com.infineon.aurix.buildsystem.managed.external.gcc.builtin.configuration.debug.1333370870" name="TriCore Debug (GCC)">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider class="com.infineon.aurix.buildsystem.managed.gcc.AURIXGCC11BuiltinSpecsDetector" console="false" env-hash="1122385352849106138" id="com.infineon.aurix.buildsystem.managed.CrossGCC11BuiltinSpecsDetector" keep-relative-paths="false" name="AURIXCrossGCC11compilerSpecsDetector" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/>
</provider>
</extension>
</configuration>
<configuration id="com.infineon.aurix.buildsystem.managed.external.gcc.builtin.configuration.release.856945873" name="TriCore Release (GCC)">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider class="com.infineon.aurix.buildsystem.managed.gcc.AURIXGCC11BuiltinSpecsDetector" console="false" env-hash="1122385352849106138" id="com.infineon.aurix.buildsystem.managed.CrossGCC11BuiltinSpecsDetector" keep-relative-paths="false" name="AURIXCrossGCC11compilerSpecsDetector" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/>
</provider>
</extension>
</configuration>
</project>

View File

@ -121,10 +121,10 @@ MEMORY
pfls1_hdr (rx!p): org = LCF_WOLFBOOT_BOOT_PART_BASEADDR, len = LCF_WOLFBOOT_HEADER_OFFSET
/* pfls1 is the remainder of the wolfBoot BOOT partition. Everything goes here */
pfls1 (rx!p): org = LCF_CODE_BASE_ADDR, len = (0x17E000 - LCF_WOLFBOOT_HEADER_OFFSET)
pfls1 (rx!p): org = LCF_CODE_BASE_ADDR, len = (0x17C000 - LCF_WOLFBOOT_HEADER_OFFSET)
/* reserved for wolfBoot UPDATE partition */
pfls1_update (rwx!p): org = 0xA047E000, len = 0x17E000 /* ~1.5MiB */
pfls1_update (rwx!p): org = 0xA047C000, len = 0x17C000 /* ~1.5MiB */
/* SWAP sector for wolfBoot image update */
pfls1_swap (rwx!p): org = 0xA05FC000, len = 16K /* last sector of PFLASH1 */

File diff suppressed because it is too large Load Diff

View File

@ -477,6 +477,8 @@
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/wolfBoot/include/wolfboot}&quot;"/>
</option>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="com.infineon.aurix.buildsystem.managed.tool.c.compiler.option.preprocessor.def.symbols.2021414389" name="Defined symbols (-D)" superClass="com.infineon.aurix.buildsystem.managed.tool.c.compiler.option.preprocessor.def.symbols" useByScannerDiscovery="false" valueType="definedSymbols">
<listOptionValue builtIn="false" value="PRINTF_ENABLED"/>
<listOptionValue builtIn="false" value="DEBUG_UART"/>
<listOptionValue builtIn="false" value="__WOLFBOOT"/>
<listOptionValue builtIn="false" value="IFX_USE_SW_MANAGED_INT"/>
<listOptionValue builtIn="false" value="GNU"/>

View File

@ -123,18 +123,11 @@ MEMORY
/* wolfBoot program code - all of this project's executable code goes here */
pfls0_nc (rx!p): org = 0xA00A8000, len = 0x23000 /* 140K total (incl 32K for startup) */
/* reserved for wolfBoot BOOT partition */
pfls1_boot (rwx!p): org = 0x80300000, len = 0x17E000 /* ~1.5MiB */
pfls1_boot_nc (rwx!p): org = 0xA0300000, len = 0x17E000 /* ~1.5MiB */
/* reserved for BOOT, UPDATE, and SWAP partitions */
pfls1(rwx!p): org = 0x80300000, len = 0x300000 /* 3MiB */
pfls1_nc(rwx!p): org = 0xA0300000, len = 0x300000 /* 3MiB */
/* reserved for wolfBoot UPDATE partition */
pfls1_update (rwx!p): org = 0x8047E000, len = 0x17E000 /* ~1.5MiB */
pfls1_update_nc (rwx!p): org = 0xA047E000, len = 0x17E000 /* ~1.5MiB */
/* SWAP sector for wolfBoot image update */
pfls1_swap (rwx!p): org = 0x805FC000, len = 16K /* last sector of PFLASH1 */
dfls0 (rx!p): org = 0xaf000000, len = 256K
fls0 (rx!p): org = 0xaf000000, len = 256K
ucb (rx!p): org = 0xaf400000, len = 24K

View File

@ -6,3 +6,5 @@
@WOLFBOOT_USE_WOLFHSM_PUBKEY_ID@
@ML_DSA_LEVEL@
@ML_DSA_IMAGE_SIGNATURE_SIZE@
@WOLFBOOT_ELF@
@WOLFBOOT_ELF_FLASH_SCATTER@

File diff suppressed because one or more lines are too long

View File

@ -1,41 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project>
<configuration id="com.infineon.aurix.buildsystem.managed.configuration.binary.1019631072" name="TriCore Debug (TASKING)">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="com.infineon.aurix.buildsystem.managed.TaskingBuiltintSpecsDetector" ref="shared-provider"/>
</extension>
</configuration>
<configuration id="com.infineon.aurix.buildsystem.managed.configuration.binary.394981185" name="TriCore Release (TASKING)">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="com.infineon.aurix.buildsystem.managed.TaskingBuiltintSpecsDetector" ref="shared-provider"/>
</extension>
</configuration>
<configuration id="com.infineon.aurix.buildsystem.managed.external.gcc.builtin.configuration.debug.401330509" name="TriCore Debug (GCC)">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider class="com.infineon.aurix.buildsystem.managed.gcc.AURIXGCC11BuiltinSpecsDetector" console="false" env-hash="1122385352849106138" id="com.infineon.aurix.buildsystem.managed.CrossGCC11BuiltinSpecsDetector" keep-relative-paths="false" name="AURIXCrossGCC11compilerSpecsDetector" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/>
</provider>
</extension>
</configuration>
<configuration id="com.infineon.aurix.buildsystem.managed.external.gcc.builtin.configuration.release.1853938077" name="TriCore Release (GCC)">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider class="com.infineon.aurix.buildsystem.managed.gcc.AURIXGCC11BuiltinSpecsDetector" console="false" env-hash="1122385352849106138" id="com.infineon.aurix.buildsystem.managed.CrossGCC11BuiltinSpecsDetector" keep-relative-paths="false" name="AURIXCrossGCC11compilerSpecsDetector" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/>
</provider>
</extension>
</configuration>
</project>

View File

@ -123,16 +123,9 @@ MEMORY
/* wolfBoot program code - all of this project's executable code goes here */
pfls0_nc (rx!p): org = 0xA00A8000, len = 0x23000 /* 140K total (incl 32K for startup) */
/* reserved for wolfBoot BOOT partition */
pfls1_boot (rwx!p): org = 0x80300000, len = 0x17E000 /* ~1.5MiB */
pfls1_boot_nc (rwx!p): org = 0xA0300000, len = 0x17E000 /* ~1.5MiB */
/* reserved for wolfBoot UPDATE partition */
pfls1_update (rwx!p): org = 0x8047E000, len = 0x17E000 /* ~1.5MiB */
pfls1_update_nc (rwx!p): org = 0xA047E000, len = 0x17E000 /* ~1.5MiB */
/* SWAP sector for wolfBoot image update */
pfls1_swap (rwx!p): org = 0x805FC000, len = 16K /* last sector of PFLASH1 */
/* reserved for BOOT, UPDATE, and SWAP partitions */
pfls1(rwx!p): org = 0x80300000, len = 0x300000 /* 3MiB */
pfls1_nc(rwx!p): org = 0xA0300000, len = 0x300000 /* 3MiB */
dfls0 (rx!p): org = 0xaf000000, len = 256K

View File

@ -6,3 +6,5 @@
@WOLFBOOT_USE_WOLFHSM_PUBKEY_ID@
@ML_DSA_LEVEL@
@ML_DSA_IMAGE_SIGNATURE_SIZE@
@WOLFBOOT_ELF@
@WOLFBOOT_ELF_FLASH_SCATTER@

View File

@ -243,6 +243,12 @@ keytools:
@echo "Building key tools"
@$(MAKE) -C tools/keytools -j
squashelf:
@echo "Building squashelf tool"
@$(MAKE) -C tools/squashelf -j
squashelf_check: squashelf
tpmtools: include/target.h keys
@echo "Building TPM tools"
@$(MAKE) -C tools/tpm -s clean
@ -267,6 +273,10 @@ test-app/image.elf: wolfboot.elf
$(Q)$(MAKE) -C test-app WOLFBOOT_ROOT="$(WOLFBOOT_ROOT)" image.elf
$(Q)$(SIZE) test-app/image.elf
ifeq ($(ELF_FLASH_SCATTER),1)
test-app/image.elf: squashelf
endif
assemble_internal_flash.dd: FORCE
$(Q)$(BINASSEMBLE) internal_flash.dd \
0 wolfboot.bin \
@ -376,6 +386,7 @@ utilsclean: clean
$(Q)$(MAKE) -C tools/uart-flash-server -s clean
$(Q)$(MAKE) -C tools/unit-tests -s clean
$(Q)$(MAKE) -C tools/keytools/otp -s clean
$(Q)$(MAKE) -C tools/squashelf -s clean
keysclean: clean
$(Q)rm -f *.pem *.der tags ./src/*_pub_key.c ./src/keystore.c include/target.h
@ -467,4 +478,4 @@ pico-sdk-info: FORCE
FORCE:
.PHONY: FORCE clean keytool_check
.PHONY: FORCE clean keytool_check squashelf_check

View File

@ -1105,7 +1105,7 @@ ifeq ($(ARCH),sim)
LD_END_GROUP=
BOOT_IMG=test-app/image.elf
CFLAGS+=-DARCH_SIM
ifneq ($(ELF_SCATTERED),1)
ifneq ($(ELF_FLASH_SCATTER),1)
CFLAGS+=-DWOLFBOOT_USE_STDLIBC
endif
ifeq ($(FORCE_32BIT),1)

View File

@ -5,7 +5,7 @@ HASH?=SHA256
WOLFBOOT_SMALL_STACK?=0
SPI_FLASH=0
DEBUG=1
ELF_SCATTERED=1
ELF_FLASH_SCATTER=1
ELF=1
@ -15,7 +15,7 @@ WOLFBOOT_SECTOR_SIZE=0x1000
WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80000
# if on external flash, it should be multiple of system page size
# Address from 0x100000 to 0x1FFFFF is reserved for ELF_SCATTERED
# Address from 0x100000 to 0x1FFFFF is reserved for ELF_FLASH_SCATTER
WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x200000
WOLFBOOT_PARTITION_SWAP_ADDRESS=0x280000

View File

@ -7,7 +7,7 @@ SPI_FLASH=0
DEBUG=1
FORCE_32BIT=1
ELF=1
ELF_SCATTERED=1
ELF_FLASH_SCATTER=1
# sizes should be multiple of system page size
WOLFBOOT_PARTITION_SIZE=0x40000

View File

@ -171,4 +171,53 @@ of the patch. If all checks succeed, the new version is installed by applying th
If the update is not confirmed, at the next reboot wolfBoot will restore the original base `image_v1_signed.bin`, using
the reverse patch contained in the delta update bundle.
## ELF loading
wolfBoot supports loading ELF (Executable and Linkable Format) images via both the RAM [update_ram.c](../src/update_ram.c) and [flash update](../src/update_flash.c) mechanisms.
### RAM ELF loading
The wolfBoot RAM loader supports loading ELF images from flash into RAM before booting. When using the RAM loader [update_ram.c](../src/update_ram.c) with `WOLFBOOT_ELF` defined, wolfBoot will verify the ELF file signature and hash as stored in the boot or update partition, and then load the ELF file into RAM based on the LMA (Load Memory Address) of each section before jumping to the entry point.
### Flash ELF loading
The wolfBoot flash loader also supports loading ELF files containing scattered LMA (Load Memory Address) segments to their respective locations in flash. This feature allows firmware images to be distributed as ELF files with sections only containing loadable regions, rather than requiring a contiguous/flat binary image for the entire memory space or image size. The flash elf loading procedure only supports loading ELF file program segments into flash memory with the same access restrictions as the BOOT partition (e.g. will use the same hal_flash/ext_flash functions) and does not support loading sections into RAM.
#### How it works
When wolfBoot is compiled with `WOLFBOOT_ELF_FLASH_SCATTER` defined, it includes support for:
1. **Section Loading**: During boot or update, wolfBoot:
- Parses the ELF headers to identify section locations
- Loads each section into its designated memory address
- Sets up the proper entry point for execution
- Verifies the scattered hash after loading
2. **Dual-Layer Verification**: wolfBoot performs two distinct integrity checks:
- Initial verification of the ELF file signature and integrity check (hash) as stored in the boot or update partition
- A second "scattered hash" verification that re-computes the same image hash as in the manifest header, but computed over the actual loaded segments in their final memory locations (Load Memory Address - LMA) rather than their data in the ELF file
3. **Update Support**: The scattered ELF support is transparently integrated with wolfBoot's flash update mechanism:
- ELF images can be used as update packages
- The bootloader automatically handles loading and verifying scattered sections during the update process
- If the scattered hash verification fails after an update, the system can fall back to the previous version
#### Using Scattered ELF Images
To use scattered ELF images:
1. Ensure wolfBoot is compiled with `WOLFBOOT_ELF` and `WOLFBOOT_ELF_FLASH_SCATTER`
2. Build your firmware as an ELF file with the desired memory layout
3. Preprocess the ELF file using [squashelf](../tools/squashelf/README.md) to strip the ELF file to just loadable sections and ensure it is compatible with wolfBoot (if building using wolfBoot's Makefile build system, this step is performed automatically)
4. Sign the ELF image using the wolfBoot signing tools
5. Transfer the signed ELF image to the target like any other update
The on boot or during update, the bootloader will automatically perform both layers of verification:
1. Verifying the signature and hash over the image in the BOOT or UPDATE partition
2. Computing and verifying the scattered hash after loading sections to their final locations
Note: When using scattered ELF images, ensure that:
- The ELF file adheres to the ELF file specification and was generated by a toolchain supporting the target architecture
- All section addresses are within valid executable memory regions and **do not overlap with the wolfBoot image, nor the BOOT, UPDATE and SWAP partitions**.

View File

@ -33,6 +33,11 @@
#include "IfxPort.h" /* for IfxPort_* */
#include "IfxScuRcu.h" /* for IfxScuRcu_performReset */
#include "Ifx_Ssw_Infra.h" /* for Ifx_Ssw_jumpToFunction */
#if defined(DEBUG_UART)
#include "IfxAsclin_Asc.h"
#include "Cpu/Irq/IfxCpu_Irq.h"
#include "IfxCpu.h"
#endif /* DEBUG_UART */
#ifdef WOLFBOOT_ENABLE_WOLFHSM_CLIENT
/* wolfHSM headers */
@ -81,6 +86,15 @@ static uint32_t sectorBuffer[WOLFBOOT_SECTOR_SIZE / sizeof(uint32_t)];
#define LED_OFF(led)
#endif /* WOLFBOOT_AURIX_GPIO_TIMING */
#if defined(DEBUG_UART)
#define UART_PIN_RX IfxAsclin0_RXA_P14_1_IN /* RX pin of the board */
#define UART_PIN_TX IfxAsclin0_TX_P14_0_OUT /* TX pin of the board */
#define UART_BAUDRATE 115200
static Ifx_ASCLIN* g_asclinRegs = &MODULE_ASCLIN0;
static int uartInit(void);
static int uartTx(const uint8_t c);
#endif /* DEBUG_UART */
#ifdef WOLFBOOT_ENABLE_WOLFHSM_CLIENT
int hal_hsm_init_connect(void);
int hal_hsm_disconnect(void);
@ -353,6 +367,10 @@ void hal_init(void)
LED_OFF(LED_PROG);
LED_OFF(LED_ERASE);
LED_OFF(LED_READ);
#if defined(DEBUG_UART)
uartInit();
#endif
}
/*
@ -441,25 +459,91 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
{
LED_ON(LED_ERASE);
const uint32_t sectorAddr = GET_SECTOR_ADDR(address);
const size_t numSectors =
(len == 0) ? 0 : ((len - 1) / WOLFBOOT_SECTOR_SIZE) + 1;
const IfxFlash_FlashType type = getFlashTypeFromAddr(address);
/* Handle zero length case */
if (len <= 0) {
LED_OFF(LED_ERASE);
return 0;
}
/* Disable ENDINIT protection */
const uint16 endInitSafetyPassword =
const uint32_t startSectorAddr = GET_SECTOR_ADDR(address);
const uint32_t endAddress = address + len - 1;
const uint32_t endSectorAddr = GET_SECTOR_ADDR(endAddress);
const IfxFlash_FlashType type = getFlashTypeFromAddr(address);
const uint16 endInitSafetyPassword =
IfxScuWdt_getSafetyWatchdogPasswordInline();
IfxScuWdt_clearSafetyEndinitInline(endInitSafetyPassword);
uint32_t currentSectorAddr;
IfxFlash_eraseMultipleSectors(sectorAddr, numSectors);
/* If address and len are both sector-aligned, perform simple bulk erase */
if ((address == startSectorAddr) &&
(endAddress == endSectorAddr + WOLFBOOT_SECTOR_SIZE - 1)) {
const size_t numSectors =
(endSectorAddr - startSectorAddr) / WOLFBOOT_SECTOR_SIZE + 1;
/* Reenable ENDINIT protection */
IfxScuWdt_setSafetyEndinitInline(endInitSafetyPassword);
/* Disable ENDINIT protection */
IfxScuWdt_clearSafetyEndinitInline(endInitSafetyPassword);
IfxFlash_waitUnbusy(FLASH_MODULE, type);
IfxFlash_eraseMultipleSectors(startSectorAddr, numSectors);
/* Reenable ENDINIT protection */
IfxScuWdt_setSafetyEndinitInline(endInitSafetyPassword);
IfxFlash_waitUnbusy(FLASH_MODULE, type);
}
/* For non-sector aligned erases, handle each sector carefully */
else {
/* Process each affected sector */
for (currentSectorAddr = startSectorAddr;
currentSectorAddr <= endSectorAddr;
currentSectorAddr += WOLFBOOT_SECTOR_SIZE) {
/* Check if this is a partial sector erase */
const int isFirstSector = (currentSectorAddr == startSectorAddr);
const int isLastSector = (currentSectorAddr == endSectorAddr);
const int isPartialStart =
isFirstSector && (address > startSectorAddr);
const int isPartialEnd =
isLastSector &&
(endAddress < (endSectorAddr + WOLFBOOT_SECTOR_SIZE - 1));
/* For partial sectors, need to read-modify-write */
if (isPartialStart || isPartialEnd) {
/* Read the sector into the sector buffer */
cacheSector(currentSectorAddr, type);
/* Calculate which bytes within the sector to erase */
uint32_t eraseStartOffset =
isPartialStart ? (address - currentSectorAddr) : 0;
uint32_t eraseEndOffset = isPartialEnd
? (endAddress - currentSectorAddr)
: (WOLFBOOT_SECTOR_SIZE - 1);
uint32_t eraseLen = eraseEndOffset - eraseStartOffset + 1;
/* Fill the section to be erased with the erased byte value */
memset((uint8_t*)sectorBuffer + eraseStartOffset,
FLASH_BYTE_ERASED, eraseLen);
/* Erase the sector */
IfxScuWdt_clearSafetyEndinitInline(endInitSafetyPassword);
IfxFlash_eraseSector(currentSectorAddr);
IfxScuWdt_setSafetyEndinitInline(endInitSafetyPassword);
IfxFlash_waitUnbusy(FLASH_MODULE, type);
/* Program the modified buffer back */
programCachedSector(currentSectorAddr, type);
}
/* For full sector erase, just erase directly */
else {
IfxScuWdt_clearSafetyEndinitInline(endInitSafetyPassword);
IfxFlash_eraseSector(currentSectorAddr);
IfxScuWdt_setSafetyEndinitInline(endInitSafetyPassword);
IfxFlash_waitUnbusy(FLASH_MODULE, type);
}
}
}
LED_OFF(LED_ERASE);
return 0;
}
@ -681,3 +765,118 @@ int hal_hsm_disconnect(void)
#endif /* WOLFBOOT_ENABLE_WOLFHSM_CLIENT */
#if defined(DEBUG_UART)
static int uartInit(void)
{
/* Define local pin structures for init function */
IfxAsclin_Rx_In uartRxPin = UART_PIN_RX;
IfxAsclin_Tx_Out uartTxPin = UART_PIN_TX;
IfxAsclin_enableModule(g_asclinRegs);
IfxAsclin_setClockSource(g_asclinRegs, IfxAsclin_ClockSource_noClock);
IfxAsclin_initRxPin(&uartRxPin, IfxPort_InputMode_pullUp,
IfxPort_PadDriver_cmosAutomotiveSpeed1);
IfxAsclin_initTxPin(&uartTxPin, IfxPort_OutputMode_pushPull,
IfxPort_PadDriver_cmosAutomotiveSpeed1);
IfxAsclin_setFrameMode(g_asclinRegs, IfxAsclin_FrameMode_initialise);
/* Configure baudrate - must temporarily enable clocks */
IfxAsclin_setClockSource(g_asclinRegs, IfxAsclin_ClockSource_ascFastClock);
IfxAsclin_setPrescaler(g_asclinRegs, 1);
if (IfxAsclin_setBitTiming(
g_asclinRegs, (float32)UART_BAUDRATE,
IfxAsclin_OversamplingFactor_16,
IfxAsclin_SamplePointPosition_9, /* Sample point 9 */
IfxAsclin_SamplesPerBit_three) == FALSE) { /* Three samples */
IfxAsclin_disableModule(g_asclinRegs);
return -1;
}
IfxAsclin_setClockSource(g_asclinRegs, IfxAsclin_ClockSource_noClock);
IfxAsclin_setDataLength(g_asclinRegs, IfxAsclin_DataLength_8);
IfxAsclin_enableParity(g_asclinRegs, FALSE);
IfxAsclin_setStopBit(g_asclinRegs, IfxAsclin_StopBit_1);
IfxAsclin_setIdleDelay(g_asclinRegs, IfxAsclin_IdleDelay_0);
IfxAsclin_enableLoopBackMode(g_asclinRegs, FALSE);
IfxAsclin_setShiftDirection(g_asclinRegs,
IfxAsclin_ShiftDirection_lsbFirst);
IfxAsclin_setRxFifoOutletWidth(g_asclinRegs, IfxAsclin_RxFifoOutletWidth_1);
IfxAsclin_setRxFifoInterruptLevel(g_asclinRegs,
IfxAsclin_RxFifoInterruptLevel_1);
IfxAsclin_setRxFifoInterruptMode(g_asclinRegs,
IfxAsclin_FifoInterruptMode_combined);
IfxAsclin_setTxFifoInletWidth(g_asclinRegs, IfxAsclin_TxFifoInletWidth_1);
IfxAsclin_setTxFifoInterruptLevel(g_asclinRegs,
IfxAsclin_TxFifoInterruptLevel_15);
IfxAsclin_setTxFifoInterruptMode(g_asclinRegs,
IfxAsclin_FifoInterruptMode_combined);
IfxAsclin_setFrameMode(g_asclinRegs, IfxAsclin_FrameMode_asc);
IfxAsclin_setClockSource(g_asclinRegs, IfxAsclin_ClockSource_ascFastClock);
IfxAsclin_disableAllFlags(g_asclinRegs);
IfxAsclin_clearAllFlags(g_asclinRegs);
IfxAsclin_enableRxFifoInlet(g_asclinRegs, TRUE);
IfxAsclin_enableTxFifoOutlet(g_asclinRegs, TRUE);
IfxAsclin_flushRxFifo(g_asclinRegs);
IfxAsclin_flushTxFifo(g_asclinRegs);
return 0; /* Success */
}
static int uartTx(const uint8_t c)
{
/* Write the data value to the ASCLIN peripheral's transmit FIFO. */
/* Note: IfxAsclin_write8 takes a pointer */
uint8 data_to_send = c;
IfxAsclin_write8(g_asclinRegs, &data_to_send, 1);
/* Wait (poll) for the transmit FIFO to be empty */
/* TODO: Consider adding a timeout mechanism here if necessary */
while (IfxAsclin_getTxFifoFillLevel(g_asclinRegs) != 0) {
/* Busy wait */
}
return 0; /* Success */
}
void uart_write(const char* buf, unsigned int sz)
{
static char rambuf[512];
const static char lf = '\r';
unsigned int i;
if (sz > sizeof(rambuf)) {
sz = sizeof(rambuf);
}
memcpy(rambuf, buf, sz);
buf = rambuf;
for (i = 0; i < sz; i++) {
/* If newline character is detected, send carriage return first */
if (buf[i] == '\n') {
/* Send carriage return before newline */
if (uartTx(lf) != 0) {
/* Handle error if needed */
break;
}
}
/* Call uart_tx for each byte, which now polls until TX FIFO is empty */
if (uartTx((uint8_t)buf[i]) != 0) {
/* Handle error if needed */
break;
}
}
/* No final wait needed here, as uart_tx waits after each byte */
}
#endif /* DEBUG_UART */

View File

@ -43,7 +43,7 @@
#include "target.h"
#include "printf.h"
#ifdef WOLFBOOT_ELF_SCATTERED
#ifdef WOLFBOOT_ELF_FLASH_SCATTER
#include "elf.h"
#endif
@ -355,7 +355,7 @@ void do_boot(const uint32_t *app_offset)
main = (main_entry)((uint8_t*)pSymbolAddress + epc->entryoff);
main(main_argc, main_argv, NULL, NULL);
#elif defined (WOLFBOOT_ELF_SCATTERED)
#elif defined (WOLFBOOT_ELF_FLASH_SCATTER)
uint8_t *entry_point = (sim_ram_base + (unsigned long)app_offset);
printf("entry point: %p\n", entry_point);
printf("app offset: %p\n", app_offset);
@ -365,7 +365,7 @@ void do_boot(const uint32_t *app_offset)
/* TODO: call main ! */
/* main(main_argc, main_argv); */
wolfBoot_printf("Simulator for ELF_SCATTERED image not implemented yet. Exiting...\n");
wolfBoot_printf("Simulator for ELF_FLASH_SCATTER image not implemented yet. Exiting...\n");
exit(0);
#else
char *envp[1] = {NULL};

View File

@ -168,9 +168,7 @@ typedef struct elf64_program_header {
typedef int (*elf_mmu_map_cb)(uint64_t, uint64_t, uint32_t);
int elf_load_image_mmu(uint8_t *image, uintptr_t *entry, elf_mmu_map_cb mmu_cb);
int elf_load_image(uint8_t *image, uintptr_t *entry, int is_ext);
int elf_store_image_scattered(const unsigned char *image, unsigned long *entry_out, int ext_flash);
int elf_check_image_scattered(uint8_t part, unsigned long *entry_out);
int elf_hdr_size(const unsigned char *ehdr);
int64_t elf_hdr_pht_combined_size(const unsigned char* ehdr);
int elf_open(const unsigned char *ehdr, int *is_elf32);

View File

@ -838,6 +838,13 @@ int wolfBoot_set_partition_state(uint8_t part, uint8_t newst);
int wolfBoot_get_update_sector_flag(uint16_t sector, uint8_t *flag);
int wolfBoot_set_update_sector_flag(uint16_t sector, uint8_t newflag);
#ifdef WOLFBOOT_ELF_FLASH_SCATTER
/* Support for ELF scatter/gather format */
int wolfBoot_load_flash_image_elf(int part, unsigned long* entry_out,
int ext_flash);
int wolfBoot_check_flash_image_elf(uint8_t part, unsigned long* entry_out);
#endif
uint8_t* wolfBoot_peek_image(struct wolfBoot_image *img, uint32_t offset,
uint32_t* sz);

View File

@ -79,7 +79,6 @@ extern "C" {
#define HDR_SIGNATURE 0x20
#define HDR_POLICY_SIGNATURE 0x21
#define HDR_SECONDARY_SIGNATURE 0x22
#define HDR_ELF_SCATTERED_HASH 0x23
#define HDR_PADDING 0xFF
/* Auth Key types */

View File

@ -786,8 +786,8 @@ ifeq ($(ELF),1)
ifneq ($(DEBUG_ELF),)
CFLAGS+=-DDEBUG_ELF=$(DEBUG_ELF)
endif
ifeq ($(ELF_SCATTERED),1)
CFLAGS+=-D"WOLFBOOT_ELF_SCATTERED=1"
ifeq ($(ELF_FLASH_SCATTER),1)
CFLAGS+=-D"WOLFBOOT_ELF_FLASH_SCATTER=1"
endif
endif

170
src/elf.c
View File

@ -33,10 +33,6 @@
#include "hal/nxp_ppc.h"
#endif
#ifdef WOLFBOOT_ELF_SCATTERED
#include "image.h"
#endif
/* support for elf parsing debug printf */
#if defined(DEBUG) || defined(ELF_PARSER)
#if defined(DEBUG_ELF) && DEBUG_ELF == 0
@ -48,6 +44,12 @@
#endif
#ifdef WOLFBOOT_ELF_FLASH_SCATTER
static int check_scatter_format(const unsigned char* ehdr, int is_elf32);
#endif
#if defined(MMU) || defined (WOLFBOOT_FSP) || defined (ARCH_PPC)
/* Loader for elf32 or elf64 format program headers
* Returns the entry point function
@ -160,134 +162,66 @@ int elf_open(const unsigned char *ehdr, int *is_elf32)
}
wolfBoot_printf("ELF image found\n");
*is_elf32 = !!(ident[ELF_CLASS_OFF] == ELF_CLASS_32);
return 0;
#ifdef WOLFBOOT_ELF_FLASH_SCATTER
return check_scatter_format(ehdr, *is_elf32);
#else
return 0;
#endif
}
int elf_hdr_size(const unsigned char *ehdr)
#ifdef WOLFBOOT_ELF_FLASH_SCATTER
/* Opens an elf file, also checking that the file is formatted correctly for
* scattered loading. Returns 0 if the elf file is formatted correctly, -1
* otherwise. */
static int check_scatter_format(const unsigned char* ehdr, int is_elf32)
{
/* Check that the program header table immediately follows the elf header */
if (is_elf32) {
const elf32_header* elf32_hdr = (const elf32_header*)ehdr;
/* For 32-bit ELF, program header table should start at offset equal to
* sizeof(elf32_header) */
if (elf32_hdr->ph_offset != sizeof(elf32_header)) {
wolfBoot_printf("ELF32: Program header table not immediately after "
"ELF header\n");
return -1;
}
}
else {
const elf64_header* elf64_hdr = (const elf64_header*)ehdr;
/* For 64-bit ELF, program header table should start at offset equal to
* sizeof(elf64_header) */
if (elf64_hdr->ph_offset != sizeof(elf64_header)) {
wolfBoot_printf("ELF64: Program header table not immediately after "
"ELF header\n");
return -1;
}
}
return 0;
}
/* Returns the combined size of the elf header and program header table. This
* assumes the program header table immediately follows the elf header. */
int64_t elf_hdr_pht_combined_size(const unsigned char* ehdr)
{
int sz = 0;
int is_elf32;
if (elf_open(ehdr, &is_elf32) != 0)
return -1;
if (is_elf32) {
const elf32_header *elf32_hdr = (const elf32_header *)ehdr;
sz = sizeof(elf32_header);
const elf32_header* elf32_hdr = (const elf32_header*)ehdr;
sz = sizeof(elf32_header);
sz += elf32_hdr->ph_entry_count * sizeof(elf32_program_header);
} else {
const elf64_header *elf64_hdr = (const elf64_header *)ehdr;
sz = sizeof(elf64_header);
}
else {
const elf64_header* elf64_hdr = (const elf64_header*)ehdr;
sz = sizeof(elf64_header);
sz += elf64_hdr->ph_entry_count * sizeof(elf64_program_header);
}
return sz;
}
#if !defined(MMU) && !defined(WOLFBOOT_FSP) && !defined(ARCH_PPC) && defined (WOLFBOOT_ELF_SCATTERED)
#endif /* WOLFBOOT_ELF_FLASH_SCATTER */
#ifdef ARCH_SIM
# define BASE_OFF ARCH_FLASH_OFFSET
#else
# define BASE_OFF 0
#endif
int elf_store_image_scattered(const unsigned char *hdr, unsigned long *entry_out, int ext_flash) {
const unsigned char *image;
int is_elf32;
unsigned short entry_count;
unsigned long entry_off;
int i;
image = hdr + IMAGE_HEADER_SIZE;
if (elf_open(image, &is_elf32) != 0) {
return -1;
}
if (is_elf32) {
const elf32_header *eh;
const elf32_program_header *ph;
wolfBoot_printf("ELF image is 32 bit\n");
eh = (const elf32_header *)image;
entry_count = eh->ph_entry_count;
entry_off = eh->ph_offset;
*entry_out = (unsigned long)eh->entry;
ph = (const elf32_program_header *)(image + entry_off);
for (i = 0; i < entry_count; ++i) {
unsigned long paddr;
unsigned long filesz;
unsigned long offset;
if (ph[i].type != ELF_PT_LOAD)
continue;
paddr = (unsigned long)ph[i].paddr;
offset = (unsigned long)ph[i].offset;
filesz = (unsigned long)ph[i].file_size;
wolfBoot_printf("Writing section at address %lx offset %lx\n", paddr, offset);
#ifdef EXT_FLASH
if (ext_flash) {
ext_flash_unlock();
ext_flash_erase(paddr + BASE_OFF, filesz);
ext_flash_write(paddr + BASE_OFF, image + offset, filesz);
ext_flash_lock();
}
else
#endif
{
hal_flash_unlock();
hal_flash_erase(paddr + BASE_OFF, filesz);
hal_flash_write(paddr + BASE_OFF, image + offset, filesz);
hal_flash_lock();
}
}
} else { /* 64 bit ELF */
const elf64_header *eh;
const elf64_program_header *ph;
wolfBoot_printf("ELF image is 64 bit\n");
eh = (const elf64_header *)image;
entry_count = eh->ph_entry_count;
entry_off = eh->ph_offset;
*entry_out = (unsigned long)eh->entry;
ph = (const elf64_program_header *)(image + entry_off);
for (i = 0; i < entry_count; ++i) {
unsigned long paddr;
unsigned long filesz;
unsigned long offset;
if (ph[i].type != ELF_PT_LOAD)
continue;
paddr = (unsigned long)ph[i].paddr;
offset = (unsigned long)ph[i].offset;
filesz = (unsigned long)ph[i].file_size;
wolfBoot_printf("Writing section at address %lx offset %lx\n", paddr, offset);
#ifdef EXT_FLASH
if (ext_flash) {
ext_flash_unlock();
ext_flash_erase(paddr + BASE_OFF, filesz);
ext_flash_write(paddr + BASE_OFF, image + offset, filesz);
ext_flash_lock();
}
else
#endif
{
hal_flash_unlock();
hal_flash_erase(paddr + BASE_OFF, filesz);
hal_flash_write(paddr + BASE_OFF, image + offset, filesz);
hal_flash_lock();
}
}
}
return 0;
}
#endif /* !defined(MMU) && !defined(WOLFBOOT_FSP) && !defined(ARCH_PPC) && defined (WOLFBOOT_ELF_SCATTERED) */
int elf_load_image(uint8_t *image, uintptr_t *entry, int ext_flash)
{
#ifdef MMU
return elf_load_image_mmu(image, entry, NULL);
#else
return elf_store_image_scattered(image, (unsigned long *)entry, ext_flash);
#endif
}
#endif /* WOLFBOOT_ELF */

View File

@ -1327,283 +1327,548 @@ int wolfBoot_verify_integrity(struct wolfBoot_image *img)
return 0;
}
#ifdef WOLFBOOT_ELF_SCATTERED
#ifdef WOLFBOOT_ELF_FLASH_SCATTER
#include "elf.h"
#define PADDING_BLOCK_SIZE 64
#ifdef ARCH_SIM
#define BASE_OFF ARCH_FLASH_OFFSET
#else
#define BASE_OFF 0
#endif
int elf_check_image_scattered(uint8_t part, unsigned long *entry_out)
/* Maximum size of ELF header for any architecture */
typedef union {
elf32_header elf32;
elf64_header elf64;
} elfHeaderMaxBuf;
/*
* Copies an arbitrary amount of data between two flash memory locations
* (internal or external) using an intermediate RAM buffer.
*/
static int copy_flash_buffered(uintptr_t src_addr, uintptr_t dst_addr,
size_t total_size, int is_src_ext,
int is_dst_ext)
{
size_t bytes_copied = 0;
#ifndef BUFFER_DECLARED
#define BUFFER_DECLARED
static uint8_t buffer[FLASHBUFFER_SIZE];
#endif
#ifdef WOLFBOOT_FLASH_MULTI_SECTOR_ERASE
/* Mass erase destination flash in one go before writing */
#ifdef EXT_FLASH
if (is_dst_ext) {
ext_flash_unlock();
ext_flash_erase(dst_addr, total_size);
ext_flash_lock();
}
else
#endif
{
hal_flash_unlock();
hal_flash_erase(dst_addr, total_size);
hal_flash_lock();
}
#endif /* WOLFBOOT_FLASH_MULTI_SECTOR_ERASE */
/* Loop until all requested bytes are copied */
while (bytes_copied < total_size) {
/* Determine the size of the next chunk to copy */
size_t remaining_bytes = total_size - bytes_copied;
size_t chunk_size = (remaining_bytes > FLASHBUFFER_SIZE)
? FLASHBUFFER_SIZE
: remaining_bytes;
/* Read a chunk from the source flash into the RAM buffer */
#ifdef EXT_FLASH
if (is_src_ext) {
ext_flash_unlock();
ext_flash_read(src_addr + bytes_copied, buffer, chunk_size);
ext_flash_lock();
}
else
#endif
{
memcpy(buffer, (const void*)(src_addr + bytes_copied), chunk_size);
}
/* Write the chunk from the RAM buffer to the destination flash */
#ifdef EXT_FLASH
if (is_dst_ext) {
ext_flash_unlock();
#ifndef WOLFBOOT_FLASH_MULTI_SECTOR_ERASE
ext_flash_erase(dst_addr + bytes_copied, chunk_size);
#endif
ext_flash_write(dst_addr + bytes_copied, buffer, chunk_size);
ext_flash_lock();
}
else
#endif
{
hal_flash_unlock();
#ifndef WOLFBOOT_FLASH_MULTI_SECTOR_ERASE
hal_flash_erase(dst_addr + bytes_copied, chunk_size);
#endif
hal_flash_write(dst_addr + bytes_copied, buffer, chunk_size);
hal_flash_lock();
}
/* Update the count of bytes successfully copied */
bytes_copied += chunk_size;
}
/* All bytes copied successfully */
return 0;
}
/*
* Reads data from a given wolfBoot partition's firmware image, properly
* handling internal/external flash.
*/
static int read_flash_fwimage(struct wolfBoot_image* img, uint32_t offset,
void* buffer, uint32_t size)
{
if (img == NULL || buffer == NULL) {
return -1;
}
/* Prevent reading past the end of the image */
if ((uint64_t)offset + size > img->fw_size) {
wolfBoot_printf(
"ERROR: read_flash_fwimage attempt to read past fw_size! "
"Offset %lu, Size %u, TotalSize %lu\n",
(unsigned long)offset, size, (unsigned long)img->fw_size);
return -1;
}
#ifdef EXT_FLASH
if (PART_IS_EXT(img)) {
if (ext_flash_check_read((uintptr_t)img->fw_base + offset, buffer,
size) != 0) {
wolfBoot_printf(
"ERROR: ext_flash_check_read failed at offset %lu, size %u\n",
(unsigned long)offset, size);
return -1;
}
}
else
#endif
{
/* Internal flash: Direct memory access */
memcpy(buffer, (uint8_t*)img->fw_base + offset, size);
}
return 0;
}
/*
* Reads data from a raw flash address (no offset) into a RAM buffer,
* properly handling internal/external flash.
*/
static int read_flash_addr(void* src, void* buffer, uint32_t size, int src_ext)
{
if (src == NULL || buffer == NULL) {
return -1;
}
#ifdef EXT_FLASH
if (src_ext) {
if (ext_flash_check_read((uintptr_t)src, buffer, size) != 0) {
wolfBoot_printf(
"ERROR: ext_flash_check_read failed at address %p, size %u\n",
src, size);
return -1;
}
}
else
#endif
{
/* Internal flash: Direct memory access */
memcpy(buffer, src, size);
}
return 0;
}
/*
* Hashes a chunk of the firmware image one SHA block at a time, properly
* handling internal/external flash
*/
static int update_hash_flash_fwimg(wolfBoot_hash_t* ctx,
struct wolfBoot_image* img, uint32_t offset,
uint32_t size)
{
uint32_t current_offset = offset;
uint32_t remaining_size = size;
uint8_t read_buf[WOLFBOOT_SHA_BLOCK_SIZE]; /* Use local buffer */
while (remaining_size > 0) {
uint32_t read_size = (remaining_size > WOLFBOOT_SHA_BLOCK_SIZE)
? WOLFBOOT_SHA_BLOCK_SIZE
: remaining_size;
if (read_flash_fwimage(img, current_offset, read_buf, read_size) != 0) {
wolfBoot_printf("ERROR: Failed to read image data for hashing. "
"Offset: %lu, Size: %u\n",
(unsigned long)current_offset, read_size);
return -1;
}
update_hash(ctx, read_buf, read_size);
remaining_size -= read_size;
current_offset += read_size;
}
return 0;
}
/*
* Hashes a chunk of flash memory at a given absolute address, reading one
* SHA block at a time, properly handling internal/external flash
*/
static int update_hash_flash_addr(wolfBoot_hash_t* ctx, uintptr_t addr,
uint32_t size, int src_ext)
{
uint8_t buffer[WOLFBOOT_SHA_BLOCK_SIZE];
uint32_t remaining_size = size;
uintptr_t current_addr = addr;
while (remaining_size > 0) {
uint32_t read_size = (remaining_size > WOLFBOOT_SHA_BLOCK_SIZE)
? WOLFBOOT_SHA_BLOCK_SIZE
: remaining_size;
if (read_flash_addr((void*)current_addr, buffer, read_size, src_ext) !=
0) {
wolfBoot_printf(
"ERROR: Failed to read data from address %p, size %u\n",
(void*)current_addr, read_size);
return -1;
}
update_hash(ctx, buffer, read_size);
remaining_size -= read_size;
current_addr += read_size;
}
return 0;
}
int wolfBoot_check_flash_image_elf(uint8_t part, unsigned long* entry_out)
{
/* Open the partition containing the image */
int is_elf32;
struct wolfBoot_image boot;
uint8_t *elf_h, *p;
int elf_hdr_sz = 0;
int len;
int is_elf32;
unsigned short entry_count;
unsigned short entry_size;
unsigned long entry_off;
long final_offset = -1;
uint8_t calc_digest[WOLFBOOT_SHA_DIGEST_SIZE];
uint8_t *exp_digest;
int stored_sha_len;
int i;
uint8_t padding_block[PADDING_BLOCK_SIZE];
int entry_out_set = 0;
uint8_t * elf_h;
size_t elf_hdr_sz = 0;
uint32_t len;
uint16_t entry_count = 0;
size_t entry_off = 0;
size_t ph_size = 0;
size_t current_ph_offset = 0;
int64_t final_offset = -1;
uint8_t calc_digest[WOLFBOOT_SHA_DIGEST_SIZE];
uint8_t* exp_digest;
int32_t stored_sha_len;
int i;
int32_t entry_out_set = 0;
uint8_t elfHdrBuf[sizeof(elfHeaderMaxBuf)];
uint8_t ph_buf[sizeof(elf64_program_header)]; /* Buffer for current PH */
uint8_t ph_next_buf[sizeof(elf64_program_header)]; /* Buffer for next PH */
wolfBoot_hash_t ctx;
if (wolfBoot_open_image(&boot, part) < 0)
if (wolfBoot_open_image(&boot, part) < 0) {
return -1;
p = get_img_hdr(&boot);
}
/* Initialize hash, feed the manifest header to it */
if (header_hash(&ctx, &boot) < 0)
if (header_hash(&ctx, &boot) < 0) {
return -1;
}
stored_sha_len = get_header(&boot, HDR_HASH, &exp_digest);
if (stored_sha_len != WOLFBOOT_SHA_DIGEST_SIZE)
if (stored_sha_len != WOLFBOOT_SHA_DIGEST_SIZE) {
return -1;
/* Get the elf header size */
elf_h = p + IMAGE_HEADER_SIZE;
if (elf_open(elf_h, &is_elf32) < 0)
return -1;
elf_hdr_sz = elf_hdr_size(elf_h);
wolfBoot_printf("Elf header size: %d\n", elf_hdr_sz);
memset(padding_block, 0, PADDING_BLOCK_SIZE);
/* Feed the elf header to the hash function */
len = elf_hdr_sz;
p = elf_h;
while (len > 0) {
if (len > WOLFBOOT_SHA_BLOCK_SIZE) {
update_hash(&ctx, p, WOLFBOOT_SHA_BLOCK_SIZE);
len -= WOLFBOOT_SHA_BLOCK_SIZE;
} else {
update_hash(&ctx, p, len);
break;
}
p += WOLFBOOT_SHA_BLOCK_SIZE;
}
wolfBoot_printf("Hashed ELF header.\n");
/* Feed the program headers to the hash function */
/* Get the elf header from the image into a local buffer. We may overread
* the buffer depending on architecture */
memset(elfHdrBuf, 0, sizeof(elfHdrBuf));
read_flash_fwimage(&boot, 0, elfHdrBuf, sizeof(elfHeaderMaxBuf));
elf_h = elfHdrBuf;
if (elf_open(elf_h, &is_elf32) < 0) {
return -1;
}
/* Set up common variables based on ELF type */
if (is_elf32) {
elf32_header *eh = (elf32_header *)elf_h;
elf32_program_header *ph;
entry_count = eh->ph_entry_count;
entry_size = eh->ph_entry_size;
entry_off = eh->ph_offset;
elf32_header* eh = (elf32_header*)elf_h;
entry_count = eh->ph_entry_count;
entry_off = eh->ph_offset;
ph_size = sizeof(elf32_program_header);
if (!entry_out_set) {
*entry_out = eh->entry;
*entry_out = eh->entry;
entry_out_set = 1;
}
wolfBoot_printf("EH entry offset: %d\n", (int)entry_off);
ph = (elf32_program_header *)(elf_h + entry_off);
/* Add padding until the first program header into hash function */
len = ph[0].offset - elf_hdr_sz;
wolfBoot_printf("Adding %d bytes padding\n", (int)len);
while (len > 0) {
if (len > PADDING_BLOCK_SIZE) {
update_hash(&ctx, padding_block, PADDING_BLOCK_SIZE);
len -= PADDING_BLOCK_SIZE;
} else {
update_hash(&ctx, padding_block, len);
break;
}
}
for (i = 0; i < entry_count; i++) {
unsigned long paddr;
unsigned long filesz;
unsigned long offset;
paddr = (unsigned long)ph[i].paddr;
offset = (unsigned long)ph[i].offset;
filesz = (unsigned long)ph[i].file_size;
wolfBoot_printf("Paddr: 0x%lx offset: %lu, size: %lu\n", paddr,
offset, filesz);
/* Feed any non-loaded parts to the hash function */
if (ph[i].type != ELF_PT_LOAD) {
len = filesz;
//wolfBoot_printf("Feeding ghost segment, len %d\n", len);
continue;
while (len > 0) {
if (len > WOLFBOOT_SHA_BLOCK_SIZE) {
update_hash(&ctx, elf_h + offset, WOLFBOOT_SHA_BLOCK_SIZE);
len -= WOLFBOOT_SHA_BLOCK_SIZE;
paddr += WOLFBOOT_SHA_BLOCK_SIZE;
} else {
update_hash(&ctx, elf_h + offset, len);
break;
}
}
} else {
/* Feed the loaded parts to the hash function */
len = filesz;
wolfBoot_printf("Feeding stored segment, len %d\n", len);
while (len > 0) {
if (len > WOLFBOOT_SHA_BLOCK_SIZE) {
update_hash(&ctx, (void *)(paddr + BASE_OFF),
WOLFBOOT_SHA_BLOCK_SIZE);
len -= WOLFBOOT_SHA_BLOCK_SIZE;
paddr += WOLFBOOT_SHA_BLOCK_SIZE;
} else {
update_hash(&ctx, (void *)(paddr + BASE_OFF),
len);
break;
}
}
}
/* Add padding until next program header, if any. */
if ((i < entry_count - 1) && (ph[i+1].offset > (offset + filesz))) {
unsigned long padding = ph[i+1].offset - (offset + filesz);
wolfBoot_printf("Adding padding: %lu (from %lx to %lx)\n", padding, (unsigned long)offset + filesz, (unsigned long)ph[i+1].offset);
while (padding > 0) {
if (padding > PADDING_BLOCK_SIZE) {
update_hash(&ctx, padding_block, PADDING_BLOCK_SIZE);
padding -= PADDING_BLOCK_SIZE;
} else {
update_hash(&ctx, padding_block, padding);
break;
}
}
} else {
final_offset = offset + filesz;
}
}
} else { /* 64-bit ELF */
elf64_header *eh = (elf64_header *)elf_h;
elf64_program_header *ph;
entry_count = eh->ph_entry_count;
entry_size = eh->ph_entry_size;
entry_off = eh->ph_offset;
wolfBoot_printf("ELF: [CHECK] 32-bit, entry=0x%08X, "
"ph_offset=0x%lX, ph_count=%u\n",
eh->entry, (unsigned long)entry_off, entry_count);
}
else { /* 64-bit ELF */
elf64_header* eh = (elf64_header*)elf_h;
entry_count = eh->ph_entry_count;
entry_off = eh->ph_offset;
ph_size = sizeof(elf64_program_header);
if (!entry_out_set) {
*entry_out = eh->entry;
*entry_out = eh->entry;
entry_out_set = 1;
}
wolfBoot_printf("ELF: [CHECK] 64-bit, entry=0x%08lx, "
"ph_offset=0x%08lx, ph_count=%d\n",
eh->entry, (unsigned long)entry_off, entry_count);
}
wolfBoot_printf("EH entry offset: %d\n", (int)entry_off);
ph = (elf64_program_header *)(elf_h + entry_off);
/* Add padding until the first program header into hash function */
len = ph[0].offset - elf_hdr_sz;
wolfBoot_printf("Adding %d bytes padding\n", len);
while (len > 0) {
if (len > PADDING_BLOCK_SIZE) {
update_hash(&ctx, padding_block, PADDING_BLOCK_SIZE);
len -= PADDING_BLOCK_SIZE;
} else {
update_hash(&ctx, padding_block, len);
break;
}
elf_hdr_sz = (size_t)elf_hdr_pht_combined_size(elf_h);
wolfBoot_printf("ELF: [CHECK] Header size: %zu bytes\n", elf_hdr_sz);
/* Hash the elf header and program header in the image, assuming the PHT
* immediately follows the ELF header */
update_hash_flash_fwimg(&ctx, &boot, 0, elf_hdr_sz);
current_ph_offset = entry_off;
/* Calculate padding between ELF+PHT header and first segment */
if (entry_count > 0) {
uint64_t first_offset;
read_flash_fwimage(&boot, current_ph_offset, ph_buf, ph_size);
if (is_elf32) {
first_offset = ((elf32_program_header*)ph_buf)->offset;
}
else {
first_offset = ((elf64_program_header*)ph_buf)->offset;
}
for (i = 0; i < entry_count; i++) {
unsigned long paddr;
unsigned long filesz;
unsigned long offset;
paddr = (unsigned long)ph[i].paddr;
offset = (unsigned long)ph[i].offset;
filesz = (unsigned long)ph[i].file_size;
wolfBoot_printf("Paddr: 0x%lx offset: %lu, size: %lu\n", paddr,
offset, filesz);
/* Feed any non-loaded parts to the hash function */
if (ph[i].type != ELF_PT_LOAD) {
len = filesz;
//wolfBoot_printf("Feeding ghost segment, len %d\n", len);
continue;
while (len > 0) {
if (len > WOLFBOOT_SHA_BLOCK_SIZE) {
update_hash(&ctx, elf_h + offset, WOLFBOOT_SHA_BLOCK_SIZE);
len -= WOLFBOOT_SHA_BLOCK_SIZE;
paddr += WOLFBOOT_SHA_BLOCK_SIZE;
} else {
update_hash(&ctx, elf_h + offset, len);
break;
}
}
} else {
/* Feed the loaded parts to the hash function */
len = filesz;
wolfBoot_printf("Feeding stored segment, len %d\n", len);
while (len > 0) {
if (len > WOLFBOOT_SHA_BLOCK_SIZE) {
update_hash(&ctx, (void *)(paddr + BASE_OFF),
WOLFBOOT_SHA_BLOCK_SIZE);
len -= WOLFBOOT_SHA_BLOCK_SIZE;
paddr += WOLFBOOT_SHA_BLOCK_SIZE;
} else {
update_hash(&ctx, (void *)(paddr + BASE_OFF),
len);
break;
}
}
}
/* Add padding until next program header, if any. */
if ((i < entry_count - 1) && (ph[i+1].offset > (offset + filesz))) {
unsigned long padding = ph[i+1].offset - (offset + filesz);
wolfBoot_printf("Adding padding: %lu\n", padding);
while (padding > 0) {
if (padding > PADDING_BLOCK_SIZE) {
update_hash(&ctx, padding_block, PADDING_BLOCK_SIZE);
padding -= PADDING_BLOCK_SIZE;
} else {
update_hash(&ctx, padding_block, padding);
break;
}
}
} else {
final_offset = offset + filesz;
}
if (first_offset > elf_hdr_sz) {
len = first_offset - elf_hdr_sz;
wolfBoot_printf(
"ELF: [CHECK] Adding %d bytes padding before first segment\n",
(int32_t)len);
update_hash_flash_fwimg(&ctx, &boot, elf_hdr_sz, len); /* Hash actual file content */
}
}
if (final_offset < 0)
return -1;
if (final_offset + IMAGE_HEADER_SIZE > (long)boot.fw_size)
return -1;
/* Walk the program header table and hash each loadable segment. */
for (i = 0; i < entry_count; i++) {
uint64_t paddr;
uint64_t filesz;
uint64_t offset;
uint32_t type;
uint64_t next_offset = 0; /* Initialize */
/* read the current program header into a local buffer */
read_flash_fwimage(&boot, current_ph_offset, ph_buf, ph_size);
/* Extract common fields based on ELF type */
if (is_elf32) {
elf32_program_header* ph = (elf32_program_header*)ph_buf;
paddr = ph->paddr;
offset = ph->offset;
filesz = ph->file_size;
type = ph->type;
}
else { /* 64-bit */
elf64_program_header* ph = (elf64_program_header*)ph_buf;
paddr = ph->paddr;
offset = ph->offset;
filesz = ph->file_size;
type = ph->type;
}
/* Handle loadable segments */
if (type == ELF_PT_LOAD) {
uintptr_t load_addr = (uintptr_t)(paddr + BASE_OFF);
/* Feed the loadable parts to the hash function */
wolfBoot_printf("ELF: [CHECK] Hashing loadable segment: "
"paddr = 0x%08lx, loadaddr = 0x%08lx, "
"offset = 0x%08lx, size = %lu\n",
(unsigned long)paddr, (unsigned long)load_addr,
(unsigned long)offset, (unsigned long)filesz);
update_hash_flash_addr(&ctx, load_addr, (uint32_t)filesz,
PART_IS_EXT(&boot));
}
else {
wolfBoot_printf("ELF: [CHECK] ERROR: non-loadable segment\n");
return -1;
}
/* Add padding until next program header, if any. */
if (i < entry_count - 1) {
read_flash_fwimage(&boot, current_ph_offset + ph_size, ph_next_buf,
ph_size);
if (is_elf32) {
next_offset = ((elf32_program_header*)ph_next_buf)->offset;
}
else {
next_offset = ((elf64_program_header*)ph_next_buf)->offset;
}
if (next_offset > (offset + filesz)) {
uint32_t padding = next_offset - (offset + filesz);
wolfBoot_printf("ELF: [CHECK] Adding padding: %u bytes (from "
"0x%08lx to 0x%08lx)\n",
padding, (unsigned long)(offset + filesz),
(unsigned long)next_offset);
update_hash_flash_fwimg(&ctx, &boot, offset + filesz, padding); /* Hash actual file content */
}
}
final_offset =
offset + filesz; /* Track end offset of last processed segment */
current_ph_offset += ph_size;
} /* End of program header loop */
if (final_offset < 0 && entry_count > 0) {
/* Should have processed at least one segment if entry_count > 0 */
wolfBoot_printf("ELF: [CHECK] Error determining final offset\n");
return -1;
}
else if (final_offset < 0 && entry_count == 0) {
/* No program headers, hash only ELF header + PHT */
final_offset = elf_hdr_sz;
}
/* Check if final offset is valid */
if (final_offset > (int64_t)boot.fw_size) {
wolfBoot_printf("ELF: [CHECK] Final offset (%d) exceeds image size (%d)\n",
(int32_t)final_offset, (int32_t)boot.fw_size);
return -1;
}
/* Hash any trailing data after the last segment/header */
len = boot.fw_size - final_offset;
p = boot.hdr + IMAGE_HEADER_SIZE + final_offset;
p = get_img_hdr(&boot) + IMAGE_HEADER_SIZE + final_offset;
wolfBoot_printf("Appending %d bytes of data from image, from position %lu...(0x%p)\n", len, IMAGE_HEADER_SIZE + final_offset, p);
while (len > 0) {
if (len > WOLFBOOT_SHA_BLOCK_SIZE) {
update_hash(&ctx, p, WOLFBOOT_SHA_BLOCK_SIZE);
len -= WOLFBOOT_SHA_BLOCK_SIZE;
p += WOLFBOOT_SHA_BLOCK_SIZE;
} else {
update_hash(&ctx, p, len);
break;
}
if (len > 0) {
wolfBoot_printf("ELF: [CHECK] Hashing %u bytes of trailing data from "
"offset 0x%llX\n",
len, (unsigned long long)final_offset);
update_hash_flash_fwimg(&ctx, &boot, final_offset, len);
}
/* Finalize SHA calculation */
final_hash(&ctx, calc_digest);
if (memcmp(calc_digest, exp_digest, WOLFBOOT_SHA_DIGEST_SIZE) != 0) {
wolfBoot_printf("SHA failed for scattered ELF!\n");
wolfBoot_printf("Expected %02x%02x%02x%02x%02x%02x%02x%02x\n",
exp_digest[0], exp_digest[1], exp_digest[2], exp_digest[3],
exp_digest[4], exp_digest[5], exp_digest[6], exp_digest[7]);
wolfBoot_printf("Calculated %02x%02x%02x%02x%02x%02x%02x%02x\n",
calc_digest[0], calc_digest[1], calc_digest[2], calc_digest[3],
calc_digest[4], calc_digest[5], calc_digest[6], calc_digest[7]);
wolfBoot_printf("ELF: [CHECK] SHA verification FAILED\n");
wolfBoot_printf(
"ELF: [CHECK] Expected %02x%02x%02x%02x%02x%02x%02x%02x\n",
exp_digest[0], exp_digest[1], exp_digest[2], exp_digest[3],
exp_digest[4], exp_digest[5], exp_digest[6], exp_digest[7]);
wolfBoot_printf(
"ELF: [CHECK] Calculated %02x%02x%02x%02x%02x%02x%02x%02x\n",
calc_digest[0], calc_digest[1], calc_digest[2], calc_digest[3],
calc_digest[4], calc_digest[5], calc_digest[6], calc_digest[7]);
return -2;
}
wolfBoot_printf("Scattered ELF verified.\n");
wolfBoot_printf("ELF: [CHECK] Verification successful\n");
return 0;
}
int wolfBoot_load_flash_image_elf(int part, unsigned long* entry_out, int ext_flash)
{
const unsigned char* image;
int is_elf32;
uint16_t entry_count;
size_t entry_off;
size_t ph_size;
int i;
const void* eh;
struct wolfBoot_image boot;
uint8_t elfHdrBuf[sizeof(elfHeaderMaxBuf)];
if (wolfBoot_open_image(&boot, part) < 0) {
return -1;
}
image = boot.fw_base;
/* Get the elf header from the image into a local buffer. We may overread
* the buffer depending on architecture */
memset(elfHdrBuf, 0, sizeof(elfHdrBuf));
read_flash_fwimage(&boot, 0, elfHdrBuf, sizeof(elfHeaderMaxBuf));
if (elf_open(elfHdrBuf, &is_elf32) != 0) {
return -1;
}
/* Set up header pointers based on ELF type */
if (is_elf32) {
eh = (const elf32_header*)elfHdrBuf;
entry_count = ((const elf32_header*)eh)->ph_entry_count;
entry_off = ((const elf32_header*)eh)->ph_offset;
*entry_out = (unsigned long)((const elf32_header*)eh)->entry;
wolfBoot_printf("ELF: [STORE] 32-bit, entry=0x%08lx, "
"ph_offset=0x%08lx, ph_count=%d\n",
(unsigned long)((const elf32_header*)eh)->entry,
(unsigned long)entry_off, entry_count);
}
else {
eh = (const elf64_header*)elfHdrBuf;
entry_count = ((const elf64_header*)eh)->ph_entry_count;
entry_off = ((const elf64_header*)eh)->ph_offset;
*entry_out = (unsigned long)((const elf64_header*)eh)->entry;
wolfBoot_printf("ELF: [STORE] 64-bit, entry=0x%08lx, "
"ph_offset=0x%08lx, ph_count=%d\n",
(unsigned long)((const elf64_header*)eh)->entry,
(unsigned long)entry_off, entry_count);
}
/* Walk the program header table and store each loadable segment */
for (i = 0; i < entry_count; ++i) {
unsigned long paddr, filesz, offset;
int is_loadable;
uintptr_t load_addr;
/* Read the current program header into a local buffer */
if (is_elf32) {
elf32_program_header p32;
read_flash_fwimage(&boot, entry_off, &p32, sizeof(p32));
is_loadable = (p32.type == ELF_PT_LOAD);
paddr = (unsigned long)p32.paddr;
offset = (unsigned long)p32.offset;
filesz = (unsigned long)p32.file_size;
ph_size = sizeof(p32);
}
else {
elf64_program_header p64;
read_flash_fwimage(&boot, entry_off, &p64, sizeof(p64));
is_loadable = (p64.type == ELF_PT_LOAD);
paddr = (unsigned long)p64.paddr;
offset = (unsigned long)p64.offset;
filesz = (unsigned long)p64.file_size;
ph_size = sizeof(p64);
}
/* Skip non-loadable segments */
if (!is_loadable) {
wolfBoot_printf("ELF: [STORE] ERROR: non-loadable segment\n");
return -1;
}
load_addr = (uintptr_t)(paddr + BASE_OFF);
wolfBoot_printf("ELF: [STORE] Writing loadable segment: "
"loadaddr=0x%08lx, offset=0x%08lx, size=%lu\n",
load_addr, offset, filesz);
copy_flash_buffered((uintptr_t)(image + offset), load_addr, filesz,
ext_flash, ext_flash);
entry_off += ph_size;
}
wolfBoot_printf("ELF: [STORE] Image loading complete\n");
return 0;
}
#undef BASE_OFF
#endif

View File

@ -392,6 +392,7 @@ void uart_vprintf(const char* fmt, va_list argp)
uart_write("0x", 2);
/* fall through */
case 'x':
case 'X':
{
int n = (int)va_arg(argp, int);
uart_writenum(n, 16, zeropad, maxdigits);

View File

@ -38,11 +38,6 @@
int WP11_Library_Init(void);
#endif
/* Support for ELF scatter/gather format */
#ifdef WOLFBOOT_ELF_SCATTERED
#include "elf.h"
#endif
#ifdef RAM_CODE
#ifndef TARGET_rp2350
extern unsigned int _start_text;
@ -223,13 +218,17 @@ static int RAMFUNCTION wolfBoot_copy_sector(struct wolfBoot_image *src,
*
* This function handles the final phase of the three-way swap update process.
* It ensures that the update is atomic and power-fail safe by:
* 1. Saving the last sector of the boot partition to the swap area
* 1. Saving the sector at tmpBootPos (staging sector) to the swap area
* 2. Setting a magic trailer value to mark the swap as in progress
* 3. Erasing the last sector(s) of the boot partition
* 4. Restoring the saved sector from swap back to boot
* 3. Erasing the last sector(s) of the boot partition (where partition state is stored)
* 4. Restoring the saved staging sector from swap back to boot
* 5. Setting the boot partition state to TESTING
* 6. Erasing the last sector(s) of the update partition
*
* The staging sector (tmpBootPos) is positioned right before the final sectors
* that will be erased. This sector is preserved and used to store a magic trailer
* that indicates a swap operation is in progress.
*
* The function can be called in two modes:
* - Normal mode (resume=0): Initiates the swap and erase process
* - Resume mode (resume=1): Checks if a swap was interrupted and completes it
@ -250,6 +249,8 @@ static int wolfBoot_swap_and_final_erase(int resume)
#endif
);
int swapDone = 0;
/* Calculate position of staging sector - just before the final sectors
* that store partition state */
uintptr_t tmpBootPos = WOLFBOOT_PARTITION_SIZE - eraseLen -
WOLFBOOT_SECTOR_SIZE;
uint32_t tmpBuffer[TRAILER_OFFSET_WORDS + 1];
@ -260,7 +261,8 @@ static int wolfBoot_swap_and_final_erase(int resume)
wolfBoot_open_image(swap, PART_SWAP);
wolfBoot_get_partition_state(PART_UPDATE, &updateState);
/* read trailer */
/* Read the trailer from the staging sector to check if we're resuming an
* interrupted operation */
#if defined(EXT_FLASH) && PARTN_IS_EXT(PART_BOOT)
ext_flash_read((uintptr_t)(boot->hdr + tmpBootPos), (void*)tmpBuffer,
sizeof(tmpBuffer));
@ -268,12 +270,13 @@ static int wolfBoot_swap_and_final_erase(int resume)
memcpy(tmpBuffer, boot->hdr + tmpBootPos, sizeof(tmpBuffer));
#endif
/* check for trailing magic (BOOT) */
/* Check if the magic trailer exists - indicates an interrupted swap
* operation */
/* final swap and erase flag is WOLFBOOT_MAGIC_TRAIL */
if (tmpBuffer[TRAILER_OFFSET_WORDS] == WOLFBOOT_MAGIC_TRAIL) {
swapDone = 1;
}
/* if resuming, quit if swap isn't done */
/* If we're in resume mode but no swap was in progress, return */
if ((resume == 1) && (swapDone == 0) &&
(updateState != IMG_STATE_FINAL_FLAGS)
) {
@ -285,35 +288,38 @@ static int wolfBoot_swap_and_final_erase(int resume)
ext_flash_unlock();
#endif
/* If update state isn't set to FINAL_FLAGS, this is the first run of the function */
/* IMG_STATE_FINAL_FLAGS allows re-entry without blowing away swap */
if (updateState != IMG_STATE_FINAL_FLAGS) {
/* store the sector at tmpBootPos into swap */
/* First, backup the staging sector (sector at tmpBootPos) into swap partition */
/* This sector will be modified with the magic trailer, so we need to preserve it */
wolfBoot_copy_sector(boot, swap, tmpBootPos / WOLFBOOT_SECTOR_SIZE);
/* set FINAL_SWAP for re-entry */
/* Mark update as being in final swap phase to allow resumption if power fails */
wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_FINAL_FLAGS);
}
#ifdef EXT_ENCRYPTED
if (swapDone == 0) {
/* get encryption key and iv if encryption is enabled */
/* For encrypted images: Get the encryption key and IV */
wolfBoot_get_encrypt_key((uint8_t*)tmpBuffer,
(uint8_t*)&tmpBuffer[ENCRYPT_KEY_SIZE/sizeof(uint32_t)]);
/* write TRAIL, encryption key and iv if enabled to tmpBootPos*/
/* Set the magic trailer in the buffer and write it to the staging sector */
tmpBuffer[TRAILER_OFFSET_WORDS] = WOLFBOOT_MAGIC_TRAIL;
wb_flash_erase(boot, tmpBootPos, WOLFBOOT_SECTOR_SIZE);
wb_flash_write(boot, tmpBootPos, (void*)tmpBuffer, sizeof(tmpBuffer));
}
#endif
/* erase the last boot sector(s) */
/* Erase the last sector(s) of boot partition (where partition state is stored) */
wb_flash_erase(boot, WOLFBOOT_PARTITION_SIZE - eraseLen, eraseLen);
/* set the encryption key */
#ifdef EXT_ENCRYPTED
/* Initialize encryption with the saved key */
wolfBoot_set_encrypt_key((uint8_t*)tmpBuffer,
(uint8_t*)&tmpBuffer[ENCRYPT_KEY_SIZE/sizeof(uint32_t)]);
/* wolfBoot_set_encrypt_key calls hal_flash_unlock, need to unlock again */
hal_flash_unlock();
#endif
/* write the original contents of tmpBootPos back */
/* Restore the original contents of the staging sector (with the magic trailer if encrypted) */
if (tmpBootPos < boot->fw_size + IMAGE_HEADER_SIZE) {
wolfBoot_copy_sector(swap, boot, tmpBootPos / WOLFBOOT_SECTOR_SIZE);
}
@ -321,10 +327,11 @@ static int wolfBoot_swap_and_final_erase(int resume)
wb_flash_erase(boot, tmpBootPos, WOLFBOOT_SECTOR_SIZE);
}
/* mark boot as TESTING */
/* Mark boot partition as TESTING - this tells bootloader to fallback if update fails */
wolfBoot_set_partition_state(PART_BOOT, IMG_STATE_TESTING);
/* erase the last sector(s) of update. This resets the update partition state
* to IMG_STATE_NEW */
/* Erase the last sector(s) of update partition */
/* This resets the update partition state to IMG_STATE_NEW */
wb_flash_erase(update, WOLFBOOT_PARTITION_SIZE - eraseLen, eraseLen);
#ifdef EXT_FLASH
@ -815,31 +822,35 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
wolfBoot_swap_and_final_erase(0);
#else /* DISABLE_BACKUP */
#ifdef WOLFBOOT_ELF_SCATTERED
#ifdef WOLFBOOT_ELF_FLASH_SCATTER
unsigned long entry;
void *base = (void *)WOLFBOOT_PARTITION_BOOT_ADDRESS;
void* base = (void*)WOLFBOOT_PARTITION_BOOT_ADDRESS;
wolfBoot_printf("ELF Scattered image digest check\n");
if (elf_check_image_scattered(PART_BOOT, &entry) < 0) {
wolfBoot_printf("ELF Scattered image digest check: failed. Restoring scattered image...\n");
elf_store_image_scattered(base, &entry, PART_IS_EXT(boot));
if (elf_check_image_scattered(PART_BOOT, &entry) < 0) {
wolfBoot_printf("Fatal: Could not verify digest after scattering. Panic().\n");
if (wolfBoot_check_flash_image_elf(PART_BOOT, &entry) < 0) {
wolfBoot_printf("ELF Scattered image digest check: failed. Restoring "
"scattered image...\n");
wolfBoot_load_flash_image_elf(PART_BOOT, &entry, PART_IS_EXT(boot));
if (wolfBoot_check_flash_image_elf(PART_BOOT, &entry) < 0) {
wolfBoot_printf(
"Fatal: Could not verify digest after scattering. Panic().\n");
wolfBoot_panic();
}
}
wolfBoot_printf("Scattered image correctly verified. Setting entry point to %lx\n", entry);
boot.fw_base = (void *)entry;
wolfBoot_printf(
"Scattered image correctly verified. Setting entry point to %lx\n",
entry);
boot.fw_base = (void*)entry;
#endif
/* Direct Swap without power fail safety */
hal_flash_unlock();
#ifdef EXT_FLASH
#ifdef EXT_FLASH
ext_flash_unlock();
#endif
#endif
#ifdef EXT_ENCRYPTED
#ifdef EXT_ENCRYPTED
wolfBoot_get_encrypt_key(key, nonce);
#endif
#endif
/* Directly copy the content of the UPDATE partition into the BOOT
* partition. */
@ -1079,20 +1090,28 @@ void RAMFUNCTION wolfBoot_start(void)
}
PART_SANITY_CHECK(&boot);
#ifdef WOLFBOOT_ELF_SCATTERED
#ifdef WOLFBOOT_ELF_FLASH_SCATTER
unsigned long entry;
void *base = (void *)WOLFBOOT_PARTITION_BOOT_ADDRESS;
wolfBoot_printf("ELF Scattered image digest check\n");
if (elf_check_image_scattered(PART_BOOT, &entry) < 0) {
wolfBoot_printf("ELF Scattered image digest check: failed. Restoring scattered image...\n");
elf_store_image_scattered(base, &entry, PART_IS_EXT(boot));
if (elf_check_image_scattered(PART_BOOT, &entry) < 0) {
wolfBoot_printf("Fatal: Could not verify digest after scattering. Panic().\n");
if (wolfBoot_check_flash_image_elf(PART_BOOT, &entry) < 0) {
wolfBoot_printf("ELF Scattered image digest check: failed. Restoring "
"scattered image...\n");
if (wolfBoot_load_flash_image_elf(PART_BOOT, &entry,
PART_IS_EXT(&boot)) < 0) {
wolfBoot_printf(
"ELF: [BOOT] ERROR: could not store scattered image\n");
wolfBoot_panic();
}
if (wolfBoot_check_flash_image_elf(PART_BOOT, &entry) < 0) {
wolfBoot_printf(
"Fatal: Could not verify digest after scattering. Panic().\n");
wolfBoot_panic();
}
}
wolfBoot_printf("Scattered image correctly verified. Setting entry point to %lx\n", entry);
boot.fw_base = (void *)entry;
wolfBoot_printf(
"Scattered image correctly verified. Setting entry point to %lx\n",
entry);
boot.fw_base = (void*)entry;
#endif

View File

@ -238,10 +238,10 @@ ifeq ($(TARGET),sim)
# Override linker flags
LDFLAGS+=-Wl,-Map=image.map
endif
ifeq ($(ELF_SCATTERED),1)
ifeq ($(ELF_FLASH_SCATTER),1)
LSCRIPT_TEMPLATE=sim_scattered.ld
APP_OBJS=app_sim_scattered.o ../src/string.o
CFLAGS+=-D"WOLFBOOT_ELF_SCATTERED=1" -nostartfiles -ffreestanding -static -nostdlib
CFLAGS+=-D"WOLFBOOT_ELF_FLASH_SCATTER=1" -nostartfiles -ffreestanding -static -nostdlib
LDFLAGS+=-ffreestanding -nostartfiles -static -T$(LSCRIPT) -nostdlib
else
APP_OBJS=app_sim.o
@ -478,10 +478,22 @@ image.srec: image.elf
@echo "\t[SREC] $@"
$(Q)$(OBJCOPY) $(OBJCOPY_FLAGS) -O srec $^ $@
image.elf: $(APP_OBJS) $(LSCRIPT)
ifeq ($(ELF_FLASH_SCATTER),1)
# When ELF_FLASH_SCATTER=1, preprocess the ELF file with the squashelf tool
SQUASHELF_TOOL = ../tools/squashelf/squashelf
image-orig.elf: $(APP_OBJS) $(LSCRIPT)
@echo "\t[LD] $@"
$(Q)$(LD) $(LDFLAGS) $(APP_OBJS) $(OUTPUT_FLAG) $@
image.elf: image-orig.elf
@echo "\t[SQUASHELF] $@"
$(Q)$(SQUASHELF_TOOL) -v --nosht $< $@
else
# Default behavior when ELF_FLASH_SCATTER is not set
image.elf: $(APP_OBJS) $(LSCRIPT)
@echo "\t[LD] $@"
$(Q)$(LD) $(LDFLAGS) $(APP_OBJS) $(OUTPUT_FLAG) $@
endif
standalone: image.bin

View File

@ -30,14 +30,22 @@ NVM_CONFIG="$WOLFBOOT_DIR/tools/scripts/tc3xx/wolfBoot-wolfHSM-keys.nvminit"
NVM_BIN="whNvmImage.bin"
NVM_HEX="whNvmImage.hex"
# Tool paths (relative to project root)
SQUASHELF="$WOLFBOOT_DIR/tools/squashelf/squashelf"
WHNVMTOOL="$WOLFBOOT_DIR/lib/wolfHSM/tools/whnvmtool/whnvmtool"
# Default algorithm configuration
DEFAULT_SIGN_ALGO="ecc256"
DEFAULT_HASH_ALGO="sha256"
# Default values
HSM=""
ELF=""
OPERATIONS=()
# Important Constants
PFLASH1_RANGE="0xA0300000-0xA0500000"
# Structure to hold command options
declare -A KEYGEN_OPTS=(
[sign_algo]="$DEFAULT_SIGN_ALGO"
@ -47,10 +55,12 @@ declare -A SIGN_OPTS=(
[sign_algo]="$DEFAULT_SIGN_ALGO"
[hash_algo]="$DEFAULT_HASH_ALGO"
[build_type]="Release"
[file_ext]=".bin"
)
declare -A MACROS_OPTS=(
[sign_algo]=""
[hash_algo]=""
[use_elf_format]=""
)
CURRENT_OPTS=""
@ -112,6 +122,12 @@ declare -A COMMON_OPTS=(
# Add LCF_OPTS to command options structure
declare -A LCF_OPTS=(
[sign_algo]="$DEFAULT_SIGN_ALGO"
[use_elf_format]=""
)
# Add TARGET_OPTS to command options structure
declare -A TARGET_OPTS=(
[use_elf_format]=""
)
# Helper function to display usage
@ -120,6 +136,7 @@ usage() {
echo ""
echo "Global Options:"
echo " --hsm Use wolfHSM version"
echo " --elf Use ELF format for firmware images (affects target, macros, lcf, and sign commands)"
echo ""
echo "Commands and their options:"
echo " keygen"
@ -152,7 +169,7 @@ usage() {
echo " $0 sign --hash-algo sha256 --debug"
echo " $0 keygen --sign-algo ecc256 sign --hash-algo sha256"
echo " $0 --hsm keygen --sign-algo ecc256 --localkeys sign --debug"
echo " $0 target"
echo " $0 --elf target macros lcf sign"
echo " $0 clean"
echo " $0 macros"
echo " $0 nvm"
@ -188,7 +205,17 @@ do_sign() {
local sign_algo="${SIGN_OPTS[sign_algo]:-${KEYGEN_OPTS[sign_algo]}}"
local pq_params="${COMMON_OPTS[sign_pq_params]}"
local header_size
local bin_path="$base_path/$app_name/TriCore ${SIGN_OPTS[build_type]} (GCC)/$app_name.bin"
local bin_path="$base_path/$app_name/TriCore ${SIGN_OPTS[build_type]} (GCC)/$app_name${SIGN_OPTS[file_ext]}"
# If signing an elf file, first preprocess it with squashelf
if [[ "${SIGN_OPTS[file_ext]}" == ".elf" ]]; then
local temp_file="${bin_path}.squashed"
echo "Preprocessing ELF file with $SQUASHELF"
"$SQUASHELF" -v --nosht -r "$PFLASH1_RANGE" "$bin_path" "$temp_file"
echo "Replacing original ELF with squashed version"
cp "$temp_file" "$bin_path"
rm "$temp_file"
fi
# Get header size for current algorithm
header_size=$(get_header_size "$sign_algo" "$pq_params")
@ -209,10 +236,28 @@ do_gen_target() {
local target_h_template="${TARGET_H}.in"
local wolfboot_sector_size=0x4000
local wolfboot_partition_size=0x17E000
local wolfboot_partition_boot_address=0xA0300000
local wolfboot_partition_update_address=0xA047E000
local wolfboot_partition_swap_address=0xA05FC000
# Select partition values based on whether --elf option was specified
local wolfboot_partition_size
local wolfboot_partition_boot_address
local wolfboot_partition_update_address
local wolfboot_partition_swap_address
if [[ -n "${TARGET_OPTS[use_elf_format]}" ]]; then
# These addresses and values must match those defined in the test-app
# linker file
wolfboot_partition_size=0xC0000
wolfboot_partition_boot_address=0xA047C000
wolfboot_partition_update_address=0xA053C000
wolfboot_partition_swap_address=0xA05FC000
else
# These addresses and values must match those defined in the test-app
# linker file
wolfboot_partition_size=0x17C000
wolfboot_partition_boot_address=0xA0300000
wolfboot_partition_update_address=0xA047C000
wolfboot_partition_swap_address=0xA05FC000
fi
local wolfboot_dts_boot_address=""
local wolfboot_dts_update_address=""
@ -237,7 +282,7 @@ do_gen_target() {
do_clean() {
echo "Cleaning generated files"
local macros_out="$WOLFBOOT_DIR/IDE/AURIX/wolfBoot-tc3xx${HSM:+-wolfHSM}/wolfBoot_macros.txt"
rm -f "$PRVKEY_DER"
rm -f "$PUBKEY_DER"
rm -f "$TARGET_H"
@ -265,6 +310,8 @@ do_gen_macros() {
local image_signature_size=""
local ml_dsa_image_signature_size=""
local ml_dsa_level=""
local use_wolfboot_elf=""
local use_wolfboot_elf_flash_scattered=""
# Map algorithms to their macro names
local sign_macro="${SIGN_ALGO_MAP[${sign_algo,,}]:-}"
@ -289,6 +336,12 @@ do_gen_macros() {
use_wolfhsm_pubkey_id="-DWOLFBOOT_USE_WOLFHSM_PUBKEY_ID"
fi
# Set ELF format macros if --elf option was specified
if [[ -n "${MACROS_OPTS[use_elf_format]}" ]]; then
use_wolfboot_elf="-DWOLFBOOT_ELF"
use_wolfboot_elf_flash_scattered="-DWOLFBOOT_ELF_FLASH_SCATTER"
fi
# Quirk: set additional (redundant) macros for ML DSA based on pq_params
if [[ "${sign_algo,,}" == ml_dsa* ]]; then
local level="${pq_params:-2}" # Default to level 2 if not specified
@ -318,6 +371,8 @@ do_gen_macros() {
-e "s/@ML_DSA_IMAGE_SIGNATURE_SIZE@/$ml_dsa_image_signature_size/g" \
-e "s/@WOLFBOOT_HUGE_STACK@/$use_huge_stack/g" \
-e "s/@WOLFBOOT_USE_WOLFHSM_PUBKEY_ID@/$use_wolfhsm_pubkey_id/g" \
-e "s/@WOLFBOOT_ELF@/$use_wolfboot_elf/g" \
-e "s/@WOLFBOOT_ELF_FLASH_SCATTER@/$use_wolfboot_elf_flash_scattered/g" \
"$macros_in" > "$macros_out"
# Remove empty lines from the output file, as they cause compiler errors
@ -327,8 +382,8 @@ do_gen_macros() {
# Function to generate a wolfHSM NVM image
do_gen_nvm() {
echo "Generating HSM NVM image"
echo "Running: whnvmtool --image=$NVM_BIN --size=0x10000 --invert-erased-byte $NVM_CONFIG"
whnvmtool --image="$NVM_BIN" --size=0x10000 --invert-erased-byte "$NVM_CONFIG"
echo "Running: $WHNVMTOOL --image=$NVM_BIN --size=0x10000 --invert-erased-byte $NVM_CONFIG"
"$WHNVMTOOL" --image="$NVM_BIN" --size=0x10000 --invert-erased-byte "$NVM_CONFIG"
echo "Converting to Intel HEX format"
echo "Running: objcopy -I binary -O ihex --change-address 0xAFC00000 $NVM_BIN $NVM_HEX"
@ -345,7 +400,16 @@ do_gen_lcf() {
# Determine target directory based on HSM flag
local base_dir="$WOLFBOOT_DIR/IDE/AURIX"
local app_dir="test-app${HSM:+-wolfHSM}"
local lcf_template="$base_dir/$app_dir/Lcf_Gnuc_Tricore_Tc.lsl.in"
# Select template file based on whether --elf option was specified
local template_name
if [[ -n "${LCF_OPTS[use_elf_format]}" ]]; then
template_name="Lcf_Gnuc_Tricore_elf.lsl.in"
else
template_name="Lcf_Gnuc_Tricore_Tc.lsl.in"
fi
local lcf_template="$base_dir/$app_dir/$template_name"
local lcf_output="$base_dir/$app_dir/Lcf_Gnuc_Tricore_Tc.lsl"
header_size=$(get_header_size "$sign_algo" "$pq_params")
@ -363,6 +427,15 @@ while [[ $# -gt 0 ]]; do
KEYGEN_OPTS[nolocalkeys]="1"
shift
;;
--elf)
ELF="1"
# Set ELF mode for relevant command options
SIGN_OPTS[file_ext]=".elf"
LCF_OPTS[use_elf_format]="1"
TARGET_OPTS[use_elf_format]="1"
MACROS_OPTS[use_elf_format]="1"
shift
;;
keygen|sign|target|clean|macros|nvm|lcf)
break
;;
@ -391,7 +464,7 @@ while [[ $# -gt 0 ]]; do
;;
target)
OPERATIONS+=("target")
CURRENT_OPTS=""
CURRENT_OPTS="TARGET_OPTS"
shift
;;
clean)

View File

@ -13,7 +13,7 @@ flash.erase all
flash.reprogram all /erase
data.load.binary "&testApp" 0xA0300000
data.load.binary "&updateApp" 0xA047E000
data.load.binary "&updateApp" 0xA047C000
data.load.elf "&wolfBoot"
flash.reprogram off

View File

@ -0,0 +1,31 @@
system.down
system.up
LOCAL &wolfBoot &testApp &updateApp
&wolfBoot="..\..\..\IDE\AURIX\wolfBoot-tc3xx\TriCore Debug (GCC)\wolfBoot-tc3xx.elf"
&testApp="..\..\..\IDE\AURIX\test-app\TriCore Debug (GCC)\test-app_v1_signed.bin"
&updateApp="..\..\..\IDE\AURIX\test-app\TriCore Debug (GCC)\test-app_v2_signed.bin"
DO ~~/demo/tricore/flash/tc37x.cmm PREPAREONLY
flash.erase all
flash.reprogram all /erase
data.load.binary "&testApp" 0xA047C000
data.load.binary "&updateApp" 0xA053C000
data.load.elf "&wolfBoot"
flash.reprogram off
break.set core0_main
system.down
system.up
go
; Bring up the important views
SYStem
Frame /Locals /Caller
List

View File

@ -0,0 +1,31 @@
system.down
system.up
LOCAL &wolfBoot &testApp &updateApp
&wolfBoot="..\..\..\IDE\AURIX\wolfBoot-tc3xx\TriCore Release (GCC)\wolfBoot-tc3xx.elf"
&testApp="..\..\..\IDE\AURIX\test-app\TriCore Release (GCC)\test-app_v1_signed.bin"
&updateApp="..\..\..\IDE\AURIX\test-app\TriCore Release (GCC)\test-app_v2_signed.bin"
DO ~~/demo/tricore/flash/tc37x.cmm PREPAREONLY
flash.erase all
flash.reprogram all /erase
data.load.binary "&testApp" 0xA047C000
data.load.binary "&updateApp" 0xA053C000
data.load.elf "&wolfBoot"
flash.reprogram off
break.set core0_main
system.down
system.up
go
; Bring up the important views
SYStem
Frame /Locals /Caller
List

View File

@ -13,7 +13,7 @@ flash.erase all
flash.reprogram all /erase
data.load.binary "&testApp" 0xA0300000
data.load.binary "&updateApp" 0xA047E000
data.load.binary "&updateApp" 0xA047C000
data.load.elf "&wolfBoot"
flash.reprogram off

View File

@ -16,7 +16,7 @@ FLASH.Erase 0xA00A0000--0xA05FFFFF
flash.reprogram all /erase
data.load.binary "&testApp" 0xA0300000
data.load.binary "&updateApp" 0xA047E000
data.load.binary "&updateApp" 0xA047C000
data.load.elf "&wolfBoot"
flash.reprogram off

View File

@ -16,7 +16,7 @@ FLASH.Erase 0xA00A0000--0xA05FFFFF
flash.reprogram all /erase
data.load.binary "&testApp" 0xA0300000
data.load.binary "&updateApp" 0xA047E000
data.load.binary "&updateApp" 0xA047C000
data.load.elf "&wolfBoot"
flash.reprogram off

View File

@ -0,0 +1,36 @@
# wolfBoot Squash ELF Tool
CC = gcc
LD = gcc
CFLAGS = -Wall -Wextra -Werror
LDFLAGS =
TARGET = squashelf
CFLAGS_EXTRA = # Allow additional flags to be passed via command line
# option variables
DEBUG_FLAGS = -g -DDEBUG
OPTIMIZE = -O2
# Options
ifeq ($(DEBUG),1)
CFLAGS+=$(DEBUG_FLAGS)
else
CFLAGS+=$(OPTIMIZE)
endif
.PHONY: clean all debug
all: $(TARGET)
debug: CFLAGS+=$(DEBUG_FLAGS)
debug: all
$(TARGET): $(TARGET).o
@echo "Building squashelf tool"
$(CC) -o $@ $< $(LDFLAGS) $(CFLAGS_EXTRA)
%.o: %.c
$(CC) $(CFLAGS) $(CFLAGS_EXTRA) -c -o $@ $<
clean:
rm -f $(TARGET) *.o

View File

@ -0,0 +1,62 @@
# squashelf
`squashelf` is a command-line utility that processes ELF (Executable and Linkable Format) files. It extracts `PT_LOAD` segments, optionally filters them based on specified Load Memory Address (LMA) ranges, sorts them by LMA, and writes them to a new, reorganized ELF file. The output ELF file contains only the selected `PT_LOAD` segments and their corresponding data, potentially omitting the Section Header Table (SHT).
## Purpose
This tool can be useful for:
* Creating stripped-down ELF files containing only loadable code and data segments.
* Preparing ELF files for specific bootloaders or embedded systems environments that primarily work with `PT_LOAD` segments.
## Usage
```bash
squashelf [options] <input.elf> <output.elf>
```
## Options
* `-n`, `--nosht`:
Omit the Section Header Table (SHT) from the output ELF. By default, a minimal SHT with a single NULL section is created. Omitting the SHT shouldn't have any effect on loaders that only use PT_LOAD segments, but may cause tools like readelf to complain. Leave it in for max compatibility, or remove it for the smallest possible elf file.
* `-r <min>-<max>[,<min>-<max>...]`, `--range <min>-<max>[,<min>-<max>...]`:
Specify one or more LMA ranges. Only `PT_LOAD` segments fully contained within any of these ranges (inclusive of `min`, exclusive of `max`) will be included in the output. Addresses can be provided in decimal or hexadecimal (using `0x` prefix).
Multiple ranges can be specified by separating them with commas.
Example: `-r 0x10000-0x20000,0x30000-0x40000` or `-r 65536-131072,196608-262144`.
* `-v`, `--verbose`:
Enable verbose output, providing detailed information about the processing steps, segment selection, and file operations.
* `-z`, `--zero-size-segments`:
Include segments with zero file size in the output. By default, these segments are excluded.
* `-h`, `--help`:
Display a help message with detailed information about all available options and examples.
## Examples
* Extract all `PT_LOAD` segments from `input.elf`, sort them by LMA, and write them to `output_all.elf` with a minimal SHT:
```bash
squashelf input.elf output_all.elf
```
* Extract `PT_LOAD` segments from `input.elf` that fall within the LMA range `0x80000000` to `0x8FFFFFFF`, omit the SHT, and write the result to `output_filtered.elf`:
```bash
squashelf --nosht --range 0x80000000-0x8FFFFFFF input.elf output_filtered.elf
```
* Extract segments from multiple memory regions with verbose output:
```bash
squashelf -v --range 0x10000000-0x20000000,0x30000000-0x40000000 input.elf output_multi.elf
```
* Include zero-size segments and show detailed processing information:
```bash
squashelf -v -z --range 0x10000000-0x20000000 input.elf output_with_zeros.elf
```
* Display help message with all options and examples:
```bash
squashelf --help
```

View File

@ -0,0 +1,953 @@
/* squashelf.c
*
* ELF file squasher
*
* Run on HOST machine to preprocess (squash) ELF files for the wolfBoot flash
* updater by extracting PT_LOAD segments, optionally filtering them based on
* specified LMA ranges, sorting them by LMA, and writing them to a new,
* reorganized ELF file. See README.md for more information.
*
* Copyright (C) 2025 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <getopt.h>
#include <stdint.h>
#include <stdarg.h> /* Needed for variadic macros */
#include <stdbool.h> /* Needed for bool type */
#include "../../include/elf.h"
/* Macro for verbose printing */
#define DEBUG_PRINT(fmt, ...) \
do { \
if (verbose) \
fprintf(stderr, fmt, ##__VA_ARGS__); \
} while (0)
/* Constants needed from libelf/gelf.h but not in wolfBoot's elf.h */
#define EI_NIDENT 16
#define SHN_UNDEF 0
/* Structure to store an address range */
typedef struct {
uint64_t min;
uint64_t max;
} AddressRange;
/*
* comparePhdr:
* qsort comparator ordering program headers by load address (p_paddr).
* Sorts ascending so segments land in increasing memory order.
*/
static int comparePhdr32(const void* a, const void* b)
{
const elf32_program_header* pa = a;
const elf32_program_header* pb = b;
if (pa->paddr < pb->paddr) {
return -1;
}
if (pa->paddr > pb->paddr) {
return 1;
}
return 0;
}
static int comparePhdr64(const void* a, const void* b)
{
const elf64_program_header* pa = a;
const elf64_program_header* pb = b;
if (pa->paddr < pb->paddr) {
return -1;
}
if (pa->paddr > pb->paddr) {
return 1;
}
return 0;
}
/* Function to parse a single range string */
static int parseRange(const char* rangeStr, AddressRange* range)
{
char* copyStr = strdup(rangeStr);
if (!copyStr) {
return 0;
}
/* Parse the range string (e.g., "0xA00000000-0xB0000000") */
char* dashPos = strchr(copyStr, '-');
if (!dashPos) {
free(copyStr);
return 0;
}
/* Split the string */
*dashPos = '\0';
char* minStr = copyStr;
char* maxStr = dashPos + 1;
range->min = strtoull(minStr, NULL, 0);
range->max = strtoull(maxStr, NULL, 0);
free(copyStr);
if (range->min >= range->max) {
return 0;
}
return 1;
}
/* Function to check if an address is within any of the specified ranges */
static bool isInRanges(uint64_t addr, AddressRange* ranges, int rangeCount)
{
for (int i = 0; i < rangeCount; i++) {
if (addr >= ranges[i].min && addr <= ranges[i].max) {
return true;
}
}
return false;
}
/* Function to check if two ranges overlap */
static bool rangesOverlap(const AddressRange* a, const AddressRange* b)
{
return (a->min <= b->max && b->min <= a->max);
}
/* Function to check if any ranges in the array overlap */
static bool hasOverlappingRanges(AddressRange* ranges, int rangeCount)
{
for (int i = 0; i < rangeCount; i++) {
for (int j = i + 1; j < rangeCount; j++) {
if (rangesOverlap(&ranges[i], &ranges[j])) {
return true;
}
}
}
return false;
}
/* Function to parse range argument and populate ranges array */
static bool parseRangeArgument(const char* optarg, AddressRange** ranges,
int* rangeCount, int verbose)
{
/* First, count the number of ranges (commas + 1) */
const char* ptr = optarg;
*rangeCount = 1;
while ((ptr = strchr(ptr, ',')) != NULL) {
(*rangeCount)++;
ptr++;
}
/* Allocate memory for ranges */
*ranges = malloc(*rangeCount * sizeof(AddressRange));
if (!*ranges) {
fprintf(stderr, "Memory allocation failed\n");
return false;
}
/* Parse each range */
char* rangeStr = strdup(optarg);
if (!rangeStr) {
fprintf(stderr, "Memory allocation failed\n");
free(*ranges);
*ranges = NULL;
return false;
}
char* token;
char* saveptr;
int currRange = 0;
token = strtok_r(rangeStr, ",", &saveptr);
while (token != NULL && currRange < *rangeCount) {
if (!parseRange(token, &(*ranges)[currRange])) {
fprintf(stderr, "Invalid range format in '%s'. Expected: min-max\n",
token);
free(rangeStr);
free(*ranges);
*ranges = NULL;
return false;
}
DEBUG_PRINT("Range %d: 0x%lx - 0x%lx\n", currRange + 1,
(*ranges)[currRange].min, (*ranges)[currRange].max);
currRange++;
token = strtok_r(NULL, ",", &saveptr);
}
free(rangeStr);
if (currRange != *rangeCount) {
fprintf(stderr, "Error parsing ranges\n");
free(*ranges);
*ranges = NULL;
return false;
}
/* Check for overlapping ranges */
if (hasOverlappingRanges(*ranges, *rangeCount)) {
fprintf(stderr,
"Warning: Address ranges contain overlapping regions.\n");
}
return true;
}
/* Function to print detailed help message */
static void printHelp(const char* programName)
{
printf("Usage: %s [options] <input.elf> <output.elf>\n\n", programName);
printf("Process ELF files by extracting PT_LOAD segments, optionally "
"filtering them based on\n");
printf("specified Load Memory Address (LMA) ranges, sorting them by LMA, "
"and writing them to\n");
printf("a new, reorganized ELF file.\n\n");
printf("Options:\n");
printf(" -n, --nosht Omit the Section Header Table (SHT) "
"from the output ELF.\n");
printf(" By default, a minimal SHT with a "
"single NULL section is created.\n");
printf(" Omitting the SHT shouldn't have any "
"effect on loaders that only\n");
printf(" use PT_LOAD segments, but may cause "
"tools like readelf to complain.\n");
printf(" Leave it in for max compatibility, "
"or remove it for the smallest\n");
printf(" possible elf file.\n\n");
printf(" -r, --range <min>-<max>[,<min>-<max>...]\n");
printf(" Specify one or more LMA ranges. Only "
"PT_LOAD segments fully\n");
printf(" contained within any of these ranges "
"(inclusive of min, exclusive\n");
printf(" of max) will be included in the "
"output. Addresses can be provided\n");
printf(" in decimal or hexadecimal (using 0x "
"prefix).\n");
printf(" Multiple ranges can be specified by "
"separating them with commas.\n");
printf(" Example: -r "
"0x10000-0x20000,0x30000-0x40000\n\n");
printf(" -v, --verbose Enable verbose output, providing "
"detailed information about\n");
printf(" the processing steps, segment "
"selection, and file operations.\n\n");
printf(" -z, --zero-size-segments Include segments with zero file size "
"in the output.\n");
printf(" By default, these segments are "
"excluded.\n\n");
printf(
" -h, --help Display this help message and exit.\n\n");
printf("Examples:\n");
printf(" %s input.elf output.elf\n", programName);
printf(" Extract all PT_LOAD segments, sort them by LMA, and write to "
"output.elf\n\n");
printf(" %s --nosht --range 0x80000000-0x8FFFFFFF input.elf "
"output_filtered.elf\n",
programName);
printf(" Extract segments within the specified range and omit the "
"SHT\n\n");
printf(" %s -v --range 0x10000000-0x20000000,0x30000000-0x40000000 "
"input.elf output_multi.elf\n",
programName);
printf(" Extract segments from multiple memory regions with verbose "
"output\n\n");
printf(" %s -v -z --range 0x10000000-0x20000000 input.elf "
"output_with_zeros.elf\n",
programName);
printf(" Include zero-size segments and show detailed processing "
"information\n\n");
}
/* Function to print usage message */
static void printUsage(const char* programName)
{
fprintf(stderr,
"Usage: %s [-n | --nosht] [-r | --range "
"min-max[,min-max,...]] "
"[-v | --verbose] [-z | --zero-size-segments] "
"[-h | --help] "
"<input.elf> <output.elf>\n",
programName);
}
/* Read ELF header from file */
static bool read_elf_header(int fd, void* ehdr, int* elfClass, bool* is_elf32)
{
uint8_t ident[EI_NIDENT];
/* Read ELF identification bytes */
if (pread(fd, ident, EI_NIDENT, 0) != EI_NIDENT) {
perror("read ELF identification");
return false;
}
/* Check if this is a valid ELF file */
if (memcmp(ident, ELF_IDENT_STR, 4) != 0) {
fprintf(stderr, "Not a valid ELF file\n");
return false;
}
/* Determine ELF class (32 or 64 bit) */
*elfClass = ident[ELF_CLASS_OFF];
if (*elfClass != ELF_CLASS_32 && *elfClass != ELF_CLASS_64) {
fprintf(stderr, "Unsupported ELF class: %d\n", *elfClass);
return false;
}
*is_elf32 = (*elfClass == ELF_CLASS_32);
/* Read the appropriate header based on class */
if (*is_elf32) {
elf32_header* hdr32 = (elf32_header*)ehdr;
if (pread(fd, hdr32, sizeof(*hdr32), 0) != sizeof(*hdr32)) {
perror("read ELF header");
return false;
}
}
else {
elf64_header* hdr64 = (elf64_header*)ehdr;
if (pread(fd, hdr64, sizeof(*hdr64), 0) != sizeof(*hdr64)) {
perror("read ELF header");
return false;
}
}
return true;
}
/* Read a program header from file */
static bool read_program_header(int fd, void* phdr, bool is_elf32, size_t index,
off_t ph_offset, size_t ph_entsize)
{
off_t offset = ph_offset + (index * ph_entsize);
if (is_elf32) {
elf32_program_header* ph32 = (elf32_program_header*)phdr;
if (pread(fd, ph32, sizeof(*ph32), offset) != sizeof(*ph32)) {
perror("read program header");
return false;
}
}
else {
elf64_program_header* ph64 = (elf64_program_header*)phdr;
if (pread(fd, ph64, sizeof(*ph64), offset) != sizeof(*ph64)) {
perror("read program header");
return false;
}
}
return true;
}
int main(int argCount, char** argValues)
{
bool success = false;
int noSht = 0;
int hasRange = 0;
int allowZeroSizeSeg = 0; /* New flag for zero-size segments */
AddressRange* ranges = NULL;
int rangeCount = 0;
const char* inputFile = NULL;
const char* outputFile = NULL;
int verbose = 0;
int opt;
int option_index = 0; /* For getopt_long */
int inputFd = -1;
int outputFd = -1;
void** data_buffers = NULL;
size_t loadCount = 0;
size_t phdrCount = 0;
int elfClass = 0;
bool is_elf32 = false;
void* phdrs = NULL;
void* outPhdrs = NULL;
/* Squash maybe uninitialized warnings introduced by -Wextra */
phdrs = NULL;
outPhdrs = NULL;
/* Allocate memory for headers */
union {
elf32_header h32;
elf64_header h64;
} elfHeader;
/* Define long options */
static struct option long_options[] = {
{"nosht", no_argument, 0, 'n'}, /* --nosht is equivalent to -n */
{"range", required_argument, 0, 'r'}, /* --range is equivalent to -r */
{"verbose", no_argument, 0, 'v'}, /* --verbose is equivalent to -v */
{"zero-size-segments", no_argument, 0, 'z'}, /* --zero-size-segments */
{"help", no_argument, 0, 'h'}, /* --help is equivalent to -h */
{0, 0, 0, 0}};
/* Use getopt_long to parse command-line options */
optind = 1; /* Reset optind */
while ((opt = getopt_long(argCount, argValues, "nr:vzh", long_options,
&option_index)) != -1) {
switch (opt) {
case 'n':
noSht = 1;
break;
case 'r': {
hasRange = 1;
if (!parseRangeArgument(optarg, &ranges, &rangeCount,
verbose)) {
return EXIT_FAILURE;
}
} break;
case 'v':
verbose = 1;
break;
case 'z':
allowZeroSizeSeg = 1;
break;
case 'h':
printHelp(argValues[0]);
return EXIT_SUCCESS;
case '?': /* getopt_long prints an error message */
printUsage(argValues[0]);
if (ranges) {
free(ranges);
}
return EXIT_FAILURE;
default:
/* Should not happen */
if (ranges) {
free(ranges);
}
abort();
}
}
/* Check for the correct number of positional arguments */
if (optind + 2 != argCount) {
printUsage(argValues[0]);
if (ranges) {
free(ranges);
}
return EXIT_FAILURE;
}
inputFile = argValues[optind];
outputFile = argValues[optind + 1];
/* Print initial configuration if verbose */
DEBUG_PRINT("Verbose mode enabled.\n");
DEBUG_PRINT("Input file: %s\n", inputFile);
DEBUG_PRINT("Output file: %s\n", outputFile);
DEBUG_PRINT("No SHT: %s\n", noSht ? "yes" : "no");
DEBUG_PRINT("Allow zero-size segments: %s\n",
allowZeroSizeSeg ? "yes" : "no");
if (hasRange) {
DEBUG_PRINT("Range filter: %d ranges specified\n", rangeCount);
for (int i = 0; i < rangeCount; i++) {
DEBUG_PRINT(" Range %d: 0x%lx - 0x%lx\n", i + 1, ranges[i].min,
ranges[i].max);
}
}
/* Open input ELF file for reading */
inputFd = open(inputFile, O_RDONLY);
if (inputFd < 0) {
perror("open inputFile");
goto cleanup;
}
DEBUG_PRINT("Opened input file: %s (fd: %d)\n", inputFile, inputFd);
/* Read ELF header */
if (!read_elf_header(inputFd, &elfHeader, &elfClass, &is_elf32)) {
fprintf(stderr, "Failed to read ELF header\n");
goto cleanup;
}
DEBUG_PRINT("Detected ELF class: %s\n", is_elf32 ? "ELF32" : "ELF64");
/* Get program header count */
if (is_elf32) {
phdrCount = elfHeader.h32.ph_entry_count;
DEBUG_PRINT("Read input ELF header. Program header count: %u\n",
elfHeader.h32.ph_entry_count);
}
else {
phdrCount = elfHeader.h64.ph_entry_count;
DEBUG_PRINT("Read input ELF header. Program header count: %u\n",
elfHeader.h64.ph_entry_count);
}
DEBUG_PRINT("Confirmed program header count: %zu\n", phdrCount);
if (is_elf32) {
phdrs = malloc(phdrCount * sizeof(elf32_program_header));
if (!phdrs) {
perror("malloc phdrs");
goto cleanup;
}
}
else {
phdrs = malloc(phdrCount * sizeof(elf64_program_header));
if (!phdrs) {
perror("malloc phdrs");
goto cleanup;
}
}
/* Extract only PT_LOAD segments from the input PHT */
for (size_t i = 0; i < phdrCount; i++) {
union {
elf32_program_header h32;
elf64_program_header h64;
} ph;
if (!read_program_header(inputFd, &ph, is_elf32, i,
is_elf32 ? elfHeader.h32.ph_offset
: elfHeader.h64.ph_offset,
is_elf32 ? elfHeader.h32.ph_entry_size
: elfHeader.h64.ph_entry_size)) {
continue;
}
uint32_t p_type = is_elf32 ? ph.h32.type : ph.h64.type;
if (p_type == ELF_PT_LOAD) {
uint64_t p_filesz = is_elf32 ? ph.h32.file_size : ph.h64.file_size;
uint64_t p_paddr = is_elf32 ? ph.h32.paddr : ph.h64.paddr;
uint64_t p_memsz = is_elf32 ? ph.h32.mem_size : ph.h64.mem_size;
/* Skip segments with zero filesz unless explicitly allowed */
if (p_filesz == 0 && !allowZeroSizeSeg) {
DEBUG_PRINT(" Skipping segment %zu (LMA 0x%lx) - "
"zero filesz\n",
i, (unsigned long)p_paddr);
continue;
}
/* Apply range filter if specified */
if (hasRange) {
uint64_t segmentStart = p_paddr;
uint64_t segmentEnd = p_paddr + p_memsz - 1;
/* Check if segment start and end are both within any range */
bool startInRange =
isInRanges(segmentStart, ranges, rangeCount);
bool endInRange = isInRanges(segmentEnd, ranges, rangeCount);
if (!startInRange || !endInRange) {
DEBUG_PRINT(" Skipping segment %zu (LMA 0x%lx - 0x%lx) - "
"outside specified ranges\n",
i, (unsigned long)segmentStart,
(unsigned long)segmentEnd);
continue;
}
}
/* Add the segment to the loadable segments array */
if (is_elf32) {
elf32_program_header* ph32_array = (elf32_program_header*)phdrs;
memcpy(&ph32_array[loadCount], &ph.h32,
sizeof(elf32_program_header));
DEBUG_PRINT(
" Keeping segment %zu (LMA 0x%lx, size 0x%lx/0x%lx, "
"offset 0x%lx, align %lu)\n",
i, (unsigned long)ph.h32.paddr,
(unsigned long)ph.h32.file_size,
(unsigned long)ph.h32.mem_size,
(unsigned long)ph.h32.offset, (unsigned long)ph.h32.align);
}
else {
elf64_program_header* ph64_array = (elf64_program_header*)phdrs;
memcpy(&ph64_array[loadCount], &ph.h64,
sizeof(elf64_program_header));
DEBUG_PRINT(
" Keeping segment %zu (LMA 0x%lx, size 0x%lx/0x%lx, "
"offset 0x%lx, align %lu)\n",
i, (unsigned long)ph.h64.paddr,
(unsigned long)ph.h64.file_size,
(unsigned long)ph.h64.mem_size,
(unsigned long)ph.h64.offset, (unsigned long)ph.h64.align);
}
loadCount++;
}
else {
DEBUG_PRINT(" Skipping segment %zu (type %u)\n", i, p_type);
}
}
DEBUG_PRINT("Found %zu PT_LOAD segments matching criteria.\n", loadCount);
if (loadCount == 0) {
fprintf(stderr, "No PT_LOAD segments found\n");
goto cleanup;
}
/* Allocate memory for the output program headers */
if (is_elf32) {
/* Sort the loadable segments by their LMA (paddr) */
qsort(phdrs, loadCount, sizeof(elf32_program_header), comparePhdr32);
outPhdrs = malloc(loadCount * sizeof(elf32_program_header));
if (!outPhdrs) {
perror("malloc outPhdrs");
goto cleanup;
}
memcpy(outPhdrs, phdrs, loadCount * sizeof(elf32_program_header));
}
else {
/* Sort the loadable segments by their LMA (paddr) */
qsort(phdrs, loadCount, sizeof(elf64_program_header), comparePhdr64);
outPhdrs = malloc(loadCount * sizeof(elf64_program_header));
if (!outPhdrs) {
perror("malloc outPhdrs");
goto cleanup;
}
memcpy(outPhdrs, phdrs, loadCount * sizeof(elf64_program_header));
}
DEBUG_PRINT("Sorted PT_LOAD segments by LMA.\n");
/* Allocate storage for segment data */
data_buffers = calloc(loadCount, sizeof(void*));
if (!data_buffers) {
perror("calloc data_buffers");
goto cleanup;
}
/* Read segment data from input file */
for (size_t i = 0; i < loadCount; i++) {
uint64_t p_offset, p_filesz;
if (is_elf32) {
elf32_program_header* ph32_array = (elf32_program_header*)outPhdrs;
p_offset = ph32_array[i].offset;
p_filesz = ph32_array[i].file_size;
}
else {
elf64_program_header* ph64_array = (elf64_program_header*)outPhdrs;
p_offset = ph64_array[i].offset;
p_filesz = ph64_array[i].file_size;
}
if (p_filesz > 0) {
data_buffers[i] = malloc(p_filesz);
if (!data_buffers[i]) {
perror("malloc segment buffer");
goto cleanup;
}
/* Read the data */
ssize_t bytes_read =
pread(inputFd, data_buffers[i], p_filesz, p_offset);
if (bytes_read < 0) {
perror("pread segment data");
goto cleanup;
}
else if ((size_t)bytes_read != p_filesz) {
fprintf(stderr,
"Short read for segment %zu (expected %lu, got %zd)\n",
i, (unsigned long)p_filesz, bytes_read);
goto cleanup;
}
DEBUG_PRINT("Read %zu bytes for segment %zu\n", (size_t)p_filesz,
i);
}
}
/* Open output file for writing */
outputFd = open(outputFile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (outputFd < 0) {
perror("open outputFile");
goto cleanup;
}
DEBUG_PRINT("Opened output file: %s (fd: %d)\n", outputFile, outputFd);
/*
* Now we manually construct the output ELF file in this format:
* [ELF header][Program Header Table][Loadable Segments][Optional Section
* Header Table]
*/
/* Step 1: Calculate file layout */
size_t ehdr_size = (is_elf32) ? sizeof(elf32_header) : sizeof(elf64_header);
size_t phdr_size = (is_elf32) ? sizeof(elf32_program_header)
: sizeof(elf64_program_header);
/* Calculate PHT offset and size */
size_t pht_offset = ehdr_size;
size_t pht_size = loadCount * phdr_size;
/* Calculate segment offsets and update PHDRs */
size_t current_offset = pht_offset + pht_size;
/* No forced global alignment - we'll respect each segment's individual
* alignment */
DEBUG_PRINT("Starting segment layout at offset: 0x%lx\n", current_offset);
/* Update segment offsets */
for (size_t i = 0; i < loadCount; i++) {
uint64_t p_align, p_filesz;
if (is_elf32) {
elf32_program_header* ph32_array = (elf32_program_header*)outPhdrs;
p_align = ph32_array[i].align;
/* Align the segment according to its alignment requirement if
* needed */
if (p_align > 1) {
current_offset =
(current_offset + p_align - 1) & ~(p_align - 1);
}
/* Update the segment's offset */
ph32_array[i].offset = current_offset;
p_filesz = ph32_array[i].file_size;
DEBUG_PRINT(" Segment %zu offset: 0x%lx\n", i,
(unsigned long)current_offset);
}
else {
elf64_program_header* ph64_array = (elf64_program_header*)outPhdrs;
p_align = ph64_array[i].align;
/* Align the segment according to its alignment requirement if
* needed */
if (p_align > 1) {
current_offset =
(current_offset + p_align - 1) & ~(p_align - 1);
}
/* Update the segment's offset */
ph64_array[i].offset = current_offset;
p_filesz = ph64_array[i].file_size;
DEBUG_PRINT(" Segment %zu offset: 0x%lx\n", i,
(unsigned long)current_offset);
}
/* Move to next position */
current_offset += p_filesz;
}
/* Calculate SHT offset if needed */
size_t sht_offset = 0;
if (!noSht) {
/* Align SHT to 8-byte boundary */
current_offset = (current_offset + 7) & ~7;
sht_offset = current_offset;
}
/* Step 2: Prepare and write ELF header */
/* Update header fields */
if (is_elf32) {
elfHeader.h32.ph_offset = pht_offset;
elfHeader.h32.ph_entry_count = loadCount;
if (noSht) {
elfHeader.h32.sh_offset = 0;
elfHeader.h32.sh_entry_count = 0;
elfHeader.h32.sh_str_index = SHN_UNDEF;
}
else {
elfHeader.h32.sh_offset = sht_offset;
elfHeader.h32.sh_entry_count = 1; /* Just the NULL section */
elfHeader.h32.sh_str_index = SHN_UNDEF;
}
/* Write ELF header to output file */
if (write(outputFd, &elfHeader.h32, sizeof(elfHeader.h32)) !=
sizeof(elfHeader.h32)) {
perror("write ELF header");
goto cleanup;
}
}
else {
elfHeader.h64.ph_offset = pht_offset;
elfHeader.h64.ph_entry_count = loadCount;
if (noSht) {
elfHeader.h64.sh_offset = 0;
elfHeader.h64.sh_entry_count = 0;
elfHeader.h64.sh_str_index = SHN_UNDEF;
}
else {
elfHeader.h64.sh_offset = sht_offset;
elfHeader.h64.sh_entry_count = 1; /* Just the NULL section */
elfHeader.h64.sh_str_index = SHN_UNDEF;
}
/* Write ELF header to output file */
if (write(outputFd, &elfHeader.h64, sizeof(elfHeader.h64)) !=
sizeof(elfHeader.h64)) {
perror("write ELF header");
goto cleanup;
}
}
DEBUG_PRINT("Wrote ELF header to output file.\n");
/* Step 3: Write Program Header Table */
if (is_elf32) {
elf32_program_header* ph32_array = (elf32_program_header*)outPhdrs;
for (size_t i = 0; i < loadCount; i++) {
if (write(outputFd, &ph32_array[i], sizeof(ph32_array[i])) !=
sizeof(ph32_array[i])) {
perror("write program header");
goto cleanup;
}
}
}
else {
elf64_program_header* ph64_array = (elf64_program_header*)outPhdrs;
for (size_t i = 0; i < loadCount; i++) {
if (write(outputFd, &ph64_array[i], sizeof(ph64_array[i])) !=
sizeof(ph64_array[i])) {
perror("write program header");
goto cleanup;
}
}
}
DEBUG_PRINT("Wrote Program Header Table (%zu entries).\n", loadCount);
/* Step 4: Write segment data */
for (size_t i = 0; i < loadCount; i++) {
uint64_t p_offset, p_filesz;
if (is_elf32) {
elf32_program_header* ph32_array = (elf32_program_header*)outPhdrs;
p_offset = ph32_array[i].offset;
p_filesz = ph32_array[i].file_size;
}
else {
elf64_program_header* ph64_array = (elf64_program_header*)outPhdrs;
p_offset = ph64_array[i].offset;
p_filesz = ph64_array[i].file_size;
}
/* Seek to the offset where this segment should be written */
if (lseek(outputFd, p_offset, SEEK_SET) != (off_t)p_offset) {
perror("lseek to segment offset");
goto cleanup;
}
/* Skip segments with zero file size */
if (p_filesz == 0) {
DEBUG_PRINT(" Segment %zu has zero filesz, skipping data write\n",
i);
continue;
}
/* Write the segment data */
ssize_t bytes_written = write(outputFd, data_buffers[i], p_filesz);
if (bytes_written < 0) {
perror("write segment data");
goto cleanup;
}
else if ((size_t)bytes_written != p_filesz) {
fprintf(stderr,
"Short write for segment %zu (expected %lu, wrote %zd)\n",
i, (unsigned long)p_filesz, bytes_written);
goto cleanup;
}
DEBUG_PRINT(" Wrote segment %zu data (0x%lx bytes at offset 0x%lx)\n",
i, (unsigned long)p_filesz, (unsigned long)p_offset);
}
/* Step 5: Write Section Header Table if not using --nosht */
if (!noSht) {
/* Seek to the Section Header Table offset */
if (lseek(outputFd, sht_offset, SEEK_SET) != (off_t)sht_offset) {
perror("lseek to SHT offset");
goto cleanup;
}
/* Write a NULL section header (all zeros) */
if (is_elf32) {
elf32_section_header shdr32 = {0}; /* All zeros for NULL section */
if (write(outputFd, &shdr32, sizeof(shdr32)) != sizeof(shdr32)) {
perror("write NULL section header");
goto cleanup;
}
}
else {
elf64_section_header shdr64 = {0}; /* All zeros for NULL section */
if (write(outputFd, &shdr64, sizeof(shdr64)) != sizeof(shdr64)) {
perror("write NULL section header");
goto cleanup;
}
}
DEBUG_PRINT("Wrote NULL section header at offset 0x%lx\n", sht_offset);
}
DEBUG_PRINT("Successfully wrote output ELF file.\n");
success = true;
cleanup:
/* Clean up resources */
if (data_buffers) {
for (size_t i = 0; i < loadCount; i++) {
free(data_buffers[i]);
}
free(data_buffers);
}
if (phdrs) {
free(phdrs);
}
if (outPhdrs) {
free(outPhdrs);
}
if (inputFd >= 0) {
close(inputFd);
}
if (outputFd >= 0) {
close(outputFd);
}
if (ranges) {
free(ranges);
}
return (success ? EXIT_SUCCESS : EXIT_FAILURE);
}