Skip to content

Commit

Permalink
Move can_unwind into unwind
Browse files Browse the repository at this point in the history
Signed-off-by: Klim Tsoutsman <[email protected]>
  • Loading branch information
tsoutsman committed Dec 26, 2022
1 parent 6601443 commit cd80e0a
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 64 deletions.
70 changes: 7 additions & 63 deletions kernel/task_cancel/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,15 @@
#![feature(abi_x86_interrupt, try_blocks)]
#![no_std]

use core::{default::Default, iter::Iterator, sync::atomic::Ordering};
use gimli::{BaseAddresses, EhFrame, NativeEndian, UninitializedUnwindContext, UnwindSection};
use memory_structs::VirtualAddress;
use mod_mgmt::SectionType;
use core::sync::atomic::Ordering;
use task::{KillReason, TaskRef};
use x86_64::structures::idt::InterruptStackFrame;

pub fn cancel_task(task: TaskRef) {
task.cancel_requested.store(true, Ordering::Relaxed);
}

pub fn set_trap_flag(stack_frame: &mut InterruptStackFrame) {
fn set_trap_flag(stack_frame: &mut InterruptStackFrame) {
unsafe { stack_frame.as_mut() }.update(|stack_frame| stack_frame.cpu_flags |= 0x100);
}

Expand All @@ -25,63 +22,8 @@ pub extern "x86-interrupt" fn interrupt_handler(mut stack_frame: InterruptStackF

log::info!("instruction pointer: {instruction_pointer:0x?}");

let can_unwind = Option::<_>::is_some(
&try {
let task = task::get_my_current_task().expect("couldn't get current task");

// TODO: Search external unwind info.

let krate = task.namespace.get_crate_containing_address(
VirtualAddress::new_canonical(instruction_pointer as usize),
false,
)?;

let krate = krate.lock_as_ref();
let text_address = krate.text_pages.as_ref()?.1.start.value();
let eh_frame_section = krate
.sections
.values()
.find(|s| s.typ == SectionType::EhFrame)?;
let eh_frame_address = eh_frame_section.virt_addr.value();

let base_addresses = BaseAddresses::default()
.set_text(text_address as u64)
.set_eh_frame(eh_frame_address as u64);
log::info!("{base_addresses:0x?}");

let pages = eh_frame_section.mapped_pages.lock();
let bytes = pages
.as_slice(eh_frame_section.mapped_pages_offset, eh_frame_section.size)
.ok()?;
let eh_frame = EhFrame::new(bytes, NativeEndian);

let frame_description_entry = eh_frame
.fde_for_address(
&base_addresses,
instruction_pointer,
EhFrame::cie_from_offset,
)
.ok()?;

let mut unwind_context = UninitializedUnwindContext::new();
let _ = frame_description_entry
.unwind_info_for_address(
&eh_frame,
&base_addresses,
&mut unwind_context,
instruction_pointer,
)
.ok()?;

log::info!("covering");

Some(())
},
);

log::info!("stack frame: {:0x?}", stack_frame.stack_pointer);

if can_unwind {
if unwind::can_unwind(instruction_pointer) {
log::info!("unwinding a cancelled task");
unwind::start_remote_unwinding(
KillReason::Requested,
0,
Expand All @@ -90,7 +32,9 @@ pub extern "x86-interrupt" fn interrupt_handler(mut stack_frame: InterruptStackF
)
.expect("failed to unwind");
} else {
// FIXME: What happens if the APIC interrupt triggers here?
// The trap flag is reset after every debug interrupt. Since we can't unwind at
// this instruction, we reset the flag to check again at the next instruction.
set_trap_flag(&mut stack_frame);
// FIXME: What happens if a LAPIC timer interrupt triggers here?
}
}
82 changes: 81 additions & 1 deletion kernel/unwind/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ impl FallibleIterator for StackFrameIter {
let size_of_exception_stack_frame: i64 = 5 * 8;
#[cfg(not(downtime_eval))]
trace!("StackFrameIter: next stack frame is an exception handler: adding {:#X} to cfa, new cfa: {:#X}", size_of_exception_stack_frame, cfa);

this_frame_is_exception_handler = true;
Some(size_of_error_code + size_of_exception_stack_frame)
} else {
Expand Down Expand Up @@ -456,6 +456,86 @@ unsafe fn deref_ptr(ptr: Pointer) -> u64 {
pub trait FuncWithRegisters = FnMut(Registers) -> Result<(), &'static str>;
type FuncWithRegistersRefMut<'a> = &'a mut dyn FuncWithRegisters;

/// Returns whether unwinding can begin from the given instruction pointer.
///
/// This happens when one of the following two criteria is met:
///
/// 1. The pointer is covered by the unwind tables, but does not have an LSDA.
/// This occurs when the function does not have any associated drop handlers,
/// either because it has nothing to drop or Rust has determined that the
/// function will never be unwound.
///
/// 2. The pointer is covered by the unwind tables, has an LSDA, and is covered
/// by the LSDA's call site table. This occurs when the function has associated
/// drop handlers and the instruction pointer is at an instruction that could
/// cause an unwinding (usually a call site to a function that can cause an
/// unwinding).
pub fn can_unwind(instruction_pointer: u64) -> bool {
can_unwind_inner(instruction_pointer).is_some()
}

fn can_unwind_inner(instruction_pointer: u64) -> Option<()> {
let task = task::get_my_current_task()?;
let instruction_address = VirtualAddress::new(instruction_pointer as usize)?;
let krate = task
.namespace
.get_crate_containing_address(instruction_address, false)?;

let (eh_frame, base_addresses) = get_eh_frame_info(&krate)
.map(|(eh_frame_section, base_addresses)| {
(EhFrameReference::Section(eh_frame_section), base_addresses)
})
.or_else(|| {
external_unwind_info::get_unwind_info(instruction_address).map(|unwind_info| {
let base_addresses = BaseAddresses::default()
.set_eh_frame(unwind_info.unwind_info.start.value() as u64)
.set_text(unwind_info.text_section.start.value() as u64);
(EhFrameReference::External(unwind_info), base_addresses)
})
})?;

let unwind_row = UnwindRowReference {
caller: instruction_pointer,
eh_frame_sec: eh_frame,
base_addrs: base_addresses,
};
let StackFrame {
lsda: lsda_address,
initial_address,
call_site_address,
..
} = unwind_row
.with_unwind_info(|fde, _| {
Ok(StackFrame {
personality: None,
lsda: fde.lsda().map(|address| unsafe { deref_ptr(address) }),
initial_address: fde.initial_address(),
call_site_address: instruction_pointer,
})
})
.ok()?;

let lsda_address = match lsda_address {
Some(address) => VirtualAddress::new_canonical(address as usize),
None => return Some(()),
};

let (lsda_section, _) = task
.namespace
.get_section_containing_address(lsda_address, true)?;
let start =
lsda_section.mapped_pages_offset + (lsda_address.value() - lsda_section.virt_addr.value());
let length = lsda_section.virt_addr.value() + lsda_section.size - lsda_address.value();

let lsda_pages = lsda_section.mapped_pages.lock();
let lsda_slice = lsda_pages.as_slice::<u8>(start, length).ok()?;
let table = lsda::GccExceptTableArea::new(lsda_slice, NativeEndian, initial_address);

table
.call_site_table_entry_for_address(call_site_address)
.ok()
.map(|_| ())
}

/// This function saves the current CPU register values onto the stack (to preserve them)
/// and then invokes the given closure with those registers as the argument.
Expand Down

0 comments on commit cd80e0a

Please sign in to comment.