use super::load_command::{LoadCommand, LC_SEGMENT, LC_SEGMENT_64};
use super::relocation::RelocationEntry;
use crate::errors::FileParseError;
use crate::field::{ByteOrder, Field, FixedBytes, NumericFieldMut};
#[derive(Debug)]
pub struct Section32Fields {
pub sectname: Field<FixedBytes<16>>,
pub segname: Field<FixedBytes<16>>,
pub addr: Field<u32>,
pub size: Field<u32>,
pub offset: Field<u32>,
pub align: Field<u32>,
pub reloff: Field<u32>,
pub nreloc: Field<u32>,
pub flags: Field<u32>,
}
#[derive(Debug)]
pub struct Section64Fields {
pub sectname: Field<FixedBytes<16>>,
pub segname: Field<FixedBytes<16>>,
pub addr: Field<u64>,
pub size: Field<u64>,
pub offset: Field<u32>,
pub align: Field<u32>,
pub reloff: Field<u32>,
pub nreloc: Field<u32>,
pub flags: Field<u32>,
}
#[derive(Debug)]
pub enum SectionEntry {
Section32(Section32Fields),
Section64(Section64Fields),
}
impl SectionEntry {
fn record_size(is_64: bool) -> usize {
if is_64 {
80
} else {
68
}
}
pub fn name(&self) -> &str {
match self {
SectionEntry::Section32(f) => f.sectname.value.as_str(),
SectionEntry::Section64(f) => f.sectname.value.as_str(),
}
}
pub fn segment_name(&self) -> &str {
match self {
SectionEntry::Section32(f) => f.segname.value.as_str(),
SectionEntry::Section64(f) => f.segname.value.as_str(),
}
}
pub fn addr(&self) -> u64 {
match self {
SectionEntry::Section32(f) => f.addr.value as u64,
SectionEntry::Section64(f) => f.addr.value,
}
}
pub fn addr_mut(&mut self) -> NumericFieldMut<'_> {
match self {
SectionEntry::Section32(f) => NumericFieldMut::U32(&mut f.addr),
SectionEntry::Section64(f) => NumericFieldMut::U64(&mut f.addr),
}
}
pub fn size(&self) -> u64 {
match self {
SectionEntry::Section32(f) => f.size.value as u64,
SectionEntry::Section64(f) => f.size.value,
}
}
pub fn size_mut(&mut self) -> NumericFieldMut<'_> {
match self {
SectionEntry::Section32(f) => NumericFieldMut::U32(&mut f.size),
SectionEntry::Section64(f) => NumericFieldMut::U64(&mut f.size),
}
}
pub fn offset(&self) -> u32 {
match self {
SectionEntry::Section32(f) => f.offset.value,
SectionEntry::Section64(f) => f.offset.value,
}
}
pub fn reloff(&self) -> u32 {
match self {
SectionEntry::Section32(f) => f.reloff.value,
SectionEntry::Section64(f) => f.reloff.value,
}
}
pub fn nreloc(&self) -> u32 {
match self {
SectionEntry::Section32(f) => f.nreloc.value,
SectionEntry::Section64(f) => f.nreloc.value,
}
}
pub fn flags(&self) -> u32 {
match self {
SectionEntry::Section32(f) => f.flags.value,
SectionEntry::Section64(f) => f.flags.value,
}
}
pub fn relocations(
&self,
buffer: &[u8],
order: ByteOrder,
) -> Result<Vec<RelocationEntry>, FileParseError> {
let count = self.nreloc() as usize;
if count == 0 {
return Ok(Vec::new());
}
RelocationEntry::parse_table(buffer, self.reloff() as usize, count, order)
}
pub fn record_offset(&self) -> usize {
match self {
SectionEntry::Section32(f) => f.sectname.offset,
SectionEntry::Section64(f) => f.sectname.offset,
}
}
pub fn is_64(&self) -> bool {
matches!(self, SectionEntry::Section64(_))
}
fn parse_one(
buffer: &[u8],
offset: usize,
is_64: bool,
order: ByteOrder,
) -> Result<Self, FileParseError> {
if buffer.len() < offset + Self::record_size(is_64) {
return Err(FileParseError::BufferOverflow);
}
let sectname = FixedBytes::from_slice(&buffer[offset..offset + 16]);
let segname = FixedBytes::from_slice(&buffer[offset + 16..offset + 32]);
if is_64 {
Ok(SectionEntry::Section64(Section64Fields {
sectname: Field::new(sectname, offset, 16),
segname: Field::new(segname, offset + 16, 16),
addr: Field::new(order.read_u64(buffer, offset + 32)?, offset + 32, 8),
size: Field::new(order.read_u64(buffer, offset + 40)?, offset + 40, 8),
offset: Field::new(order.read_u32(buffer, offset + 48)?, offset + 48, 4),
align: Field::new(order.read_u32(buffer, offset + 52)?, offset + 52, 4),
reloff: Field::new(order.read_u32(buffer, offset + 56)?, offset + 56, 4),
nreloc: Field::new(order.read_u32(buffer, offset + 60)?, offset + 60, 4),
flags: Field::new(order.read_u32(buffer, offset + 64)?, offset + 64, 4),
}))
} else {
Ok(SectionEntry::Section32(Section32Fields {
sectname: Field::new(sectname, offset, 16),
segname: Field::new(segname, offset + 16, 16),
addr: Field::new(order.read_u32(buffer, offset + 32)?, offset + 32, 4),
size: Field::new(order.read_u32(buffer, offset + 36)?, offset + 36, 4),
offset: Field::new(order.read_u32(buffer, offset + 40)?, offset + 40, 4),
align: Field::new(order.read_u32(buffer, offset + 44)?, offset + 44, 4),
reloff: Field::new(order.read_u32(buffer, offset + 48)?, offset + 48, 4),
nreloc: Field::new(order.read_u32(buffer, offset + 52)?, offset + 52, 4),
flags: Field::new(order.read_u32(buffer, offset + 56)?, offset + 56, 4),
}))
}
}
pub(crate) fn parse_sections(
buffer: &[u8],
load_commands: &[LoadCommand],
order: ByteOrder,
) -> Result<Vec<Self>, FileParseError> {
let mut sections = Vec::new();
for cmd in load_commands {
let is_64 = match cmd.cmd.value {
LC_SEGMENT => false,
LC_SEGMENT_64 => true,
_ => continue,
};
let seg_off = cmd.cmd.offset;
let nsects_off = if is_64 { seg_off + 64 } else { seg_off + 48 };
let nsects = order.read_u32(buffer, nsects_off)?;
let header_size = if is_64 { 72 } else { 56 };
let mut sect_off = seg_off + header_size;
for _ in 0..nsects {
let section = Self::parse_one(buffer, sect_off, is_64, order)?;
sect_off += Self::record_size(is_64);
sections.push(section);
}
}
Ok(sections)
}
}
pub struct NewSection {
pub name: String,
pub addr: u64,
pub size: u64,
pub offset: u32,
pub align: u32,
pub flags: u32,
}