Add ElfHeader and ElfSectionHeader traits to abstract out differences between ELF32 and ELF64. Implement these for ELF64.
This is in preparation for upcoming ELF32 section support, and for auto-selecting ELF32 or ELF64. Signed-off-by: John Hubbard <[email protected]> --- drivers/gpu/nova-core/firmware.rs | 118 ++++++++++++++++++++---------- 1 file changed, 80 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs index 5ed079a45ec2..1cb5897778f8 100644 --- a/drivers/gpu/nova-core/firmware.rs +++ b/drivers/gpu/nova-core/firmware.rs @@ -256,17 +256,60 @@ mod elf { use kernel::str::CStr; use kernel::transmute::FromBytes; - /// Newtype to provide a [`FromBytes`] implementation. + /// Trait to abstract over ELF header differences (32-bit vs 64-bit). + trait ElfHeader: FromBytes { + fn shnum(&self) -> u16; + fn shoff(&self) -> u64; + fn shstrndx(&self) -> u16; + } + + /// Trait to abstract over ELF section header differences (32-bit vs 64-bit). + trait ElfSectionHeader: FromBytes { + fn name(&self) -> u32; + fn offset(&self) -> u64; + fn size(&self) -> u64; + } + + /// Newtype to provide [`FromBytes`] and [`ElfHeader`] implementations. #[repr(transparent)] struct Elf64Hdr(bindings::elf64_hdr); // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability. unsafe impl FromBytes for Elf64Hdr {} + impl ElfHeader for Elf64Hdr { + fn shnum(&self) -> u16 { + self.0.e_shnum + } + + fn shoff(&self) -> u64 { + self.0.e_shoff + } + + fn shstrndx(&self) -> u16 { + self.0.e_shstrndx + } + } + + /// Newtype to provide [`FromBytes`] and [`ElfSectionHeader`] implementations. #[repr(transparent)] struct Elf64SHdr(bindings::elf64_shdr); // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability. unsafe impl FromBytes for Elf64SHdr {} + impl ElfSectionHeader for Elf64SHdr { + fn name(&self) -> u32 { + self.0.sh_name + } + + fn offset(&self) -> u64 { + self.0.sh_offset + } + + fn size(&self) -> u64 { + self.0.sh_size + } + } + /// Check if the section name at `strtab_offset + name_offset` equals `target`. fn section_name_eq(elf: &[u8], strtab_offset: u64, name_offset: u32, target: &str) -> bool { strtab_offset @@ -285,48 +328,47 @@ fn section_name_eq(elf: &[u8], strtab_offset: u64, name_offset: u32, target: &st .is_some_and(|s| s == target) } - /// Tries to extract section with name `name` from the ELF64 image `elf`, and returns it. - pub(super) fn elf64_section<'a, 'b>(elf: &'a [u8], name: &'b str) -> Option<&'a [u8]> { - let hdr = &elf - .get(0..size_of::<bindings::elf64_hdr>()) - .and_then(Elf64Hdr::from_bytes)? - .0; - - // Get all the section headers. - let mut shdr = { - let shdr_num = usize::from(hdr.e_shnum); - let shdr_start = usize::try_from(hdr.e_shoff).ok()?; - let shdr_end = shdr_num - .checked_mul(size_of::<Elf64SHdr>()) - .and_then(|v| v.checked_add(shdr_start))?; - - elf.get(shdr_start..shdr_end) - .map(|slice| slice.chunks_exact(size_of::<Elf64SHdr>()))? - }; + fn elf_section_generic<'a, H, S>(elf: &'a [u8], name: &str) -> Option<&'a [u8]> + where + H: ElfHeader, + S: ElfSectionHeader, + { + let hdr = H::from_bytes(elf.get(0..size_of::<H>())?)?; + + let shdr_num = usize::from(hdr.shnum()); + let shdr_start = usize::try_from(hdr.shoff()).ok()?; + let shdr_end = shdr_num + .checked_mul(size_of::<S>()) + .and_then(|v| v.checked_add(shdr_start))?; + + // Get all the section headers as an iterator over byte chunks. + let shdr_bytes = elf.get(shdr_start..shdr_end)?; + let mut shdr_iter = shdr_bytes.chunks_exact(size_of::<S>()); // Get the strings table. - let strhdr = shdr + let strhdr = shdr_iter .clone() - .nth(usize::from(hdr.e_shstrndx)) - .and_then(Elf64SHdr::from_bytes)?; + .nth(usize::from(hdr.shstrndx())) + .and_then(S::from_bytes)?; // Find the section which name matches `name` and return it. - shdr.find(|&sh| { - let Some(hdr) = Elf64SHdr::from_bytes(sh) else { - return false; - }; - - section_name_eq(elf, strhdr.0.sh_offset, hdr.0.sh_name, name) - }) - // Return the slice containing the section. - .and_then(|sh| { - let hdr = Elf64SHdr::from_bytes(sh)?; - let start = usize::try_from(hdr.0.sh_offset).ok()?; - let end = usize::try_from(hdr.0.sh_size) - .ok() - .and_then(|sh_size| start.checked_add(sh_size))?; - - elf.get(start..end) + shdr_iter.find_map(|sh_bytes| { + let sh = S::from_bytes(sh_bytes)?; + + if section_name_eq(elf, strhdr.offset(), sh.name(), name) { + let start = usize::try_from(sh.offset()).ok()?; + let end = usize::try_from(sh.size()) + .ok() + .and_then(|sz| start.checked_add(sz))?; + elf.get(start..end) + } else { + None + } }) } + + /// Extract the section with name `name` from the ELF64 image `elf`. + pub(super) fn elf64_section<'a>(elf: &'a [u8], name: &str) -> Option<&'a [u8]> { + elf_section_generic::<Elf64Hdr, Elf64SHdr>(elf, name) + } } -- 2.52.0
