Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
76 changes: 76 additions & 0 deletions fixtures/linux/aarch64/fp-basic-unwind-a64.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// clear && clang++ -std=c++23 fp-basic-unwind-a64.cc -o fp-basic-unwind-a64 && lldb ./fp-basic-unwind-a64
// A little demo program demonstrating unwinding a jit region without debug information

/*

Dumping the stack using lldb:

bt
p/x $pc
image lookup -a `$pc`
p/x ((void***) $fp)[0][1]
image lookup -a `((void***) $fp)[0][1]`
p/x ((void****) $fp)[0][0][1]
image lookup -a `((void****) $fp)[0][0][1]`
p/x ((void*****) $fp)[0][0][0][1]
image lookup -a `((void*****) $fp)[0][0][0][1]`
p/x ((void******) $fp)[0][0][0][0][1]
image lookup -a `((void******) $fp)[0][0][0][0][1]`

p/x $sp
p/x ((void******) $fp)[0][0][0][0] # Last stack frame

image list
image dump sections

# To get stack bounds:
(gdb) info proc mapping

memory read --outfile ./fp-basic-unwind-a64.stack.bin 0xfffffffdf000 0x1000000000000 --binary --force

*/

#include <cstdio>
#include <bit>
#include <cstdint>
#include <cstring>
#include <sys/mman.h>

extern "C" void breakpoint_mock()
{
__asm__ volatile (
"brk #0\n"
);
}

extern "C" void baseline_mock(uintptr_t baseline_mock_2, uintptr_t breakpoint_mock);
__asm__ (
"baseline_mock:" "\n"
" stp fp, lr, [sp, #-16]!" "\n"
" mov fp, sp" "\n"
" sub sp, fp, #96" "\n"
" movz x16, 0xBEEF" "\n"
" stur x16, [sp]" "\n"
" blr x0" "\n"
"baseline_mock_2:" "\n"
" stp fp, lr, [sp, #-16]!" "\n"
" mov fp, sp" "\n"
" sub sp, fp, #512" "\n"
" movz x16, 0xBFFF" "\n"
" stur x16, [sp]" "\n"
" blr x1" "\n"
);

int main(void)
{
void* jit = mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (jit == (void *) -1)
return 1;

printf("Have native stack %p, jit %p\n", __builtin_frame_address(0), jit);

// baseline_mock((uintptr_t)baseline_mock + 6 * 4, (uintptr_t)breakpoint_mock);
std::memcpy(jit, (void*) baseline_mock, 1024);

((void (*)(uintptr_t, uintptr_t))jit)((uintptr_t)jit + 6 * 4, (uintptr_t)breakpoint_mock);
}
Binary file not shown.
Binary file not shown.
98 changes: 98 additions & 0 deletions fixtures/linux/armhf/fp-basic-unwind-a32.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// clear && clang++ -g -std=c++23 fp-basic-unwind-a32.cc -o fp-basic-unwind-a32 -mthumb -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer && lldb ./fp-basic-unwind-a32
// A little demo program demonstrating unwinding a jit region without debug information

/*

Dumping the stack using lldb:

bt
p/x (uintptr_t)$pc - 0x00400000
image lookup -a `$pc`
p/x ((uintptr_t*) $r7)[1] - 0x00400000
image lookup -a `((void**) $r7)[1]`
p/x ((uintptr_t**) $r7)[0][1] - 0x00400000
image lookup -a `((void***) $r7)[0][1]`
p/x ((uintptr_t***) $r7)[0][0][1] - 0x00400000
image lookup -a `((void****) $r7)[0][0][1]`
p/x ((uintptr_t****) $r7)[0][0][0][1] - 0xf7c8a000
image lookup -a `((void*****) $r7)[0][0][0][1]`
p/x ((uintptr_t*****) $r7)[0][0][0][0][1]
image lookup -a `((void******) $r7)[0][0][0][0][1]`

p/x $sp
p/x ((void******) $r7)[0][0][0][0] # Last stack frame

image list
image dump sections

# To get stack bounds:
(gdb) info proc mapping

memory read --outfile ./fp-basic-unwind-a32.stack.bin 0xfffcf000 0xffff0000 --binary --force

p/x 0xffff0000-$r7
p/x 0xffff0000-$sp
p/x $pc-0x00400000
p/x $lr-0x00400000

*/

#include <cstdio>
#include <bit>
#include <cstdint>
#include <cstring>
#include <sys/mman.h>

__attribute__((target("thumb")))
extern "C" void breakpoint_mock()
{
__asm__ volatile (
".thumb\n"
".thumb_func\n"
"nop\n"
"nop\n"
"nop\n"
"bkpt #0\n"
"nop\n"
"nop\n"
"nop\n"
"nop\n"
);
}

__attribute__((target("thumb")))
extern "C" void baseline_mock(uint8_t* baseline_mock_2, uint8_t* breakpoint_mock);
__asm__ (
".thumb" "\n"
".thumb_func" "\n"
"baseline_mock:" "\n"
" push.w {r7, lr}" "\n"
" mov.w r7, sp" "\n"
" sub.w sp, #0x20" "\n"
" mov r2, 0xBEEF" "\n"
" str.w r2, [sp, #4]" "\n"
" blx r0" "\n"
".thumb" "\n"
".thumb_func" "\n"
"baseline_mock_2:" "\n"
" push.w {r7, lr}" "\n"
" mov.w r7, sp" "\n"
" sub.w sp, #0x28" "\n"
" mov r2, 0xBEEF" "\n"
" str.w r2, [sp, #4]" "\n"
" blx r1" "\n"
);

int main(void)
{
void* jit = mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (jit == (void *) -1)
return 1;

printf("Have native stack %p, jit %p\n", __builtin_frame_address(0), jit);

// baseline_mock((uint8_t*)baseline_mock + 22, (uint8_t*)breakpoint_mock);
std::memcpy(jit, (void*)((uint8_t*) baseline_mock - 1), 1024);

((void (*)(uint8_t*, uint8_t*)) ((uint8_t*)jit + 1))((uint8_t*)jit + 22 + 1, (uint8_t*)breakpoint_mock);
}
Binary file not shown.
10 changes: 10 additions & 0 deletions src/armhf/arch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use super::unwind_rule::UnwindRuleArmhf;
use super::unwindregs::UnwindRegsArmhf;
use crate::arch::Arch;

/// The Armhf CPU architecture.
pub struct ArchArmhf;
impl Arch for ArchArmhf {
type UnwindRule = UnwindRuleArmhf;
type UnwindRegs = UnwindRegsArmhf;
}
30 changes: 30 additions & 0 deletions src/armhf/cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use super::unwind_rule::*;
use crate::cache::*;

/// The unwinder cache type for [`UnwinderArmhf`](super::UnwinderArmhf).
pub struct CacheArmhf<P: AllocationPolicy = MayAllocateDuringUnwind>(pub Cache<UnwindRuleArmhf, P>);

impl CacheArmhf<MayAllocateDuringUnwind> {
/// Create a new cache.
pub fn new() -> Self {
Self(Cache::new())
}
}

impl<P: AllocationPolicy> CacheArmhf<P> {
/// Create a new cache.
pub fn new_in() -> Self {
Self(Cache::new())
}

/// Returns a snapshot of the cache usage statistics.
pub fn stats(&self) -> CacheStats {
self.0.rule_cache.stats()
}
}

impl<P: AllocationPolicy> Default for CacheArmhf<P> {
fn default() -> Self {
Self::new_in()
}
}
39 changes: 39 additions & 0 deletions src/armhf/dwarf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use gimli::{
Encoding, EvaluationStorage, Reader, Register, UnwindContextStorage, UnwindSection,
UnwindTableRow,
};

use super::{arch::ArchArmhf, unwind_rule::UnwindRuleArmhf, unwindregs::UnwindRegsArmhf};

use crate::unwind_result::UnwindResult;

use crate::dwarf::{DwarfUnwindRegs, DwarfUnwinderError, DwarfUnwinding};

impl DwarfUnwindRegs for UnwindRegsArmhf {
fn get(&self, _: Register) -> Option<u64> {
None
}
}

impl DwarfUnwinding for ArchArmhf {
fn unwind_frame<F, R, UCS, ES>(
_: &impl UnwindSection<R>,
_: &UnwindTableRow<R::Offset, UCS>,
_: Encoding,
_: &mut Self::UnwindRegs,
_: bool,
_: &mut F,
) -> Result<UnwindResult<Self::UnwindRule>, DwarfUnwinderError>
where
F: FnMut(u64) -> Result<u64, ()>,
R: Reader,
UCS: UnwindContextStorage<R::Offset>,
ES: EvaluationStorage<R>,
{
Err(DwarfUnwinderError::DidNotAdvance)
}

fn rule_if_uncovered_by_fde() -> Self::UnwindRule {
UnwindRuleArmhf::NoOpIfFirstFrameOtherwiseFp
}
}
12 changes: 12 additions & 0 deletions src/armhf/instruction_analysis.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use super::arch::ArchArmhf;
use crate::instruction_analysis::InstructionAnalysis;

impl InstructionAnalysis for ArchArmhf {
fn rule_from_prologue_analysis(_: &[u8], _: usize) -> Option<Self::UnwindRule> {
None
}

fn rule_from_epilogue_analysis(_: &[u8], _: usize) -> Option<Self::UnwindRule> {
None
}
}
21 changes: 21 additions & 0 deletions src/armhf/macho.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use super::arch::ArchArmhf;
use super::unwind_rule::UnwindRuleArmhf;
use crate::macho::{CompactUnwindInfoUnwinderError, CompactUnwindInfoUnwinding, CuiUnwindResult};
use macho_unwind_info::Function;

impl CompactUnwindInfoUnwinding for ArchArmhf {
fn unwind_frame(
_: Function,
_: bool,
_: usize,
_: Option<&[u8]>,
) -> Result<CuiUnwindResult<UnwindRuleArmhf>, CompactUnwindInfoUnwinderError> {
Err(CompactUnwindInfoUnwinderError::ArmhfUnsupported)
}

fn rule_for_stub_helper(
_: u32,
) -> Result<CuiUnwindResult<UnwindRuleArmhf>, CompactUnwindInfoUnwinderError> {
Ok(CuiUnwindResult::ExecRule(UnwindRuleArmhf::NoOp))
}
}
15 changes: 15 additions & 0 deletions src/armhf/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
mod arch;
mod cache;
mod dwarf;
mod instruction_analysis;
mod macho;
mod pe;
mod unwind_rule;
mod unwinder;
mod unwindregs;

pub use arch::*;
pub use cache::*;
pub use unwind_rule::*;
pub use unwinder::*;
pub use unwindregs::*;
19 changes: 19 additions & 0 deletions src/armhf/pe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use super::arch::ArchArmhf;
use crate::pe::{PeSections, PeUnwinderError, PeUnwinding};
use crate::unwind_result::UnwindResult;

impl PeUnwinding for ArchArmhf {
fn unwind_frame<F, D>(
_sections: PeSections<D>,
_address: u32,
_regs: &mut Self::UnwindRegs,
_is_first_frame: bool,
_read_stack: &mut F,
) -> Result<UnwindResult<Self::UnwindRule>, PeUnwinderError>
where
F: FnMut(u64) -> Result<u64, ()>,
D: core::ops::Deref<Target = [u8]>,
{
Err(PeUnwinderError::ArmhfUnsupported)
}
}
Loading