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(&regs, 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)
}