use crate::errors::FileParseError;
use crate::field::{ByteOrder, Field};
pub const R_SCATTERED: u32 = 0x8000_0000;
#[derive(Debug)]
pub struct RelocationInfo {
pub r_address: Field<i32>,
pub r_symbolnum: Field<u32>,
pub r_pcrel: bool,
pub r_length: u8,
pub r_extern: bool,
pub r_type: u8,
}
#[derive(Debug)]
pub struct ScatteredRelocationInfo {
pub r_address: Field<u32>,
pub r_type: Field<u8>,
pub r_length: Field<u8>,
pub r_pcrel: bool,
pub r_value: Field<i32>,
}
#[derive(Debug)]
pub enum RelocationEntry {
Regular(RelocationInfo),
Scattered(ScatteredRelocationInfo),
}
impl RelocationEntry {
pub const SIZE: usize = 8;
pub fn parse(buffer: &[u8], offset: usize, order: ByteOrder) -> Result<Self, FileParseError> {
if buffer.len() < offset + Self::SIZE {
return Err(FileParseError::BufferOverflow);
}
let word0 = order.read_u32(buffer, offset)?;
if word0 & R_SCATTERED != 0 {
let word1 = order.read_u32(buffer, offset + 4)?;
Ok(RelocationEntry::Scattered(ScatteredRelocationInfo {
r_address: Field::new(word0 & 0x00FF_FFFF, offset, 4),
r_type: Field::new(((word0 >> 24) & 0x0F) as u8, offset, 4),
r_length: Field::new(((word0 >> 28) & 0x03) as u8, offset, 4),
r_pcrel: (word0 >> 30) & 1 != 0,
r_value: Field::new(word1 as i32, offset + 4, 4),
}))
} else {
let word1 = order.read_u32(buffer, offset + 4)?;
Ok(RelocationEntry::Regular(RelocationInfo {
r_address: Field::new(word0 as i32, offset, 4),
r_symbolnum: Field::new(word1 & 0x00FF_FFFF, offset + 4, 4),
r_pcrel: (word1 >> 24) & 1 != 0,
r_length: ((word1 >> 25) & 0x03) as u8,
r_extern: (word1 >> 27) & 1 != 0,
r_type: ((word1 >> 28) & 0x0F) as u8,
}))
}
}
pub fn parse_table(
buffer: &[u8],
offset: usize,
count: usize,
order: ByteOrder,
) -> Result<Vec<Self>, FileParseError> {
let end = offset
.checked_add(count * Self::SIZE)
.ok_or(FileParseError::BufferOverflow)?;
if buffer.len() < end {
return Err(FileParseError::BufferOverflow);
}
(0..count)
.map(|i| Self::parse(buffer, offset + i * Self::SIZE, order))
.collect()
}
}