Better fix for elf squashing (#341)
Don't squash into occupied areas, and don't squash metadata blocks
This reverts commit 0d160860227c83133502b8e1c504313e40ae9124
* Catch invalid cycles in block loops (e.g. lollipop block loops)
* Fix XIP SRAM only binary support
diff --git a/bintool/bintool.cpp b/bintool/bintool.cpp
index 9d54431..6c11693 100644
--- a/bintool/bintool.cpp
+++ b/bintool/bintool.cpp
@@ -6,6 +6,7 @@
#include <random>
#include <cinttypes>
#include <tuple>
+#include <set>
#include "boot/picobin.h"
#include <map>
@@ -255,6 +256,8 @@
if (psize == 0) continue;
if (paddr >= model->sram_start() && paddr < model->sram_striped_end()) {
highest_ram_address = std::max(paddr + psize, highest_ram_address);
+ } else if (paddr >= model->xip_sram_start() && paddr < model->xip_sram_end()) {
+ highest_ram_address = std::max(paddr + psize, highest_ram_address);
} else if (paddr >= model->flash_start() && paddr < model->flash_end()) {
highest_flash_address = std::max(paddr + psize, highest_flash_address);
}
@@ -283,44 +286,11 @@
} else {
DEBUG_LOG("There is already a block loop\n");
if (set_others_ignored) set_block_ignored(elf, first_block->physical_addr);
- uint32_t next_block_addr = first_block->physical_addr + first_block->next_block_rel;
- while (true) {
- auto segment = elf->segment_from_physical_address(next_block_addr);
- if (segment == nullptr) {
- fail(ERROR_NOT_POSSIBLE, "The ELF file does not contain the next block address %x", next_block_addr);
- }
- auto data = elf->content(*segment);
- auto offset = next_block_addr - segment->physical_address();
- std::vector<uint32_t> words = lsb_bytes_to_words(data.begin() + offset, data.end());
- if (words.front() != PICOBIN_BLOCK_MARKER_START) {
- fail(ERROR_UNKNOWN, "Block loop is not valid - no block found at %08x\n", (int)(next_block_addr));
- }
- words.erase(words.begin());
- DEBUG_LOG("Checking block at %x\n", next_block_addr);
- for(auto next_item = words.begin(); next_item < words.end(); ) {
- unsigned int size = item::decode_size(*next_item);
- if ((uint8_t)*next_item == PICOBIN_BLOCK_ITEM_2BS_LAST) {
- if (size == next_item - words.begin()) {
- if (next_item < words.end() && next_item[2] == PICOBIN_BLOCK_MARKER_END) {
- DEBUG_LOG("is a valid block\n");
- new_first_block = block::parse(next_block_addr, next_item + 1, words.begin(), words.begin() + size);
- break;
- }
- }
- } else {
- next_item += size;
- }
- }
- if (new_first_block->physical_addr + new_first_block->next_block_rel == first_block->physical_addr) {
- DEBUG_LOG("Found last block in block loop\n");
- break;
- } else {
- DEBUG_LOG("Continue looping\n");
- if (set_others_ignored) set_block_ignored(elf, new_first_block->physical_addr);
- next_block_addr = new_first_block->physical_addr + new_first_block->next_block_rel;
- new_first_block.reset();
- }
+ auto all_blocks = get_all_blocks(elf, first_block);
+ for (auto &block : all_blocks) {
+ if (set_others_ignored) set_block_ignored(elf, block->physical_addr);
}
+ new_first_block = std::move(all_blocks.back());
set_next_block(elf, new_first_block, highest_address);
new_block_addr = new_first_block->physical_addr + new_first_block->next_block_rel;
loop_start_rel = first_block->physical_addr - new_block_addr;
@@ -362,8 +332,65 @@
}
+std::vector<std::unique_ptr<block>> get_all_blocks(elf_file *elf, std::unique_ptr<block> &first_block) {
+ uint32_t next_block_addr = first_block->physical_addr + first_block->next_block_rel;
+ std::set<uint32_t> next_block_addrs = {next_block_addr};
+ std::vector<std::unique_ptr<block>> all_blocks;
+ while (true) {
+ auto segment = elf->segment_from_physical_address(next_block_addr);
+ if (segment == nullptr) {
+ fail(ERROR_NOT_POSSIBLE, "The ELF file does not contain the next block address %x", next_block_addr);
+ }
+ auto data = elf->content(*segment);
+ auto offset = next_block_addr - segment->physical_address();
+ std::vector<uint32_t> words = lsb_bytes_to_words(data.begin() + offset, data.end());
+ if (words.front() != PICOBIN_BLOCK_MARKER_START) {
+ fail(ERROR_UNKNOWN, "Block loop is not valid - no block found at %08x\n", (int)(next_block_addr));
+ }
+ words.erase(words.begin());
+ DEBUG_LOG("Checking block at %x\n", next_block_addr);
+ std::unique_ptr<block> new_first_block;
+ for(auto next_item = words.begin(); next_item < words.end(); ) {
+ unsigned int size = item::decode_size(*next_item);
+ if ((uint8_t)*next_item == PICOBIN_BLOCK_ITEM_2BS_LAST) {
+ if (size == next_item - words.begin()) {
+ if (next_item < words.end() && next_item[2] == PICOBIN_BLOCK_MARKER_END) {
+ DEBUG_LOG("is a valid block\n");
+ new_first_block = block::parse(next_block_addr, next_item + 1, words.begin(), words.begin() + size);
+ break;
+ }
+ }
+ } else {
+ next_item += size;
+ }
+ }
+ if (new_first_block == nullptr) {
+ fail(ERROR_UNKNOWN, "Block loop is not valid - incomplete block found at %08x\n", (int)(next_block_addr));
+ }
+ if (new_first_block->physical_addr + new_first_block->next_block_rel == first_block->physical_addr) {
+ DEBUG_LOG("Found last block in block loop\n");
+ all_blocks.push_back(std::move(new_first_block));
+ break;
+ } else {
+ DEBUG_LOG("Continue looping\n");
+ next_block_addr = new_first_block->physical_addr + new_first_block->next_block_rel;
+ if (next_block_addrs.find(next_block_addr) != next_block_addrs.end()) {
+ fail(ERROR_UNKNOWN,
+ "Block loop is not valid - contains a loop from %08x to %08x, but first block at %08x\n",
+ (int)(new_first_block->physical_addr), (int)(next_block_addr), (int)(first_block->physical_addr)
+ );
+ }
+ next_block_addrs.insert(next_block_addr);
+ all_blocks.push_back(std::move(new_first_block));
+ }
+ }
+ return all_blocks;
+}
+
+
std::vector<std::unique_ptr<block>> get_all_blocks(std::vector<uint8_t> &bin, uint32_t storage_addr, std::unique_ptr<block> &first_block, get_more_bin_cb more_cb) {
uint32_t next_block_addr = first_block->physical_addr + first_block->next_block_rel;
+ std::set<uint32_t> next_block_addrs = {next_block_addr};
std::vector<std::unique_ptr<block>> all_blocks;
uint32_t read_size = PICOBIN_MAX_BLOCK_SIZE;
uint32_t current_bin_start = storage_addr;
@@ -406,6 +433,13 @@
} else {
DEBUG_LOG("Continue looping\n");
next_block_addr = new_first_block->physical_addr + new_first_block->next_block_rel;
+ if (next_block_addrs.find(next_block_addr) != next_block_addrs.end()) {
+ fail(ERROR_UNKNOWN,
+ "Block loop is not valid - contains a loop from %08x to %08x, but first block at %08x\n",
+ (int)(new_first_block->physical_addr), (int)(next_block_addr), (int)(first_block->physical_addr)
+ );
+ }
+ next_block_addrs.insert(next_block_addr);
all_blocks.push_back(std::move(new_first_block));
}
}
@@ -428,6 +462,8 @@
const uint32_t psize = bin.size();
if (paddr >= model->sram_start() && paddr < model->sram_striped_end()) {
highest_ram_address = std::max(paddr + psize, highest_ram_address);
+ } else if (paddr >= model->xip_sram_start() && paddr < model->xip_sram_end()) {
+ highest_ram_address = std::max(paddr + psize, highest_ram_address);
} else if (paddr >= model->flash_start() && paddr < model->flash_end()) {
highest_flash_address = std::max(paddr + psize, highest_flash_address);
}
@@ -454,6 +490,7 @@
if (set_others_ignored) set_block_ignored(bin, storage_addr, first_block->physical_addr);
} else {
DEBUG_LOG("Ooh, there is already a block loop - lets find it's end\n");
+ if (set_others_ignored) set_block_ignored(bin, storage_addr, first_block->physical_addr);
auto all_blocks = get_all_blocks(bin, storage_addr, first_block);
for (auto &block : all_blocks) {
if (set_others_ignored) set_block_ignored(bin, storage_addr, block->physical_addr);
diff --git a/bintool/bintool.h b/bintool/bintool.h
index f8c60ff..d13f150 100644
--- a/bintool/bintool.h
+++ b/bintool/bintool.h
@@ -25,6 +25,7 @@
// Elfs
std::unique_ptr<block> find_first_block(elf_file *elf);
+std::vector<std::unique_ptr<block>> get_all_blocks(elf_file *elf, std::unique_ptr<block> &first_block);
block place_new_block(elf_file *elf, std::unique_ptr<block> &first_block, model_t model, bool set_others_ignored=false);
#if HAS_MBEDTLS
int hash_andor_sign(elf_file *elf, block *new_block, const public_t public_key, const private_t private_key, model_t model, bool hash_value, bool sign, bool clear_sram = false, bool pin_xip_sram = false);
diff --git a/elf/elf_file.cpp b/elf/elf_file.cpp
index d5cd5e9..d4f8d53 100644
--- a/elf/elf_file.cpp
+++ b/elf/elf_file.cpp
@@ -527,6 +527,18 @@
return nullptr;
}
+bool elf_file::physical_address_range_is_occupied(uint32_t paddr, uint32_t psize) {
+ uint32_t pstart = paddr;
+ uint32_t pend = paddr + psize;
+ for (int i = 0; i < eh.ph_num; i++) {
+ if (!ph_entries[i].is_load() || ph_entries[i].filez == 0) continue; // allow overlapping with non-loaded segments
+ if (pstart < ph_entries[i].paddr + ph_entries[i].filez && ph_entries[i].paddr < pend) {
+ return true;
+ }
+ }
+ return false;
+}
+
const elf32_ph_entry* elf_file::segment_from_virtual_address(uint32_t vaddr) {
for (int i = 0; i < eh.ph_num; i++) {
if (vaddr >= ph_entries[i].vaddr && vaddr < ph_entries[i].vaddr + ph_entries[i].memsz) {
@@ -567,49 +579,44 @@
return const_sorted_segs;
}
-void elf_file::store_squashed(model_t model) {
- uint32_t highest_ram_address = 0;
- uint32_t highest_flash_address = 0;
-
+void elf_file::store_squashed(model_t model, const std::vector<uint32_t> &pinned_addresses) {
for(const auto &seg : sorted_segments()) {
const uint32_t paddr = seg->physical_address();
const uint32_t psize = seg->physical_size();
if (psize == 0) continue;
- if (paddr >= model->sram_start() && paddr < model->sram_striped_end()) {
- highest_ram_address = std::max(paddr + psize, highest_ram_address);
- } else if (paddr >= model->flash_start() && paddr < model->flash_end()) {
- highest_flash_address = std::max(paddr + psize, highest_flash_address);
+ if (paddr >= model->flash_start() && paddr < model->flash_end()) {
+ // cannot squash flash binaries
+ return;
}
}
- if (highest_flash_address != 0) {
- // cannot squash flash binaries
- return;
- }
-
uint32_t last_seg_end = 0;
- uint32_t last_seg_start = 0;
std::vector<elf32_ph_entry *> xip_sram_segs = {};
+ auto contains_pinned_address = [&](const elf32_ph_entry *seg) {
+ for (uint32_t addr : pinned_addresses) {
+ if (addr >= seg->physical_address() && addr < seg->physical_address() + seg->physical_size()) {
+ return true;
+ }
+ }
+ return false;
+ };
+
auto squash_seg = [&](elf32_ph_entry *seg) {
const uint32_t paddr = seg->physical_address();
const uint32_t psize = seg->physical_size();
if (!seg->is_load()) return;
-
- const bool is_alias = paddr == last_seg_start;
- last_seg_start = paddr;
-
- if (psize == 0 || is_alias) return;
+ if (psize == 0) return;
if (last_seg_end) {
- if (paddr != last_seg_end) {
+ if (paddr != last_seg_end && !contains_pinned_address(seg) && !physical_address_range_is_occupied(last_seg_end, psize)) {
if (verbose) printf("squashing %08x to %08x\n", paddr, last_seg_end);
seg->paddr = last_seg_end;
}
}
- // May have been modified, so read again
- last_seg_end = seg->physical_address() + seg->physical_size();
+ // May have been modified, so read again. Use max so last_seg_end never moves backwards.
+ last_seg_end = std::max(last_seg_end, seg->physical_address() + seg->physical_size());
};
for(auto &seg : sorted_segments_modifiable()) {
diff --git a/elf/elf_file.h b/elf/elf_file.h
index 7b06a88..96c147d 100644
--- a/elf/elf_file.h
+++ b/elf/elf_file.h
@@ -46,8 +46,9 @@
const elf32_ph_entry* segment_from_physical_address(uint32_t paddr);
const elf32_ph_entry* segment_from_virtual_address(uint32_t vaddr);
const elf32_ph_entry* segment_from_section(const elf32_sh_entry &sh);
+ bool physical_address_range_is_occupied(uint32_t paddr, uint32_t psize);
void dump(void) const;
- void store_squashed(model_t model);
+ void store_squashed(model_t model, const std::vector<uint32_t> &pinned_addresses = {});
void move_all(int dist);
void remove_ph_holes(void);
diff --git a/main.cpp b/main.cpp
index 9d042bb..d53d530 100644
--- a/main.cpp
+++ b/main.cpp
@@ -6184,8 +6184,20 @@
elf_file *elf = &source_file;
elf->read_file(get_file(ios::in|ios::binary));
if (!settings.seal.no_squash) {
+ // Segments containing a block that is already part of a metadata block loop must not be
+ // moved, as other blocks in the loop link to them by absolute physical address
+ std::vector<uint32_t> pinned_addresses;
+ std::unique_ptr<block> first_block = find_first_block(elf);
+ if (first_block) {
+ pinned_addresses.push_back(first_block->physical_addr);
+ if (first_block->next_block_rel) {
+ for (auto &blk : get_all_blocks(elf, first_block)) {
+ pinned_addresses.push_back(blk->physical_addr);
+ }
+ }
+ }
// Squash the segments together
- elf->store_squashed(model);
+ elf->store_squashed(model, pinned_addresses);
}
// Remove any holes in the ELF file, as these cause issues when signing/hashing
elf->remove_sh_holes();