object/read/macho/
relocation.rs

1use core::{fmt, slice};
2
3use crate::endian::Endianness;
4use crate::macho;
5use crate::read::{
6    ReadRef, Relocation, RelocationEncoding, RelocationFlags, RelocationKind, RelocationTarget,
7    SectionIndex, SymbolIndex,
8};
9
10use super::{MachHeader, MachOFile};
11
12/// An iterator for the relocations in a [`MachOSection32`](super::MachOSection32).
13pub type MachORelocationIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
14    MachORelocationIterator<'data, 'file, macho::MachHeader32<Endian>, R>;
15/// An iterator for the relocations in a [`MachOSection64`](super::MachOSection64).
16pub type MachORelocationIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
17    MachORelocationIterator<'data, 'file, macho::MachHeader64<Endian>, R>;
18
19/// An iterator for the relocations in a [`MachOSection`](super::MachOSection).
20pub struct MachORelocationIterator<'data, 'file, Mach, R = &'data [u8]>
21where
22    Mach: MachHeader,
23    R: ReadRef<'data>,
24{
25    pub(super) file: &'file MachOFile<'data, Mach, R>,
26    pub(super) relocations: slice::Iter<'data, macho::Relocation<Mach::Endian>>,
27}
28
29impl<'data, 'file, Mach, R> Iterator for MachORelocationIterator<'data, 'file, Mach, R>
30where
31    Mach: MachHeader,
32    R: ReadRef<'data>,
33{
34    type Item = (u64, Relocation);
35
36    fn next(&mut self) -> Option<Self::Item> {
37        use RelocationEncoding as E;
38        use RelocationKind as K;
39
40        let mut paired_addend = 0;
41        loop {
42            let reloc = self.relocations.next()?;
43            let endian = self.file.endian;
44            let cputype = self.file.header.cputype(endian);
45            if reloc.r_scattered(endian, cputype) {
46                // FIXME: handle scattered relocations
47                // We need to add `RelocationTarget::Address` for this.
48                continue;
49            }
50            let reloc = reloc.info(self.file.endian);
51            let flags = RelocationFlags::MachO {
52                r_type: reloc.r_type,
53                r_pcrel: reloc.r_pcrel,
54                r_length: reloc.r_length,
55            };
56            let g = E::Generic;
57            let unknown = (K::Unknown, E::Generic);
58            let (kind, encoding) = match cputype {
59                macho::CPU_TYPE_ARM => match (reloc.r_type, reloc.r_pcrel) {
60                    (macho::ARM_RELOC_VANILLA, false) => (K::Absolute, g),
61                    _ => unknown,
62                },
63                macho::CPU_TYPE_ARM64 | macho::CPU_TYPE_ARM64_32 => {
64                    match (reloc.r_type, reloc.r_pcrel) {
65                        (macho::ARM64_RELOC_UNSIGNED, false) => (K::Absolute, g),
66                        (macho::ARM64_RELOC_ADDEND, _) => {
67                            paired_addend = i64::from(reloc.r_symbolnum)
68                                .wrapping_shl(64 - 24)
69                                .wrapping_shr(64 - 24);
70                            continue;
71                        }
72                        _ => unknown,
73                    }
74                }
75                macho::CPU_TYPE_X86 => match (reloc.r_type, reloc.r_pcrel) {
76                    (macho::GENERIC_RELOC_VANILLA, false) => (K::Absolute, g),
77                    _ => unknown,
78                },
79                macho::CPU_TYPE_X86_64 => match (reloc.r_type, reloc.r_pcrel) {
80                    (macho::X86_64_RELOC_UNSIGNED, false) => (K::Absolute, g),
81                    (macho::X86_64_RELOC_SIGNED, true) => (K::Relative, E::X86RipRelative),
82                    (macho::X86_64_RELOC_BRANCH, true) => (K::Relative, E::X86Branch),
83                    (macho::X86_64_RELOC_GOT, true) => (K::GotRelative, g),
84                    (macho::X86_64_RELOC_GOT_LOAD, true) => (K::GotRelative, E::X86RipRelativeMovq),
85                    _ => unknown,
86                },
87                _ => unknown,
88            };
89            let size = 8 << reloc.r_length;
90            let target = if reloc.r_extern {
91                RelocationTarget::Symbol(SymbolIndex(reloc.r_symbolnum as usize))
92            } else {
93                RelocationTarget::Section(SectionIndex(reloc.r_symbolnum as usize))
94            };
95            let implicit_addend = paired_addend == 0;
96            let mut addend = paired_addend;
97            if reloc.r_pcrel {
98                // For PC relative relocations on some architectures, the
99                // addend does not include the offset required due to the
100                // PC being different from the place of the relocation.
101                // This differs from other file formats, so adjust the
102                // addend here to account for this.
103                match cputype {
104                    macho::CPU_TYPE_X86 => {
105                        addend -= 1 << reloc.r_length;
106                    }
107                    macho::CPU_TYPE_X86_64 => {
108                        addend -= 1 << reloc.r_length;
109                        match reloc.r_type {
110                            macho::X86_64_RELOC_SIGNED_1 => addend -= 1,
111                            macho::X86_64_RELOC_SIGNED_2 => addend -= 2,
112                            macho::X86_64_RELOC_SIGNED_4 => addend -= 4,
113                            _ => {}
114                        }
115                    }
116                    // TODO: maybe missing support for some architectures and relocations
117                    _ => {}
118                }
119            }
120            return Some((
121                reloc.r_address as u64,
122                Relocation {
123                    kind,
124                    encoding,
125                    size,
126                    target,
127                    addend,
128                    implicit_addend,
129                    flags,
130                },
131            ));
132        }
133    }
134}
135
136impl<'data, 'file, Mach, R> fmt::Debug for MachORelocationIterator<'data, 'file, Mach, R>
137where
138    Mach: MachHeader,
139    R: ReadRef<'data>,
140{
141    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142        f.debug_struct("MachORelocationIterator").finish()
143    }
144}