Skip to main content

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