use core::fmt::Debug;
use core::mem;
use crate::elf;
use crate::endian::{self, U32};
use crate::pod::Pod;
use crate::read::util;
use crate::read::{self, Bytes, Error, ReadError};
use super::FileHeader;
#[derive(Debug)]
pub struct NoteIterator<'data, Elf>
where
Elf: FileHeader,
{
endian: Elf::Endian,
align: usize,
data: Bytes<'data>,
}
impl<'data, Elf> NoteIterator<'data, Elf>
where
Elf: FileHeader,
{
pub fn new(endian: Elf::Endian, align: Elf::Word, data: &'data [u8]) -> read::Result<Self> {
let align = match align.into() {
0u64..=4 => 4,
8 => 8,
_ => return Err(Error("Invalid ELF note alignment")),
};
Ok(NoteIterator {
endian,
align,
data: Bytes(data),
})
}
pub fn next(&mut self) -> read::Result<Option<Note<'data, Elf>>> {
if self.data.is_empty() {
return Ok(None);
}
let result = self.parse().map(Some);
if result.is_err() {
self.data = Bytes(&[]);
}
result
}
fn parse(&mut self) -> read::Result<Note<'data, Elf>> {
let header = self
.data
.read_at::<Elf::NoteHeader>(0)
.read_error("ELF note is too short")?;
let offset = mem::size_of::<Elf::NoteHeader>();
let namesz = header.n_namesz(self.endian) as usize;
let name = self
.data
.read_bytes_at(offset, namesz)
.read_error("Invalid ELF note namesz")?
.0;
let offset = util::align(offset + namesz, self.align);
let descsz = header.n_descsz(self.endian) as usize;
let desc = self
.data
.read_bytes_at(offset, descsz)
.read_error("Invalid ELF note descsz")?
.0;
let offset = util::align(offset + descsz, self.align);
if self.data.skip(offset).is_err() {
self.data = Bytes(&[]);
}
Ok(Note { header, name, desc })
}
}
impl<'data, Elf: FileHeader> Iterator for NoteIterator<'data, Elf> {
type Item = read::Result<Note<'data, Elf>>;
fn next(&mut self) -> Option<Self::Item> {
self.next().transpose()
}
}
#[derive(Debug)]
pub struct Note<'data, Elf>
where
Elf: FileHeader,
{
header: &'data Elf::NoteHeader,
name: &'data [u8],
desc: &'data [u8],
}
impl<'data, Elf: FileHeader> Note<'data, Elf> {
pub fn n_type(&self, endian: Elf::Endian) -> u32 {
self.header.n_type(endian)
}
pub fn n_namesz(&self, endian: Elf::Endian) -> u32 {
self.header.n_namesz(endian)
}
pub fn n_descsz(&self, endian: Elf::Endian) -> u32 {
self.header.n_descsz(endian)
}
pub fn name_bytes(&self) -> &'data [u8] {
self.name
}
pub fn name(&self) -> &'data [u8] {
let mut name = self.name;
while let [rest @ .., 0] = name {
name = rest;
}
name
}
pub fn desc(&self) -> &'data [u8] {
self.desc
}
pub fn gnu_properties(
&self,
endian: Elf::Endian,
) -> Option<GnuPropertyIterator<'data, Elf::Endian>> {
if self.name() != elf::ELF_NOTE_GNU || self.n_type(endian) != elf::NT_GNU_PROPERTY_TYPE_0 {
return None;
}
let align = if Elf::is_type_64_sized() { 8 } else { 4 };
Some(GnuPropertyIterator {
endian,
align,
data: Bytes(self.desc),
})
}
}
#[allow(missing_docs)]
pub trait NoteHeader: Debug + Pod {
type Endian: endian::Endian;
fn n_namesz(&self, endian: Self::Endian) -> u32;
fn n_descsz(&self, endian: Self::Endian) -> u32;
fn n_type(&self, endian: Self::Endian) -> u32;
}
impl<Endian: endian::Endian> NoteHeader for elf::NoteHeader32<Endian> {
type Endian = Endian;
#[inline]
fn n_namesz(&self, endian: Self::Endian) -> u32 {
self.n_namesz.get(endian)
}
#[inline]
fn n_descsz(&self, endian: Self::Endian) -> u32 {
self.n_descsz.get(endian)
}
#[inline]
fn n_type(&self, endian: Self::Endian) -> u32 {
self.n_type.get(endian)
}
}
impl<Endian: endian::Endian> NoteHeader for elf::NoteHeader64<Endian> {
type Endian = Endian;
#[inline]
fn n_namesz(&self, endian: Self::Endian) -> u32 {
self.n_namesz.get(endian)
}
#[inline]
fn n_descsz(&self, endian: Self::Endian) -> u32 {
self.n_descsz.get(endian)
}
#[inline]
fn n_type(&self, endian: Self::Endian) -> u32 {
self.n_type.get(endian)
}
}
#[derive(Debug)]
pub struct GnuPropertyIterator<'data, Endian: endian::Endian> {
endian: Endian,
align: usize,
data: Bytes<'data>,
}
impl<'data, Endian: endian::Endian> GnuPropertyIterator<'data, Endian> {
pub fn next(&mut self) -> read::Result<Option<GnuProperty<'data>>> {
if self.data.is_empty() {
return Ok(None);
}
let result = self.parse().map(Some);
if result.is_err() {
self.data = Bytes(&[]);
}
result
}
fn parse(&mut self) -> read::Result<GnuProperty<'data>> {
(|| -> Result<_, ()> {
let pr_type = self.data.read_at::<U32<Endian>>(0)?.get(self.endian);
let pr_datasz = self.data.read_at::<U32<Endian>>(4)?.get(self.endian) as usize;
let pr_data = self.data.read_bytes_at(8, pr_datasz)?.0;
self.data.skip(util::align(8 + pr_datasz, self.align))?;
Ok(GnuProperty { pr_type, pr_data })
})()
.read_error("Invalid ELF GNU property")
}
}
impl<'data, Endian: endian::Endian> Iterator for GnuPropertyIterator<'data, Endian> {
type Item = read::Result<GnuProperty<'data>>;
fn next(&mut self) -> Option<Self::Item> {
self.next().transpose()
}
}
#[derive(Debug)]
pub struct GnuProperty<'data> {
pr_type: u32,
pr_data: &'data [u8],
}
impl<'data> GnuProperty<'data> {
pub fn pr_type(&self) -> u32 {
self.pr_type
}
pub fn pr_data(&self) -> &'data [u8] {
self.pr_data
}
pub fn data_u32<E: endian::Endian>(&self, endian: E) -> read::Result<u32> {
Bytes(self.pr_data)
.read_at::<U32<E>>(0)
.read_error("Invalid ELF GNU property data")
.map(|val| val.get(endian))
}
}