Skip to main content

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