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 | 99 ++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs index 72cefc3142ea..6ed76a7e15f1 100644 --- a/drivers/gpu/nova-core/firmware.rs +++ b/drivers/gpu/nova-core/firmware.rs @@ -453,17 +453,60 @@ mod elf { 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 + } + } + /// Returns a NULL-terminated string from the ELF image at `offset`. fn elf_str(elf: &[u8], offset: u64) -> Option<&str> { let idx = usize::try_from(offset).ok()?; @@ -471,47 +514,49 @@ fn elf_str(elf: &[u8], offset: u64) -> Option<&str> { CStr::from_bytes_until_nul(bytes).ok()?.to_str().ok() } - /// 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; + 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>())?)?; - // 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))?; + 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))?; - elf.get(shdr_start..shdr_end) - .map(|slice| slice.chunks_exact(size_of::<Elf64SHdr>()))? - }; + // 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_map(|sh| { - let hdr = Elf64SHdr::from_bytes(sh)?; - let name_offset = strhdr.0.sh_offset.checked_add(u64::from(hdr.0.sh_name))?; + shdr_iter.find_map(|sh_bytes| { + let sh = S::from_bytes(sh_bytes)?; + let name_offset = strhdr.offset().checked_add(u64::from(sh.name()))?; let section_name = elf_str(elf, name_offset)?; if section_name != name { return None; } - let start = usize::try_from(hdr.0.sh_offset).ok()?; - let end = usize::try_from(hdr.0.sh_size) + let start = usize::try_from(sh.offset()).ok()?; + let end = usize::try_from(sh.size()) .ok() - .and_then(|sh_size| start.checked_add(sh_size))?; - + .and_then(|sz| start.checked_add(sz))?; elf.get(start..end) }) } + + /// 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
