1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925
//! Support for unwinding the call stack and cleaning up stack frames.
//!
//! Uses DWARF debugging information (`.eh_frame` and `.gcc_except_table` sections) from object files.
//! It can also be used to generate stack traces (backtrace) using only that debug information
//! without using frame pointer registers.
//!
//! The general flow of the unwinding procedure is as follows:
//! * A panic occurs, which jumps to the panic entry point.
//! * The panic entry point invokes the panic_wrapper, which handles said panic
//! by invoking `start_unwinding()` in this crate with the reason for the panic.
//! * `start_unwinding()` generates an iterator over all stack frames in the call stack
//! (the knowledge for which comes from parsing the .eh_frame section).
//! * `start_unwinding()` creates an unwinding context, which contains the stack frame iterator,
//! the reason for the panic, and a reference to the current task being unwound.
//! It then skips the first several stack frames, which correspond to the panic and unwind handlers themselves.
//! Note that we cannot unwind those frames because they contain resources that we are currently using for unwinding purposes.
//! * At any point hereafter, the unwinding context must be manually cleaned up.
//! * `start_unwinding()` calls `continue_unwinding()`, which contains the bulk of the unwinding logic.
//! * `continue_unwinding()` iterates to the "next" stack frame (the previous frame in the call stack),
//! and invokes its cleanup routine (landing pad) if it has one.
//! * Once the cleanup routine is complete, it jumps to `_Unwind_Resume` automatically.
//! This cannot be changed and is an artifact of how unwinding routines are generated by the compiler.
//! * `_Unwind_Resume` is defined alongside the panic entry pointer, and is nothing more
//! than a simple wrapper that invokes `continue_unwinding()` here.
//! * `continue_unwinding()` continues iterating up the call stack.
//! Once it reaches the end of the call stack (or an error occurs),
//! we invoke a finalizer routine called `cleanup_unwinding_context()`.
//! * In `cleanup_unwinding_context()`, the unwinding context pointer is recovered and all unwinding resources are freed.
//! Finally, the task is marked as killed so it can no longer be scheduled in.
//!
//!
//! The flow of some functions was inspired by gcc's `libunwind`
//! and from `gimli/unwind-rs/src/glue.rs`.
#![no_std]
#![feature(panic_info_message)]
#![feature(naked_functions)]
#![feature(trait_alias)]
extern crate alloc;
#[macro_use] extern crate log;
extern crate memory;
extern crate mod_mgmt;
extern crate task;
extern crate gimli;
extern crate fallible_iterator;
extern crate interrupts;
extern crate external_unwind_info;
mod registers;
mod lsda;
use core::{arch::asm, fmt};
use alloc::{
sync::Arc,
boxed::Box,
};
use external_unwind_info::ExternalUnwindInfo;
use gimli::{
UnwindSection,
UnwindTableRow,
EhFrame,
BaseAddresses,
UninitializedUnwindContext,
FrameDescriptionEntry,
Pointer,
EndianSlice,
NativeEndian,
CfaRule,
RegisterRule,
X86_64
};
use registers::{Registers, LandingRegisters, SavedRegs};
use fallible_iterator::FallibleIterator;
use mod_mgmt::{
CrateNamespace,
SectionType,
StrongCrateRef,
StrongSectionRef,
};
use memory::VirtualAddress;
use task::{TaskRef, KillReason};
/// This is the context/state that is used during unwinding and passed around
/// to the callback functions in the various unwinding stages, such as in `_Unwind_Resume()`.
///
/// Because those callbacks follow an extern "C" ABI, this structure is passed as a pointer
/// rather than directly by value or by reference.
/// Thus, it must be manually freed when unwinding is finished (or if it fails in the middle)
/// in order to avoid leaking memory, e.g., not dropping reference counts.
pub struct UnwindingContext {
/// The iterator over the current call stack, in which the "next" item in the iterator
/// is the previous frame in the call stack (the caller frame).
stack_frame_iter: StackFrameIter,
/// The reason why we're performing unwinding, which should be set in the panic entry point handler.
cause: KillReason,
/// A reference to the current task that is being unwound.
current_task: TaskRef,
}
impl From<UnwindingContext> for (StackFrameIter, KillReason, TaskRef) {
fn from(val: UnwindingContext) -> Self {
(val.stack_frame_iter, val.cause, val.current_task)
}
}
/// A single frame in the stack, which contains
/// unwinding-related information for a single function call's stack frame.
///
/// See each method for documentation about the members of this struct.
#[derive(Debug)]
pub struct StackFrame {
personality: Option<u64>,
lsda: Option<u64>,
initial_address: u64,
call_site_address: u64,
}
impl StackFrame {
/// The address of the personality function that corresponds
/// to this stack frame's unwinding routine, if needed for this stack frame.
/// In Rust, this is always the same function, the one defined as the `eh_personality`
/// language item, something required by the compiler.
///
/// Note that in Theseus we do not use a personality function,
/// as we use a custom unwinding flow that bypasses invoking the personality function.
pub fn personality(&self) -> Option<u64> {
self.personality
}
/// The address of the Language-Specific Data Area (LSDA)
/// that is needed to discover the unwinding cleanup routines (landing pads)
/// for this stack frame.
/// Typically, this points to an area within a `.gcc_except_table` section,
/// which then needs to be parsed.
pub fn lsda(&self) -> Option<u64> {
self.lsda
}
/// The *call site* of this stack frame, i.e.,
/// the address of the instruction that called the next function in the call stack.
pub fn call_site_address(&self) -> u64 {
self.call_site_address
}
/// The address (starting instruction pointer) of the function
/// corresponding to this stack frame.
/// This points to the top (entry point) of that function.
pub fn initial_address(&self) -> u64 {
self.initial_address
}
}
/// An iterator over the stack frames on the current task's call stack,
/// which works in reverse calling order from the current function
/// up the call stack to the very first function on the stack,
/// at which point it will return `None`.
///
/// This is a lazy iterator: the previous frame in the call stack
/// is only calculated upon invocation of the `next()` method.
///
/// This can be used with the `FallibleIterator` trait.
pub struct StackFrameIter {
/// The namespace (set of crates/sections) that is used to resolve symbols
/// and section addresses when iterating over stack frames.
namespace: Arc<CrateNamespace>,
/// The values of the registers that exited during the stack frame
/// that is currently being iterated over.
///
/// These register values will change on each invocation of `next()`
/// as different stack frames are successively iterated over.
registers: Registers,
/// Unwinding state related to the previous frame in the call stack:
/// a reference to its row/entry in the unwinding table,
/// and the Canonical Frame Address (CFA value) that is used to determine the next frame.
state: Option<(UnwindRowReference, u64)>,
/// An extra offset that is used to adjust the calculation of the CFA in certain circumstances,
/// primarily when unwinding through an exception/interrupt handler stack frame `B`
/// to a frame `A` that caused the exception, even though frame `A` did not "call" frame `B`.
/// This will have a different value based on what the CPU did, e.g., pushed error codes onto the stack.
///
/// If `Some`, the latest stack frame produced by this iterator was an exception handler stack frame.
cfa_adjustment: Option<i64>,
/// This is set to true when the previous stack frame was an exception/interrupt handler,
/// which is useful in the case of taking into account the CPU pushing an `InterruptStackFrame` onto the stack.
/// The DWARF debugging/unwinding info cannot account for this because an interrupt or exception happening
/// is not the same as a regular function "call" happening.
last_frame_was_exception_handler: bool,
}
impl fmt::Debug for StackFrameIter {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// write!(f, "StackFrameIter {{\nRegisters: {:?},\nstate: {:#X?}\n}}", self.registers, self.state)
write!(f, "StackFrameIter {{\nRegisters: {:?}\n}}", self.registers)
}
}
impl StackFrameIter {
/// Create a new iterator over stack frames that starts from the current frame
/// and uses the given `Registers` values as a starting point.
///
/// Note: ideally, this shouldn't be public since it needs to be invoked with the correct initial register values.
#[doc(hidden)]
pub fn new(namespace: Arc<CrateNamespace>, registers: Registers) -> Self {
StackFrameIter {
namespace,
registers,
state: None,
cfa_adjustment: None,
last_frame_was_exception_handler: false,
}
}
/// Returns the array of register values as they existed during the stack frame
/// that is currently being iterated over.
///
/// After the [`next()`](#method.next.html) is invoked to iterate to a given stack frame,
/// this function will return the register values for that frame that was just iterated to.
/// Successive calls to this function will keep returning the same register values
/// until the `next()` method is invoked again.
///
/// This is necessary in order to restore the proper register values
/// before jumping to the **landing pad** (a cleanup function or exception/panic catcher)
/// such that the landing pad function will actually execute properly with the right context.
pub fn registers(&self) -> &Registers {
&self.registers
}
/// Returns a reference to the underlying `CrateNamespace`
/// that is used for symbol resolution while iterating over these stack frames.
pub fn namespace(&self) -> &Arc<CrateNamespace> {
&self.namespace
}
}
// Here we implement the main logic for traversing up the call stack.
impl FallibleIterator for StackFrameIter {
type Item = StackFrame;
type Error = &'static str;
fn next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
let registers = &mut self.registers;
let prev_cfa_adjustment = self.cfa_adjustment;
if let Some((unwind_row_ref, cfa)) = self.state.take() {
let mut newregs = registers.clone();
newregs[X86_64::RA] = None;
// For x86_64, the stack pointer is defined to be the previously-calculated CFA.
newregs[X86_64::RSP] = Some(cfa);
// If this frame is an exception/interrupt handler, we need to adjust RSP and the return address RA accordingly.
if let Some(extra_offset) = prev_cfa_adjustment {
newregs[X86_64::RSP] = Some(cfa.wrapping_add(extra_offset as u64));
trace!("adjusting RSP to {:X?}", newregs[X86_64::RSP]);
}
unwind_row_ref.with_unwind_info(|_fde, row| {
// There is some strange behavior when moving up the call stack
// from an exception handler function's frame `B` to a frame `A` that resulted in the exception,
// since frame `A` did not call frame `B` directly,
// and since the CPU may have pushed an error code onto the stack,
// which messes up the DWARF info that calculates register values properly.
//
// In this case, the `cfa` value must be modified to account for that error code
// being pushed onto the stack by adding `8` (the error code's size in bytes) to the `cfa` value.
//
// Also, the return address (RA) must be calculated differently, not using the below register rules.
for &(reg_num, ref rule) in row.registers() {
// debug!("Looking at register rule: {:?} {:?}", reg_num, rule);
// The stack pointer (RSP) is given by the CFA calculated during the previous iteration;
// there should *not* be a register rule defining the value of the RSP directly.
if reg_num == X86_64::RSP {
warn!("Ignoring unwind row's register rule for RSP {:?}, which is invalid on x86_64 because RSP is always set to the CFA value.", rule);
continue;
}
// If this stack frame is an exception handler, the return address wouldn't have been pushed onto the stack as with normal call instructions.
// Instead, it would've been pushed onto the stack by the CPU as part of the InterruptStackFrame, so we have to look for it there.
//
// We know that the stack currently looks like this:
// |-- address --|------------ Item on the stack -------------|
// | <lower> | ... |
// | CFA | error code |
// | CFA + 0x08 | Exception stack frame: instruction pointer |
// | CFA + 0x10 | code segment |
// | CFA + 0x18 | cpu flags |
// | CFA + 0x20 | stack pointer |
// | CFA + 0x28 | stack segment |
// | <higher> | ... |
// |-------------|---------------------------------------------|
//
// Thus, we want to skip the error code so we can get the instruction pointer,
// i.e., the value at CFA + 0x08.
if reg_num == X86_64::RA && prev_cfa_adjustment.is_some() {
let size_of_error_code = core::mem::size_of::<usize>();
// TODO FIXME: only skip the error code if the prev_cfa_adjustment included it
let value = unsafe { *(cfa.wrapping_add(size_of_error_code as u64) as *const u64) };
trace!("Using return address from CPU-pushed exception stack frame. Value: {:#X}", value);
newregs[X86_64::RA] = Some(value);
continue;
}
newregs[reg_num] = match *rule {
RegisterRule::Undefined => return Err("StackFrameIter: encountered an unsupported RegisterRule::Undefined"), // registers[reg_num],
RegisterRule::SameValue => registers[reg_num],
RegisterRule::Register(other_reg_num) => registers[other_reg_num],
// This is the most common register rule (in fact, the only one we've seen),
// so we may have to adapt the logic herein for use in other rules.
RegisterRule::Offset(offset) => {
let value = unsafe { *(cfa.wrapping_add(offset as u64) as *const u64) };
// trace!(" cfa: {:#X}, addr: {:#X}, value: {:#X}", cfa, cfa.wrapping_add(offset as u64), value);
Some(value)
}
RegisterRule::ValOffset(offset) => Some(cfa.wrapping_add(offset as u64)),
RegisterRule::Expression(_) => return Err("StackFrameIter: encountered an unsupported RegisterRule::Expression"),
RegisterRule::ValExpression(_) => return Err("StackFrameIter: encountered an unsupported RegisterRule::ValExpression"),
RegisterRule::Architectural => return Err("StackFrameIter: encountered an unsupported RegisterRule::Architectural"),
};
}
Ok(())
})?;
*registers = newregs;
}
// The return address (used to find the caller's stack frame) should be in the newly-calculated register set.
// If there isn't one, or if it's 0, then we have reached the beginning of the call stack, and are done iterating.
let return_address = match registers[X86_64::RA] {
Some(0) | None => return Ok(None),
Some(ra) => ra,
};
// The return address (RA register) actually points to the *next* instruction (1 byte past the call instruction),
// because the processor has advanced it to continue executing after the function returns.
// As x86 has variable-length instructions, we don't know exactly where the previous instruction starts,
// but we know that subtracting `1` will give us an address *within* that previous instruction.
let caller = return_address - 1;
// TODO FIXME: only subtract 1 for non-"fault" exceptions, e.g., page faults should NOT subtract 1
// trace!("call_site_address: {:#X}", caller);
let caller_virt_addr = VirtualAddress::new(caller as usize)
.ok_or("caller wasn't a valid virtual address")?;
// Get unwind info for the call site ("caller") address.
let (eh_frame_sec, base_addrs) = self.namespace
// First: search the current namespace's crates to see if any of them contain the caller address.
.get_crate_containing_address(caller_virt_addr, false)
.and_then(|crate_ref| get_eh_frame_info(&crate_ref)
.map(|(eh_frame_sec, base_addrs)|
(EhFrameReference::Section(eh_frame_sec), base_addrs)
)
)
// Second: search externally-registered unwind info for the caller address.
.or_else(|| external_unwind_info::get_unwind_info(caller_virt_addr)
.map(|uw_info| {
let base_addrs = BaseAddresses::default()
.set_eh_frame(uw_info.unwind_info.start.value() as u64)
.set_text(uw_info.text_section.start.value() as u64);
(EhFrameReference::External(uw_info), base_addrs)
})
)
// Otherwise, the caller address isn't known to the system, so return an error.
.ok_or_else(|| {
error!("StackTraceIter::next(): couldn't get unwind info for call site address: {:#X}", caller);
"couldn't get unwind info for call site address"
})?;
let mut cfa_adjustment: Option<i64> = None;
let mut this_frame_is_exception_handler = false;
let row_ref = UnwindRowReference { caller, eh_frame_sec, base_addrs };
let (cfa, frame) = row_ref.with_unwind_info(|fde, row| {
// trace!("ok: {:?} (0x{:x} - 0x{:x})", row.cfa(), row.start_address(), row.end_address());
let cfa = match *row.cfa() {
CfaRule::RegisterAndOffset{register, offset} => {
// debug!("CfaRule:RegisterAndOffset: reg {:?}, offset: {:#X}", register, offset);
let reg_value = registers[register].ok_or_else(|| {
error!("CFA rule specified register {:?} with offset {:#X}, but register {:?}({}) had no value!", register, offset, register, register.0);
"CFA rule specified register with offset, but that register had no value."
})?;
reg_value.wrapping_add(offset as u64)
}
CfaRule::Expression(_expr) => {
error!("CFA rules based on Expressions are not yet supported. Expression: {:?}", _expr);
return Err("CFA rules based on Expressions are not yet supported.");
}
};
// trace!("initial_address: {:#X}", fde.initial_address());
// If the next stack frame is an exception handler, then the CPU pushed an `InterruptStackFrame`
// onto the stack, completely unbeknownst to the DWARF debug info.
// Thus, we need to adjust this next frame's stack pointer (i.e., `cfa` which becomes the stack pointer)
// to account for the change in stack contents.
// TODO FIXME: check for any type of exception/interrupt handler, and differentiate between error codes
cfa_adjustment = if interrupts::is_exception_handler_with_error_code(fde.initial_address()) {
let size_of_error_code: i64 = core::mem::size_of::<usize>() as i64;
trace!("StackFrameIter: next stack frame has a CPU-pushed error code on the stack, adjusting CFA to {:#X}", cfa);
// TODO: we need to set this to true for any exception/interrupt handler, not just those with error codes.
// If there is an error code pushed, then we need to account for that additionally beyond the exception stack frame being pushed.
let size_of_exception_stack_frame: i64 = 5 * 8;
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 {
None
};
// trace!("cfa is {:#X}", cfa);
let frame = StackFrame {
personality: None, // we don't use the personality function in Theseus
lsda: fde.lsda().map(|x| unsafe { deref_ptr(x) }),
initial_address: fde.initial_address(),
call_site_address: caller,
};
Ok((cfa, frame))
})?;
// since we can't double-borrow `self` mutably in the above closure, we assign its state(s) here.
self.cfa_adjustment = cfa_adjustment;
self.last_frame_was_exception_handler = this_frame_is_exception_handler;
self.state = Some((row_ref, cfa));
// return the stack frame that we just iterated to
Ok(Some(frame))
}
}
/// Dereferences a `Pointer` type found in unwinding information,
/// which is either direct (no dereference) or indirect.
/// Doing so is unsafe because the value of the `Pointer` is not checked.
unsafe fn deref_ptr(ptr: Pointer) -> u64 {
match ptr {
Pointer::Direct(x) => x,
Pointer::Indirect(x) => *(x as *const u64),
}
}
pub trait FuncWithRegisters = FnMut(Registers) -> Result<(), &'static str>;
type FuncWithRegistersRefMut<'a> = &'a mut dyn FuncWithRegisters;
/// 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.
///
/// In general, this is useful for jumpstarting the unwinding procedure,
/// since we have to start from the current call frame and work backwards up the call stack
/// while applying the rules for register value changes in each call frame
/// in order to arrive at the proper register values for a prior call frame.
pub fn invoke_with_current_registers<F>(f: &mut F) -> Result<(), &'static str>
where F: FuncWithRegisters
{
let mut f: FuncWithRegistersRefMut = f; // cast to a &mut trait object
let result = unsafe {
let res_ptr = unwind_trampoline(&mut f);
let res_boxed = Box::from_raw(res_ptr);
*res_boxed
};
return result;
// this is the end of the code in this function, the following is just inner functions.
/// This is an internal assembly function used by `invoke_with_current_registers()`
/// that saves the current register values by pushing them onto the stack
/// before invoking the function "unwind_recorder" with those register values as the only argument.
/// This is needed because the unwind info tables describe register values as operations (offsets/addends)
/// that are relative to the current register values, so we must have those current values as a starting point.
///
/// The argument is a pointer to a function reference, so effectively a pointer to a pointer.
#[naked]
unsafe extern "C" fn unwind_trampoline(_func: *mut FuncWithRegistersRefMut) -> *mut Result<(), &'static str> {
// This is a naked function, so you CANNOT place anything here before the asm block, not even log statements.
// This is because we rely on the value of registers to stay the same as whatever the caller set them to.
// DO NOT touch RDI register, which has the `_func` function; it needs to be passed into unwind_recorder.
asm!(
// copy the stack pointer to RSI
"
movq %rsp, %rsi
pushq %rbp
pushq %rbx
pushq %r12
pushq %r13
pushq %r14
pushq %r15
",
// To invoke `unwind_recorder`, we need to put:
// (1) the func in RDI (it's already there, just don't overwrite it),
// (2) the stack in RSI,
// (3) a pointer to the saved registers in RDX.
"
movq %rsp, %rdx # pointer to saved regs (on the stack)
call unwind_recorder
",
// Finally, restore saved registers
"
popq %r15
popq %r14
popq %r13
popq %r12
popq %rbx
popq %rbp
ret
",
options(att_syntax, noreturn)
);
}
/// The calling convention dictates the following order of arguments:
/// * first arg in `RDI` register, the function (or closure) to invoke with the saved registers arg,
/// * second arg in `RSI` register, the stack pointer,
/// * third arg in `RDX` register, the saved register values used to recover execution context
/// after we change the register values during unwinding,
#[no_mangle]
unsafe extern "C" fn unwind_recorder(
func: *mut FuncWithRegistersRefMut,
stack: u64,
saved_regs: *mut SavedRegs,
) -> *mut Result<(), &'static str> {
let func = &mut *func;
let saved_regs = &*saved_regs;
let mut registers = Registers::default();
registers[X86_64::RBX] = Some(saved_regs.rbx);
registers[X86_64::RBP] = Some(saved_regs.rbp);
registers[X86_64::RSP] = Some(stack + 8); // the stack value passed in is one pointer width before the real RSP
registers[X86_64::R12] = Some(saved_regs.r12);
registers[X86_64::R13] = Some(saved_regs.r13);
registers[X86_64::R14] = Some(saved_regs.r14);
registers[X86_64::R15] = Some(saved_regs.r15);
registers[X86_64::RA] = Some(*(stack as *const u64));
let res = func(registers);
Box::into_raw(Box::new(res))
}
}
/// **Landing** refers to the process of jumping to a handler for a stack frame,
/// e.g., an unwinding cleanup function, or an exception "catch" block.
///
/// This function basically fills the actual CPU registers with the values in the given `LandingRegisters`
/// and then jumps to the exception handler (landing pad) pointed to by the stack pointer (RSP) in those `LandingRegisters`.
///
/// This is similar in design to how the latter half of a context switch routine
/// must restore the previously-saved registers for the next task.
unsafe fn land(regs: &Registers, landing_pad_address: u64) -> Result<(), &'static str> {
let mut landing_regs = LandingRegisters {
rax: regs[X86_64::RAX].unwrap_or(0),
rbx: regs[X86_64::RBX].unwrap_or(0),
rcx: regs[X86_64::RCX].unwrap_or(0),
rdx: regs[X86_64::RDX].unwrap_or(0),
rdi: regs[X86_64::RDI].unwrap_or(0),
rsi: regs[X86_64::RSI].unwrap_or(0),
rbp: regs[X86_64::RBP].unwrap_or(0),
r8: regs[X86_64::R8 ].unwrap_or(0),
r9: regs[X86_64::R9 ].unwrap_or(0),
r10: regs[X86_64::R10].unwrap_or(0),
r11: regs[X86_64::R11].unwrap_or(0),
r12: regs[X86_64::R12].unwrap_or(0),
r13: regs[X86_64::R13].unwrap_or(0),
r14: regs[X86_64::R14].unwrap_or(0),
r15: regs[X86_64::R15].unwrap_or(0),
rsp: regs[X86_64::RSP].ok_or("unwind::land(): RSP was None, \
it must be set so that the landing pad function can execute properly."
)?,
};
// Now place the landing pad function's address at the "bottom" of the stack
// -- not really the bottom of the whole stack, just the last thing to be popped off after the landing_regs.
landing_regs.rsp -= core::mem::size_of::<u64>() as u64;
*(landing_regs.rsp as *mut u64) = landing_pad_address;
// trace!("unwind_lander regs: {:#X?}", landing_regs);
unwind_lander(&landing_regs);
// this is the end of the code in this function, the following is just inner functions.
/// This function places the values of the given landing registers
/// into the actual CPU registers, and then jumps to the landing pad address
/// specified by the stack pointer in those registers.
///
/// It is marked as divergent (returning `!`) because it doesn't return to the caller,
/// instead it returns (jumps to) that landing pad address.
#[naked]
unsafe extern "C" fn unwind_lander(_regs: *const LandingRegisters) -> ! {
asm!("
movq %rdi, %rsp
popq %rax
popq %rbx
popq %rcx
popq %rdx
popq %rdi
popq %rsi
popq %rbp
popq %r8
popq %r9
popq %r10
popq %r11
popq %r12
popq %r13
popq %r14
popq %r15
movq 0(%rsp), %rsp
ret # jump to the actual landing pad function
",
options(att_syntax, noreturn)
);
}
}
type NativeEndianSliceReader<'i> = EndianSlice<'i, NativeEndian>;
/// An abstraction of a reference to the contents of an `.eh_frame` section.
#[derive(Debug)]
enum EhFrameReference {
/// A reference to a "native" (Theseus-owned) `.eh_frame` [`mod_mgmt::LoadedSection`].
Section(StrongSectionRef),
/// A reference to an externally-registered `.eh_frame` section's bounds in memory.
External(ExternalUnwindInfo),
}
/// Due to lifetime and locking issues, we cannot store a direct reference to an unwind table row.
/// Instead, here we store references to the objects needed to calculate/obtain an unwind table row.
#[derive(Debug)]
struct UnwindRowReference {
caller: u64,
eh_frame_sec: EhFrameReference,
base_addrs: BaseAddresses,
}
impl UnwindRowReference {
/// Accepts a closure/function that will be invoked with following unwinding information:
/// a frame description entry and an unwinding table row.
fn with_unwind_info<O, F>(&self, mut f: F) -> Result<O, &'static str>
where F: FnMut(&FrameDescriptionEntry<NativeEndianSliceReader, usize>, &UnwindTableRow<NativeEndianSliceReader>) -> Result<O, &'static str>
{
// an inner closure that invokes the passed-in closure `f`.
let mut invoke_f_with_eh_frame_slice = |eh_frame_slice: &[u8]| {
let eh_frame = EhFrame::new(eh_frame_slice, NativeEndian);
let mut unwind_ctx = UninitializedUnwindContext::new();
let fde = eh_frame.fde_for_address(&self.base_addrs, self.caller, EhFrame::cie_from_offset).map_err(|_e| {
error!("gimli error: {:?}", _e);
"gimli error while finding FDE for address"
})?;
let unwind_table_row = fde.unwind_info_for_address(&eh_frame, &self.base_addrs, &mut unwind_ctx, self.caller).map_err(|_e| {
error!("gimli error: {:?}", _e);
"gimli error while finding unwind info for address"
})?;
f(&fde, unwind_table_row)
};
// The actual logic of this function that handles the `EhFrameReference` abstraction.
match &self.eh_frame_sec {
EhFrameReference::Section(sec) => {
let sec_pages = sec.mapped_pages.lock();
let eh_frame_slice: &[u8] = sec_pages.as_slice(sec.mapped_pages_offset, sec.size)?;
invoke_f_with_eh_frame_slice(eh_frame_slice)
}
EhFrameReference::External(uw_info) => {
let eh_frame_ptr = uw_info.unwind_info.start.value() as *const u8;
let eh_frame_size = uw_info.unwind_info.end.value() - uw_info.unwind_info.start.value();
let eh_frame_slice = unsafe {
core::slice::from_raw_parts(eh_frame_ptr, eh_frame_size)
};
invoke_f_with_eh_frame_slice(eh_frame_slice)
}
}
}
}
/// Returns a tuple of:
/// 1. The `.eh_frame` section for the given `crate_ref`
/// 2. The base addresses of that crate's main `.text` section and `.eh_frame` section.
///
/// # Locking / Deadlock
/// Obtains the lock on the given `crate_ref`.
fn get_eh_frame_info(crate_ref: &StrongCrateRef) -> Option<(StrongSectionRef, BaseAddresses)> {
let krate = crate_ref.lock_as_ref();
let eh_frame_sec = krate.sections.values()
.find(|s| s.typ == SectionType::EhFrame)?;
let eh_frame_vaddr = eh_frame_sec.virt_addr.value();
let text_pages_vaddr = krate.text_pages.as_ref()?.1.start.value();
let base_addrs = BaseAddresses::default()
.set_eh_frame(eh_frame_vaddr as u64)
.set_text(text_pages_vaddr as u64);
Some((eh_frame_sec.clone(), base_addrs))
}
/// Starts the unwinding procedure for the current task
/// by working backwards up the call stack starting from the current stack frame.
///
/// # Arguments
/// * `reason`: the reason why the current task is being killed, e.g., due to a panic, exception, etc.
/// * `stack_frames_to_skip`: the number of stack frames that can be skipped in order to avoid unwinding them.
/// Those frames should have nothing that needs to be unwound, e.g., no landing pads that invoke drop handlers.
/// For example, for a panic, the first `5` frames in the call stack can be ignored.
///
/// ## Note: Skipping frames
/// If you are unsure how many frames you could possibly skip, then it's always safe to pass `0`
/// such that all function frames on the stack are unwound.
///
#[doc(hidden)]
pub fn start_unwinding(reason: KillReason, stack_frames_to_skip: usize) -> Result<(), &'static str> {
// Here we have to be careful to have no resources waiting to be dropped/freed/released on the stack.
let unwinding_context_ptr = {
let current_task = task::get_my_current_task().ok_or("couldn't get current task")?;
let namespace = current_task.get_namespace();
Box::into_raw(Box::new(
UnwindingContext {
stack_frame_iter: StackFrameIter::new(
Arc::clone(namespace),
// we will set the real register values later, in the `invoke_with_current_registers()` closure.
Registers::default()
),
cause: reason,
current_task,
}
))
};
// IMPORTANT NOTE!!!!
// From this point on, if there is a failure, we need to free the unwinding context pointer to avoid leaking things.
// We pass a pointer to the unwinding context to this closure.
let res = invoke_with_current_registers(&mut |registers| {
// set the proper register values before start the actual unwinding procedure.
{
// SAFE: we just created this pointer above
let unwinding_context = unsafe { &mut *unwinding_context_ptr };
unwinding_context.stack_frame_iter.registers = registers;
// Skip the first several frames, e.g., to skip unwinding the panic entry point functions themselves.
for _i in 0..stack_frames_to_skip {
unwinding_context.stack_frame_iter.next()
.map_err(|_e| {
error!("error skipping call stack frame {} in unwinder", _i);
"error skipping call stack frame in unwinder"
})?
.ok_or("call stack frame did not exist (we were trying to skip it)")?;
}
}
continue_unwinding(unwinding_context_ptr)
});
match res {
Ok(()) => {
debug!("unwinding procedure has reached the end of the stack.");
}
Err(e) => {
error!("BUG: unwinding the first stack frame returned unexpectedly. Error: {}", e);
}
}
cleanup_unwinding_context(unwinding_context_ptr);
}
/// Continues the unwinding process from the point it left off at,
/// which is defined by the given unwinding context.
///
/// This returns an error upon failure,
/// and an `Ok(())` when it reaches the end of the stack and there are no more frames to unwind.
/// When either value is returned (upon a return of any kind),
/// **the caller is responsible for cleaning up** the given `UnwindingContext`.
///
/// Upon successfully continuing to iterate up the call stack, this function will actually not return at all.
fn continue_unwinding(unwinding_context_ptr: *mut UnwindingContext) -> Result<(), &'static str> {
let stack_frame_iter = unsafe { &mut (*unwinding_context_ptr).stack_frame_iter };
trace!("continue_unwinding(): stack_frame_iter: {:#X?}", stack_frame_iter);
let (mut regs, landing_pad_address) = if let Some(frame) = stack_frame_iter.next().map_err(|e| {
error!("continue_unwinding: error getting next stack frame in the call stack: {}", e);
"continue_unwinding: error getting next stack frame in the call stack"
})? {
if true {
info!("Unwinding StackFrame: {:#X?}", frame);
info!(" In func: {:?}", stack_frame_iter.namespace().get_section_containing_address(VirtualAddress::new_canonical(frame.initial_address() as usize), false));
info!(" Regs: {:?}", stack_frame_iter.registers());
}
if let Some(lsda) = frame.lsda() {
let lsda = VirtualAddress::new_canonical(lsda as usize);
if let Some((sec, _)) = stack_frame_iter.namespace().get_section_containing_address(lsda, true) {
info!(" parsing LSDA section: {:?}", sec);
let starting_offset = sec.mapped_pages_offset + (lsda.value() - sec.virt_addr.value());
let length_til_end_of_mp = sec.virt_addr.value() + sec.size - lsda.value();
let sec_mp = sec.mapped_pages.lock();
let lsda_slice = sec_mp.as_slice::<u8>(starting_offset, length_til_end_of_mp)
.map_err(|_e| "continue_unwinding(): couldn't get LSDA pointer as a slice")?;
let table = lsda::GccExceptTableArea::new(lsda_slice, NativeEndian, frame.initial_address());
// {
// let mut iter = table.call_site_table_entries().map_err(|_| "BAD TABLE")?;
// while let Some(entry) = iter.next().map_err(|_| "BAD ITER")? {
// debug!(" {:#X?}", entry);
// }
// }
let entry = match table.call_site_table_entry_for_address(frame.call_site_address()) {
Ok(x) => x,
Err(e) => {
error!("continue_unwinding(): couldn't find a call site table entry for this stack frame's call site address {:#X}. Error: {}", frame.call_site_address(), e);
// Now we don't have an exact match. We try to use the previous
let mut iter = table.call_site_table_entries().map_err(|_e| {"Couldn't find call_site_table_entries"})?;
let mut closest_entry = None;
while let Some(entry) = iter.next().map_err(|_e| {"Couldn't iterate through the entries"})? {
if entry.range_of_covered_addresses().start < frame.call_site_address() {
closest_entry = Some(entry);
}
}
if let Some (closest_entry) = closest_entry {
debug!("No unwind info for address. Using the closeset");
closest_entry
} else {
return Err("continue_unwinding(): couldn't find a call site table entry for this stack frame's call site address.");
}
}
};
debug!("Found call site entry for address {:#X}: {:#X?}", frame.call_site_address(), entry);
(stack_frame_iter.registers().clone(), entry.landing_pad_address())
} else {
error!(" BUG: couldn't find LSDA section (.gcc_except_table) for LSDA address: {:#X}", lsda);
return Err("BUG: couldn't find LSDA section (.gcc_except_table) for LSDA address specified in stack frame");
}
} else {
trace!("continue_unwinding(): stack frame has no LSDA");
return continue_unwinding(unwinding_context_ptr);
}
} else {
trace!("continue_unwinding(): NO REMAINING STACK FRAMES");
return Ok(());
};
// Even if this frame has LSDA, it may still not have a landing pad function.
let landing_pad_address = match landing_pad_address {
Some(lpa) => lpa,
_ => {
warn!("continue_unwinding(): stack frame has LSDA but no landing pad");
return continue_unwinding(unwinding_context_ptr);
}
};
// Exception/interrupt handlers appear to have no real cleanup routines, despite having an LSDA entry.
// Thus, we skip unwinding an exception handler frame because its landing pad will point to an invalid instruction (usually `ud2`).
if stack_frame_iter.last_frame_was_exception_handler {
let landing_pad_value: u16 = unsafe { *(landing_pad_address as *const u16) };
warn!("Skipping exception/interrupt handler's landing pad (cleanup function) at {:#X}, which points to {:#X} (UD2: {})",
landing_pad_address, landing_pad_value, landing_pad_value == 0x0B0F, // the `ud2` instruction
);
return continue_unwinding(unwinding_context_ptr);
}
// Jump to the actual landing pad function, or rather, a function that will jump there after setting up register values properly.
debug!("Jumping to landing pad (cleanup function) at {:#X}", landing_pad_address);
// Once the unwinding cleanup function is done, it will call _Unwind_Resume (technically, it jumps to it),
// and pass the value in the landing registers' RAX register as the argument to _Unwind_Resume.
// So, whatever we put into RAX in the landing regs will be placed into the first arg (RDI) in _Unwind_Resume.
// This is arch-specific; for x86_64 the transfer is from RAX -> RDI, for ARM/AARCH64, the transfer is from R0 -> R1 or X0 -> X1.
// See this for more mappings: <https://github.com/rust-lang/rust/blob/master/src/libpanic_unwind/gcc.rs#L102>
regs[gimli::X86_64::RAX] = Some(unwinding_context_ptr as u64);
unsafe {
land(®s, landing_pad_address)?;
}
error!("BUG: call to unwind::land() returned, which should never happen!");
Err("BUG: call to unwind::land() returned, which should never happen!")
}
/// This function is invoked after each unwinding cleanup routine has finished.
/// Thus, this is a middle point in the unwinding execution flow;
/// here we need to continue (*resume*) the unwinding procedure
/// by basically figuring out where we just came from and picking up where we left off.
#[doc(hidden)]
pub fn unwind_resume(unwinding_context_ptr: usize) -> ! {
// trace!("unwind_resume(): unwinding_context_ptr value: {:#X}", unwinding_context_ptr);
let unwinding_context_ptr = unwinding_context_ptr as *mut UnwindingContext;
match continue_unwinding(unwinding_context_ptr) {
Ok(()) => {
debug!("unwind_resume(): continue_unwinding() returned Ok(), meaning it's at the end of the call stack.");
}
Err(e) => {
error!("BUG: in unwind_resume(): continue_unwinding() returned an error: {}", e);
}
}
// here, cleanup the unwinding state and kill the task
cleanup_unwinding_context(unwinding_context_ptr);
}
/// This function should be invoked when the unwinding procedure is finished, or cannot be continued any further.
/// It cleans up the `UnwindingContext` object pointed to by the given pointer and marks the current task as killed.
fn cleanup_unwinding_context(unwinding_context_ptr: *mut UnwindingContext) -> ! {
// Recover ownership of the unwinding context from its pointer
let unwinding_context_boxed = unsafe { Box::from_raw(unwinding_context_ptr) };
let unwinding_context = *unwinding_context_boxed;
let (stack_frame_iter, cause, current_task) = unwinding_context.into();
drop(stack_frame_iter);
warn!("cleanup_unwinding_context(): invoking the task_cleanup_failure function for task {:?}", current_task);
let (exitable_taskref, failure_cleanup_function) =
task::ExitableTaskRef::obtain_for_unwinder(current_task);
failure_cleanup_function(exitable_taskref, cause)
}