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 {
130     ($size:ident, $isize:ty) => {
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 pub mod reloc32 {
222 
223     pub use crate::elf::reloc::*;
224 
225     elf_reloc!(u32, i32);
226 
227     pub const SIZEOF_RELA: usize = 4 + 4 + 4;
228     pub const SIZEOF_REL: usize = 4 + 4;
229 
230     #[inline(always)]
r_sym(info: u32) -> u32231     pub fn r_sym(info: u32) -> u32 {
232         info >> 8
233     }
234 
235     #[inline(always)]
r_type(info: u32) -> u32236     pub fn r_type(info: u32) -> u32 {
237         info & 0xff
238     }
239 
240     #[inline(always)]
r_info(sym: u32, typ: u32) -> u32241     pub fn r_info(sym: u32, typ: u32) -> u32 {
242         (sym << 8) + (typ & 0xff)
243     }
244 
245     elf_rela_std_impl!(u32, i32);
246 }
247 
248 pub mod reloc64 {
249     pub use crate::elf::reloc::*;
250 
251     elf_reloc!(u64, i64);
252 
253     pub const SIZEOF_RELA: usize = 8 + 8 + 8;
254     pub const SIZEOF_REL: usize = 8 + 8;
255 
256     #[inline(always)]
r_sym(info: u64) -> u32257     pub fn r_sym(info: u64) -> u32 {
258         (info >> 32) as u32
259     }
260 
261     #[inline(always)]
r_type(info: u64) -> u32262     pub fn r_type(info: u64) -> u32 {
263         (info & 0xffff_ffff) as u32
264     }
265 
266     #[inline(always)]
r_info(sym: u64, typ: u64) -> u64267     pub fn r_info(sym: u64, typ: u64) -> u64 {
268         (sym << 32) + typ
269     }
270 
271     elf_rela_std_impl!(u64, i64);
272 }
273 
274 //////////////////////////////
275 // Generic Reloc
276 /////////////////////////////
277 if_alloc! {
278     use scroll::{ctx, Pread};
279     use scroll::ctx::SizeWith;
280     use core::fmt;
281     use core::result;
282     use crate::container::{Ctx, Container};
283     use alloc::vec::Vec;
284 
285     #[derive(Clone, Copy, PartialEq, Default)]
286     /// A unified ELF relocation structure
287     pub struct Reloc {
288         /// Address
289         pub r_offset: u64,
290         /// Addend
291         pub r_addend: Option<i64>,
292         /// The index into the corresponding symbol table - either dynamic or regular
293         pub r_sym: usize,
294         /// The relocation type
295         pub r_type: u32,
296     }
297 
298     impl Reloc {
299         pub fn size(is_rela: bool, ctx: Ctx) -> usize {
300             use scroll::ctx::SizeWith;
301             Reloc::size_with(&(is_rela, ctx))
302         }
303     }
304 
305     type RelocCtx = (bool, Ctx);
306 
307     impl ctx::SizeWith<RelocCtx> for Reloc {
308         fn size_with( &(is_rela, Ctx { container, .. }): &RelocCtx) -> usize {
309             match container {
310                 Container::Little => {
311                     if is_rela { reloc32::SIZEOF_RELA } else { reloc32::SIZEOF_REL }
312                 },
313                 Container::Big => {
314                     if is_rela { reloc64::SIZEOF_RELA } else { reloc64::SIZEOF_REL }
315                 }
316             }
317         }
318     }
319 
320     impl<'a> ctx::TryFromCtx<'a, RelocCtx> for Reloc {
321         type Error = crate::error::Error;
322         fn try_from_ctx(bytes: &'a [u8], (is_rela, Ctx { container, le }): RelocCtx) -> result::Result<(Self, usize), Self::Error> {
323             use scroll::Pread;
324             let reloc = match container {
325                 Container::Little => {
326                     if is_rela {
327                         (bytes.pread_with::<reloc32::Rela>(0, le)?.into(), reloc32::SIZEOF_RELA)
328                     } else {
329                         (bytes.pread_with::<reloc32::Rel>(0, le)?.into(), reloc32::SIZEOF_REL)
330                     }
331                 },
332                 Container::Big => {
333                     if is_rela {
334                         (bytes.pread_with::<reloc64::Rela>(0, le)?.into(), reloc64::SIZEOF_RELA)
335                     } else {
336                         (bytes.pread_with::<reloc64::Rel>(0, le)?.into(), reloc64::SIZEOF_REL)
337                     }
338                 }
339             };
340             Ok(reloc)
341         }
342     }
343 
344     impl ctx::TryIntoCtx<RelocCtx> for Reloc {
345         type Error = crate::error::Error;
346         /// Writes the relocation into `bytes`
347         fn try_into_ctx(self, bytes: &mut [u8], (is_rela, Ctx {container, le}): RelocCtx) -> result::Result<usize, Self::Error> {
348             use scroll::Pwrite;
349             match container {
350                 Container::Little => {
351                     if is_rela {
352                         let rela: reloc32::Rela = self.into();
353                         Ok(bytes.pwrite_with(rela, 0, le)?)
354                     } else {
355                         let rel: reloc32::Rel = self.into();
356                         Ok(bytes.pwrite_with(rel, 0, le)?)
357                     }
358                 },
359                 Container::Big => {
360                     if is_rela {
361                         let rela: reloc64::Rela = self.into();
362                         Ok(bytes.pwrite_with(rela, 0, le)?)
363                     } else {
364                         let rel: reloc64::Rel = self.into();
365                         Ok(bytes.pwrite_with(rel, 0, le)?)
366                     }
367                 },
368             }
369         }
370     }
371 
372     impl ctx::IntoCtx<(bool, Ctx)> for Reloc {
373         /// Writes the relocation into `bytes`
374         fn into_ctx(self, bytes: &mut [u8], ctx: RelocCtx) {
375             use scroll::Pwrite;
376             bytes.pwrite_with(self, 0, ctx).unwrap();
377         }
378     }
379 
380     impl fmt::Debug for Reloc {
381         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
382             f.debug_struct("Reloc")
383                 .field("r_offset", &format_args!("{:x}", self.r_offset))
384                 .field("r_addend", &format_args!("{:x}", self.r_addend.unwrap_or(0)))
385                 .field("r_sym", &self.r_sym)
386                 .field("r_type", &self.r_type)
387                 .finish()
388         }
389     }
390 
391     #[derive(Default)]
392     /// An ELF section containing relocations, allowing lazy iteration over symbols.
393     pub struct RelocSection<'a> {
394         bytes: &'a [u8],
395         count: usize,
396         ctx: RelocCtx,
397         start: usize,
398         end: usize,
399     }
400 
401     impl<'a> fmt::Debug for RelocSection<'a> {
402         fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
403             let len = self.bytes.len();
404             fmt.debug_struct("RelocSection")
405                 .field("bytes", &len)
406                 .field("range", &format!("{:#x}..{:#x}", self.start, self.end))
407                 .field("count", &self.count)
408                 .field("Relocations", &self.to_vec())
409                 .finish()
410         }
411     }
412 
413     impl<'a> RelocSection<'a> {
414         #[cfg(feature = "endian_fd")]
415         /// Parse a REL or RELA section of size `filesz` from `offset`.
416         pub fn parse(bytes: &'a [u8], offset: usize, filesz: usize, is_rela: bool, ctx: Ctx) -> crate::error::Result<RelocSection<'a>> {
417             // TODO: better error message when too large (see symtab implementation)
418             let bytes = bytes.pread_with(offset, filesz)?;
419 
420             Ok(RelocSection {
421                 bytes: bytes,
422                 count: filesz / Reloc::size(is_rela, ctx),
423                 ctx: (is_rela, ctx),
424                 start: offset,
425                 end: offset + filesz,
426             })
427         }
428 
429         /// Try to parse a single relocation from the binary, at `index`.
430         #[inline]
431         pub fn get(&self, index: usize) -> Option<Reloc> {
432             if index >= self.count {
433                 None
434             } else {
435                 Some(self.bytes.pread_with(index * Reloc::size_with(&self.ctx), self.ctx).unwrap())
436             }
437         }
438 
439         /// The number of relocations in the section.
440         #[inline]
441         pub fn len(&self) -> usize {
442             self.count
443         }
444 
445         /// Returns true if section has no relocations.
446         #[inline]
447         pub fn is_empty(&self) -> bool {
448             self.count == 0
449         }
450 
451         /// Iterate over all relocations.
452         pub fn iter(&self) -> RelocIterator<'a> {
453             self.into_iter()
454         }
455 
456         /// Parse all relocations into a vector.
457         pub fn to_vec(&self) -> Vec<Reloc> {
458             self.iter().collect()
459         }
460     }
461 
462     impl<'a, 'b> IntoIterator for &'b RelocSection<'a> {
463         type Item = <RelocIterator<'a> as Iterator>::Item;
464         type IntoIter = RelocIterator<'a>;
465 
466         #[inline]
467         fn into_iter(self) -> Self::IntoIter {
468             RelocIterator {
469                 bytes: self.bytes,
470                 offset: 0,
471                 index: 0,
472                 count: self.count,
473                 ctx: self.ctx,
474             }
475         }
476     }
477 
478     pub struct RelocIterator<'a> {
479         bytes: &'a [u8],
480         offset: usize,
481         index: usize,
482         count: usize,
483         ctx: RelocCtx,
484     }
485 
486     impl<'a> fmt::Debug for RelocIterator<'a> {
487         fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
488             fmt.debug_struct("RelocIterator")
489                 .field("bytes", &"<... redacted ...>")
490                 .field("offset", &self.offset)
491                 .field("index", &self.index)
492                 .field("count", &self.count)
493                 .field("ctx", &self.ctx)
494                 .finish()
495         }
496     }
497 
498     impl<'a> Iterator for RelocIterator<'a> {
499         type Item = Reloc;
500 
501         #[inline]
502         fn next(&mut self) -> Option<Self::Item> {
503             if self.index >= self.count {
504                 None
505             } else {
506                 self.index += 1;
507                 Some(self.bytes.gread_with(&mut self.offset, self.ctx).unwrap())
508             }
509         }
510     }
511 
512     impl<'a> ExactSizeIterator for RelocIterator<'a> {
513         #[inline]
514         fn len(&self) -> usize {
515             self.count - self.index
516         }
517     }
518 } // end if_alloc
519