use std::prelude::v1::*;
use std::{cmp, fmt, iter, mem, slice};
use crate::image::{IMAGE_BASE_RELOCATION, IMAGE_REL_BASED_ABSOLUTE};
use crate::util::{extend_in_place, AlignTo};
use crate::{Error, Result};
#[derive(Copy, Clone)]
pub struct BaseRelocs<'a> {
relocs: &'a [u8],
}
impl<'a> BaseRelocs<'a> {
pub(crate) unsafe fn new(relocs: &'a [u8]) -> BaseRelocs<'a> {
debug_assert!(relocs.as_ptr().aligned_to(4)); BaseRelocs { relocs }
}
pub fn parse(relocs: &'a [u8]) -> Result<BaseRelocs<'a>> {
if !(cfg!(feature = "unsafe_alignment") || relocs.as_ptr().aligned_to(4)) { return Err(Error::Misaligned);
}
Ok(BaseRelocs { relocs })
}
pub fn image(&self) -> &'a [u8] {
self.relocs
}
pub fn iter_blocks(&self) -> IterBlocks<'a> {
IterBlocks { data: self.relocs }
}
pub fn for_each<F: FnMut(u32, u8)>(&self, mut f: F) {
self.fold((), |(), rva, ty| f(rva, ty))
}
pub fn fold<T, F>(&self, init: T, mut f: F) -> T where F: FnMut(T, u32, u8) -> T {
let mut accum = init;
for block in self.iter_blocks() {
for word in block.words() {
let ty = block.type_of(word);
if ty != IMAGE_REL_BASED_ABSOLUTE {
let rva = block.rva_of(word);
accum = f(accum, rva, ty);
}
}
}
accum
}
}
impl<'a> fmt::Debug for BaseRelocs<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("BaseRelocs").finish()
}
}
#[derive(Clone)]
pub struct IterBlocks<'a> {
data: &'a [u8],
}
impl<'a> IterBlocks<'a> {
fn peek(&self) -> Option<Block<'a>> {
if mem::size_of_val(self.data) >= mem::size_of::<IMAGE_BASE_RELOCATION>() { Some(unsafe {
let image_p = self.data.as_ptr() as *const IMAGE_BASE_RELOCATION;
let image = &*image_p;
let len = cmp::min(image.SizeOfBlock as usize, self.data.len()).saturating_sub(mem::size_of::<IMAGE_BASE_RELOCATION>()) / 2;
let words = slice::from_raw_parts(image_p.offset(1) as *const u16, len);
Block { image, words }
})
}
else {
None
}
}
}
impl<'a> Iterator for IterBlocks<'a> {
type Item = Block<'a>;
fn next(&mut self) -> Option<Block<'a>> {
if let Some(block) = self.peek() {
let block_size = block.image.SizeOfBlock;
let block_size = cmp::max(block_size, mem::size_of::<IMAGE_BASE_RELOCATION>() as u32);
let block_size = block_size.align_to(4); let block_size = cmp::min(block_size as usize, self.data.len());
self.data = &self.data[block_size..];
Some(block)
}
else {
None
}
}
}
impl<'a> iter::FusedIterator for IterBlocks<'a> {}
#[derive(Copy, Clone)]
pub struct Block<'a> {
image: &'a IMAGE_BASE_RELOCATION,
words: &'a [u16],
}
impl<'a> Block<'a> {
pub fn image(&self) -> &'a IMAGE_BASE_RELOCATION {
self.image
}
pub fn words(&self) -> &'a [u16] {
self.words
}
pub fn rva_of(&self, word: &u16) -> u32 {
let offset = (word & 0x0fff) as u32;
self.image.VirtualAddress.wrapping_add(offset)
}
pub fn type_of(&self, word: &u16) -> u8 {
(word >> 12) as u8
}
}
impl<'a> fmt::Debug for Block<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Block")
.field("virtual_address", &format_args!("{:#x?}", self.image.VirtualAddress))
.field("words.len", &self.words().len())
.finish()
}
}
#[cfg(feature = "serde")]
mod serde {
use crate::util::serde_helper::*;
use super::BaseRelocs;
impl<'a> Serialize for BaseRelocs<'a> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut state = serializer.serialize_struct("BaseRelocs", 2)?;
let mut rvas = Vec::new();
let mut types = Vec::new();
self.for_each(|rva, ty| {
rvas.push(rva);
types.push(ty);
});
state.serialize_field("rvas", &*rvas)?;
state.serialize_field("types", &*types)?;
state.end()
}
}
}
fn encode_type_offset(base: u32, rva: u32, ty: u8) -> u16 {
(((rva - base) | (ty as u32) << 12) & 0xffff) as u16
}
pub fn build(mut rvas: &[u32], mut types: &[u8]) -> Vec<u8> {
assert_eq!(rvas.len(), types.len());
let mut result = Vec::<u8>::new();
while rvas.len() > 0 {
let start = rvas[0] & !0x0fff;
let end = start + 0x0fff;
let mut n = 0;
while n < rvas.len() && rvas[n] >= start && rvas[n] < end {
n += 1;
}
let size = (8 + 2 * n).align_to(4);
unsafe {
extend_in_place(&mut result, size, |bytes| {
let block_ptr = bytes.as_mut_ptr() as *mut IMAGE_BASE_RELOCATION;
(*block_ptr).VirtualAddress = start;
(*block_ptr).SizeOfBlock = size as u32;
let words = slice::from_raw_parts_mut(block_ptr.offset(1) as *mut u16, n.align_to(2));
for i in 0..n {
let rva = *rvas.get_unchecked(i);
let ty = *types.get_unchecked(i);
words[i] = encode_type_offset(start, rva, ty);
}
if n < words.len() {
words[n] = 0;
}
});
}
rvas = &rvas[n..];
types = &types[n..];
}
result
}
#[cfg(windows)]
#[test]
fn test_build_self() {
if crate::image::IMAGE_BASE_PANICS {
return;
}
use crate::pe::*;
let view = unsafe { PeView::new() };
if let Ok(base_relocs) = view.base_relocs() {
let mut rvas = Vec::new();
let mut types = Vec::new();
base_relocs.for_each(|rva, ty| {
rvas.push(rva);
types.push(ty);
});
let rebuild = build(&rvas, &types);
assert_eq!(rebuild, base_relocs.image());
}
}