use core::{fmt, slice};
use crate::endian::Endianness;
use crate::macho;
use crate::read::{
ReadRef, Relocation, RelocationEncoding, RelocationFlags, RelocationKind, RelocationTarget,
SectionIndex, SymbolIndex,
};
use super::{MachHeader, MachOFile};
pub type MachORelocationIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
MachORelocationIterator<'data, 'file, macho::MachHeader32<Endian>, R>;
pub type MachORelocationIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
MachORelocationIterator<'data, 'file, macho::MachHeader64<Endian>, R>;
pub struct MachORelocationIterator<'data, 'file, Mach, R = &'data [u8]>
where
Mach: MachHeader,
R: ReadRef<'data>,
{
pub(super) file: &'file MachOFile<'data, Mach, R>,
pub(super) relocations: slice::Iter<'data, macho::Relocation<Mach::Endian>>,
}
impl<'data, 'file, Mach, R> Iterator for MachORelocationIterator<'data, 'file, Mach, R>
where
Mach: MachHeader,
R: ReadRef<'data>,
{
type Item = (u64, Relocation);
fn next(&mut self) -> Option<Self::Item> {
let mut paired_addend = 0;
loop {
let reloc = self.relocations.next()?;
let endian = self.file.endian;
let cputype = self.file.header.cputype(endian);
if reloc.r_scattered(endian, cputype) {
continue;
}
let reloc = reloc.info(self.file.endian);
let flags = RelocationFlags::MachO {
r_type: reloc.r_type,
r_pcrel: reloc.r_pcrel,
r_length: reloc.r_length,
};
let mut encoding = RelocationEncoding::Generic;
let kind = match cputype {
macho::CPU_TYPE_ARM => match (reloc.r_type, reloc.r_pcrel) {
(macho::ARM_RELOC_VANILLA, false) => RelocationKind::Absolute,
_ => RelocationKind::Unknown,
},
macho::CPU_TYPE_ARM64 | macho::CPU_TYPE_ARM64_32 => {
match (reloc.r_type, reloc.r_pcrel) {
(macho::ARM64_RELOC_UNSIGNED, false) => RelocationKind::Absolute,
(macho::ARM64_RELOC_ADDEND, _) => {
paired_addend = i64::from(reloc.r_symbolnum)
.wrapping_shl(64 - 24)
.wrapping_shr(64 - 24);
continue;
}
_ => RelocationKind::Unknown,
}
}
macho::CPU_TYPE_X86 => match (reloc.r_type, reloc.r_pcrel) {
(macho::GENERIC_RELOC_VANILLA, false) => RelocationKind::Absolute,
_ => RelocationKind::Unknown,
},
macho::CPU_TYPE_X86_64 => match (reloc.r_type, reloc.r_pcrel) {
(macho::X86_64_RELOC_UNSIGNED, false) => RelocationKind::Absolute,
(macho::X86_64_RELOC_SIGNED, true) => {
encoding = RelocationEncoding::X86RipRelative;
RelocationKind::Relative
}
(macho::X86_64_RELOC_BRANCH, true) => {
encoding = RelocationEncoding::X86Branch;
RelocationKind::Relative
}
(macho::X86_64_RELOC_GOT, true) => RelocationKind::GotRelative,
(macho::X86_64_RELOC_GOT_LOAD, true) => {
encoding = RelocationEncoding::X86RipRelativeMovq;
RelocationKind::GotRelative
}
_ => RelocationKind::Unknown,
},
_ => RelocationKind::Unknown,
};
let size = 8 << reloc.r_length;
let target = if reloc.r_extern {
RelocationTarget::Symbol(SymbolIndex(reloc.r_symbolnum as usize))
} else {
RelocationTarget::Section(SectionIndex(reloc.r_symbolnum as usize))
};
let implicit_addend = paired_addend == 0;
let mut addend = paired_addend;
if reloc.r_pcrel {
match cputype {
macho::CPU_TYPE_X86 => {
addend -= 1 << reloc.r_length;
}
macho::CPU_TYPE_X86_64 => {
addend -= 1 << reloc.r_length;
match reloc.r_type {
macho::X86_64_RELOC_SIGNED_1 => addend -= 1,
macho::X86_64_RELOC_SIGNED_2 => addend -= 2,
macho::X86_64_RELOC_SIGNED_4 => addend -= 4,
_ => {}
}
}
_ => {}
}
}
return Some((
reloc.r_address as u64,
Relocation {
kind,
encoding,
size,
target,
addend,
implicit_addend,
flags,
},
));
}
}
}
impl<'data, 'file, Mach, R> fmt::Debug for MachORelocationIterator<'data, 'file, Mach, R>
where
Mach: MachHeader,
R: ReadRef<'data>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MachORelocationIterator").finish()
}
}