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 
68 include!("constants_relocation.rs");
69 
70 macro_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 
129 macro_rules! elf_rela_std_impl { ($size:ident, $isize:ty) => {
130 
131     if_alloc! {
132             use crate::elf::reloc::Reloc;
133 
134             use core::slice;
135 
136             if_std! {
137                 use crate::error::Result;
138 
139                 use std::fs::File;
140                 use std::io::{Read, Seek};
141                 use std::io::SeekFrom::Start;
142             }
143 
144             impl From<Rela> for Reloc {
145                 fn from(rela: Rela) -> Self {
146                     Reloc {
147                         r_offset: u64::from(rela.r_offset),
148                         r_addend: Some(i64::from(rela.r_addend)),
149                         r_sym: r_sym(rela.r_info) as usize,
150                         r_type: r_type(rela.r_info),
151                     }
152                 }
153             }
154 
155             impl From<Rel> for Reloc {
156                 fn from(rel: Rel) -> Self {
157                     Reloc {
158                         r_offset: u64::from(rel.r_offset),
159                         r_addend: None,
160                         r_sym: r_sym(rel.r_info) as usize,
161                         r_type: r_type(rel.r_info),
162                     }
163                 }
164             }
165 
166             impl From<Reloc> for Rela {
167                 fn from(rela: Reloc) -> Self {
168                     let r_info = r_info(rela.r_sym as $size, $size::from(rela.r_type));
169                     Rela {
170                         r_offset: rela.r_offset as $size,
171                         r_info: r_info,
172                         r_addend: rela.r_addend.unwrap_or(0) as $isize,
173                     }
174                 }
175             }
176 
177             impl From<Reloc> for Rel {
178                 fn from(rel: Reloc) -> Self {
179                     let r_info = r_info(rel.r_sym as $size, $size::from(rel.r_type));
180                     Rel {
181                         r_offset: rel.r_offset as $size,
182                         r_info: r_info,
183                     }
184                 }
185             }
186 
187             /// Gets the rela entries given a rela pointer and the _size_ of the rela section in the binary,
188             /// in bytes.
189             /// Assumes the pointer is valid and can safely return a slice of memory pointing to the relas because:
190             /// 1. `ptr` points to memory received from the kernel (i.e., it loaded the executable), _or_
191             /// 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.
192             /// 3. Or if you obtained the pointer in some other lawful manner
193             pub unsafe fn from_raw_rela<'a>(ptr: *const Rela, size: usize) -> &'a [Rela] {
194                 slice::from_raw_parts(ptr, size / SIZEOF_RELA)
195             }
196 
197             /// Gets the rel entries given a rel pointer and the _size_ of the rel section in the binary,
198             /// in bytes.
199             /// Assumes the pointer is valid and can safely return a slice of memory pointing to the rels because:
200             /// 1. `ptr` points to memory received from the kernel (i.e., it loaded the executable), _or_
201             /// 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.
202             /// 3. Or if you obtained the pointer in some other lawful manner
203             pub unsafe fn from_raw_rel<'a>(ptr: *const Rel, size: usize) -> &'a [Rel] {
204                 slice::from_raw_parts(ptr, size / SIZEOF_REL)
205             }
206 
207             #[cfg(feature = "std")]
208             pub fn from_fd(fd: &mut File, offset: usize, size: usize) -> Result<Vec<Rela>> {
209                 let count = size / SIZEOF_RELA;
210                 let mut relocs = vec![Rela::default(); count];
211                 fd.seek(Start(offset as u64))?;
212                 unsafe {
213                     fd.read_exact(plain::as_mut_bytes(&mut *relocs))?;
214                 }
215                 Ok(relocs)
216             }
217         } // end if_alloc
218     };
219 }
220 
221 
222 pub 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)]
r_sym(info: u32) -> u32232     pub fn r_sym(info: u32) -> u32 {
233         info >> 8
234     }
235 
236     #[inline(always)]
r_type(info: u32) -> u32237     pub fn r_type(info: u32) -> u32 {
238         info & 0xff
239     }
240 
241     #[inline(always)]
r_info(sym: u32, typ: u32) -> u32242     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 
249 
250 pub mod reloc64 {
251     pub use crate::elf::reloc::*;
252 
253     elf_reloc!(u64, i64);
254 
255     pub const SIZEOF_RELA: usize = 8 + 8 + 8;
256     pub const SIZEOF_REL: usize = 8 + 8;
257 
258     #[inline(always)]
r_sym(info: u64) -> u32259     pub fn r_sym(info: u64) -> u32 {
260         (info >> 32) as u32
261     }
262 
263     #[inline(always)]
r_type(info: u64) -> u32264     pub fn r_type(info: u64) -> u32 {
265         (info & 0xffff_ffff) as u32
266     }
267 
268     #[inline(always)]
r_info(sym: u64, typ: u64) -> u64269     pub fn r_info(sym: u64, typ: u64) -> u64 {
270         (sym << 32) + typ
271     }
272 
273     elf_rela_std_impl!(u64, i64);
274 }
275 
276 //////////////////////////////
277 // Generic Reloc
278 /////////////////////////////
279 if_alloc! {
280     use scroll::{ctx, Pread};
281     use scroll::ctx::SizeWith;
282     use core::fmt;
283     use core::result;
284     use crate::container::{Ctx, Container};
285     #[cfg(feature = "endian_fd")]
286     use crate::alloc::vec::Vec;
287 
288     #[derive(Clone, Copy, PartialEq, Default)]
289     /// A unified ELF relocation structure
290     pub struct Reloc {
291         /// Address
292         pub r_offset: u64,
293         /// Addend
294         pub r_addend: Option<i64>,
295         /// The index into the corresponding symbol table - either dynamic or regular
296         pub r_sym: usize,
297         /// The relocation type
298         pub r_type: u32,
299     }
300 
301     impl Reloc {
302         pub fn size(is_rela: bool, ctx: Ctx) -> usize {
303             use scroll::ctx::SizeWith;
304             Reloc::size_with(&(is_rela, ctx))
305         }
306     }
307 
308     type RelocCtx = (bool, Ctx);
309 
310     impl ctx::SizeWith<RelocCtx> for Reloc {
311         type Units = usize;
312         fn size_with( &(is_rela, Ctx { container, .. }): &RelocCtx) -> Self::Units {
313             match container {
314                 Container::Little => {
315                     if is_rela { reloc32::SIZEOF_RELA } else { reloc32::SIZEOF_REL }
316                 },
317                 Container::Big => {
318                     if is_rela { reloc64::SIZEOF_RELA } else { reloc64::SIZEOF_REL }
319                 }
320             }
321         }
322     }
323 
324     impl<'a> ctx::TryFromCtx<'a, RelocCtx> for Reloc {
325         type Error = crate::error::Error;
326         type Size = usize;
327         fn try_from_ctx(bytes: &'a [u8], (is_rela, Ctx { container, le }): RelocCtx) -> result::Result<(Self, Self::Size), Self::Error> {
328             use scroll::Pread;
329             let reloc = match container {
330                 Container::Little => {
331                     if is_rela {
332                         (bytes.pread_with::<reloc32::Rela>(0, le)?.into(), reloc32::SIZEOF_RELA)
333                     } else {
334                         (bytes.pread_with::<reloc32::Rel>(0, le)?.into(), reloc32::SIZEOF_REL)
335                     }
336                 },
337                 Container::Big => {
338                     if is_rela {
339                         (bytes.pread_with::<reloc64::Rela>(0, le)?.into(), reloc64::SIZEOF_RELA)
340                     } else {
341                         (bytes.pread_with::<reloc64::Rel>(0, le)?.into(), reloc64::SIZEOF_REL)
342                     }
343                 }
344             };
345             Ok(reloc)
346         }
347     }
348 
349     impl ctx::TryIntoCtx<RelocCtx> for Reloc {
350         type Error = crate::error::Error;
351         type Size = usize;
352         // TODO: I think this is a bad idea
353         /// Writes the relocation into `bytes`
354         fn try_into_ctx(self, bytes: &mut [u8], (is_rela, Ctx {container, le}): RelocCtx) -> result::Result<Self::Size, Self::Error> {
355             use scroll::Pwrite;
356             match container {
357                 Container::Little => {
358                     if is_rela {
359                         let rela: reloc32::Rela = self.into();
360                         Ok(bytes.pwrite_with(rela, 0, le)?)
361                     } else {
362                         let rel: reloc32::Rel = self.into();
363                         Ok(bytes.pwrite_with(rel, 0, le)?)
364                     }
365                 },
366                 Container::Big => {
367                     if is_rela {
368                         let rela: reloc64::Rela = self.into();
369                         Ok(bytes.pwrite_with(rela, 0, le)?)
370                     } else {
371                         let rel: reloc64::Rel = self.into();
372                         Ok(bytes.pwrite_with(rel, 0, le)?)
373                     }
374                 },
375             }
376         }
377     }
378 
379     impl ctx::IntoCtx<(bool, Ctx)> for Reloc {
380         /// Writes the relocation into `bytes`
381         fn into_ctx(self, bytes: &mut [u8], ctx: RelocCtx) {
382             use scroll::Pwrite;
383             bytes.pwrite_with(self, 0, ctx).unwrap();
384         }
385     }
386 
387     impl fmt::Debug for Reloc {
388         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
389             f.debug_struct("Reloc")
390                 .field("r_offset", &format_args!("{:x}", self.r_offset))
391                 .field("r_addend", &format_args!("{:x}", self.r_addend.unwrap_or(0)))
392                 .field("r_sym", &self.r_sym)
393                 .field("r_type", &self.r_type)
394                 .finish()
395         }
396     }
397 
398     #[derive(Default)]
399     /// An ELF section containing relocations, allowing lazy iteration over symbols.
400     pub struct RelocSection<'a> {
401         bytes: &'a [u8],
402         count: usize,
403         ctx: RelocCtx,
404         start: usize,
405         end: usize,
406     }
407 
408     impl<'a> fmt::Debug for RelocSection<'a> {
409         fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
410             let len = self.bytes.len();
411             fmt.debug_struct("RelocSection")
412                 .field("bytes", &len)
413                 .field("range", &format!("{:#x}..{:#x}", self.start, self.end))
414                 .field("count", &self.count)
415                 .field("Relocations", &self.to_vec())
416                 .finish()
417         }
418     }
419 
420     impl<'a> RelocSection<'a> {
421         #[cfg(feature = "endian_fd")]
422         /// Parse a REL or RELA section of size `filesz` from `offset`.
423         pub fn parse(bytes: &'a [u8], offset: usize, filesz: usize, is_rela: bool, ctx: Ctx) -> crate::error::Result<RelocSection<'a>> {
424             // TODO: better error message when too large (see symtab implementation)
425             let bytes = bytes.pread_with(offset, filesz)?;
426 
427             Ok(RelocSection {
428                 bytes: bytes,
429                 count: filesz / Reloc::size(is_rela, ctx),
430                 ctx: (is_rela, ctx),
431                 start: offset,
432                 end: offset + filesz,
433             })
434         }
435 
436         /// Try to parse a single relocation from the binary, at `index`.
437         #[inline]
438         pub fn get(&self, index: usize) -> Option<Reloc> {
439             if index >= self.count {
440                 None
441             } else {
442                 Some(self.bytes.pread_with(index * Reloc::size_with(&self.ctx), self.ctx).unwrap())
443             }
444         }
445 
446         /// The number of relocations in the section.
447         #[inline]
448         pub fn len(&self) -> usize {
449             self.count
450         }
451 
452         /// Returns true if section has no relocations.
453         #[inline]
454         pub fn is_empty(&self) -> bool {
455             self.count == 0
456         }
457 
458         /// Iterate over all relocations.
459         pub fn iter(&self) -> RelocIterator<'a> {
460             self.into_iter()
461         }
462 
463         /// Parse all relocations into a vector.
464         pub fn to_vec(&self) -> Vec<Reloc> {
465             self.iter().collect()
466         }
467     }
468 
469     impl<'a, 'b> IntoIterator for &'b RelocSection<'a> {
470         type Item = <RelocIterator<'a> as Iterator>::Item;
471         type IntoIter = RelocIterator<'a>;
472 
473         #[inline]
474         fn into_iter(self) -> Self::IntoIter {
475             RelocIterator {
476                 bytes: self.bytes,
477                 offset: 0,
478                 index: 0,
479                 count: self.count,
480                 ctx: self.ctx,
481             }
482         }
483     }
484 
485     pub struct RelocIterator<'a> {
486         bytes: &'a [u8],
487         offset: usize,
488         index: usize,
489         count: usize,
490         ctx: RelocCtx,
491     }
492 
493     impl<'a> fmt::Debug for RelocIterator<'a> {
494         fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
495             fmt.debug_struct("RelocIterator")
496                 .field("bytes", &"<... redacted ...>")
497                 .field("offset", &self.offset)
498                 .field("index", &self.index)
499                 .field("count", &self.count)
500                 .field("ctx", &self.ctx)
501                 .finish()
502         }
503     }
504 
505     impl<'a> Iterator for RelocIterator<'a> {
506         type Item = Reloc;
507 
508         #[inline]
509         fn next(&mut self) -> Option<Self::Item> {
510             if self.index >= self.count {
511                 None
512             } else {
513                 self.index += 1;
514                 Some(self.bytes.gread_with(&mut self.offset, self.ctx).unwrap())
515             }
516         }
517     }
518 
519     impl<'a> ExactSizeIterator for RelocIterator<'a> {
520         #[inline]
521         fn len(&self) -> usize {
522             self.count - self.index
523         }
524     }
525 } // end if_alloc
526