| 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 | |