I am experimenting with building a JIT that executes x86 instructions that the program produces. I think I have created a valid slice of x86 byte code that should print "Hello World", but I am not sure how to call it.
I am casting a pointer to the start of a vector to a void function and calling it:
fn main() {
    let msg: &[u8] = b"Hello World\0";
    let mut byte_codes: Vec<u8> = Vec::with_capacity(1000);
    // Move width into edx
    byte_codes.extend_from_slice(&[0xba, msg.len() as u8, 0, 0, 0]);
    
    // Msg to write
    byte_codes.push(0xb9);
    byte_codes.extend_from_slice(&(msg.as_ptr() as u64).to_be_bytes());
    
    // File descriptor and sys call
    byte_codes.extend_from_slice(&[0xbb, 0x01, 0, 0, 0]);
    byte_codes.extend_from_slice(&[0xb8, 0x04, 0, 0, 0]);
    
    // Sys call
    byte_codes.extend_from_slice(&[0xcd, 0x80]);
    // Return
    byte_codes.push(0xc3); 
    let func_ptr = byte_codes.as_ptr();
    unsafe {
        let func: fn() -> () = func_ptr.cast::<fn() -> ()>().read();
        func();
    }
}
Executing this returns:
error: process didn't exit successfully: `target\debug\run-bytecode.exe` (exit code: 0xc0000005, STATUS_ACCESS_VIOLATION)
Removing all bytecode except the return call also leads to the same error.
I'm not sure what that error means. Is there a issue with the bytecode or is my function casting incorrect? How can I get it printing "Hello World"?
Here's a version that works:
use memmap::MmapMut;
fn main() {
    let msg: &[u8] = b"Hello World\0";
    let mut byte_codes: Vec<u8> = Vec::with_capacity(1000);
    // Move width into edx
    byte_codes.extend_from_slice(&[0xba, msg.len() as u8, 0, 0, 0]);
    // Msg to write
    byte_codes.push(0xb9);
    byte_codes.extend_from_slice(&(msg.as_ptr() as u32).to_le_bytes());
    // File descriptor and sys call
    byte_codes.extend_from_slice(&[0xbb, 0x01, 0, 0, 0]);
    byte_codes.extend_from_slice(&[0xb8, 0x04, 0, 0, 0]);
    // Sys call
    byte_codes.extend_from_slice(&[0xcd, 0x80]);
    // Return
    byte_codes.push(0xc3);
    let mut m = MmapMut::map_anon(byte_codes.len()).unwrap();
    m.clone_from_slice(&byte_codes);
    let m = m.make_exec().unwrap();
    let func_ptr = m.as_ptr();
    unsafe {
        let func: extern "C" fn() = std::mem::transmute(func_ptr);
        func();
    }
}
A couple of things needed to be fixed:
byte_codes is 32-bit x86 Linux code, so it will need to be run with something like cargo run --target i686-unknown-linux-gnu
msg.as_ptr() to u32..to_le_bytes()
func_ptr.cast::<fn() -> ()>().read() doesn't cast to a function pointer, it casts the first 4/8 bytes of byte_codes to a function pointer.extern "C" fn() makes sure Rust knows about the proper ABImake_exec().If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With