use byteorder::{ByteOrder, LittleEndian};
use std::fmt::Debug;
use std::convert::TryFrom;
#[derive(Debug)]
pub struct ImpossibleRelocation { }
pub trait Relocation {
fn from_encoding(encoding: u8) -> Self;
fn from_size(kind: RelocationKind, size: RelocationSize) -> Self;
fn size(&self) -> usize;
fn write_value(&self, buf: &mut [u8], value: isize) -> Result<(), ImpossibleRelocation>;
fn read_value(&self, buf: &[u8]) -> isize;
fn kind(&self) -> RelocationKind;
fn page_size() -> usize;
}
pub trait ArchitectureRelocationEncoding : Clone + Copy + Debug {
fn decode(code: u8) -> Self;
}
#[derive(Clone, Copy, Debug)]
pub enum RelocationKind {
Relative,
AbsToRel,
RelToAbs,
Absolute
}
#[derive(Clone, Copy, Debug)]
pub enum RelocationEncoding<A: ArchitectureRelocationEncoding> {
Simple(RelocationSize),
ArchSpecific(A)
}
#[derive(Clone, Copy, Debug)]
pub struct RelocationType<A: ArchitectureRelocationEncoding> {
pub kind: RelocationKind,
pub encoding: RelocationEncoding<A>
}
impl<A: ArchitectureRelocationEncoding> RelocationType<A> {
pub fn decode(code: u8) -> Self {
let kind = match code >> 6 {
0 => RelocationKind::Relative,
1 => RelocationKind::AbsToRel,
2 => RelocationKind::RelToAbs,
3 => RelocationKind::Absolute,
_ => unreachable!()
};
let encoding = match code & 0x3F {
0 => RelocationEncoding::Simple(RelocationSize::Byte),
1 => RelocationEncoding::Simple(RelocationSize::Word),
2 => RelocationEncoding::Simple(RelocationSize::DWord),
3 => RelocationEncoding::Simple(RelocationSize::QWord),
c => RelocationEncoding::ArchSpecific(A::decode(c - 4))
};
RelocationType {
kind,
encoding
}
}
pub fn from_size(kind: RelocationKind, size: RelocationSize) -> Self {
RelocationType {
kind,
encoding: RelocationEncoding::Simple(size)
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum RelocationSize {
Byte = 1,
Word = 2,
DWord = 4,
QWord = 8,
}
impl RelocationSize {
pub fn size(&self) -> usize {
*self as usize
}
pub fn write_value(&self, buf: &mut [u8], value: isize) -> Result<(), ImpossibleRelocation> {
match self {
RelocationSize::Byte => buf[0] =
i8::try_from(value).map_err(|_| ImpossibleRelocation { } )?
as u8,
RelocationSize::Word => LittleEndian::write_i16(buf,
i16::try_from(value).map_err(|_| ImpossibleRelocation { } )?
),
RelocationSize::DWord => LittleEndian::write_i32(buf,
i32::try_from(value).map_err(|_| ImpossibleRelocation { } )?
),
RelocationSize::QWord => LittleEndian::write_i64(buf,
i64::try_from(value).map_err(|_| ImpossibleRelocation { } )?
),
}
Ok(())
}
pub fn read_value(&self, buf: &[u8]) -> isize {
match self {
RelocationSize::Byte => buf[0] as i8 as isize,
RelocationSize::Word => LittleEndian::read_i16(buf) as isize,
RelocationSize::DWord => LittleEndian::read_i32(buf) as isize,
RelocationSize::QWord => LittleEndian::read_i64(buf) as isize,
}
}
}
#[derive(Copy, Clone, Debug)]
enum NoComplexRelocationEncodings {}
impl ArchitectureRelocationEncoding for NoComplexRelocationEncodings {
fn decode(code: u8) -> Self {
panic!("Invalid complex relocation code {code} given for the current architecture");
}
}
#[derive(Debug, Clone, Copy)]
pub struct SimpleRelocation(RelocationType<NoComplexRelocationEncodings>);
impl Relocation for SimpleRelocation {
fn from_encoding(encoding: u8) -> Self {
SimpleRelocation(RelocationType::decode(encoding))
}
fn from_size(kind: RelocationKind, size: RelocationSize) -> Self {
SimpleRelocation(RelocationType::from_size(kind, size))
}
fn size(&self) -> usize {
match self.0.encoding {
RelocationEncoding::Simple(s) => s.size(),
_ => unreachable!()
}
}
fn write_value(&self, buf: &mut [u8], value: isize) -> Result<(), ImpossibleRelocation> {
match self.0.encoding {
RelocationEncoding::Simple(s) => s.write_value(buf, value),
_ => unreachable!()
}
}
fn read_value(&self, buf: &[u8]) -> isize {
match self.0.encoding {
RelocationEncoding::Simple(s) => s.read_value(buf),
_ => unreachable!()
}
}
fn kind(&self) -> RelocationKind {
self.0.kind
}
fn page_size() -> usize {
4096
}
}
pub(crate) fn fits_signed_bitfield(value: i64, bits: u8) -> bool {
if bits >= 64 {
return true;
}
let half = 1i64 << (bits - 1);
value < half && value >= -half
}