Skip to main content

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        let mut subtractor = None;
42        loop {
43            let reloc = self.relocations.next()?;
44            let endian = self.file.endian;
45            let cputype = self.file.header.cputype(endian);
46            if reloc.r_scattered(endian, cputype) {
47                // FIXME: handle scattered relocations
48                // We need to add `RelocationTarget::Address` for this.
49                continue;
50            }
51            let reloc = reloc.info(self.file.endian);
52            let flags = RelocationFlags::MachO {
53                r_type: reloc.r_type,
54                r_pcrel: reloc.r_pcrel,
55                r_length: reloc.r_length,
56            };
57            let mut size = 8 << reloc.r_length;
58            let g = E::Generic;
59            let unknown = (K::Unknown, E::Unknown);
60            let (kind, encoding) = match cputype {
61                macho::CPU_TYPE_ARM => match (reloc.r_type, reloc.r_pcrel) {
62                    (macho::ARM_RELOC_VANILLA, false) => (K::Absolute, g),
63                    _ => unknown,
64                },
65                macho::CPU_TYPE_ARM64 | macho::CPU_TYPE_ARM64_32 => {
66                    match (reloc.r_type, reloc.r_pcrel) {
67                        (macho::ARM64_RELOC_UNSIGNED, false) => (K::Absolute, g),
68                        (macho::ARM64_RELOC_BRANCH26, true) => {
69                            size = 26;
70                            (K::PltRelative, E::AArch64Call)
71                        }
72                        (macho::ARM64_RELOC_ADDEND, _) => {
73                            paired_addend = i64::from(reloc.r_symbolnum)
74                                .wrapping_shl(64 - 24)
75                                .wrapping_shr(64 - 24);
76                            continue;
77                        }
78                        (macho::ARM64_RELOC_SUBTRACTOR, _) => {
79                            subtractor = Some(SymbolIndex(reloc.r_symbolnum as usize));
80                            continue;
81                        }
82                        _ => unknown,
83                    }
84                }
85                macho::CPU_TYPE_X86 => match (reloc.r_type, reloc.r_pcrel) {
86                    (macho::GENERIC_RELOC_VANILLA, false) => (K::Absolute, g),
87                    _ => unknown,
88                },
89                macho::CPU_TYPE_X86_64 => match (reloc.r_type, reloc.r_pcrel) {
90                    (macho::X86_64_RELOC_UNSIGNED, false) => (K::Absolute, g),
91                    (macho::X86_64_RELOC_SIGNED, true) => (K::Relative, E::X86RipRelative),
92                    (macho::X86_64_RELOC_BRANCH, true) => (K::PltRelative, E::X86Branch),
93                    (macho::X86_64_RELOC_GOT, true) => (K::GotRelative, g),
94                    (macho::X86_64_RELOC_GOT_LOAD, true) => (K::GotRelative, E::X86RipRelativeMovq),
95                    (macho::X86_64_RELOC_SUBTRACTOR, _) => {
96                        subtractor = Some(SymbolIndex(reloc.r_symbolnum as usize));
97                        continue;
98                    }
99                    _ => unknown,
100                },
101                macho::CPU_TYPE_POWERPC | macho::CPU_TYPE_POWERPC64 => {
102                    match (reloc.r_type, reloc.r_pcrel) {
103                        (macho::PPC_RELOC_VANILLA, false) => (K::Absolute, g),
104                        _ => unknown,
105                    }
106                }
107                _ => unknown,
108            };
109            let target = if reloc.r_extern {
110                RelocationTarget::Symbol(SymbolIndex(reloc.r_symbolnum as usize))
111            } else {
112                RelocationTarget::Section(SectionIndex(reloc.r_symbolnum as usize))
113            };
114            let implicit_addend = paired_addend == 0;
115            let mut addend = paired_addend;
116            if reloc.r_pcrel {
117                // For PC relative relocations on some architectures, the
118                // addend does not include the offset required due to the
119                // PC being different from the place of the relocation.
120                // This differs from other file formats, so adjust the
121                // addend here to account for this.
122                match cputype {
123                    macho::CPU_TYPE_X86 => {
124                        addend -= 1 << reloc.r_length;
125                    }
126                    macho::CPU_TYPE_X86_64 => {
127                        addend -= 1 << reloc.r_length;
128                        match reloc.r_type {
129                            macho::X86_64_RELOC_SIGNED_1 => addend -= 1,
130                            macho::X86_64_RELOC_SIGNED_2 => addend -= 2,
131                            macho::X86_64_RELOC_SIGNED_4 => addend -= 4,
132                            _ => {}
133                        }
134                    }
135                    // TODO: maybe missing support for some architectures and relocations
136                    _ => {}
137                }
138            }
139            return Some((
140                reloc.r_address as u64,
141                Relocation {
142                    kind,
143                    encoding,
144                    size,
145                    target,
146                    subtractor,
147                    addend,
148                    implicit_addend,
149                    flags,
150                },
151            ));
152        }
153    }
154}
155
156impl<'data, 'file, Mach, R> fmt::Debug for MachORelocationIterator<'data, 'file, Mach, R>
157where
158    Mach: MachHeader,
159    R: ReadRef<'data>,
160{
161    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162        f.debug_struct("MachORelocationIterator").finish()
163    }
164}