vivisect/elf/reloc.rs
1#![allow(hidden_glob_reexports)]
2//! # Relocation computations
3//!
4//! The following notation is used to describe relocation computations
5//! specific to x86_64 ELF.
6//!
7//! * A: The addend used to compute the value of the relocatable field.
8//! * B: The base address at which a shared object is loaded into memory
9//! during execution. Generally, a shared object file is built with a
10//! base virtual address of 0. However, the execution address of the
11//! shared object is different.
12//! * G: The offset into the global offset table at which the address of
13//! the relocation entry's symbol resides during execution.
14//! * GOT: The address of the global offset table.
15//! * L: The section offset or address of the procedure linkage table entry
16//! for a symbol.
17//! * P: The section offset or address of the storage unit being relocated,
18//! computed using r_offset.
19//! * S: The value of the symbol whose index resides in the relocation entry.
20//! * Z: The size of the symbol whose index resides in the relocation entry.
21//!
22//! Below are some common x86_64 relocation computations you might find useful:
23//!
24//! | Relocation | Value | Size | Formula |
25//! |:--------------------------|:------|:----------|:------------------|
26//! | `R_X86_64_NONE` | 0 | NONE | NONE |
27//! | `R_X86_64_64` | 1 | 64 | S + A |
28//! | `R_X86_64_PC32` | 2 | 32 | S + A - P |
29//! | `R_X86_64_GOT32` | 3 | 32 | G + A |
30//! | `R_X86_64_PLT32` | 4 | 32 | L + A - P |
31//! | `R_X86_64_COPY` | 5 | NONE | NONE |
32//! | `R_X86_64_GLOB_DAT` | 6 | 64 | S |
33//! | `R_X86_64_JUMP_SLOT` | 7 | 64 | S |
34//! | `R_X86_64_RELATIVE` | 8 | 64 | B + A |
35//! | `R_X86_64_GOTPCREL` | 9 | 32 | G + GOT + A - P |
36//! | `R_X86_64_32` | 10 | 32 | S + A |
37//! | `R_X86_64_32S` | 11 | 32 | S + A |
38//! | `R_X86_64_16` | 12 | 16 | S + A |
39//! | `R_X86_64_PC16` | 13 | 16 | S + A - P |
40//! | `R_X86_64_8` | 14 | 8 | S + A |
41//! | `R_X86_64_PC8` | 15 | 8 | S + A - P |
42//! | `R_X86_64_DTPMOD64` | 16 | 64 | |
43//! | `R_X86_64_DTPOFF64` | 17 | 64 | |
44//! | `R_X86_64_TPOFF64` | 18 | 64 | |
45//! | `R_X86_64_TLSGD` | 19 | 32 | |
46//! | `R_X86_64_TLSLD` | 20 | 32 | |
47//! | `R_X86_64_DTPOFF32` | 21 | 32 | |
48//! | `R_X86_64_GOTTPOFF` | 22 | 32 | |
49//! | `R_X86_64_TPOFF32` | 23 | 32 | |
50//! | `R_X86_64_PC64` | 24 | 64 | S + A - P |
51//! | `R_X86_64_GOTOFF64` | 25 | 64 | S + A - GOT |
52//! | `R_X86_64_GOTPC32` | 26 | 32 | GOT + A - P |
53//! | `R_X86_64_SIZE32` | 32 | 32 | Z + A |
54//! | `R_X86_64_SIZE64` | 33 | 64 | Z + A |
55//! | `R_X86_64_GOTPC32_TLSDESC` 34 | 32 | |
56//! | `R_X86_64_TLSDESC_CALL` | 35 | NONE | |
57//! | `R_X86_64_TLSDESC` | 36 | 64 × 2 | |
58//! | `R_X86_64_IRELATIVE` | 37 | 64 | indirect (B + A) |
59//!
60//! TLS information is at http://people.redhat.com/aoliva/writeups/TLS/RFC-TLSDESC-x86.txt
61//!
62//! `R_X86_64_IRELATIVE` is similar to `R_X86_64_RELATIVE` except that
63//! the value used in this relocation is the program address returned by the function,
64//! which takes no arguments, at the address of the result of the corresponding
65//! `R_X86_64_RELATIVE` relocation.
66//!
67//! Read more https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-54839.html
68
69include!("constants_relocation.rs");
70
71macro_rules! elf_reloc {
72 ($size:ident, $isize:ty) => {
73 use core::fmt;
74 #[cfg(feature = "alloc")]
75 use scroll::{Pread, Pwrite, SizeWith};
76 #[repr(C)]
77 #[derive(Clone, Copy, PartialEq, Eq, Default)]
78 #[cfg_attr(feature = "alloc", derive(Pread, Pwrite, SizeWith))]
79 /// Relocation with an explicit addend
80 pub struct Rela {
81 /// Address
82 pub r_offset: $size,
83 /// Relocation type and symbol index
84 pub r_info: $size,
85 /// Addend
86 pub r_addend: $isize,
87 }
88 #[repr(C)]
89 #[derive(Clone, PartialEq, Eq, Default)]
90 #[cfg_attr(feature = "alloc", derive(Pread, Pwrite, SizeWith))]
91 /// Relocation without an addend
92 pub struct Rel {
93 /// address
94 pub r_offset: $size,
95 /// relocation type and symbol address
96 pub r_info: $size,
97 }
98 use plain;
99 unsafe impl plain::Plain for Rela {}
100 unsafe impl plain::Plain for Rel {}
101
102 impl fmt::Debug for Rela {
103 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
104 let sym = r_sym(self.r_info);
105 let typ = r_type(self.r_info);
106 f.debug_struct("Rela")
107 .field("r_offset", &format_args!("{:x}", self.r_offset))
108 .field("r_info", &format_args!("{:x}", self.r_info))
109 .field("r_addend", &format_args!("{:x}", self.r_addend))
110 .field("r_typ", &typ)
111 .field("r_sym", &sym)
112 .finish()
113 }
114 }
115 impl fmt::Debug for Rel {
116 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
117 let sym = r_sym(self.r_info);
118 let typ = r_type(self.r_info);
119 f.debug_struct("Rel")
120 .field("r_offset", &format_args!("{:x}", self.r_offset))
121 .field("r_info", &format_args!("{:x}", self.r_info))
122 .field("r_typ", &typ)
123 .field("r_sym", &sym)
124 .finish()
125 }
126 }
127 };
128}
129
130macro_rules! elf_rela_std_impl {
131 ($size:ident, $isize:ty) => {
132 if_alloc! {
133 use crate::elf::reloc::Reloc;
134
135 use core::slice;
136
137 if_std! {
138 use crate::error::Result;
139
140 use std::fs::File;
141 use std::io::{Read, Seek};
142 use std::io::SeekFrom::Start;
143 }
144
145 impl From<Rela> for Reloc {
146 fn from(rela: Rela) -> Self {
147 Reloc {
148 r_offset: u64::from(rela.r_offset),
149 r_addend: Some(i64::from(rela.r_addend)),
150 r_sym: r_sym(rela.r_info) as usize,
151 r_type: r_type(rela.r_info),
152 }
153 }
154 }
155
156 impl From<Rel> for Reloc {
157 fn from(rel: Rel) -> Self {
158 Reloc {
159 r_offset: u64::from(rel.r_offset),
160 r_addend: None,
161 r_sym: r_sym(rel.r_info) as usize,
162 r_type: r_type(rel.r_info),
163 }
164 }
165 }
166
167 impl From<Reloc> for Rela {
168 fn from(rela: Reloc) -> Self {
169 let r_info = r_info(rela.r_sym as $size, $size::from(rela.r_type));
170 Rela {
171 r_offset: rela.r_offset as $size,
172 r_info,
173 r_addend: rela.r_addend.unwrap_or(0) as $isize,
174 }
175 }
176 }
177
178 impl From<Reloc> for Rel {
179 fn from(rel: Reloc) -> Self {
180 let r_info = r_info(rel.r_sym as $size, $size::from(rel.r_type));
181 Rel {
182 r_offset: rel.r_offset as $size,
183 r_info,
184 }
185 }
186 }
187
188 /// Gets the rela entries given a rela pointer and the _size_ of the rela section in the binary,
189 /// in bytes.
190 /// Assumes the pointer is valid and can safely return a slice of memory pointing to the relas because:
191 /// 1. `ptr` points to memory received from the kernel (i.e., it loaded the executable), _or_
192 /// 2. The binary has already been mmapped (i.e., it's a `SharedObject`), and hence it's safe to return a slice of that memory.
193 /// 3. Or if you obtained the pointer in some other lawful manner
194 pub unsafe fn from_raw_rela<'a>(ptr: *const Rela, size: usize) -> &'a [Rela] {
195 slice::from_raw_parts(ptr, size / SIZEOF_RELA)
196 }
197
198 /// Gets the rel entries given a rel pointer and the _size_ of the rel section in the binary,
199 /// in bytes.
200 /// Assumes the pointer is valid and can safely return a slice of memory pointing to the rels because:
201 /// 1. `ptr` points to memory received from the kernel (i.e., it loaded the executable), _or_
202 /// 2. The binary has already been mmapped (i.e., it's a `SharedObject`), and hence it's safe to return a slice of that memory.
203 /// 3. Or if you obtained the pointer in some other lawful manner
204 pub unsafe fn from_raw_rel<'a>(ptr: *const Rel, size: usize) -> &'a [Rel] {
205 slice::from_raw_parts(ptr, size / SIZEOF_REL)
206 }
207
208 #[cfg(feature = "std")]
209 pub fn from_fd(fd: &mut File, offset: usize, size: usize) -> Result<Vec<Rela>> {
210 let count = size / SIZEOF_RELA;
211 let mut relocs = vec![Rela::default(); count];
212 fd.seek(Start(offset as u64))?;
213 unsafe {
214 fd.read_exact(plain::as_mut_bytes(&mut *relocs))?;
215 }
216 Ok(relocs)
217 }
218 } // end if_alloc
219 };
220}
221
222pub mod reloc32 {
223
224 pub use crate::elf::reloc::*;
225
226 elf_reloc!(u32, i32);
227
228 pub const SIZEOF_RELA: usize = 4 + 4 + 4;
229 pub const SIZEOF_REL: usize = 4 + 4;
230
231 #[inline(always)]
232 pub fn r_sym(info: u32) -> u32 {
233 info >> 8
234 }
235
236 #[inline(always)]
237 pub fn r_type(info: u32) -> u32 {
238 info & 0xff
239 }
240
241 #[inline(always)]
242 pub fn r_info(sym: u32, typ: u32) -> u32 {
243 (sym << 8) + (typ & 0xff)
244 }
245
246 elf_rela_std_impl!(u32, i32);
247}
248
249pub mod reloc64 {
250 pub use crate::elf::reloc::*;
251
252 elf_reloc!(u64, i64);
253
254 pub const SIZEOF_RELA: usize = 8 + 8 + 8;
255 pub const SIZEOF_REL: usize = 8 + 8;
256
257 #[inline(always)]
258 pub fn r_sym(info: u64) -> u32 {
259 (info >> 32) as u32
260 }
261
262 #[inline(always)]
263 pub fn r_type(info: u64) -> u32 {
264 (info & 0xffff_ffff) as u32
265 }
266
267 #[inline(always)]
268 pub fn r_info(sym: u64, typ: u64) -> u64 {
269 (sym << 32) + typ
270 }
271
272 elf_rela_std_impl!(u64, i64);
273}
274
275//////////////////////////////
276// Generic Reloc
277/////////////////////////////
278if_alloc! {
279 use scroll::{ctx, Pread};
280 use scroll::ctx::SizeWith;
281 use core::fmt;
282 use core::result;
283 use crate::container::{Ctx, Container};
284 use alloc::vec::Vec;
285
286 #[derive(Clone, Copy, PartialEq, Eq, Default)]
287 /// A unified ELF relocation structure
288 pub struct Reloc {
289 /// Address
290 pub r_offset: u64,
291 /// Addend
292 pub r_addend: Option<i64>,
293 /// The index into the corresponding symbol table - either dynamic or regular
294 pub r_sym: usize,
295 /// The relocation type
296 pub r_type: u32,
297 }
298
299 impl Reloc {
300 pub fn size(is_rela: bool, ctx: Ctx) -> usize {
301 use scroll::ctx::SizeWith;
302 Reloc::size_with(&(is_rela, ctx))
303 }
304 }
305
306 type RelocCtx = (bool, Ctx);
307
308 impl ctx::SizeWith<RelocCtx> for Reloc {
309 fn size_with( &(is_rela, Ctx { container, .. }): &RelocCtx) -> usize {
310 match container {
311 Container::Little => {
312 if is_rela { reloc32::SIZEOF_RELA } else { reloc32::SIZEOF_REL }
313 },
314 Container::Big => {
315 if is_rela { reloc64::SIZEOF_RELA } else { reloc64::SIZEOF_REL }
316 }
317 }
318 }
319 }
320
321 impl<'a> ctx::TryFromCtx<'a, RelocCtx> for Reloc {
322 type Error = crate::error::Error;
323 fn try_from_ctx(bytes: &'a [u8], (is_rela, Ctx { container, le }): RelocCtx) -> result::Result<(Self, usize), Self::Error> {
324 use scroll::Pread;
325 let reloc = match container {
326 Container::Little => {
327 if is_rela {
328 (bytes.pread_with::<reloc32::Rela>(0, le)?.into(), reloc32::SIZEOF_RELA)
329 } else {
330 (bytes.pread_with::<reloc32::Rel>(0, le)?.into(), reloc32::SIZEOF_REL)
331 }
332 },
333 Container::Big => {
334 if is_rela {
335 (bytes.pread_with::<reloc64::Rela>(0, le)?.into(), reloc64::SIZEOF_RELA)
336 } else {
337 (bytes.pread_with::<reloc64::Rel>(0, le)?.into(), reloc64::SIZEOF_REL)
338 }
339 }
340 };
341 Ok(reloc)
342 }
343 }
344
345 impl ctx::TryIntoCtx<RelocCtx> for Reloc {
346 type Error = crate::error::Error;
347 /// Writes the relocation into `bytes`
348 fn try_into_ctx(self, bytes: &mut [u8], (is_rela, Ctx {container, le}): RelocCtx) -> result::Result<usize, Self::Error> {
349 use scroll::Pwrite;
350 match container {
351 Container::Little => {
352 if is_rela {
353 let rela: reloc32::Rela = self.into();
354 Ok(bytes.pwrite_with(rela, 0, le)?)
355 } else {
356 let rel: reloc32::Rel = self.into();
357 Ok(bytes.pwrite_with(rel, 0, le)?)
358 }
359 },
360 Container::Big => {
361 if is_rela {
362 let rela: reloc64::Rela = self.into();
363 Ok(bytes.pwrite_with(rela, 0, le)?)
364 } else {
365 let rel: reloc64::Rel = self.into();
366 Ok(bytes.pwrite_with(rel, 0, le)?)
367 }
368 },
369 }
370 }
371 }
372
373 impl ctx::IntoCtx<(bool, Ctx)> for Reloc {
374 /// Writes the relocation into `bytes`
375 fn into_ctx(self, bytes: &mut [u8], ctx: RelocCtx) {
376 use scroll::Pwrite;
377 bytes.pwrite_with(self, 0, ctx).unwrap();
378 }
379 }
380
381 impl fmt::Debug for Reloc {
382 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
383 f.debug_struct("Reloc")
384 .field("r_offset", &format_args!("{:x}", self.r_offset))
385 .field("r_addend", &format_args!("{:x}", self.r_addend.unwrap_or(0)))
386 .field("r_sym", &self.r_sym)
387 .field("r_type", &self.r_type)
388 .finish()
389 }
390 }
391
392 #[derive(Default, Clone)]
393 /// An ELF section containing relocations, allowing lazy iteration over symbols.
394 pub struct RelocSection<'a> {
395 bytes: &'a [u8],
396 count: usize,
397 ctx: RelocCtx,
398 start: usize,
399 end: usize,
400 }
401
402 impl<'a> fmt::Debug for RelocSection<'a> {
403 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
404 let len = self.bytes.len();
405 fmt.debug_struct("RelocSection")
406 .field("bytes", &len)
407 .field("range", &format!("{:#x}..{:#x}", self.start, self.end))
408 .field("count", &self.count)
409 .field("Relocations", &self.to_vec())
410 .finish()
411 }
412 }
413
414 impl<'a> RelocSection<'a> {
415 #[cfg(feature = "endian_fd")]
416 /// Parse a REL or RELA section of size `filesz` from `offset`.
417 pub fn parse(bytes: &'a [u8], offset: usize, filesz: usize, is_rela: bool, ctx: Ctx) -> crate::error::Result<RelocSection<'a>> {
418 // TODO: better error message when too large (see symtab implementation)
419 let bytes = bytes.pread_with(offset, filesz)?;
420
421 Ok(RelocSection {
422 bytes,
423 count: filesz / Reloc::size(is_rela, ctx),
424 ctx: (is_rela, ctx),
425 start: offset,
426 end: offset + filesz,
427 })
428 }
429
430 /// Try to parse a single relocation from the binary, at `index`.
431 #[inline]
432 pub fn get(&self, index: usize) -> Option<Reloc> {
433 if index >= self.count {
434 None
435 } else {
436 Some(self.bytes.pread_with(index * Reloc::size_with(&self.ctx), self.ctx).unwrap())
437 }
438 }
439
440 /// The number of relocations in the section.
441 #[inline]
442 pub fn len(&self) -> usize {
443 self.count
444 }
445
446 /// Returns true if section has no relocations.
447 #[inline]
448 pub fn is_empty(&self) -> bool {
449 self.count == 0
450 }
451
452 /// Iterate over all relocations.
453 pub fn iter(&self) -> RelocIterator<'a> {
454 self.into_iter()
455 }
456
457 /// Parse all relocations into a vector.
458 pub fn to_vec(&self) -> Vec<Reloc> {
459 self.iter().collect()
460 }
461 }
462
463 impl<'a, 'b> IntoIterator for &'b RelocSection<'a> {
464 type Item = <RelocIterator<'a> as Iterator>::Item;
465 type IntoIter = RelocIterator<'a>;
466
467 #[inline]
468 fn into_iter(self) -> Self::IntoIter {
469 RelocIterator {
470 bytes: self.bytes,
471 offset: 0,
472 index: 0,
473 count: self.count,
474 ctx: self.ctx,
475 }
476 }
477 }
478
479 pub struct RelocIterator<'a> {
480 bytes: &'a [u8],
481 offset: usize,
482 index: usize,
483 count: usize,
484 ctx: RelocCtx,
485 }
486
487 impl<'a> fmt::Debug for RelocIterator<'a> {
488 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
489 fmt.debug_struct("RelocIterator")
490 .field("bytes", &"<... redacted ...>")
491 .field("offset", &self.offset)
492 .field("index", &self.index)
493 .field("count", &self.count)
494 .field("ctx", &self.ctx)
495 .finish()
496 }
497 }
498
499 impl<'a> Iterator for RelocIterator<'a> {
500 type Item = Reloc;
501
502 #[inline]
503 fn next(&mut self) -> Option<Self::Item> {
504 if self.index >= self.count {
505 None
506 } else {
507 self.index += 1;
508 Some(self.bytes.gread_with(&mut self.offset, self.ctx).unwrap())
509 }
510 }
511 }
512
513 impl<'a> ExactSizeIterator for RelocIterator<'a> {
514 #[inline]
515 fn len(&self) -> usize {
516 self.count - self.index
517 }
518 }
519} // end if_alloc