use core::num::NonZero;
use crate::errors::{Error, Result};
use crate::utils::usize_align_up_offset;
const NOTE_GNU_PROPERTY_SECTION_NAME: &str = ".note.gnu.property";
const ELF_NOTE_GNU: &str = "GNU";
#[derive(Debug, Clone, Copy)]
pub(crate) struct RawGNUProperty<'bytes> {
pr_type: u32,
pr_data: &'bytes [u8],
}
impl<'bytes> RawGNUProperty<'bytes> {
fn parse_all(elf: &'bytes goblin::elf::Elf, bytes: &'bytes [u8]) -> Result<Vec<Self>> {
let u32_from_bytes = if elf.little_endian {
u32::from_le_bytes
} else {
u32::from_be_bytes
};
let word_size = if elf.is_64 {
unsafe { NonZero::new_unchecked(8) }
} else {
unsafe { NonZero::new_unchecked(4) }
};
Self::gnu_property_type0_data(elf, bytes)?
.into_iter()
.try_fold(Vec::default(), |mut acc, mut property| {
while !property.is_empty() {
let raw = Self::parse(&mut property, word_size, u32_from_bytes)?;
acc.push(raw);
}
Ok(acc)
})
}
fn parse(
data: &mut &'bytes [u8],
word_size: NonZero<usize>,
u32_from_bytes: fn([u8; 4]) -> u32,
) -> Result<Self> {
let pr_type = <[u8; 4]>::try_from(&data[..4]).map(u32_from_bytes)?;
let pr_datasz = <[u8; 4]>::try_from(&data[4..8])
.map(u32_from_bytes)?
.try_into()?;
let result = RawGNUProperty {
pr_type,
pr_data: &data[8..8_usize.saturating_add(pr_datasz)],
};
let pr_padding_size = usize_align_up_offset(pr_datasz, word_size);
let property_size = 8_usize
.saturating_add(pr_datasz)
.saturating_add(pr_padding_size);
*data = &data[property_size..];
Ok(result)
}
fn gnu_property_type0_data(
elf: &'bytes goblin::elf::Elf,
bytes: &'bytes [u8],
) -> Result<Vec<&'bytes [u8]>> {
elf.iter_note_sections(bytes, Some(NOTE_GNU_PROPERTY_SECTION_NAME))
.into_iter()
.chain(elf.iter_note_headers(bytes))
.flatten()
.try_fold(Vec::with_capacity(2), |mut elements, note| {
let note = note.map_err(|source| Error::ParseFile { source })?;
if note.name == ELF_NOTE_GNU
&& note.n_type == goblin::elf::note::NT_GNU_PROPERTY_TYPE_0
{
elements.push(note.desc);
}
Ok(elements)
})
.map(|mut elements| {
elements.sort_unstable();
elements.dedup();
elements
})
}
fn as_flags(&self, elf: &'bytes goblin::elf::Elf) -> Option<u64> {
if let Ok(pr_data) = <[u8; 8]>::try_from(self.pr_data) {
if elf.little_endian {
Some(u64::from_le_bytes(pr_data))
} else {
Some(u64::from_be_bytes(pr_data))
}
} else if let Ok(pr_data) = <[u8; 4]>::try_from(self.pr_data) {
if elf.little_endian {
Some(u32::from_le_bytes(pr_data).into())
} else {
Some(u32::from_be_bytes(pr_data).into())
}
} else if let Ok(pr_data) = <[u8; 2]>::try_from(self.pr_data) {
if elf.little_endian {
Some(u16::from_le_bytes(pr_data).into())
} else {
Some(u16::from_be_bytes(pr_data).into())
}
} else if let Ok(pr_data) = <[u8; 1]>::try_from(self.pr_data) {
Some(pr_data[0].into())
} else {
None
}
}
}
bitflags::bitflags! {
#[derive(Debug)]
pub(crate) struct GNUPropertyX86Feature1: u64 {
const IBT = 1 << 0;
const SHSTK = 1 << 1;
}
}
bitflags::bitflags! {
#[derive(Debug)]
pub(crate) struct GNUPropertyX86Feature2: u64 {
const X86 = 1 << 0;
const X87 = 1 << 1;
const MMX = 1 << 2;
const XMM = 1 << 3;
const YMM = 1 << 4;
const ZMM = 1 << 5;
const FXSR = 1 << 6;
const XSAVE = 1 << 7;
const XSAVEOPT = 1 << 8;
const XSAVEC = 1 << 9;
const TMM = 1 << 10;
const MASK = 1 << 11;
}
}
bitflags::bitflags! {
#[derive(Debug)]
pub(crate) struct GNUPropertyX86ISA1: u64 {
const BASELINE = 1 << 0;
const V2 = 1 << 1;
const V3 = 1 << 2;
const V4 = 1 << 3;
}
}
bitflags::bitflags! {
#[derive(Debug)]
pub(crate) struct GNUPropertyAArch64Feature1: u64 {
const BTI = 1 << 0;
const PAC = 1 << 1;
const GCS = 1 << 2;
}
}
bitflags::bitflags! {
#[derive(Debug)]
pub(crate) struct GNUPropertyAArch64FeaturePAuth: u64 {
const INVALID = 0x0;
const BAREMETAL = 0x1;
const LLVM_LINUX = 0x1000_0002;
}
}
#[derive(Debug)]
#[allow(unused)]
pub(crate) enum GNUProperty<'bytes> {
StackSize(u64),
NoCopyOnProtected,
ProcessorSpecific(RawGNUProperty<'bytes>),
UserSpecific(RawGNUProperty<'bytes>),
X86Feature1And(GNUPropertyX86Feature1),
X86UInt32And(u64),
X86Feature2Used(GNUPropertyX86Feature2),
X86UInt32OrAnd(u64),
X86Feature2Needed(GNUPropertyX86Feature2),
X86UInt32Or(u64),
X86ISA1Used(GNUPropertyX86ISA1),
X86ISA1Needed(GNUPropertyX86ISA1),
AARCH64Feature1And(GNUPropertyAArch64Feature1),
AARCH64FeaturePAuth(GNUPropertyAArch64FeaturePAuth),
}
const GNU_PROPERTY_STACK_SIZE: u32 = 1;
const GNU_PROPERTY_NO_COPY_ON_PROTECTED: u32 = 2;
const GNU_PROPERTY_LOPROC: u32 = 0xc000_0000;
const GNU_PROPERTY_HIPROC: u32 = 0xdfff_ffff;
const GNU_PROPERTY_LOUSER: u32 = 0xe000_0000;
const GNU_PROPERTY_HIUSER: u32 = 0xffff_ffff;
const GNU_PROPERTY_X86_UINT32_AND_LO: u32 = 0xc000_0002;
const GNU_PROPERTY_X86_FEATURE_1_AND: u32 = GNU_PROPERTY_X86_UINT32_AND_LO;
const GNU_PROPERTY_X86_UINT32_AND_HI: u32 = 0xc000_7fff;
const GNU_PROPERTY_X86_UINT32_OR_LO: u32 = 0xc000_8000;
const GNU_PROPERTY_X86_FEATURE_2_NEEDED: u32 = GNU_PROPERTY_X86_UINT32_OR_LO + 1;
const GNU_PROPERTY_X86_ISA_1_NEEDED: u32 = GNU_PROPERTY_X86_UINT32_OR_LO + 2;
const GNU_PROPERTY_X86_UINT32_OR_HI: u32 = 0xc000_ffff;
const GNU_PROPERTY_X86_UINT32_OR_AND_LO: u32 = 0xc001_0000;
const GNU_PROPERTY_X86_FEATURE_2_USED: u32 = GNU_PROPERTY_X86_UINT32_OR_AND_LO + 1;
const GNU_PROPERTY_X86_ISA_1_USED: u32 = GNU_PROPERTY_X86_UINT32_OR_AND_LO + 2;
const GNU_PROPERTY_X86_UINT32_OR_AND_HI: u32 = 0xc001_7fff;
const GNU_PROPERTY_AARCH64_FEATURE_1_AND: u32 = 0xc000_0000;
const GNU_PROPERTY_AARCH64_FEATURE_PAUTH: u32 = 0xc000_0001;
impl<'bytes> GNUProperty<'bytes> {
pub(crate) fn parse_all(
elf: &'bytes goblin::elf::Elf,
bytes: &'bytes [u8],
) -> Result<Vec<Self>> {
RawGNUProperty::parse_all(elf, bytes)?
.into_iter()
.map(|raw| Self::from_raw(&raw, elf))
.collect()
}
fn from_raw(raw: &'_ RawGNUProperty<'bytes>, elf: &'bytes goblin::elf::Elf) -> Result<Self> {
use goblin::elf::header::{EM_386, EM_AARCH64, EM_X86_64};
match raw.pr_type {
0 | 3..GNU_PROPERTY_LOPROC => Err(Error::InvalidGNUProgramPropertyType(raw.pr_type)),
GNU_PROPERTY_STACK_SIZE => {
let size = match (elf.is_64, elf.little_endian) {
(false, false) => {
u64::from(u32::from_be_bytes(<[u8; 4]>::try_from(raw.pr_data)?))
}
(false, true) => {
u64::from(u32::from_le_bytes(<[u8; 4]>::try_from(raw.pr_data)?))
}
(true, false) => u64::from_be_bytes(<[u8; 8]>::try_from(raw.pr_data)?),
(true, true) => u64::from_le_bytes(<[u8; 8]>::try_from(raw.pr_data)?),
};
Ok(Self::StackSize(size))
}
GNU_PROPERTY_NO_COPY_ON_PROTECTED => Ok(Self::NoCopyOnProtected),
GNU_PROPERTY_LOPROC..=GNU_PROPERTY_HIPROC => {
let value = match elf.header.e_machine {
EM_386 | EM_X86_64 => Self::from_raw_cpu_specific_x86(raw, elf),
EM_AARCH64 => Self::from_raw_cpu_specific_aarch64(raw, elf),
_ => None,
};
Ok(value.unwrap_or(Self::ProcessorSpecific(*raw)))
}
GNU_PROPERTY_LOUSER..=GNU_PROPERTY_HIUSER => Ok(Self::UserSpecific(*raw)),
}
}
fn from_raw_cpu_specific_x86(
raw: &'_ RawGNUProperty<'bytes>,
elf: &'bytes goblin::elf::Elf,
) -> Option<Self> {
const AFTER_GNU_PROPERTY_X86_UINT32_OR_AND_HI: u32 = GNU_PROPERTY_X86_UINT32_OR_AND_HI + 1;
match raw.pr_type {
0..GNU_PROPERTY_X86_UINT32_AND_LO | AFTER_GNU_PROPERTY_X86_UINT32_OR_AND_HI.. => None,
GNU_PROPERTY_X86_UINT32_AND_LO..=GNU_PROPERTY_X86_UINT32_AND_HI => match raw.pr_type {
0..GNU_PROPERTY_X86_FEATURE_1_AND | 0xc000_0003.. => {
raw.as_flags(elf).map(Self::X86UInt32And)
}
GNU_PROPERTY_X86_FEATURE_1_AND => raw
.as_flags(elf)
.map(GNUPropertyX86Feature1::from_bits_retain)
.map(Self::X86Feature1And),
},
GNU_PROPERTY_X86_UINT32_OR_LO..=GNU_PROPERTY_X86_UINT32_OR_HI => match raw.pr_type {
0..GNU_PROPERTY_X86_FEATURE_2_NEEDED | 0xc000_8003.. => {
raw.as_flags(elf).map(Self::X86UInt32Or)
}
GNU_PROPERTY_X86_FEATURE_2_NEEDED => raw
.as_flags(elf)
.map(GNUPropertyX86Feature2::from_bits_retain)
.map(Self::X86Feature2Needed),
GNU_PROPERTY_X86_ISA_1_NEEDED => raw
.as_flags(elf)
.map(GNUPropertyX86ISA1::from_bits_retain)
.map(Self::X86ISA1Needed),
},
GNU_PROPERTY_X86_UINT32_OR_AND_LO..=GNU_PROPERTY_X86_UINT32_OR_AND_HI => {
match raw.pr_type {
0..GNU_PROPERTY_X86_FEATURE_2_USED | 0xc001_0003.. => {
raw.as_flags(elf).map(Self::X86UInt32OrAnd)
}
GNU_PROPERTY_X86_FEATURE_2_USED => raw
.as_flags(elf)
.map(GNUPropertyX86Feature2::from_bits_retain)
.map(Self::X86Feature2Used),
GNU_PROPERTY_X86_ISA_1_USED => raw
.as_flags(elf)
.map(GNUPropertyX86ISA1::from_bits_retain)
.map(Self::X86ISA1Used),
}
}
}
}
fn from_raw_cpu_specific_aarch64(
raw: &'_ RawGNUProperty<'bytes>,
elf: &'bytes goblin::elf::Elf,
) -> Option<Self> {
const AFTER_GNU_PROPERTY_AARCH64_FEATURE_PAUTH: u32 =
GNU_PROPERTY_AARCH64_FEATURE_PAUTH + 1;
match raw.pr_type {
0..GNU_PROPERTY_AARCH64_FEATURE_1_AND | AFTER_GNU_PROPERTY_AARCH64_FEATURE_PAUTH.. => {
None
}
GNU_PROPERTY_AARCH64_FEATURE_1_AND => raw
.as_flags(elf)
.map(GNUPropertyAArch64Feature1::from_bits_retain)
.map(Self::AARCH64Feature1And),
GNU_PROPERTY_AARCH64_FEATURE_PAUTH => raw
.as_flags(elf)
.map(GNUPropertyAArch64FeaturePAuth::from_bits_retain)
.map(Self::AARCH64FeaturePAuth),
}
}
}