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