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