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