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