use crate::io::{Endian, ReadExt, WriteExt};
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use std::io::{Read, Write};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RelocationInfo {
pub r_address: i32,
pub r_symbolnum: u32,
pub r_pcrel: bool,
pub r_length: RelocLength,
pub r_extern: bool,
pub r_type: u8,
}
impl RelocationInfo {
pub const SIZE: u32 = 8;
pub fn read_from_in<R: Read>(read: &mut R, endian: Endian) -> RelocationInfo {
let r_address = read.read_i32_in(endian);
let infos = read.read_u32_in(endian);
let (r_symbolnum, r_pcrel, r_length, r_extern, r_type) = if endian == Endian::Little {
(
infos & 0x00FF_FFFF,
(infos & 0x0100_0000) > 0,
RelocLength::from_u32((infos & 0x0600_0000) >> 25),
infos & 0x0800_0000 > 0,
((infos & 0xF000_0000) >> 28) as u8,
)
} else {
(
(infos & 0xFFFF_FF00) >> 8,
infos & 0x0000_0080 > 0,
RelocLength::from_u32((infos & 0x0000_0060) >> 5),
infos & 0x0000_0010 > 0,
(infos & 0x0000_000F) as u8,
)
};
RelocationInfo {
r_address,
r_symbolnum,
r_pcrel,
r_length,
r_extern,
r_type,
}
}
#[cfg(target_endian = "little")]
pub fn write_into(self, write: &mut impl Write) {
write.write_i32_native(self.r_address);
let mut infos: u32 = 0;
infos |= self.r_symbolnum;
infos |= (self.r_pcrel as u32) * 0x0100_0000;
infos |= (self.r_length.to_u32()) << 25;
infos |= (self.r_extern as u32) * 0x0800_0000;
infos |= (self.r_type as u32) << 28;
write.write_u32_native(infos);
}
#[cfg(target_endian = "big")]
pub fn write_into(self, write: &mut impl Write) {
write.write_i32_native(self.r_address);
let mut infos: u32 = 0;
infos |= self.r_symbolnum << 8;
infos |= (self.r_pcrel as u32) * 0x0000_0080;
infos |= (self.r_length.to_u32()) << 5;
infos |= (self.r_extern as u32) * 0x0000_0010;
infos |= self.r_type as u32;
write.write_u32_native(infos);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive)]
pub enum RelocLength {
Byte = 0,
Word = 1,
Long = 2,
Quad = 3,
}
impl RelocLength {
pub fn to_u32(self) -> u32 {
self as u32
}
pub fn from_u32(n: u32) -> RelocLength {
FromPrimitive::from_u32(n).unwrap()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86RelocType {
Vanilla = 0,
Pair = 1,
Sectdiff = 2,
PbLaPtr = 3,
LocalSectdiff = 4,
Tlv = 5,
}
impl X86RelocType {
pub fn to_u8(self) -> u8 {
self as u8
}
pub fn from_u8(n: u8) -> Self {
match n {
0 => X86RelocType::Vanilla,
1 => X86RelocType::Pair,
2 => X86RelocType::Sectdiff,
3 => X86RelocType::PbLaPtr,
4 => X86RelocType::LocalSectdiff,
5 => X86RelocType::Tlv,
_ => panic!("{} is not a valid x86 reloc type", n),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86_64RelocType {
Unsigned = 0,
Signed = 1,
Branch = 2,
GotLoad = 3,
Got = 4,
Subtractor = 5,
Signed1 = 6,
Signed2 = 7,
Signed4 = 8,
Tlv = 9,
}
impl X86_64RelocType {
pub fn to_u8(self) -> u8 {
self as u8
}
pub fn from_u8(n: u8) -> Self {
match n {
0 => X86_64RelocType::Unsigned,
1 => X86_64RelocType::Signed,
2 => X86_64RelocType::Branch,
3 => X86_64RelocType::GotLoad,
4 => X86_64RelocType::Got,
5 => X86_64RelocType::Subtractor,
6 => X86_64RelocType::Signed1,
7 => X86_64RelocType::Signed2,
8 => X86_64RelocType::Signed4,
9 => X86_64RelocType::Tlv,
_ => panic!("{} is not a valid x86_64 reloc type", n),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn write_and_read_relocation_info() {
let reloc = RelocationInfo {
r_address: 42,
r_symbolnum: 0x00323100,
r_pcrel: true,
r_length: RelocLength::Byte,
r_extern: false,
r_type: X86_64RelocType::Unsigned.to_u8(),
};
let mut buf = Vec::new();
reloc.write_into(&mut buf);
assert_eq!(buf.len(), RelocationInfo::SIZE as usize);
let read_reloc = RelocationInfo::read_from_in(&mut buf.as_slice(), Endian::NATIVE);
assert_eq!(read_reloc, reloc);
}
}