1 | use core::{fmt, slice}; |
2 | |
3 | use crate::endian::Endianness; |
4 | use crate::macho; |
5 | use crate::read::{ |
6 | ReadRef, Relocation, RelocationEncoding, RelocationKind, RelocationTarget, SectionIndex, |
7 | SymbolIndex, |
8 | }; |
9 | |
10 | use super::{MachHeader, MachOFile}; |
11 | |
12 | /// An iterator for the relocations in a [`MachOSection32`](super::MachOSection32). |
13 | pub 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). |
16 | pub 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). |
20 | pub struct MachORelocationIterator<'data, 'file, Mach, R = &'data [u8]> |
21 | where |
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 | |
29 | impl<'data, 'file, Mach, R> Iterator for MachORelocationIterator<'data, 'file, Mach, R> |
30 | where |
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 | |
150 | impl<'data, 'file, Mach, R> fmt::Debug for MachORelocationIterator<'data, 'file, Mach, R> |
151 | where |
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 | |