【发布时间】:2021-05-20 09:05:00
【问题描述】:
我正在尝试构建一个执行程序生成的 x86 指令的 JIT。我想我已经创建了一个有效的 x86 字节代码片段,应该打印“Hello World”,但我不知道如何调用它。
我将指向向量开头的指针转换为 void 函数并调用它:
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();
}
}
执行返回:
error: process didn't exit successfully: `target\debug\run-bytecode.exe` (exit code: 0xc0000005, STATUS_ACCESS_VIOLATION)
除去返回调用以外的所有字节码也会导致同样的错误。
我不确定这个错误是什么意思。字节码有问题还是我的函数转换不正确?如何让它打印“Hello World”?
【问题讨论】:
-
您可能需要在调用它之前将内存设置为可执行。请参阅此处针对 Linux 平台的 mprotect 调用:github.com/jonathandturner/rustyjit/blob/master/src/main.rs
-
我预计主要问题是W^X / DEP:在大多数现代系统中,页面可以可写或可执行。因此,当您生成要执行的代码时,您需要更改页面的保护模式。这样做是特定于平台的。
-
您可能遇到的另一个问题是 ABI:您已经断言您正在调用的代码是 Rust 函数。这意味着 Rust 编译器会期望在另一端有一个 Rust 可调用的,而你不知道这意味着什么,因为 Rust 没有稳定的 ABI。因此,您需要制作您的“函数”,使其尊重“C”ABI,并且您需要通过转换为
extern fn来告诉 rustc,并且您可能需要指定ABI 明确地例如默认的"C"abi 在 Windows 和 Unices 以及 x86、x64 和 ARM 上有所不同。 -
如果你在夜间使用,内联和全局汇编都有宏。此外,在 ABI 说明中,
extern "System"可能值得一看。