object/read/elf/
relocation.rs

1use alloc::fmt;
2use alloc::vec::Vec;
3use core::fmt::Debug;
4use core::slice;
5
6use crate::elf;
7use crate::endian::{self, Endianness};
8use crate::pod::Pod;
9use crate::read::{
10    self, Error, ReadRef, Relocation, RelocationEncoding, RelocationKind, RelocationTarget,
11    SectionIndex, SymbolIndex,
12};
13
14use super::{ElfFile, FileHeader, SectionHeader, SectionTable};
15
16/// A mapping from section index to associated relocation sections.
17#[derive(Debug)]
18pub struct RelocationSections {
19    relocations: Vec<usize>,
20}
21
22impl RelocationSections {
23    /// Create a new mapping using the section table.
24    ///
25    /// Skips relocation sections that do not use the given symbol table section.
26    pub fn parse<'data, Elf: FileHeader, R: ReadRef<'data>>(
27        endian: Elf::Endian,
28        sections: &SectionTable<'data, Elf, R>,
29        symbol_section: SectionIndex,
30    ) -> read::Result<Self> {
31        let mut relocations = vec![0; sections.len()];
32        for (index, section) in sections.iter().enumerate().rev() {
33            let sh_type = section.sh_type(endian);
34            if sh_type == elf::SHT_REL || sh_type == elf::SHT_RELA {
35                // The symbol indices used in relocations must be for the symbol table
36                // we are expecting to use.
37                let sh_link = SectionIndex(section.sh_link(endian) as usize);
38                if sh_link != symbol_section {
39                    continue;
40                }
41
42                let sh_info = section.sh_info(endian) as usize;
43                if sh_info == 0 {
44                    // Skip dynamic relocations.
45                    continue;
46                }
47                if sh_info >= relocations.len() {
48                    return Err(Error("Invalid ELF sh_info for relocation section"));
49                }
50
51                // Handle multiple relocation sections by chaining them.
52                let next = relocations[sh_info];
53                relocations[sh_info] = index;
54                relocations[index] = next;
55            }
56        }
57        Ok(Self { relocations })
58    }
59
60    /// Given a section index, return the section index of the associated relocation section.
61    ///
62    /// This may also be called with a relocation section index, and it will return the
63    /// next associated relocation section.
64    pub fn get(&self, index: usize) -> Option<usize> {
65        self.relocations.get(index).cloned().filter(|x| *x != 0)
66    }
67}
68
69pub(super) enum ElfRelaIterator<'data, Elf: FileHeader> {
70    Rel(slice::Iter<'data, Elf::Rel>),
71    Rela(slice::Iter<'data, Elf::Rela>),
72}
73
74impl<'data, Elf: FileHeader> ElfRelaIterator<'data, Elf> {
75    fn is_rel(&self) -> bool {
76        match self {
77            ElfRelaIterator::Rel(_) => true,
78            ElfRelaIterator::Rela(_) => false,
79        }
80    }
81}
82
83impl<'data, Elf: FileHeader> Iterator for ElfRelaIterator<'data, Elf> {
84    type Item = Elf::Rela;
85
86    fn next(&mut self) -> Option<Self::Item> {
87        match self {
88            ElfRelaIterator::Rel(ref mut i) => i.next().cloned().map(Self::Item::from),
89            ElfRelaIterator::Rela(ref mut i) => i.next().cloned(),
90        }
91    }
92}
93
94/// An iterator over the dynamic relocations for an `ElfFile32`.
95pub type ElfDynamicRelocationIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
96    ElfDynamicRelocationIterator<'data, 'file, elf::FileHeader32<Endian>, R>;
97/// An iterator over the dynamic relocations for an `ElfFile64`.
98pub type ElfDynamicRelocationIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
99    ElfDynamicRelocationIterator<'data, 'file, elf::FileHeader64<Endian>, R>;
100
101/// An iterator over the dynamic relocations for an `ElfFile`.
102pub struct ElfDynamicRelocationIterator<'data, 'file, Elf, R = &'data [u8]>
103where
104    Elf: FileHeader,
105    R: ReadRef<'data>,
106{
107    /// The current relocation section index.
108    pub(super) section_index: SectionIndex,
109    pub(super) file: &'file ElfFile<'data, Elf, R>,
110    pub(super) relocations: Option<ElfRelaIterator<'data, Elf>>,
111}
112
113impl<'data, 'file, Elf, R> Iterator for ElfDynamicRelocationIterator<'data, 'file, Elf, R>
114where
115    Elf: FileHeader,
116    R: ReadRef<'data>,
117{
118    type Item = (u64, Relocation);
119
120    fn next(&mut self) -> Option<Self::Item> {
121        let endian = self.file.endian;
122        loop {
123            if let Some(ref mut relocations) = self.relocations {
124                if let Some(reloc) = relocations.next() {
125                    let relocation =
126                        parse_relocation(self.file.header, endian, reloc, relocations.is_rel());
127                    return Some((reloc.r_offset(endian).into(), relocation));
128                }
129                self.relocations = None;
130            }
131
132            let section = self.file.sections.section(self.section_index).ok()?;
133            self.section_index.0 += 1;
134
135            let sh_link = SectionIndex(section.sh_link(endian) as usize);
136            if sh_link != self.file.dynamic_symbols.section() {
137                continue;
138            }
139
140            match section.sh_type(endian) {
141                elf::SHT_REL => {
142                    if let Ok(relocations) = section.data_as_array(endian, self.file.data) {
143                        self.relocations = Some(ElfRelaIterator::Rel(relocations.iter()));
144                    }
145                }
146                elf::SHT_RELA => {
147                    if let Ok(relocations) = section.data_as_array(endian, self.file.data) {
148                        self.relocations = Some(ElfRelaIterator::Rela(relocations.iter()));
149                    }
150                }
151                _ => {}
152            }
153        }
154    }
155}
156
157impl<'data, 'file, Elf, R> fmt::Debug for ElfDynamicRelocationIterator<'data, 'file, Elf, R>
158where
159    Elf: FileHeader,
160    R: ReadRef<'data>,
161{
162    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163        f.debug_struct("ElfDynamicRelocationIterator").finish()
164    }
165}
166
167/// An iterator over the relocations for an `ElfSection32`.
168pub type ElfSectionRelocationIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
169    ElfSectionRelocationIterator<'data, 'file, elf::FileHeader32<Endian>, R>;
170/// An iterator over the relocations for an `ElfSection64`.
171pub type ElfSectionRelocationIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
172    ElfSectionRelocationIterator<'data, 'file, elf::FileHeader64<Endian>, R>;
173
174/// An iterator over the relocations for an `ElfSection`.
175pub struct ElfSectionRelocationIterator<'data, 'file, Elf, R = &'data [u8]>
176where
177    Elf: FileHeader,
178    R: ReadRef<'data>,
179{
180    /// The current pointer in the chain of relocation sections.
181    pub(super) section_index: SectionIndex,
182    pub(super) file: &'file ElfFile<'data, Elf, R>,
183    pub(super) relocations: Option<ElfRelaIterator<'data, Elf>>,
184}
185
186impl<'data, 'file, Elf, R> Iterator for ElfSectionRelocationIterator<'data, 'file, Elf, R>
187where
188    Elf: FileHeader,
189    R: ReadRef<'data>,
190{
191    type Item = (u64, Relocation);
192
193    fn next(&mut self) -> Option<Self::Item> {
194        let endian = self.file.endian;
195        loop {
196            if let Some(ref mut relocations) = self.relocations {
197                if let Some(reloc) = relocations.next() {
198                    let relocation =
199                        parse_relocation(self.file.header, endian, reloc, relocations.is_rel());
200                    return Some((reloc.r_offset(endian).into(), relocation));
201                }
202                self.relocations = None;
203            }
204            self.section_index = SectionIndex(self.file.relocations.get(self.section_index.0)?);
205            // The construction of RelocationSections ensures section_index is valid.
206            let section = self.file.sections.section(self.section_index).unwrap();
207            match section.sh_type(endian) {
208                elf::SHT_REL => {
209                    if let Ok(relocations) = section.data_as_array(endian, self.file.data) {
210                        self.relocations = Some(ElfRelaIterator::Rel(relocations.iter()));
211                    }
212                }
213                elf::SHT_RELA => {
214                    if let Ok(relocations) = section.data_as_array(endian, self.file.data) {
215                        self.relocations = Some(ElfRelaIterator::Rela(relocations.iter()));
216                    }
217                }
218                _ => {}
219            }
220        }
221    }
222}
223
224impl<'data, 'file, Elf, R> fmt::Debug for ElfSectionRelocationIterator<'data, 'file, Elf, R>
225where
226    Elf: FileHeader,
227    R: ReadRef<'data>,
228{
229    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230        f.debug_struct("ElfSectionRelocationIterator").finish()
231    }
232}
233
234fn parse_relocation<Elf: FileHeader>(
235    header: &Elf,
236    endian: Elf::Endian,
237    reloc: Elf::Rela,
238    implicit_addend: bool,
239) -> Relocation {
240    let mut encoding = RelocationEncoding::Generic;
241    let is_mips64el = header.is_mips64el(endian);
242    let (kind, size) = match header.e_machine(endian) {
243        elf::EM_AARCH64 => match reloc.r_type(endian, false) {
244            elf::R_AARCH64_ABS64 => (RelocationKind::Absolute, 64),
245            elf::R_AARCH64_ABS32 => (RelocationKind::Absolute, 32),
246            elf::R_AARCH64_ABS16 => (RelocationKind::Absolute, 16),
247            elf::R_AARCH64_PREL64 => (RelocationKind::Relative, 64),
248            elf::R_AARCH64_PREL32 => (RelocationKind::Relative, 32),
249            elf::R_AARCH64_PREL16 => (RelocationKind::Relative, 16),
250            elf::R_AARCH64_CALL26 => {
251                encoding = RelocationEncoding::AArch64Call;
252                (RelocationKind::PltRelative, 26)
253            }
254            r_type => (RelocationKind::Elf(r_type), 0),
255        },
256        elf::EM_ARM => match reloc.r_type(endian, false) {
257            elf::R_ARM_ABS32 => (RelocationKind::Absolute, 32),
258            r_type => (RelocationKind::Elf(r_type), 0),
259        },
260        elf::EM_AVR => match reloc.r_type(endian, false) {
261            elf::R_AVR_32 => (RelocationKind::Absolute, 32),
262            elf::R_AVR_16 => (RelocationKind::Absolute, 16),
263            r_type => (RelocationKind::Elf(r_type), 0),
264        },
265        elf::EM_BPF => match reloc.r_type(endian, false) {
266            elf::R_BPF_64_64 => (RelocationKind::Absolute, 64),
267            elf::R_BPF_64_32 => (RelocationKind::Absolute, 32),
268            r_type => (RelocationKind::Elf(r_type), 0),
269        },
270        elf::EM_386 => match reloc.r_type(endian, false) {
271            elf::R_386_32 => (RelocationKind::Absolute, 32),
272            elf::R_386_PC32 => (RelocationKind::Relative, 32),
273            elf::R_386_GOT32 => (RelocationKind::Got, 32),
274            elf::R_386_PLT32 => (RelocationKind::PltRelative, 32),
275            elf::R_386_GOTOFF => (RelocationKind::GotBaseOffset, 32),
276            elf::R_386_GOTPC => (RelocationKind::GotBaseRelative, 32),
277            elf::R_386_16 => (RelocationKind::Absolute, 16),
278            elf::R_386_PC16 => (RelocationKind::Relative, 16),
279            elf::R_386_8 => (RelocationKind::Absolute, 8),
280            elf::R_386_PC8 => (RelocationKind::Relative, 8),
281            r_type => (RelocationKind::Elf(r_type), 0),
282        },
283        elf::EM_X86_64 => match reloc.r_type(endian, false) {
284            elf::R_X86_64_64 => (RelocationKind::Absolute, 64),
285            elf::R_X86_64_PC32 => (RelocationKind::Relative, 32),
286            elf::R_X86_64_GOT32 => (RelocationKind::Got, 32),
287            elf::R_X86_64_PLT32 => (RelocationKind::PltRelative, 32),
288            elf::R_X86_64_GOTPCREL => (RelocationKind::GotRelative, 32),
289            elf::R_X86_64_32 => (RelocationKind::Absolute, 32),
290            elf::R_X86_64_32S => {
291                encoding = RelocationEncoding::X86Signed;
292                (RelocationKind::Absolute, 32)
293            }
294            elf::R_X86_64_16 => (RelocationKind::Absolute, 16),
295            elf::R_X86_64_PC16 => (RelocationKind::Relative, 16),
296            elf::R_X86_64_8 => (RelocationKind::Absolute, 8),
297            elf::R_X86_64_PC8 => (RelocationKind::Relative, 8),
298            r_type => (RelocationKind::Elf(r_type), 0),
299        },
300        elf::EM_HEXAGON => match reloc.r_type(endian, false) {
301            elf::R_HEX_32 => (RelocationKind::Absolute, 32),
302            r_type => (RelocationKind::Elf(r_type), 0),
303        },
304        elf::EM_LOONGARCH => match reloc.r_type(endian, false) {
305            elf::R_LARCH_32 => (RelocationKind::Absolute, 32),
306            elf::R_LARCH_64 => (RelocationKind::Absolute, 64),
307            r_type => (RelocationKind::Elf(r_type), 0),
308        },
309        elf::EM_MIPS => match reloc.r_type(endian, is_mips64el) {
310            elf::R_MIPS_16 => (RelocationKind::Absolute, 16),
311            elf::R_MIPS_32 => (RelocationKind::Absolute, 32),
312            elf::R_MIPS_64 => (RelocationKind::Absolute, 64),
313            r_type => (RelocationKind::Elf(r_type), 0),
314        },
315        elf::EM_MSP430 => match reloc.r_type(endian, false) {
316            elf::R_MSP430_32 => (RelocationKind::Absolute, 32),
317            elf::R_MSP430_16_BYTE => (RelocationKind::Absolute, 16),
318            r_type => (RelocationKind::Elf(r_type), 0),
319        },
320        elf::EM_PPC => match reloc.r_type(endian, false) {
321            elf::R_PPC_ADDR32 => (RelocationKind::Absolute, 32),
322            r_type => (RelocationKind::Elf(r_type), 0),
323        },
324        elf::EM_PPC64 => match reloc.r_type(endian, false) {
325            elf::R_PPC64_ADDR32 => (RelocationKind::Absolute, 32),
326            elf::R_PPC64_ADDR64 => (RelocationKind::Absolute, 64),
327            r_type => (RelocationKind::Elf(r_type), 0),
328        },
329        elf::EM_RISCV => match reloc.r_type(endian, false) {
330            elf::R_RISCV_32 => (RelocationKind::Absolute, 32),
331            elf::R_RISCV_64 => (RelocationKind::Absolute, 64),
332            r_type => (RelocationKind::Elf(r_type), 0),
333        },
334        elf::EM_S390 => match reloc.r_type(endian, false) {
335            elf::R_390_8 => (RelocationKind::Absolute, 8),
336            elf::R_390_16 => (RelocationKind::Absolute, 16),
337            elf::R_390_32 => (RelocationKind::Absolute, 32),
338            elf::R_390_64 => (RelocationKind::Absolute, 64),
339            elf::R_390_PC16 => (RelocationKind::Relative, 16),
340            elf::R_390_PC32 => (RelocationKind::Relative, 32),
341            elf::R_390_PC64 => (RelocationKind::Relative, 64),
342            elf::R_390_PC16DBL => {
343                encoding = RelocationEncoding::S390xDbl;
344                (RelocationKind::Relative, 16)
345            }
346            elf::R_390_PC32DBL => {
347                encoding = RelocationEncoding::S390xDbl;
348                (RelocationKind::Relative, 32)
349            }
350            elf::R_390_PLT16DBL => {
351                encoding = RelocationEncoding::S390xDbl;
352                (RelocationKind::PltRelative, 16)
353            }
354            elf::R_390_PLT32DBL => {
355                encoding = RelocationEncoding::S390xDbl;
356                (RelocationKind::PltRelative, 32)
357            }
358            elf::R_390_GOT16 => (RelocationKind::Got, 16),
359            elf::R_390_GOT32 => (RelocationKind::Got, 32),
360            elf::R_390_GOT64 => (RelocationKind::Got, 64),
361            elf::R_390_GOTENT => {
362                encoding = RelocationEncoding::S390xDbl;
363                (RelocationKind::GotRelative, 32)
364            }
365            elf::R_390_GOTOFF16 => (RelocationKind::GotBaseOffset, 16),
366            elf::R_390_GOTOFF32 => (RelocationKind::GotBaseOffset, 32),
367            elf::R_390_GOTOFF64 => (RelocationKind::GotBaseOffset, 64),
368            elf::R_390_GOTPC => (RelocationKind::GotBaseRelative, 64),
369            elf::R_390_GOTPCDBL => {
370                encoding = RelocationEncoding::S390xDbl;
371                (RelocationKind::GotBaseRelative, 32)
372            }
373            r_type => (RelocationKind::Elf(r_type), 0),
374        },
375        elf::EM_SPARC | elf::EM_SPARC32PLUS | elf::EM_SPARCV9 => {
376            match reloc.r_type(endian, false) {
377                elf::R_SPARC_32 | elf::R_SPARC_UA32 => (RelocationKind::Absolute, 32),
378                elf::R_SPARC_64 | elf::R_SPARC_UA64 => (RelocationKind::Absolute, 64),
379                r_type => (RelocationKind::Elf(r_type), 0),
380            }
381        }
382        _ => (RelocationKind::Elf(reloc.r_type(endian, false)), 0),
383    };
384    let sym = reloc.r_sym(endian, is_mips64el) as usize;
385    let target = if sym == 0 {
386        RelocationTarget::Absolute
387    } else {
388        RelocationTarget::Symbol(SymbolIndex(sym))
389    };
390    Relocation {
391        kind,
392        encoding,
393        size,
394        target,
395        addend: reloc.r_addend(endian).into(),
396        implicit_addend,
397    }
398}
399
400/// A trait for generic access to `Rel32` and `Rel64`.
401#[allow(missing_docs)]
402pub trait Rel: Debug + Pod + Clone {
403    type Word: Into<u64>;
404    type Sword: Into<i64>;
405    type Endian: endian::Endian;
406
407    fn r_offset(&self, endian: Self::Endian) -> Self::Word;
408    fn r_info(&self, endian: Self::Endian) -> Self::Word;
409    fn r_sym(&self, endian: Self::Endian) -> u32;
410    fn r_type(&self, endian: Self::Endian) -> u32;
411}
412
413impl<Endian: endian::Endian> Rel for elf::Rel32<Endian> {
414    type Word = u32;
415    type Sword = i32;
416    type Endian = Endian;
417
418    #[inline]
419    fn r_offset(&self, endian: Self::Endian) -> Self::Word {
420        self.r_offset.get(endian)
421    }
422
423    #[inline]
424    fn r_info(&self, endian: Self::Endian) -> Self::Word {
425        self.r_info.get(endian)
426    }
427
428    #[inline]
429    fn r_sym(&self, endian: Self::Endian) -> u32 {
430        self.r_sym(endian)
431    }
432
433    #[inline]
434    fn r_type(&self, endian: Self::Endian) -> u32 {
435        self.r_type(endian)
436    }
437}
438
439impl<Endian: endian::Endian> Rel for elf::Rel64<Endian> {
440    type Word = u64;
441    type Sword = i64;
442    type Endian = Endian;
443
444    #[inline]
445    fn r_offset(&self, endian: Self::Endian) -> Self::Word {
446        self.r_offset.get(endian)
447    }
448
449    #[inline]
450    fn r_info(&self, endian: Self::Endian) -> Self::Word {
451        self.r_info.get(endian)
452    }
453
454    #[inline]
455    fn r_sym(&self, endian: Self::Endian) -> u32 {
456        self.r_sym(endian)
457    }
458
459    #[inline]
460    fn r_type(&self, endian: Self::Endian) -> u32 {
461        self.r_type(endian)
462    }
463}
464
465/// A trait for generic access to `Rela32` and `Rela64`.
466#[allow(missing_docs)]
467pub trait Rela: Debug + Pod + Clone {
468    type Word: Into<u64>;
469    type Sword: Into<i64>;
470    type Endian: endian::Endian;
471
472    fn r_offset(&self, endian: Self::Endian) -> Self::Word;
473    fn r_info(&self, endian: Self::Endian, is_mips64el: bool) -> Self::Word;
474    fn r_addend(&self, endian: Self::Endian) -> Self::Sword;
475    fn r_sym(&self, endian: Self::Endian, is_mips64el: bool) -> u32;
476    fn r_type(&self, endian: Self::Endian, is_mips64el: bool) -> u32;
477}
478
479impl<Endian: endian::Endian> Rela for elf::Rela32<Endian> {
480    type Word = u32;
481    type Sword = i32;
482    type Endian = Endian;
483
484    #[inline]
485    fn r_offset(&self, endian: Self::Endian) -> Self::Word {
486        self.r_offset.get(endian)
487    }
488
489    #[inline]
490    fn r_info(&self, endian: Self::Endian, _is_mips64el: bool) -> Self::Word {
491        self.r_info.get(endian)
492    }
493
494    #[inline]
495    fn r_addend(&self, endian: Self::Endian) -> Self::Sword {
496        self.r_addend.get(endian)
497    }
498
499    #[inline]
500    fn r_sym(&self, endian: Self::Endian, _is_mips64el: bool) -> u32 {
501        self.r_sym(endian)
502    }
503
504    #[inline]
505    fn r_type(&self, endian: Self::Endian, _is_mips64el: bool) -> u32 {
506        self.r_type(endian)
507    }
508}
509
510impl<Endian: endian::Endian> Rela for elf::Rela64<Endian> {
511    type Word = u64;
512    type Sword = i64;
513    type Endian = Endian;
514
515    #[inline]
516    fn r_offset(&self, endian: Self::Endian) -> Self::Word {
517        self.r_offset.get(endian)
518    }
519
520    #[inline]
521    fn r_info(&self, endian: Self::Endian, is_mips64el: bool) -> Self::Word {
522        self.get_r_info(endian, is_mips64el)
523    }
524
525    #[inline]
526    fn r_addend(&self, endian: Self::Endian) -> Self::Sword {
527        self.r_addend.get(endian)
528    }
529
530    #[inline]
531    fn r_sym(&self, endian: Self::Endian, is_mips64el: bool) -> u32 {
532        self.r_sym(endian, is_mips64el)
533    }
534
535    #[inline]
536    fn r_type(&self, endian: Self::Endian, is_mips64el: bool) -> u32 {
537        self.r_type(endian, is_mips64el)
538    }
539}