elfloader: band-aid handling of PHDRs that span flash pages - inspect the PHDR p_type field without copying the entire header when checking if a PHDR is loadable (we assume the PHDR is word-aligned which is probably ok) - advance to a new flash page--as needed--when reading a PHDR - add an assert with an informative message when checking for a PHDR that spans flash pages; previously this would trigger an obscure assert when constructing the slice of raw bytes - add Elf32Phdr::get_type() to return just the p_type field of a PHDR - change Elf32Phdr::from_bytes() to take an immutable ref This hacks around problems where the latest CHERIoT lld appends PHDRs that require multiple flash pages but are not loaded. A better fix would assemble PHDR's that span flash pages. Change-Id: I1cfdc93ecb79fa7008bdfcab8a9210fbaaf89baf
diff --git a/capsules/src/elfloader_capsule.rs b/capsules/src/elfloader_capsule.rs index af19523..0e80753 100644 --- a/capsules/src/elfloader_capsule.rs +++ b/capsules/src/elfloader_capsule.rs
@@ -74,7 +74,7 @@ LoadElfHeader(u32 /* cursor */), LoadProgramHeaderTable( u32, /* cursor */ - u32, /* offset */ + usize, /* offset */ u16, /* num */ u16, /* index */ ), @@ -336,7 +336,7 @@ // Kick off reading the ELF program headers. self.state.set(ElfLoaderState::LoadProgramHeaderTable( cursor, - elf_header.phoff(), + elf_header.phoff() as usize, elf_header.phnum(), 0, )); @@ -360,14 +360,15 @@ self.read_page.map(|page| { let mut_page = page.as_mut(); match self.state.get() { - ElfLoaderState::LoadProgramHeaderTable(_, offset, _, index) => { - let phdr = Elf32Phdr::from_bytes( - &mut mut_page[(((offset as usize) - + (index as usize * core::mem::size_of::<Elf32Phdr>())) - as usize)..], - ); + ElfLoaderState::LoadProgramHeaderTable(_, page_offset, _, _index) => { + let page_bytes = &mut_page[page_offset..]; + // NB: assume word-alignment so it's safe to read the p_type field + if Elf32Phdr::get_type(page_bytes) == PT_LOAD { + // NB: we do not handle a PHDR spanning flash pages + assert!(page_offset + core::mem::size_of::<Elf32Phdr>() <= self.page_len as usize, + "ELF PHDR spans flash pages"); + let phdr = Elf32Phdr::from_bytes(page_bytes); //dprintf!("[{}] {:#08x?}\r\n", index, &phdr); - if phdr.p_type == PT_LOAD { let mut sel4_state = self.sel4_state.get(); let mut next_slot: Option<usize> = None; for i in 0..sel4_state.phdrs.len() { @@ -388,15 +389,23 @@ }; }); match self.state.get() { - ElfLoaderState::LoadProgramHeaderTable(cursor, offset, count, index) => { + ElfLoaderState::LoadProgramHeaderTable(cursor, page_offset, count, index) => { if index + 1 < count { + let mut new_page_offset = page_offset + core::mem::size_of::<Elf32Phdr>(); + let new_cursor = if new_page_offset >= self.page_len as usize { + new_page_offset %= self.page_len as usize; + cursor + self.page_len + } else { + cursor + }; self.state.set(ElfLoaderState::LoadProgramHeaderTable( - cursor, - offset, + new_cursor, + new_page_offset, count, index + 1, )); - self.read_page(cursor); + // NB: this may read the same flash page for multiple PHDRs + self.read_page(new_cursor); } else { // Record state derived from phdrs before they get clobbered. let mut segment_load_offset = 0;
diff --git a/utils/src/elf_loader.rs b/utils/src/elf_loader.rs index 3055828..288a79a 100644 --- a/utils/src/elf_loader.rs +++ b/utils/src/elf_loader.rs
@@ -59,7 +59,10 @@ } impl Elf32Phdr { - pub fn from_bytes(b: &mut [u8]) -> Elf32Phdr { + pub fn get_type(b: &[u8]) -> u32 { + unsafe { ptr::read(b.as_ptr() as *const u32) } + } + pub fn from_bytes(b: &[u8]) -> Elf32Phdr { unsafe { ptr::read(b.as_ptr() as *const Elf32Phdr) } } }