pelite/
base_relocs.rs

1/*!
2Base Relocations Directory.
3
4The base relocations directory describes a list of addresses to pointer values within its module which need to be patched when the module is located at a different address than its preferred load address.
5When the module contains pointers to itself these pointers need to be fixed when the module is loaded at a different address than its preferred load address.
6
7For a quick and easy overview of how the base relocations are laid out, see this helpful [stackoverflow answer](https://stackoverflow.com/a/22513813).
8
9# Examples
10
11```
12# #![allow(unused_variables)]
13use pelite::pe64::{Pe, PeFile};
14
15# #[allow(dead_code)]
16fn example(file: PeFile<'_>) -> pelite::Result<()> {
17	// Access the base relocations
18	let base_relocs = file.base_relocs()?;
19
20	// Iterate over the rva which need relocation
21	// Padding relocations of type absolute are skipped
22	base_relocs.for_each(|rva, ty| {});
23
24	// Iterate over the relocation blocks
25	for block in base_relocs.iter_blocks() {}
26
27	Ok(())
28}
29```
30 */
31
32use std::prelude::v1::*;
33
34use std::{cmp, fmt, iter, mem, slice};
35
36use crate::image::{IMAGE_BASE_RELOCATION, IMAGE_REL_BASED_ABSOLUTE};
37use crate::util::{extend_in_place, AlignTo};
38use crate::{Error, Result};
39
40/// Base Relocations Directory.
41///
42/// For more information see the [module-level documentation](index.html).
43#[derive(Copy, Clone)]
44pub struct BaseRelocs<'a> {
45	relocs: &'a [u8],
46}
47impl<'a> BaseRelocs<'a> {
48	pub(crate) unsafe fn new(relocs: &'a [u8]) -> BaseRelocs<'a> {
49		debug_assert!(relocs.as_ptr().aligned_to(4)); // $1
50		BaseRelocs { relocs }
51	}
52	/// Parse a base relocations directory.
53	///
54	/// Requires relocs argument pointer to have an alignment of 4 or an error is returned.
55	pub fn parse(relocs: &'a [u8]) -> Result<BaseRelocs<'a>> {
56		if !(cfg!(feature = "unsafe_alignment") || relocs.as_ptr().aligned_to(4)) { // $1
57			return Err(Error::Misaligned);
58		}
59		Ok(BaseRelocs { relocs })
60	}
61	/// Returns the base relocations image.
62	pub fn image(&self) -> &'a [u8] {
63		self.relocs
64	}
65	/// Iterates over the base relocation blocks.
66	pub fn iter_blocks(&self) -> IterBlocks<'a> {
67		IterBlocks { data: self.relocs }
68	}
69	/// Iterates over the base relocations with internal iteration.
70	pub fn for_each<F: FnMut(u32, u8)>(&self, mut f: F) {
71		self.fold((), |(), rva, ty| f(rva, ty))
72	}
73	/// Folds over the base relocations with internal iteration.
74	pub fn fold<T, F>(&self, init: T, mut f: F) -> T where F: FnMut(T, u32, u8) -> T {
75		let mut accum = init;
76		for block in self.iter_blocks() {
77			for word in block.words() {
78				let ty = block.type_of(word);
79				if ty != IMAGE_REL_BASED_ABSOLUTE {
80					let rva = block.rva_of(word);
81					accum = f(accum, rva, ty);
82				}
83			}
84		}
85		accum
86	}
87}
88impl<'a> fmt::Debug for BaseRelocs<'a> {
89	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
90		f.debug_struct("BaseRelocs").finish()
91	}
92}
93
94//----------------------------------------------------------------
95
96/// Iterator over the base relocation blocks.
97#[derive(Clone)]
98pub struct IterBlocks<'a> {
99	data: &'a [u8],
100}
101impl<'a> IterBlocks<'a> {
102	fn peek(&self) -> Option<Block<'a>> {
103		if mem::size_of_val(self.data) >= mem::size_of::<IMAGE_BASE_RELOCATION>() { // $2
104			Some(unsafe {
105				// The blocks pointer is dword aligned (see $1) and is at least large enough (see $2).
106				let image_p = self.data.as_ptr() as *const IMAGE_BASE_RELOCATION;
107				let image = &*image_p;
108				// Calculate the number of words following the base relocation carefully
109				let len = cmp::min(image.SizeOfBlock as usize, self.data.len()).saturating_sub(mem::size_of::<IMAGE_BASE_RELOCATION>()) / 2;
110				let words = slice::from_raw_parts(image_p.offset(1) as *const u16, len);
111				Block { image, words }
112			})
113		}
114		else {
115			None
116		}
117	}
118}
119impl<'a> Iterator for IterBlocks<'a> {
120	type Item = Block<'a>;
121	fn next(&mut self) -> Option<Block<'a>> {
122		if let Some(block) = self.peek() {
123			let block_size = block.image.SizeOfBlock;
124			// Avoid infinite loop by skipping at least the image base relocation header
125			let block_size = cmp::max(block_size, mem::size_of::<IMAGE_BASE_RELOCATION>() as u32);
126			// Ensure that the data pointer remains dword aligned
127			let block_size = block_size.align_to(4); // $1
128			// Clamp the length to the data size
129			let block_size = cmp::min(block_size as usize, self.data.len());
130			self.data = &self.data[block_size..];
131			Some(block)
132		}
133		else {
134			None
135		}
136	}
137}
138impl<'a> iter::FusedIterator for IterBlocks<'a> {}
139
140//----------------------------------------------------------------
141
142/// Base Relocation Block.
143#[derive(Copy, Clone)]
144pub struct Block<'a> {
145	image: &'a IMAGE_BASE_RELOCATION,
146	words: &'a [u16],
147}
148impl<'a> Block<'a> {
149	/// Returns the underlying base relocation block image.
150	pub fn image(&self) -> &'a IMAGE_BASE_RELOCATION {
151		self.image
152	}
153	/// Gets the types and offsets.
154	pub fn words(&self) -> &'a [u16] {
155		self.words
156	}
157	/// Gets the final Rva of a type-offset word.
158	pub fn rva_of(&self, word: &u16) -> u32 {
159		let offset = (word & 0x0fff) as u32;
160		self.image.VirtualAddress.wrapping_add(offset)
161	}
162	/// Gets the type of a type-offset word.
163	pub fn type_of(&self, word: &u16) -> u8 {
164		(word >> 12) as u8
165	}
166}
167impl<'a> fmt::Debug for Block<'a> {
168	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
169		f.debug_struct("Block")
170			.field("virtual_address", &format_args!("{:#x?}", self.image.VirtualAddress))
171			.field("words.len", &self.words().len())
172			.finish()
173	}
174}
175
176//----------------------------------------------------------------
177
178/*
179	"base_relocs": {
180		"rvas": [1000, 1002, 1018, 2048, 2498],
181		"types": [3, 3, 3, 3, 3, 3]
182	}
183*/
184
185#[cfg(feature = "serde")]
186mod serde {
187	use crate::util::serde_helper::*;
188	use super::BaseRelocs;
189
190	impl<'a> Serialize for BaseRelocs<'a> {
191		fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
192			let mut state = serializer.serialize_struct("BaseRelocs", 2)?;
193			let mut rvas = Vec::new();
194			let mut types = Vec::new();
195			self.for_each(|rva, ty| {
196				rvas.push(rva);
197				types.push(ty);
198			});
199			state.serialize_field("rvas", &*rvas)?;
200			state.serialize_field("types", &*types)?;
201			state.end()
202		}
203	}
204}
205
206//----------------------------------------------------------------
207
208fn encode_type_offset(base: u32, rva: u32, ty: u8) -> u16 {
209	(((rva - base) | (ty as u32) << 12) & 0xffff) as u16
210}
211
212/// Builds a new base relocation directory with given rvas and types.
213///
214/// For optimal results, ensure the inputs are sorted by their rvas.
215pub fn build(mut rvas: &[u32], mut types: &[u8]) -> Vec<u8> {
216	assert_eq!(rvas.len(), types.len());
217
218	let mut result = Vec::<u8>::new();
219	while rvas.len() > 0 {
220		// Given RVA range for the relocation block
221		let start = rvas[0] & !0x0fff;
222		let end = start + 0x0fff;
223
224		// Figure the number of rvas to fit in this block
225		let mut n = 0;
226		while n < rvas.len() && rvas[n] >= start && rvas[n] < end {
227			n += 1;
228		}
229
230		// Size of block should be multiple of 4 to ensure alignment
231		let size = (8 + 2 * n).align_to(4);
232
233		unsafe {
234			extend_in_place(&mut result, size, |bytes| {
235				// Encode the relocation block header
236				let block_ptr = bytes.as_mut_ptr() as *mut IMAGE_BASE_RELOCATION;
237				(*block_ptr).VirtualAddress = start;
238				(*block_ptr).SizeOfBlock = size as u32;
239				// Encode the type and offsets
240				let words = slice::from_raw_parts_mut(block_ptr.offset(1) as *mut u16, n.align_to(2));
241				for i in 0..n {
242					let rva = *rvas.get_unchecked(i);
243					let ty = *types.get_unchecked(i);
244					words[i] = encode_type_offset(start, rva, ty);
245				}
246				// Add alignment padding
247				if n < words.len() {
248					words[n] = 0;
249				}
250			});
251		}
252
253		rvas = &rvas[n..];
254		types = &types[n..];
255	}
256	result
257}
258
259#[cfg(windows)]
260#[test]
261fn test_build_self() {
262	if crate::image::IMAGE_BASE_PANICS {
263		return;
264	}
265	use crate::pe::*;
266	let view = unsafe { PeView::new() };
267	if let Ok(base_relocs) = view.base_relocs() {
268		let mut rvas = Vec::new();
269		let mut types = Vec::new();
270		base_relocs.for_each(|rva, ty| {
271			rvas.push(rva);
272			types.push(ty);
273		});
274		let rebuild = build(&rvas, &types);
275		assert_eq!(rebuild, base_relocs.image());
276	}
277}