#![deny(clippy::arithmetic_side_effects)]
use crate::arch::aarch64;
use crate::error::{Error, Result};
use crate::ids::SymbolId;
use crate::macho::read::consts::{
ARM64_RELOC_ADDEND, ARM64_RELOC_AUTHENTICATED_POINTER, ARM64_RELOC_BRANCH26,
ARM64_RELOC_GOT_LOAD_PAGE21, ARM64_RELOC_GOT_LOAD_PAGEOFF12, ARM64_RELOC_PAGE21,
ARM64_RELOC_PAGEOFF12, ARM64_RELOC_POINTER_TO_GOT, ARM64_RELOC_SUBTRACTOR,
ARM64_RELOC_TLVP_LOAD_PAGE21, ARM64_RELOC_TLVP_LOAD_PAGEOFF12, ARM64_RELOC_UNSIGNED,
CPU_TYPE_ARM64, N_ABS, N_SECT, S_THREAD_LOCAL_VARIABLES, X86_64_RELOC_BRANCH, X86_64_RELOC_GOT,
X86_64_RELOC_GOT_LOAD, X86_64_RELOC_SIGNED, X86_64_RELOC_SIGNED_1, X86_64_RELOC_SIGNED_2,
X86_64_RELOC_SIGNED_4, X86_64_RELOC_SUBTRACTOR, X86_64_RELOC_TLV, X86_64_RELOC_UNSIGNED,
};
use crate::macho::read::{PairedRelocation, RelocationTarget};
use super::buf::{get32, get64, to_usize};
use super::object::{LinkObject, NOT_GLOBAL};
use super::state::{Link, SymbolDef, atom_containing};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Referent {
Global(SymbolId),
Local(u32),
Address {
section: usize,
address: u64,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct PtrAuth {
pub key: u8,
pub address_diversity: bool,
pub diversity: u16,
}
impl PtrAuth {
pub const AUTH_GOT: Self = Self {
key: 0,
address_diversity: true,
diversity: 0,
};
#[must_use]
pub fn from_stored(stored: u64) -> Self {
Self {
key: ((stored >> 49) & 3) as u8,
address_diversity: (stored >> 48) & 1 != 0,
diversity: ((stored >> 32) & 0xffff) as u16,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct Decoded {
pub offset: u64,
pub r_type: u8,
pub length: u8,
pub pcrel: bool,
pub referent: Referent,
pub addend: i64,
pub subtrahend: Option<Referent>,
pub file: usize,
pub auth: Option<PtrAuth>,
}
fn pcrel_extra(r_type: u8) -> i64 {
match r_type {
X86_64_RELOC_SIGNED_1 => 1,
X86_64_RELOC_SIGNED_2 => 2,
X86_64_RELOC_SIGNED_4 => 4,
_ => 0,
}
}
fn stored(data: &[u8], offset: usize, length: u8) -> i64 {
match length {
3 => get64(data, offset).map_or(0, |v| v as i64),
2 => get32(data, offset).map_or(0, |v| i64::from(v as i32)),
_ => 0,
}
}
pub fn decode(
link: &Link<'_>,
file: usize,
object: &LinkObject<'_>,
section: usize,
data: &[u8],
relocation: &PairedRelocation,
) -> Result<Decoded> {
let reloc = relocation.relocation;
let arm64 = object.file.header().cpu_type == CPU_TYPE_ARM64;
let header = object.file.sections().get(section);
let section_addr = header.map_or(0, |s| s.addr);
let offset = u64::from(reloc.address);
let at = to_usize(offset);
let fail = |what: &str| {
object.file.source().malformed(
header.map_or(0, |s| u64::from(s.reloff)),
format!(
"relocation at {:#x} in {} ({what})",
offset,
header.map_or_else(String::new, |s| s.display_name())
),
)
};
let symbol_referent = |target: RelocationTarget| -> Result<Option<Referent>> {
match target {
RelocationTarget::Symbol(symbol) => {
let global = object
.global_of_symbol
.get(to_usize(u64::from(symbol)))
.copied()
.ok_or_else(|| fail("symbol index out of range"))?;
if global == NOT_GLOBAL {
Ok(Some(Referent::Local(symbol)))
} else {
link.global_id(file, global)
.map(|id| Some(Referent::Global(id)))
.ok_or_else(|| fail("global symbol without an ID"))
}
}
RelocationTarget::Section(_) => Ok(None),
RelocationTarget::Scattered(_) => Err(fail("scattered relocation")),
}
};
let section_of = |target: RelocationTarget| -> Result<usize> {
match target {
RelocationTarget::Section(ordinal) => usize::try_from(ordinal)
.ok()
.and_then(|o| o.checked_sub(1))
.filter(|&s| s < object.file.sections().len())
.ok_or_else(|| fail("section ordinal out of range")),
_ => Err(fail("expected a section relocation")),
}
};
let subtrahend = match relocation.subtractor {
Some(sub) => match symbol_referent(sub.target)? {
Some(referent) => Some(referent),
None => return Err(fail("SUBTRACTOR against a section")),
},
None => None,
};
let embedded = stored(data, at, reloc.length);
let mut auth = None;
let (referent, addend) = if arm64 {
match reloc.r_type {
ARM64_RELOC_UNSIGNED => match symbol_referent(reloc.target)? {
Some(referent) => (referent, embedded),
None => (
Referent::Address {
section: section_of(reloc.target)?,
address: embedded as u64,
},
0,
),
},
ARM64_RELOC_BRANCH26 => match symbol_referent(reloc.target)? {
Some(referent) => (referent, i64::from(relocation.addend.unwrap_or(0))),
None => {
let insn = get32(data, at).unwrap_or(0);
let imm = i64::from(((insn & 0x03ff_ffff) << 6) as i32 >> 6);
let address = section_addr
.wrapping_add(offset)
.wrapping_add(imm.wrapping_mul(4) as u64);
(
Referent::Address {
section: section_of(reloc.target)?,
address,
},
0,
)
}
},
ARM64_RELOC_PAGE21
| ARM64_RELOC_PAGEOFF12
| ARM64_RELOC_GOT_LOAD_PAGE21
| ARM64_RELOC_GOT_LOAD_PAGEOFF12
| ARM64_RELOC_TLVP_LOAD_PAGE21
| ARM64_RELOC_TLVP_LOAD_PAGEOFF12
| ARM64_RELOC_POINTER_TO_GOT => match symbol_referent(reloc.target)? {
Some(referent) => (referent, i64::from(relocation.addend.unwrap_or(0))),
None => return Err(fail("section-relative page relocation")),
},
ARM64_RELOC_AUTHENTICATED_POINTER => {
if reloc.length != 3 || relocation.subtractor.is_some() {
return Err(fail("authenticated pointer that is not 64-bit"));
}
let stored = embedded as u64;
auth = Some(PtrAuth::from_stored(stored));
let low = i64::from(stored as u32 as i32);
match symbol_referent(reloc.target)? {
Some(referent) => (referent, low),
None => (
Referent::Address {
section: section_of(reloc.target)?,
address: stored & 0xffff_ffff,
},
0,
),
}
}
ARM64_RELOC_SUBTRACTOR | ARM64_RELOC_ADDEND => {
return Err(fail("unpaired relocation"));
}
_ => return Err(fail("unknown relocation type")),
}
} else {
match reloc.r_type {
X86_64_RELOC_UNSIGNED => match symbol_referent(reloc.target)? {
Some(referent) => (referent, embedded),
None => (
Referent::Address {
section: section_of(reloc.target)?,
address: embedded as u64,
},
0,
),
},
X86_64_RELOC_SIGNED
| X86_64_RELOC_SIGNED_1
| X86_64_RELOC_SIGNED_2
| X86_64_RELOC_SIGNED_4
| X86_64_RELOC_BRANCH
| X86_64_RELOC_GOT_LOAD
| X86_64_RELOC_GOT
| X86_64_RELOC_TLV => {
let extra = pcrel_extra(reloc.r_type);
match symbol_referent(reloc.target)? {
Some(referent) => (referent, embedded.wrapping_add(extra)),
None => {
let address = section_addr
.wrapping_add(offset)
.wrapping_add(4)
.wrapping_add(extra as u64)
.wrapping_add(embedded as u64);
(
Referent::Address {
section: section_of(reloc.target)?,
address,
},
0,
)
}
}
}
X86_64_RELOC_SUBTRACTOR => return Err(fail("unpaired SUBTRACTOR")),
_ => return Err(fail("unknown relocation type")),
}
};
Ok(Decoded {
offset,
r_type: reloc.r_type,
length: reloc.length,
pcrel: reloc.pcrel,
referent,
addend,
subtrahend,
file,
auth,
})
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Place {
Atom {
atom: usize,
offset: i64,
},
Symbol(SymbolId),
Absolute(u64),
}
pub fn place(
link: &Link<'_>,
file: usize,
object: &LinkObject<'_>,
referent: Referent,
addend: i64,
) -> Result<Place> {
let fail = |what: String| object.file.source().malformed(0, what);
let in_section = |section: usize, address: u64| -> Result<Place> {
let header = object
.file
.sections()
.get(section)
.ok_or_else(|| fail(format!("section {section} out of range")))?;
let offset = address
.checked_sub(header.addr)
.filter(|&o| o <= header.size)
.ok_or_else(|| {
fail(format!(
"relocation target {address:#x} (outside section {})",
header.display_name()
))
})?;
let atom = atom_containing(object, section, offset).ok_or_else(|| {
fail(format!(
"relocation target {address:#x} (no atom in {})",
header.display_name()
))
})?;
let start = object.atoms.atoms().get(atom).map_or(0, |a| a.offset);
Ok(Place::Atom {
atom: link.atom_id(file, atom),
offset: offset.wrapping_sub(start) as i64,
})
};
match referent {
Referent::Address { section, address } => {
in_section(section, address.wrapping_add(addend as u64))
}
Referent::Local(symbol) => {
let entry = object.file.symbols().get(symbol)?;
match entry.n_type & 0x0e {
N_ABS => Ok(Place::Absolute(entry.n_value.wrapping_add(addend as u64))),
N_SECT => {
let section = usize::from(entry.n_sect)
.checked_sub(1)
.ok_or_else(|| fail(format!("symbol {symbol} has no section")))?;
let target = entry.n_value.wrapping_add(addend as u64);
if let Some(atom) = object.atoms.symbol_atom(symbol)
&& let Some(info) = object.atoms.atoms().get(atom)
&& let Some(header) = object.file.sections().get(section)
{
let start = header.addr.saturating_add(info.offset);
let end = start.saturating_add(info.size);
let in_section_range = target >= header.addr
&& target <= header.addr.saturating_add(header.size);
if (target >= start && target <= end) || !in_section_range {
return Ok(Place::Atom {
atom: link.atom_id(file, atom),
offset: target.wrapping_sub(start) as i64,
});
}
}
in_section(section, target)
}
_ => Err(fail(format!(
"relocation against undefined local symbol {}",
String::from_utf8_lossy(entry.name)
))),
}
}
Referent::Global(id) => match link.defs.get(id.index()) {
Some(SymbolDef::Object {
file: def_file,
symbol,
}) => {
let def_file = usize::try_from(*def_file).unwrap_or(usize::MAX);
let Some(def_object) = link.object(def_file) else {
return Ok(Place::Symbol(id));
};
let entry = def_object.file.symbols().get(*symbol)?;
if entry.n_type & 0x0e == N_ABS {
return Ok(Place::Absolute(entry.n_value.wrapping_add(addend as u64)));
}
let atom = def_object.atoms.symbol_atom(*symbol).ok_or_else(|| {
fail(format!(
"symbol {} has no atom",
String::from_utf8_lossy(entry.name)
))
})?;
let info = def_object.atoms.atoms().get(atom);
let section = def_object
.file
.sections()
.get(usize::from(entry.n_sect).saturating_sub(1));
let start = match (info, section) {
(Some(info), Some(section)) => section.addr.saturating_add(info.offset),
_ => 0,
};
Ok(Place::Atom {
atom: link.atom_id(def_file, atom),
offset: entry
.n_value
.wrapping_sub(start)
.wrapping_add(addend as u64) as i64,
})
}
_ => Ok(Place::Symbol(id)),
},
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FixupKind {
Rebase(u64),
Bind {
import: u32,
addend: i64,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Fixup {
pub address: u64,
pub kind: FixupKind,
pub auth: Option<PtrAuth>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Value {
Address(u64),
Absolute(u64),
Import(u32, i64),
}
pub trait Resolve {
fn value(&self, place: Place, addend: i64) -> Result<Value>;
fn got(&self, id: SymbolId) -> Option<u64>;
fn local_got(&self, file: usize, symbol: u32) -> Option<u64>;
fn stub(&self, id: SymbolId) -> Option<u64>;
fn tlv_pointer(&self, id: SymbolId) -> Option<u64>;
fn tlv_template(&self) -> u64;
fn thunk(&self, from: u64, target: u64) -> Option<u64>;
fn writable(&self, address: u64) -> bool;
fn weak_import(&self, id: SymbolId) -> Option<u32>;
}
fn range_error(what: &str, value: i64, place: u64) -> Error {
Error::Limit(format!(
"{what} relocation at {place:#x} out of range (value {value:#x})"
))
}
fn put_insn(out: &mut [u8], at: usize, insn: u32) -> Result<()> {
aarch64::write_insn(out, at, insn)
.ok_or_else(|| Error::Internal("relocation outside its section".into()))
}
fn put32(out: &mut [u8], at: usize, value: u32) -> Result<()> {
super::buf::put32(out, at, value)
.ok_or_else(|| Error::Internal("relocation outside its section".into()))
}
fn put64(out: &mut [u8], at: usize, value: u64) -> Result<()> {
super::buf::put64(out, at, value)
.ok_or_else(|| Error::Internal("relocation outside its section".into()))
}
fn page(address: u64) -> i64 {
(address & !0xfff) as i64
}
fn address_of(value: Value, what: &str, place: u64) -> Result<u64> {
match value {
Value::Address(address) | Value::Absolute(address) => Ok(address),
Value::Import(..) => Err(Error::Limit(format!(
"{what} relocation at {place:#x} refers to a symbol bound at run time"
))),
}
}
#[allow(clippy::too_many_lines, clippy::too_many_arguments)]
pub fn apply(
arm64: bool,
decoded: &Decoded,
target: Place,
subtrahend: Option<Place>,
section_type: u32,
resolve: &dyn Resolve,
out: &mut [u8],
at: usize,
place_address: u64,
) -> Result<Option<Fixup>> {
let addend = decoded.addend;
let symbol = match decoded.referent {
Referent::Global(id) => Some(id),
_ => None,
};
if let Some(subtrahend) = subtrahend {
let minuend = address_of(resolve.value(target, addend)?, "SUBTRACTOR", place_address)?;
let base = address_of(resolve.value(subtrahend, 0)?, "SUBTRACTOR", place_address)?;
let value = minuend.wrapping_sub(base);
return match decoded.length {
3 => put64(out, at, value).map(|()| None),
2 => {
let signed = value as i64;
if i32::try_from(signed).is_err() && u32::try_from(value).is_err() {
return Err(range_error("SUBTRACTOR", signed, place_address));
}
put32(out, at, value as u32).map(|()| None)
}
_ => Err(Error::Limit(format!(
"SUBTRACTOR relocation at {place_address:#x} with an unsupported size"
))),
};
}
let is_unsigned = (arm64
&& matches!(
decoded.r_type,
ARM64_RELOC_UNSIGNED | ARM64_RELOC_AUTHENTICATED_POINTER
))
|| (!arm64 && decoded.r_type == X86_64_RELOC_UNSIGNED);
if is_unsigned {
let auth = decoded.auth;
let mut value = resolve.value(target, addend)?;
if auth.is_some() && matches!(value, Value::Absolute(_)) {
return Err(Error::Limit(format!(
"authenticated pointer at {place_address:#x} to an absolute value"
)));
}
if section_type == S_THREAD_LOCAL_VARIABLES && decoded.offset % 24 == 16 {
let address = address_of(value, "thread-local offset", place_address)?;
return put64(out, at, address.wrapping_sub(resolve.tlv_template())).map(|()| None);
}
if let Some(import) = symbol.and_then(|id| resolve.weak_import(id)) {
value = Value::Import(import, addend);
}
return match (decoded.length, value) {
(3, Value::Absolute(v)) => put64(out, at, v).map(|()| None),
(3, Value::Address(address)) => {
if !resolve.writable(place_address) {
return Err(Error::Limit(format!(
"pointer at {place_address:#x} in a read-only segment needs a rebase"
)));
}
put64(out, at, address)?;
Ok(Some(Fixup {
address: place_address,
kind: FixupKind::Rebase(address),
auth,
}))
}
(3, Value::Import(import, addend)) => {
if !resolve.writable(place_address) {
return Err(Error::Limit(format!(
"pointer at {place_address:#x} in a read-only segment needs a bind"
)));
}
put64(out, at, 0)?;
Ok(Some(Fixup {
address: place_address,
kind: FixupKind::Bind { import, addend },
auth,
}))
}
(2, Value::Absolute(v)) => {
if u32::try_from(v).is_err() && i32::try_from(v as i64).is_err() {
return Err(range_error("32-bit absolute", v as i64, place_address));
}
put32(out, at, v as u32).map(|()| None)
}
_ => Err(Error::Limit(format!(
"32-bit absolute address at {place_address:#x} in a position-independent image"
))),
};
}
if arm64 {
let insn = aarch64::read_insn(out, at)
.ok_or_else(|| Error::Internal("relocation outside its section".into()))?;
match decoded.r_type {
ARM64_RELOC_BRANCH26 => {
let destination = match symbol.and_then(|id| resolve.stub(id)) {
Some(stub) => stub.wrapping_add(addend as u64),
None => address_of(resolve.value(target, addend)?, "BRANCH26", place_address)?,
};
let destination = if aarch64::branch_in_range(place_address, destination) {
destination
} else {
resolve.thunk(place_address, destination).ok_or_else(|| {
range_error(
"BRANCH26",
destination.wrapping_sub(place_address) as i64,
place_address,
)
})?
};
let delta = destination.wrapping_sub(place_address) as i64;
let insn = aarch64::Field::Branch26
.encode(insn, delta)
.map_err(|_| range_error("BRANCH26", delta, place_address))?;
put_insn(out, at, insn)?;
}
ARM64_RELOC_PAGE21 | ARM64_RELOC_GOT_LOAD_PAGE21 | ARM64_RELOC_TLVP_LOAD_PAGE21 => {
let (destination, _) = indirect(
decoded.r_type,
symbol,
target,
addend,
resolve,
place_address,
)?;
let delta = page(destination).wrapping_sub(page(place_address));
let insn = aarch64::Field::Adrp21
.encode(insn, delta)
.map_err(|_| range_error("PAGE21", delta, place_address))?;
put_insn(out, at, insn)?;
}
ARM64_RELOC_PAGEOFF12
| ARM64_RELOC_GOT_LOAD_PAGEOFF12
| ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
let (destination, relaxed) = indirect(
decoded.r_type,
symbol,
target,
addend,
resolve,
place_address,
)?;
let mut insn = insn;
if relaxed {
if insn & 0xbfc0_0000 != 0xb940_0000 {
return Err(Error::Limit(format!(
"relocation at {place_address:#x} expects an LDR instruction"
)));
}
insn = (insn & 0x001f_ffff) | 0x9100_0000;
}
let mut scale = 0u32;
if insn & 0x3b00_0000 == 0x3900_0000 {
scale = insn >> 30;
if scale == 0 && insn & 0x0480_0000 == 0x0480_0000 {
scale = 4;
}
}
let low = destination & 0xfff;
let mask = 1u64.checked_shl(scale).unwrap_or(1).saturating_sub(1);
if low & mask != 0 {
return Err(Error::Limit(format!(
"PAGEOFF12 relocation at {place_address:#x}: target {destination:#x} is not aligned to the access size"
)));
}
let imm = u32::try_from(low >> scale).unwrap_or(0);
put_insn(out, at, (insn & !0x003f_fc00) | (imm << 10))?;
}
ARM64_RELOC_POINTER_TO_GOT => {
let slot = got_slot(decoded, resolve).ok_or_else(|| {
Error::Internal(format!(
"POINTER_TO_GOT relocation at {place_address:#x} without a GOT slot"
))
})?;
if decoded.pcrel && decoded.length == 2 {
let delta = slot.wrapping_sub(place_address) as i64;
let delta32 = i32::try_from(delta)
.map_err(|_| range_error("POINTER_TO_GOT", delta, place_address))?;
put32(out, at, delta32 as u32)?;
} else if decoded.length == 3 {
put64(out, at, slot)?;
return Ok(Some(Fixup {
address: place_address,
kind: FixupKind::Rebase(slot),
auth: None,
}));
} else {
return Err(Error::Limit(format!(
"POINTER_TO_GOT relocation at {place_address:#x} with an unsupported size"
)));
}
}
_ => {
return Err(Error::Internal(format!(
"unhandled arm64 relocation type {}",
decoded.r_type
)));
}
}
return Ok(None);
}
let destination = match decoded.r_type {
X86_64_RELOC_BRANCH => match symbol.and_then(|id| resolve.stub(id)) {
Some(stub) => stub.wrapping_add(addend as u64),
None => address_of(resolve.value(target, addend)?, "BRANCH", place_address)?,
},
X86_64_RELOC_GOT_LOAD | X86_64_RELOC_GOT | X86_64_RELOC_TLV => {
let slot = match (decoded.r_type, symbol) {
(X86_64_RELOC_TLV, Some(id)) => resolve.tlv_pointer(id),
(X86_64_RELOC_TLV, None) => None,
_ => got_slot(decoded, resolve),
};
match slot {
Some(slot) => slot.wrapping_add(addend as u64),
None if decoded.r_type != X86_64_RELOC_GOT => {
let opcode = at
.checked_sub(2)
.and_then(|i| out.get_mut(i))
.ok_or_else(|| Error::Internal("relocation outside its section".into()))?;
if *opcode != 0x8b {
return Err(Error::Limit(format!(
"relocation at {place_address:#x} expects a MOVQ instruction"
)));
}
*opcode = 0x8d;
address_of(resolve.value(target, addend)?, "GOT_LOAD", place_address)?
}
None => {
return Err(Error::Internal(format!(
"GOT relocation at {place_address:#x} without a slot"
)));
}
}
}
X86_64_RELOC_SIGNED
| X86_64_RELOC_SIGNED_1
| X86_64_RELOC_SIGNED_2
| X86_64_RELOC_SIGNED_4 => {
address_of(resolve.value(target, addend)?, "SIGNED", place_address)?
}
_ => {
return Err(Error::Internal(format!(
"unhandled x86_64 relocation type {}",
decoded.r_type
)));
}
};
if decoded.length != 2 {
return Err(Error::Limit(format!(
"PC-relative relocation at {place_address:#x} with an unsupported size"
)));
}
let end = place_address
.wrapping_add(4)
.wrapping_add(pcrel_extra(decoded.r_type) as u64);
let delta = destination.wrapping_sub(end) as i64;
let delta32 =
i32::try_from(delta).map_err(|_| range_error("PC-relative", delta, place_address))?;
put32(out, at, delta32 as u32)?;
Ok(None)
}
fn got_slot(decoded: &Decoded, resolve: &dyn Resolve) -> Option<u64> {
match decoded.referent {
Referent::Global(id) => resolve.got(id),
Referent::Local(symbol) => resolve.local_got(decoded.file, symbol),
Referent::Address { .. } => None,
}
}
fn indirect(
r_type: u8,
symbol: Option<SymbolId>,
target: Place,
addend: i64,
resolve: &dyn Resolve,
place_address: u64,
) -> Result<(u64, bool)> {
let slot = match r_type {
ARM64_RELOC_GOT_LOAD_PAGE21 | ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {
symbol.and_then(|id| resolve.got(id))
}
ARM64_RELOC_TLVP_LOAD_PAGE21 | ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
symbol.and_then(|id| resolve.tlv_pointer(id))
}
_ => {
return Ok((
address_of(resolve.value(target, addend)?, "PAGE", place_address)?,
false,
));
}
};
match slot {
Some(slot) => Ok((slot, false)),
None => Ok((
address_of(resolve.value(target, addend)?, "GOT_LOAD", place_address)?,
true,
)),
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Needs {
pub got: bool,
pub stub: bool,
pub tlv: bool,
pub pointer: bool,
}
#[must_use]
pub fn needs(arm64: bool, r_type: u8) -> Needs {
if arm64 {
match r_type {
ARM64_RELOC_GOT_LOAD_PAGE21 | ARM64_RELOC_GOT_LOAD_PAGEOFF12 => Needs {
got: true,
..Needs::default()
},
ARM64_RELOC_POINTER_TO_GOT => Needs {
got: true,
pointer: true,
..Needs::default()
},
ARM64_RELOC_TLVP_LOAD_PAGE21 | ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => Needs {
tlv: true,
..Needs::default()
},
ARM64_RELOC_BRANCH26 => Needs {
stub: true,
..Needs::default()
},
_ => Needs::default(),
}
} else {
match r_type {
X86_64_RELOC_GOT_LOAD => Needs {
got: true,
..Needs::default()
},
X86_64_RELOC_GOT => Needs {
got: true,
pointer: true,
..Needs::default()
},
X86_64_RELOC_TLV => Needs {
tlv: true,
..Needs::default()
},
X86_64_RELOC_BRANCH => Needs {
stub: true,
..Needs::default()
},
_ => Needs::default(),
}
}
}
#[must_use]
pub fn is_pointer(arm64: bool, decoded: &Decoded) -> bool {
decoded.subtrahend.is_none()
&& decoded.length == 3
&& ((arm64
&& matches!(
decoded.r_type,
ARM64_RELOC_UNSIGNED | ARM64_RELOC_AUTHENTICATED_POINTER
))
|| (!arm64 && decoded.r_type == X86_64_RELOC_UNSIGNED))
}
const _: u8 = X86_64_RELOC_SUBTRACTOR;