summaryrefslogtreecommitdiffstats
path: root/vendor/unwinding/src/unwinder/arch/x86.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/unwinding/src/unwinder/arch/x86.rs')
-rw-r--r--vendor/unwinding/src/unwinder/arch/x86.rs136
1 files changed, 136 insertions, 0 deletions
diff --git a/vendor/unwinding/src/unwinder/arch/x86.rs b/vendor/unwinding/src/unwinder/arch/x86.rs
new file mode 100644
index 000000000..992afb66a
--- /dev/null
+++ b/vendor/unwinding/src/unwinder/arch/x86.rs
@@ -0,0 +1,136 @@
+use core::arch::asm;
+use core::fmt;
+use core::ops;
+use gimli::{Register, X86};
+
+// Match DWARF_FRAME_REGISTERS in libgcc
+pub const MAX_REG_RULES: usize = 17;
+
+#[repr(C)]
+#[derive(Clone, Default)]
+pub struct Context {
+ pub registers: [usize; 8],
+ pub ra: usize,
+ pub mcxsr: usize,
+ pub fcw: usize,
+}
+
+impl fmt::Debug for Context {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut fmt = fmt.debug_struct("Context");
+ for i in 0..=7 {
+ fmt.field(
+ X86::register_name(Register(i as _)).unwrap(),
+ &self.registers[i],
+ );
+ }
+ fmt.field("ra", &self.ra)
+ .field("mcxsr", &self.mcxsr)
+ .field("fcw", &self.fcw)
+ .finish()
+ }
+}
+
+impl ops::Index<Register> for Context {
+ type Output = usize;
+
+ fn index(&self, reg: Register) -> &usize {
+ match reg {
+ Register(0..=7) => &self.registers[reg.0 as usize],
+ X86::RA => &self.ra,
+ X86::MXCSR => &self.mcxsr,
+ _ => unimplemented!(),
+ }
+ }
+}
+
+impl ops::IndexMut<gimli::Register> for Context {
+ fn index_mut(&mut self, reg: Register) -> &mut usize {
+ match reg {
+ Register(0..=7) => &mut self.registers[reg.0 as usize],
+ X86::RA => &mut self.ra,
+ X86::MXCSR => &mut self.mcxsr,
+ _ => unimplemented!(),
+ }
+ }
+}
+
+#[naked]
+pub extern "C-unwind" fn save_context(f: extern "C" fn(&mut Context, *mut ()), ptr: *mut ()) {
+ // No need to save caller-saved registers here.
+ unsafe {
+ asm!(
+ "
+ sub esp, 52
+
+ mov [esp + 4], ecx
+ mov [esp + 8], edx
+ mov [esp + 12], ebx
+
+ /* Adjust the stack to account for the return address */
+ lea eax, [esp + 56]
+ mov [esp + 16], eax
+
+ mov [esp + 20], ebp
+ mov [esp + 24], esi
+ mov [esp + 28], edi
+
+ /* Return address */
+ mov eax, [esp + 52]
+ mov [esp + 32], eax
+
+ stmxcsr [esp + 36]
+ fnstcw [esp + 40]
+
+ mov eax, [esp + 60]
+ mov ecx, esp
+ push eax
+ push ecx
+ call [esp + 64]
+
+ add esp, 60
+ ret
+ ",
+ options(noreturn)
+ );
+ }
+}
+
+#[naked]
+pub unsafe extern "C" fn restore_context(ctx: &Context) -> ! {
+ unsafe {
+ asm!(
+ "
+ mov edx, [esp + 4]
+
+ /* Restore stack */
+ mov esp, [edx + 16]
+
+ /* Restore callee-saved control registers */
+ ldmxcsr [edx + 36]
+ fldcw [edx + 40]
+
+ /* Restore return address */
+ mov eax, [edx + 32]
+ push eax
+
+ /*
+ * Restore general-purpose registers. Non-callee-saved registers are
+ * also restored because sometimes it's used to pass unwind arguments.
+ */
+ mov eax, [edx + 0]
+ mov ecx, [edx + 4]
+ mov ebx, [edx + 12]
+ mov ebp, [edx + 20]
+ mov esi, [edx + 24]
+ mov edi, [edx + 28]
+
+ /* EDX restored last */
+ mov edx, [edx + 8]
+
+ ret
+ ",
+ options(noreturn)
+ );
+ }
+}