use crate::{dir::*, error::*, impl_from_bytes, mem::*};
use alloc::format;
use core::mem;
pub enum Relocation {
Absolute(u16),
High(u16),
Low(u16),
HighLow(u16),
HighAdj(u16),
MipsArmRiscv(u16),
ThumbRiscv(u16),
RiscvLoong(u16),
JmpAddr(u16),
Dir64(u16),
}
pub struct RelocationBlock<'a> {
data: ByteReader<'a>,
head: &'a RelocationHead,
}
impl<'a> RelocationBlock<'a> {
pub fn new(data: &'a [u8], head: &'a RelocationHead) -> Self {
Self {
data: ByteReader::new(data),
head,
}
}
pub fn page_rva(&self) -> u32 {
self.head.page_rva
}
pub fn entry_count(&self) -> usize {
(self.head.block_size as usize - mem::size_of::<RelocationHead>())
/ mem::size_of::<RelocationEntry>()
}
}
impl<'a> Iterator for RelocationBlock<'a> {
type Item = Result<Relocation>;
fn next(&mut self) -> Option<Self::Item> {
match (|| {
let entry = self.data.read::<RelocationEntry>()?;
Relocation::try_from(entry)
})() {
Ok(v) => Some(Ok(v)),
Err(Error::InsufficientBuffer) => None,
Err(e) => Some(Err(e)),
}
}
}
pub struct RelocationTable<'a> {
data: ByteReader<'a>,
}
impl<'a> DataDirectoryTable<'a> for RelocationTable<'a> {
fn new(bytes: &'a [u8], _dir: &'a DataDirectory) -> Self {
Self {
data: ByteReader::new(bytes),
}
}
fn typ() -> DataDirectoryType {
DataDirectoryType::RelocationTable
}
}
impl<'a> Iterator for RelocationTable<'a> {
type Item = Result<RelocationBlock<'a>>;
fn next(&mut self) -> Option<Self::Item> {
match self.data.read::<RelocationHead>() {
Ok(head) => {
if head.block_size == 0
|| head.block_size as usize % mem::size_of::<u32>() != 0
{
return None;
}
let data = &self.data.remaining_bytes()
[..head.block_size as usize - mem::size_of::<RelocationHead>()];
self.data.skip(SkipPos::Cur(data.len()));
Some(Ok(RelocationBlock::new(data, head)))
}
Err(Error::InsufficientBuffer) => None,
Err(e) => Some(Err(e)),
}
}
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct RelocationHead {
pub page_rva: u32,
pub block_size: u32,
}
#[derive(Clone, Copy)]
pub struct RelocationEntry(u16);
impl RelocationEntry {
pub fn offset(&self) -> u16 {
self.0 & 0x0FFF
}
pub fn kind(&self) -> u16 {
self.0 >> 12
}
}
impl TryFrom<&RelocationEntry> for Relocation {
type Error = Error;
fn try_from(value: &RelocationEntry) -> core::result::Result<Self, Self::Error> {
use Relocation::*;
Ok(match value.kind() {
0x0 => Absolute(value.offset()),
0x1 => High(value.offset()),
0x2 => Low(value.offset()),
0x3 => HighLow(value.offset()),
0x4 => HighAdj(value.offset()),
0x5 => MipsArmRiscv(value.offset()),
0x7 => ThumbRiscv(value.offset()),
0x8 => RiscvLoong(value.offset()),
0x9 => JmpAddr(value.offset()),
0xA => Dir64(value.offset()),
_ => {
return Error::make_malformed::<RelocationEntry, _>(format!(
"has invalid type ({})",
value.kind()
))
}
})
}
}
impl_from_bytes!(RelocationHead, RelocationEntry);