usesuper::arch::ArchX86_64; usesuper::unwind_rule::UnwindRuleX86_64; usecrate::instruction_analysis::InstructionAnalysis; usecrate::macho::{CompactUnwindInfoUnwinderError, CompactUnwindInfoUnwinding, CuiUnwindResult}; use macho_unwind_info::opcodes::{OpcodeX86_64, RegisterNameX86_64}; use macho_unwind_info::Function;
impl CompactUnwindInfoUnwinding for ArchX86_64 { fn unwind_frame(
function: Function,
is_first_frame: bool,
address_offset_within_function: usize,
function_bytes: Option<&[u8]>,
) -> Result<CuiUnwindResult<UnwindRuleX86_64>, CompactUnwindInfoUnwinderError> { let opcode = OpcodeX86_64::parse(function.opcode); if is_first_frame { // The pc might be in a prologue or an epilogue. The compact unwind info format ignores // prologues and epilogues; the opcodes only describe the function body. So we do some // instruction analysis to check for prologues and epilogues. iflet Some(function_bytes) = function_bytes { iflet Some(rule) = Self::rule_from_instruction_analysis(
function_bytes,
address_offset_within_function,
) { // We are inside a prologue / epilogue. Ignore the opcode and use the rule from // instruction analysis. return Ok(CuiUnwindResult::ExecRule(rule));
} if opcode == OpcodeX86_64::Null
&& function_bytes.starts_with(&[0x55, 0x48, 0x89, 0xe5])
{ // The function is uncovered but it has a `push rbp; mov rbp, rsp` prologue. return Ok(CuiUnwindResult::ExecRule(UnwindRuleX86_64::UseFramePointer));
}
} if opcode == OpcodeX86_64::Null { return Ok(CuiUnwindResult::ExecRule(UnwindRuleX86_64::JustReturn));
}
}
// At this point we know with high certainty that we are in a function body. let r = match opcode {
OpcodeX86_64::Null => { return Err(CompactUnwindInfoUnwinderError::FunctionHasNoInfo);
}
OpcodeX86_64::FramelessImmediate {
stack_size_in_bytes,
saved_regs,
} => { if stack_size_in_bytes == 8 {
CuiUnwindResult::ExecRule(UnwindRuleX86_64::JustReturn)
} else { let bp_positon_from_outside = saved_regs
.iter()
.rev()
.flatten()
.position(|r| *r == RegisterNameX86_64::Rbp); match bp_positon_from_outside {
Some(pos) => { let bp_offset_from_sp =
stack_size_in_bytes as i32 - 2 * 8 - pos as i32 * 8; let bp_storage_offset_from_sp_by_8 =
i16::try_from(bp_offset_from_sp / 8).map_err(|_| {
CompactUnwindInfoUnwinderError::BpOffsetDoesNotFit
})?;
CuiUnwindResult::ExecRule(UnwindRuleX86_64::OffsetSpAndRestoreBp {
sp_offset_by_8: stack_size_in_bytes / 8,
bp_storage_offset_from_sp_by_8,
})
}
None => CuiUnwindResult::ExecRule(UnwindRuleX86_64::OffsetSp {
sp_offset_by_8: stack_size_in_bytes / 8,
}),
}
}
}
OpcodeX86_64::FramelessIndirect {
immediate_offset_from_function_start,
stack_adjust_in_bytes,
saved_regs,
} => { let function_bytes = function_bytes.ok_or(
CompactUnwindInfoUnwinderError::NoTextBytesToLookUpIndirectStackOffset,
)?; let sub_immediate_bytes = function_bytes
.get(
immediate_offset_from_function_start as usize
..immediate_offset_from_function_start as usize + 4,
)
.ok_or(CompactUnwindInfoUnwinderError::IndirectStackOffsetOutOfBounds)?; let sub_immediate = u32::from_le_bytes([
sub_immediate_bytes[0],
sub_immediate_bytes[1],
sub_immediate_bytes[2],
sub_immediate_bytes[3],
]); let stack_size_in_bytes =
sub_immediate
.checked_add(stack_adjust_in_bytes.into())
.ok_or(CompactUnwindInfoUnwinderError::StackAdjustOverflow)?; let sp_offset_by_8 = u16::try_from(stack_size_in_bytes / 8)
.map_err(|_| CompactUnwindInfoUnwinderError::StackSizeDoesNotFit)?; let bp_positon_from_outside = saved_regs
.iter()
.rev()
.flatten()
.position(|r| *r == RegisterNameX86_64::Rbp); match bp_positon_from_outside {
Some(pos) => { let bp_offset_from_sp = stack_size_in_bytes as i32 - 2 * 8 - pos as i32 * 8; let bp_storage_offset_from_sp_by_8 =
i16::try_from(bp_offset_from_sp / 8)
.map_err(|_| CompactUnwindInfoUnwinderError::BpOffsetDoesNotFit)?;
CuiUnwindResult::ExecRule(UnwindRuleX86_64::OffsetSpAndRestoreBp {
sp_offset_by_8,
bp_storage_offset_from_sp_by_8,
})
}
None => {
CuiUnwindResult::ExecRule(UnwindRuleX86_64::OffsetSp { sp_offset_by_8 })
}
}
}
OpcodeX86_64::Dwarf { eh_frame_fde } => CuiUnwindResult::NeedDwarf(eh_frame_fde),
OpcodeX86_64::FrameBased { .. } => {
CuiUnwindResult::ExecRule(UnwindRuleX86_64::UseFramePointer)
}
OpcodeX86_64::UnrecognizedKind(kind) => { return Err(CompactUnwindInfoUnwinderError::BadOpcodeKind(kind))
}
OpcodeX86_64::InvalidFrameless => { return Err(CompactUnwindInfoUnwinderError::InvalidFrameless)
}
};
Ok(r)
}
fn rule_for_stub_helper(
offset: u32,
) -> Result<CuiUnwindResult<UnwindRuleX86_64>, CompactUnwindInfoUnwinderError> { // shared: // +0x0 235cc4 4C 8D 1D 3D 03 04 00 lea r11, qword [dyld_stub_binder_276000+8] // +0x7 235ccb 41 53 push r11 // +0x9 235ccd FF 25 2D 03 04 00 jmp qword [dyld_stub_binder_276000] ; tail call // +0xf 235cd3 90 nop // first stub: // +0x10 235cd4 68 F1 61 00 00 push 0x61f1 // +0x15 235cd9 E9 E6 FF FF FF jmp 0x235cc4 ; jump to shared // second stub: // +0x1a 235cde 68 38 62 00 00 push 0x6238 // +0x1f 235ce3 E9 DC FF FF FF jmp 0x235cc4 ; jump to shared let rule = if offset < 0x7 { // pop 1 and return
UnwindRuleX86_64::OffsetSp { sp_offset_by_8: 2 }
} elseif offset < 0x10 { // pop 2 and return
UnwindRuleX86_64::OffsetSp { sp_offset_by_8: 3 }
} else { let offset_after_shared = offset - 0x10; let offset_within_stub = offset_after_shared % 10; if offset_within_stub < 5 {
UnwindRuleX86_64::JustReturn // just return
} else { // pop 1 and return
UnwindRuleX86_64::OffsetSp { sp_offset_by_8: 2 }
}
};
Ok(CuiUnwindResult::ExecRule(rule))
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-06-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.