1 | use core::{fmt, slice}; |
2 | |
3 | use crate::endian::Endianness; |
4 | use crate::macho; |
5 | use crate::read::{ |
6 | ReadRef, Relocation, RelocationEncoding, RelocationFlags, RelocationKind, RelocationTarget, |
7 | SectionIndex, 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 | 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 | |
136 | impl<'data, 'file, Mach, R> fmt::Debug for MachORelocationIterator<'data, 'file, Mach, R> |
137 | where |
138 | Mach: MachHeader, |
139 | R: ReadRef<'data>, |
140 | { |
141 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
142 | f.debug_struct(name:"MachORelocationIterator" ).finish() |
143 | } |
144 | } |
145 | |