From 2294180c3778d0fcfa877818e98c420fcd54bb8a Mon Sep 17 00:00:00 2001 From: johannst Date: Tue, 5 Dec 2023 22:08:06 +0000 Subject: deploy: 7e98f5def5942969f97f5f015e7fb8417793d132 --- src/juicebox_asm/imm.rs.html | 2 +- src/juicebox_asm/insn.rs.html | 14 ++++- src/juicebox_asm/insn/add.rs.html | 26 +++++++- src/juicebox_asm/insn/call.rs.html | 2 +- src/juicebox_asm/insn/cmp.rs.html | 15 +++++ src/juicebox_asm/insn/dec.rs.html | 2 +- src/juicebox_asm/insn/jmp.rs.html | 2 +- src/juicebox_asm/insn/jnz.rs.html | 2 +- src/juicebox_asm/insn/jz.rs.html | 2 +- src/juicebox_asm/insn/mov.rs.html | 18 +++++- src/juicebox_asm/insn/nop.rs.html | 2 +- src/juicebox_asm/insn/ret.rs.html | 2 +- src/juicebox_asm/insn/test.rs.html | 14 ++++- src/juicebox_asm/label.rs.html | 2 +- src/juicebox_asm/lib.rs.html | 124 ++++++++++++++++++++++++++++++++++++- src/juicebox_asm/prelude.rs.html | 4 +- src/juicebox_asm/reg.rs.html | 2 +- src/juicebox_asm/rt.rs.html | 98 +++++++++++++++++++++++------ 18 files changed, 294 insertions(+), 39 deletions(-) create mode 100644 src/juicebox_asm/insn/cmp.rs.html (limited to 'src/juicebox_asm') diff --git a/src/juicebox_asm/imm.rs.html b/src/juicebox_asm/imm.rs.html index acb8add..78f4a8b 100644 --- a/src/juicebox_asm/imm.rs.html +++ b/src/juicebox_asm/imm.rs.html @@ -1,4 +1,4 @@ -imm.rs - source
1
+imm.rs - source
1
 2
 3
 4
diff --git a/src/juicebox_asm/insn.rs.html b/src/juicebox_asm/insn.rs.html
index 24e26cb..550a4ab 100644
--- a/src/juicebox_asm/insn.rs.html
+++ b/src/juicebox_asm/insn.rs.html
@@ -1,4 +1,4 @@
-insn.rs - source
1
+insn.rs - source
1
 2
 3
 4
@@ -53,10 +53,17 @@
 53
 54
 55
+56
+57
+58
+59
+60
+61
 
//! Trait definitions of various instructions.
 
 mod add;
 mod call;
+mod cmp;
 mod dec;
 mod jmp;
 mod jnz;
@@ -76,6 +83,11 @@
     fn call(&mut self, op1: T);
 }
 
+pub trait Cmp<T, U> {
+    /// Emit a compare call instruction.
+    fn cmp(&mut self, op1: T, op2: U);
+}
+
 pub trait Dec<T> {
     /// Emit a decrement instruction.
     fn dec(&mut self, op1: T);
diff --git a/src/juicebox_asm/insn/add.rs.html b/src/juicebox_asm/insn/add.rs.html
index 0a24951..d7f968a 100644
--- a/src/juicebox_asm/insn/add.rs.html
+++ b/src/juicebox_asm/insn/add.rs.html
@@ -1,4 +1,4 @@
-add.rs - source
1
+add.rs - source
1
 2
 3
 4
@@ -11,6 +11,18 @@
 11
 12
 13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
 
use crate::prelude::*;
 
 impl Add<Reg64, Reg64> for Asm {
@@ -24,4 +36,16 @@
         self.encode_rr(0x01, op1, op2);
     }
 }
+
+impl Add<MemOp, Reg16> for Asm {
+    fn add(&mut self, op1: MemOp, op2: Reg16) {
+        self.encode_mr(0x01, op1, op2);
+    }
+}
+
+impl Add<MemOp, Imm16> for Asm {
+    fn add(&mut self, op1: MemOp, op2: Imm16) {
+        self.encode_mi(0x81, 0, op1, op2);
+    }
+}
 
\ No newline at end of file diff --git a/src/juicebox_asm/insn/call.rs.html b/src/juicebox_asm/insn/call.rs.html index 38cb339..f9649ed 100644 --- a/src/juicebox_asm/insn/call.rs.html +++ b/src/juicebox_asm/insn/call.rs.html @@ -1,4 +1,4 @@ -call.rs - source
1
+call.rs - source
1
 2
 3
 4
diff --git a/src/juicebox_asm/insn/cmp.rs.html b/src/juicebox_asm/insn/cmp.rs.html
new file mode 100644
index 0000000..b50d90d
--- /dev/null
+++ b/src/juicebox_asm/insn/cmp.rs.html
@@ -0,0 +1,15 @@
+cmp.rs - source
1
+2
+3
+4
+5
+6
+7
+
use crate::prelude::*;
+
+impl Cmp<MemOp, Imm16> for Asm {
+    fn cmp(&mut self, op1: MemOp, op2: Imm16) {
+        self.encode_mi(0x81, 0x7, op1, op2);
+    }
+}
+
\ No newline at end of file diff --git a/src/juicebox_asm/insn/dec.rs.html b/src/juicebox_asm/insn/dec.rs.html index 2d46c4c..2afecd2 100644 --- a/src/juicebox_asm/insn/dec.rs.html +++ b/src/juicebox_asm/insn/dec.rs.html @@ -1,4 +1,4 @@ -dec.rs - source
1
+dec.rs - source
1
 2
 3
 4
diff --git a/src/juicebox_asm/insn/jmp.rs.html b/src/juicebox_asm/insn/jmp.rs.html
index 77b34c5..55da0e3 100644
--- a/src/juicebox_asm/insn/jmp.rs.html
+++ b/src/juicebox_asm/insn/jmp.rs.html
@@ -1,4 +1,4 @@
-jmp.rs - source
1
+jmp.rs - source
1
 2
 3
 4
diff --git a/src/juicebox_asm/insn/jnz.rs.html b/src/juicebox_asm/insn/jnz.rs.html
index 24a1e4a..45fb92d 100644
--- a/src/juicebox_asm/insn/jnz.rs.html
+++ b/src/juicebox_asm/insn/jnz.rs.html
@@ -1,4 +1,4 @@
-jnz.rs - source
1
+jnz.rs - source
1
 2
 3
 4
diff --git a/src/juicebox_asm/insn/jz.rs.html b/src/juicebox_asm/insn/jz.rs.html
index bbc67d8..4b17a71 100644
--- a/src/juicebox_asm/insn/jz.rs.html
+++ b/src/juicebox_asm/insn/jz.rs.html
@@ -1,4 +1,4 @@
-jz.rs - source
1
+jz.rs - source
1
 2
 3
 4
diff --git a/src/juicebox_asm/insn/mov.rs.html b/src/juicebox_asm/insn/mov.rs.html
index 0508d37..7be5d09 100644
--- a/src/juicebox_asm/insn/mov.rs.html
+++ b/src/juicebox_asm/insn/mov.rs.html
@@ -1,4 +1,4 @@
-mov.rs - source
1
+mov.rs - source
1
 2
 3
 4
@@ -103,6 +103,14 @@
 103
 104
 105
+106
+107
+108
+109
+110
+111
+112
+113
 
use crate::prelude::*;
 
 // -- MOV : reg reg
@@ -208,4 +216,12 @@
         self.encode_oi(0xb0, op1, op2);
     }
 }
+
+// -- MOV : mem imm
+
+impl Mov<MemOp, Imm16> for Asm {
+    fn mov(&mut self, op1: MemOp, op2: Imm16) {
+        self.encode_mi(0xc7, 0, op1, op2);
+    }
+}
 
\ No newline at end of file diff --git a/src/juicebox_asm/insn/nop.rs.html b/src/juicebox_asm/insn/nop.rs.html index 0469ab0..accc13a 100644 --- a/src/juicebox_asm/insn/nop.rs.html +++ b/src/juicebox_asm/insn/nop.rs.html @@ -1,4 +1,4 @@ -nop.rs - source
1
+nop.rs - source
1
 2
 3
 4
diff --git a/src/juicebox_asm/insn/ret.rs.html b/src/juicebox_asm/insn/ret.rs.html
index bb50d9f..97070e8 100644
--- a/src/juicebox_asm/insn/ret.rs.html
+++ b/src/juicebox_asm/insn/ret.rs.html
@@ -1,4 +1,4 @@
-ret.rs - source
1
+ret.rs - source
1
 2
 3
 4
diff --git a/src/juicebox_asm/insn/test.rs.html b/src/juicebox_asm/insn/test.rs.html
index 5245439..d636920 100644
--- a/src/juicebox_asm/insn/test.rs.html
+++ b/src/juicebox_asm/insn/test.rs.html
@@ -1,4 +1,4 @@
-test.rs - source
1
+test.rs - source
1
 2
 3
 4
@@ -11,6 +11,12 @@
 11
 12
 13
+14
+15
+16
+17
+18
+19
 
use crate::prelude::*;
 
 impl Test<Reg64, Reg64> for Asm {
@@ -24,4 +30,10 @@
         self.encode_rr(0x85, op1, op2);
     }
 }
+
+impl Test<MemOp, Imm16> for Asm {
+    fn test(&mut self, op1: MemOp, op2: Imm16) {
+        self.encode_mi(0xf7, 0, op1, op2);
+    }
+}
 
\ No newline at end of file diff --git a/src/juicebox_asm/label.rs.html b/src/juicebox_asm/label.rs.html index e1fb18a..e209289 100644 --- a/src/juicebox_asm/label.rs.html +++ b/src/juicebox_asm/label.rs.html @@ -1,4 +1,4 @@ -label.rs - source
1
+label.rs - source
1
 2
 3
 4
diff --git a/src/juicebox_asm/lib.rs.html b/src/juicebox_asm/lib.rs.html
index 4d6d4fe..83c8f81 100644
--- a/src/juicebox_asm/lib.rs.html
+++ b/src/juicebox_asm/lib.rs.html
@@ -1,4 +1,4 @@
-lib.rs - source
1
+lib.rs - source
1
 2
 3
 4
@@ -408,6 +408,65 @@
 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
 
//! A simple `x64` jit assembler with a minimal runtime to execute emitted code for fun.
 //!
 //! The following is an fibonacci example implementation.
@@ -471,8 +530,8 @@
 //!     asm.ret();
 //!
 //!     // Move code into executable page and get function pointer to it.
-//!     let rt = Runtime::new(&asm.into_code());
-//!     let fib = unsafe { rt.as_fn::<extern "C" fn(u64) -> u64>() };
+//!     let mut rt = Runtime::new();
+//!     let fib = unsafe { rt.add_code::<extern "C" fn(u64) -> u64>(&asm.into_code()) };
 //!
 //!     for n in 0..15 {
 //!         let fib_jit = fib(n);
@@ -683,6 +742,42 @@
         self.emit(&[opc, modrm]);
     }
 
+    /// Encode a memory-immediate instruction.
+    fn encode_mi<T: Imm>(&mut self, opc: u8, opc_ext: u8, op1: MemOp, op2: T)
+    where
+        Self: EncodeMI<T>,
+    {
+        // MI operand encoding.
+        //   op1 -> modrm.rm
+        //   op2 -> imm
+        let mode = match op1 {
+            MemOp::Indirect(..) => {
+                assert!(!op1.base().need_sib() && !op1.base().is_pc_rel());
+                0b00
+            }
+            MemOp::IndirectDisp(..) => {
+                assert!(!op1.base().need_sib());
+                0b10
+            }
+        };
+
+        let modrm = modrm(
+            mode,             /* mode */
+            opc_ext,          /* reg */
+            op1.base().idx(), /* rm */
+        );
+
+        let prefix = <Self as EncodeMI<T>>::legacy_prefix();
+        let rex = <Self as EncodeMI<T>>::rex(&op1);
+
+        self.emit_optional(&[prefix, rex]);
+        self.emit(&[opc, modrm]);
+        if let MemOp::IndirectDisp(_, disp) = op1 {
+            self.emit(&disp.to_ne_bytes());
+        }
+        self.emit(op2.bytes());
+    }
+
     /// Encode a memory-register instruction.
     fn encode_mr<T: Reg>(&mut self, opc: u8, op1: MemOp, op2: T)
     where
@@ -818,4 +913,27 @@
 }
 impl EncodeMR<Reg32> for Asm {}
 impl EncodeMR<Reg64> for Asm {}
+
+/// Encode helper for memory-immediate instructions.
+trait EncodeMI<T: Imm> {
+    fn legacy_prefix() -> Option<u8> {
+        None
+    }
+
+    fn rex(op1: &MemOp) -> Option<u8> {
+        if op1.base().is_ext() {
+            Some(rex(false, 0, 0, op1.base().idx()))
+        } else {
+            None
+        }
+    }
+}
+
+impl EncodeMI<Imm8> for Asm {}
+impl EncodeMI<Imm16> for Asm {
+    fn legacy_prefix() -> Option<u8> {
+        Some(0x66)
+    }
+}
+impl EncodeMI<Imm32> for Asm {}
 
\ No newline at end of file diff --git a/src/juicebox_asm/prelude.rs.html b/src/juicebox_asm/prelude.rs.html index ac02bf8..e877286 100644 --- a/src/juicebox_asm/prelude.rs.html +++ b/src/juicebox_asm/prelude.rs.html @@ -1,4 +1,4 @@ -prelude.rs - source
1
+prelude.rs - source
1
 2
 3
 4
@@ -17,5 +17,5 @@
 pub use crate::label::Label;
 pub use crate::reg::{Reg16, Reg32, Reg64, Reg8};
 
-pub use crate::insn::{Add, Call, Dec, Jmp, Jnz, Jz, Mov, Test};
+pub use crate::insn::{Add, Call, Cmp, Dec, Jmp, Jnz, Jz, Mov, Test};
 
\ No newline at end of file diff --git a/src/juicebox_asm/reg.rs.html b/src/juicebox_asm/reg.rs.html index fec18cf..a511005 100644 --- a/src/juicebox_asm/reg.rs.html +++ b/src/juicebox_asm/reg.rs.html @@ -1,4 +1,4 @@ -reg.rs - source
1
+reg.rs - source
1
 2
 3
 4
diff --git a/src/juicebox_asm/rt.rs.html b/src/juicebox_asm/rt.rs.html
index 46597b0..244be8b 100644
--- a/src/juicebox_asm/rt.rs.html
+++ b/src/juicebox_asm/rt.rs.html
@@ -1,4 +1,4 @@
-rt.rs - source
1
+rt.rs - source
1
 2
 3
 4
@@ -57,62 +57,120 @@
 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
 
//! A simple runtime which can be used to execute emitted instructions.
 
-use core::ffi::c_void;
+use core::slice;
 use nix::sys::mman::{mmap, mprotect, munmap, MapFlags, ProtFlags};
 
 /// A simple `mmap`ed runtime with executable pages.
 pub struct Runtime {
-    buf: *mut c_void,
+    buf: *mut u8,
     len: usize,
+    idx: usize,
 }
 
 impl Runtime {
     /// Create a new [Runtime].
-    pub fn new(code: impl AsRef<[u8]>) -> Runtime {
+    pub fn new() -> Runtime {
         // Allocate a single page.
         let len = core::num::NonZeroUsize::new(4096).unwrap();
         let buf = unsafe {
             mmap(
                 None,
                 len,
-                ProtFlags::PROT_WRITE,
+                ProtFlags::PROT_WRITE | ProtFlags::PROT_READ | ProtFlags::PROT_EXEC,
                 MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS,
                 0, /* fd */
                 0, /* off */
             )
-            .unwrap()
+            .unwrap() as *mut u8
         };
-        {
-            // Copy over code.
-            let code = code.as_ref();
-            assert!(code.len() < len.get());
-            unsafe { std::ptr::copy_nonoverlapping(code.as_ptr(), buf.cast(), len.get()) };
-        }
-        unsafe {
-            // Remove write permissions from code buffer and allow to read-execute from it.
-            mprotect(buf, len.get(), ProtFlags::PROT_READ | ProtFlags::PROT_EXEC)
-                .expect("Failed to RX mprotect Runtime code buffer");
-        }
 
         Runtime {
             buf,
             len: len.get(),
+            idx: 0,
+        }
+    }
+
+    /// Write protect the underlying code page(s).
+    pub fn protect(&mut self) {
+        unsafe {
+            // Remove write permissions from code buffer and allow to read-execute from it.
+            mprotect(
+                self.buf.cast(),
+                self.len,
+                ProtFlags::PROT_READ | ProtFlags::PROT_EXEC,
+            )
+            .expect("Failed to RX mprotect Runtime code buffer");
         }
     }
 
+    /// Add block of code to the runtime and get function pointer back.
+    pub unsafe fn add_code<F>(&mut self, code: impl AsRef<[u8]>) -> F {
+        // Get pointer to start of next free byte.
+        assert!(self.idx < self.len);
+        let fn_start = self.buf.add(self.idx);
+
+        // Copy over code.
+        let code = code.as_ref();
+        assert!(code.len() < (self.len - self.idx));
+        unsafe { std::ptr::copy_nonoverlapping(code.as_ptr(), fn_start, code.len()) };
+
+        // Increment index to next free byte.
+        self.idx += code.len();
+
+        // Return function to newly added code.
+        Self::as_fn::<F>(fn_start)
+    }
+
     /// Reinterpret the block of code as `F`.
     #[inline]
-    pub unsafe fn as_fn<F>(&self) -> F {
-        unsafe { std::mem::transmute_copy(&self.buf) }
+    unsafe fn as_fn<F>(fn_start: *mut u8) -> F {
+        unsafe { std::mem::transmute_copy(&fn_start) }
+    }
+
+    /// Dump the currently added code to a file called `jit.asm`. The disassembly can be inspected
+    /// as `ndisasm -b 64 jit.asm`.
+    pub fn dump(&self) {
+        let code = unsafe { slice::from_raw_parts(self.buf, self.idx) };
+        std::fs::write("jit.asm", code).unwrap();
     }
 }
 
 impl Drop for Runtime {
     fn drop(&mut self) {
         unsafe {
-            munmap(self.buf, self.len).expect("Failed to munmap Runtime");
+            munmap(self.buf.cast(), self.len).expect("Failed to munmap Runtime");
         }
     }
 }
-- 
cgit v1.2.3