mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-21 12:41:32 +00:00
Get function addresses from llvm-compiled code
This commit is contained in:
@ -1,19 +1,153 @@
|
||||
use crate::intrinsics::Intrinsics;
|
||||
use dlopen::symbor::Library;
|
||||
use inkwell::{
|
||||
module::Module,
|
||||
targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine},
|
||||
OptimizationLevel,
|
||||
};
|
||||
use std::{io::Write, ptr::NonNull};
|
||||
use tempfile::NamedTempFile;
|
||||
use libc::{
|
||||
c_char, mmap, mprotect, munmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_NONE, PROT_READ,
|
||||
PROT_WRITE,
|
||||
};
|
||||
use std::{
|
||||
ffi::CString,
|
||||
ptr::{self, NonNull},
|
||||
};
|
||||
use wasmer_runtime_core::{
|
||||
backend::FuncResolver, module::ModuleInner, structures::TypedIndex, types::LocalFuncIndex, vm,
|
||||
backend::FuncResolver,
|
||||
module::{ModuleInfo, ModuleInner},
|
||||
structures::TypedIndex,
|
||||
types::LocalFuncIndex,
|
||||
vm,
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
struct LLVMModule {
|
||||
_private: [u8; 0],
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[repr(C)]
|
||||
enum MemProtect {
|
||||
NONE,
|
||||
READ,
|
||||
READ_WRITE,
|
||||
READ_EXECUTE,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[repr(C)]
|
||||
enum LLVMResult {
|
||||
OK,
|
||||
ALLOCATE_FAILURE,
|
||||
PROTECT_FAILURE,
|
||||
DEALLOC_FAILURE,
|
||||
OBJECT_LOAD_FAILURE,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct Callbacks {
|
||||
alloc_memory: extern "C" fn(usize, MemProtect, &mut *mut u8, &mut usize) -> LLVMResult,
|
||||
protect_memory: extern "C" fn(*mut u8, usize, MemProtect) -> LLVMResult,
|
||||
dealloc_memory: extern "C" fn(*mut u8, usize) -> LLVMResult,
|
||||
|
||||
lookup_vm_symbol: extern "C" fn(*const c_char) -> *const vm::Func,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn object_load(
|
||||
mem_ptr: *const u8,
|
||||
mem_size: usize,
|
||||
callbacks: *const Callbacks,
|
||||
module_out: &mut *mut LLVMModule,
|
||||
) -> LLVMResult;
|
||||
fn get_func_symbol(module: *mut LLVMModule, name: *const c_char) -> *const vm::Func;
|
||||
}
|
||||
|
||||
fn get_callbacks() -> Callbacks {
|
||||
fn round_up_to_page_size(size: usize) -> usize {
|
||||
(size + (4096 - 1)) & !(4096 - 1)
|
||||
}
|
||||
|
||||
extern "C" fn alloc_memory(
|
||||
size: usize,
|
||||
protect: MemProtect,
|
||||
ptr_out: &mut *mut u8,
|
||||
size_out: &mut usize,
|
||||
) -> LLVMResult {
|
||||
println!("size: {}", size);
|
||||
let ptr = unsafe {
|
||||
mmap(
|
||||
ptr::null_mut(),
|
||||
round_up_to_page_size(size),
|
||||
match protect {
|
||||
MemProtect::NONE => PROT_NONE,
|
||||
MemProtect::READ => PROT_READ,
|
||||
MemProtect::READ_WRITE => PROT_READ | PROT_WRITE,
|
||||
MemProtect::READ_EXECUTE => PROT_READ | PROT_EXEC,
|
||||
},
|
||||
MAP_PRIVATE | MAP_ANON,
|
||||
-1,
|
||||
0,
|
||||
)
|
||||
};
|
||||
if ptr as isize == -1 {
|
||||
return LLVMResult::ALLOCATE_FAILURE;
|
||||
}
|
||||
*ptr_out = ptr as _;
|
||||
*size_out = size;
|
||||
LLVMResult::OK
|
||||
}
|
||||
|
||||
extern "C" fn protect_memory(ptr: *mut u8, size: usize, protect: MemProtect) -> LLVMResult {
|
||||
println!("protect memory: {:p}:{} -> {:?}", ptr, size, protect);
|
||||
let res = unsafe {
|
||||
mprotect(
|
||||
ptr as _,
|
||||
round_up_to_page_size(size),
|
||||
match protect {
|
||||
MemProtect::NONE => PROT_NONE,
|
||||
MemProtect::READ => PROT_READ,
|
||||
MemProtect::READ_WRITE => PROT_READ | PROT_WRITE,
|
||||
MemProtect::READ_EXECUTE => PROT_READ | PROT_EXEC,
|
||||
},
|
||||
)
|
||||
};
|
||||
|
||||
if res == 0 {
|
||||
LLVMResult::OK
|
||||
} else {
|
||||
LLVMResult::PROTECT_FAILURE
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn dealloc_memory(ptr: *mut u8, size: usize) -> LLVMResult {
|
||||
println!("dealloc_memory");
|
||||
let res = unsafe { munmap(ptr as _, round_up_to_page_size(size)) };
|
||||
|
||||
if res == 0 {
|
||||
LLVMResult::OK
|
||||
} else {
|
||||
LLVMResult::DEALLOC_FAILURE
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn lookup_vm_symbol(_name_ptr: *const c_char) -> *const vm::Func {
|
||||
ptr::null()
|
||||
}
|
||||
|
||||
Callbacks {
|
||||
alloc_memory,
|
||||
protect_memory,
|
||||
dealloc_memory,
|
||||
lookup_vm_symbol,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for LLVMBackend {}
|
||||
unsafe impl Sync for LLVMBackend {}
|
||||
|
||||
pub struct LLVMBackend {
|
||||
tempfile: NamedTempFile,
|
||||
library: Library,
|
||||
module: *mut LLVMModule,
|
||||
}
|
||||
|
||||
impl LLVMBackend {
|
||||
@ -42,14 +176,50 @@ impl LLVMBackend {
|
||||
let memory_buffer = target_machine
|
||||
.write_to_memory_buffer(&module, FileType::Object)
|
||||
.unwrap();
|
||||
let mem_buf_slice = memory_buffer.as_slice();
|
||||
|
||||
let mut tempfile = NamedTempFile::new().unwrap();
|
||||
tempfile.write_all(memory_buffer.as_slice()).unwrap();
|
||||
tempfile.flush().unwrap();
|
||||
let callbacks = get_callbacks();
|
||||
let mut module: *mut LLVMModule = ptr::null_mut();
|
||||
|
||||
let library = Library::open(tempfile.path()).unwrap();
|
||||
let res = unsafe {
|
||||
object_load(
|
||||
mem_buf_slice.as_ptr(),
|
||||
mem_buf_slice.len(),
|
||||
&callbacks,
|
||||
&mut module,
|
||||
)
|
||||
};
|
||||
|
||||
Self { tempfile, library }
|
||||
if res != LLVMResult::OK {
|
||||
panic!("failed to load object")
|
||||
}
|
||||
|
||||
Self { module }
|
||||
}
|
||||
|
||||
pub fn get_func(
|
||||
&self,
|
||||
info: &ModuleInfo,
|
||||
local_func_index: LocalFuncIndex,
|
||||
) -> Option<NonNull<vm::Func>> {
|
||||
let index = local_func_index.index();
|
||||
let name = if cfg!(target_os = "macos") {
|
||||
format!("_fn{}", index)
|
||||
} else {
|
||||
format!("fn{}", index)
|
||||
};
|
||||
|
||||
println!("name: {}", name);
|
||||
|
||||
let c_str = CString::new(name).ok()?;
|
||||
|
||||
let ptr = unsafe { get_func_symbol(self.module, c_str.as_ptr()) };
|
||||
|
||||
unsafe {
|
||||
disass_ptr(ptr as _, 0x20, 4);
|
||||
}
|
||||
|
||||
NonNull::new(ptr as _)
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,18 +229,35 @@ impl FuncResolver for LLVMBackend {
|
||||
module: &ModuleInner,
|
||||
local_func_index: LocalFuncIndex,
|
||||
) -> Option<NonNull<vm::Func>> {
|
||||
let index = module.info.imported_functions.len() + local_func_index.index();
|
||||
let name = if cfg!(macos) {
|
||||
format!("_fn{}", index)
|
||||
} else {
|
||||
format!("fn{}", index)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
self.library
|
||||
.symbol::<NonNull<vm::Func>>(&name)
|
||||
.ok()
|
||||
.map(|symbol| *symbol)
|
||||
}
|
||||
self.get_func(&module.info, local_func_index)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn disass_ptr(ptr: *const u8, size: usize, inst_count: usize) {
|
||||
use capstone::arch::BuildsCapstone;
|
||||
let mut cs = capstone::Capstone::new() // Call builder-pattern
|
||||
.x86() // X86 architecture
|
||||
.mode(capstone::arch::x86::ArchMode::Mode64) // 64-bit mode
|
||||
.detail(true) // Generate extra instruction details
|
||||
.build()
|
||||
.expect("Failed to create Capstone object");
|
||||
|
||||
// Get disassembled instructions
|
||||
let insns = cs
|
||||
.disasm_count(
|
||||
std::slice::from_raw_parts(ptr, size),
|
||||
ptr as u64,
|
||||
inst_count,
|
||||
)
|
||||
.expect("Failed to disassemble");
|
||||
|
||||
println!("count = {}", insns.len());
|
||||
for insn in insns.iter() {
|
||||
println!(
|
||||
"0x{:x}: {:6} {}",
|
||||
insn.address(),
|
||||
insn.mnemonic().unwrap_or(""),
|
||||
insn.op_str().unwrap_or("")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -615,8 +615,9 @@ fn parse_function(
|
||||
// This is a multi-value return.
|
||||
let struct_value = basic_value.into_struct_value();
|
||||
for i in 0..(count as u32) {
|
||||
let value =
|
||||
builder.build_extract_value(struct_value, i, &state.var_name()).unwrap();
|
||||
let value = builder
|
||||
.build_extract_value(struct_value, i, &state.var_name())
|
||||
.unwrap();
|
||||
state.push1(value);
|
||||
}
|
||||
}
|
||||
|
@ -88,24 +88,18 @@ impl Compiler for LLVMCompiler {
|
||||
|
||||
#[test]
|
||||
fn test_read_module() {
|
||||
use std::mem::transmute;
|
||||
use wabt::wat2wasm;
|
||||
use wasmer_runtime_core::vmcalls;
|
||||
use wasmer_runtime_core::{structures::TypedIndex, types::LocalFuncIndex, vm, vmcalls};
|
||||
// let wasm = include_bytes!("../../spectests/examples/simple/simple.wasm") as &[u8];
|
||||
let wat = r#"
|
||||
(module
|
||||
(type $t0 (func (param i32) (result i32)))
|
||||
(type $t1 (func (result i32)))
|
||||
(memory 1)
|
||||
(table 10 anyfunc)
|
||||
(elem (i32.const 0) $foobar)
|
||||
(global $g0 (mut i32) (i32.const 0))
|
||||
(func $foo (type $t0) (param i32) (result i32)
|
||||
get_local 0
|
||||
i32.const 0
|
||||
call_indirect (type $t0)
|
||||
)
|
||||
(func $foobar (type $t0)
|
||||
get_local 0
|
||||
))
|
||||
"#;
|
||||
let wasm = wat2wasm(wat).unwrap();
|
||||
@ -114,54 +108,15 @@ fn test_read_module() {
|
||||
|
||||
let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap();
|
||||
|
||||
// let backend = backend::LLVMBackend::new(module, intrinsics);
|
||||
let backend = backend::LLVMBackend::new(module, intrinsics);
|
||||
|
||||
extern "C" {
|
||||
fn test_cpp();
|
||||
let func_ptr = backend.get_func(&info, LocalFuncIndex::new(0)).unwrap();
|
||||
|
||||
println!("func_ptr: {:p}", func_ptr.as_ptr());
|
||||
|
||||
unsafe {
|
||||
let func: unsafe extern "C" fn(*mut vm::Ctx, i32) -> i32 = transmute(func_ptr);
|
||||
let result = func(0 as _, 42);
|
||||
println!("result: {}", result);
|
||||
}
|
||||
|
||||
unsafe { test_cpp() };
|
||||
|
||||
// let exec_engine = module
|
||||
// .create_jit_execution_engine(OptimizationLevel::Default)
|
||||
// .unwrap();
|
||||
|
||||
// exec_engine.add_global_mapping(
|
||||
// &intrinsics.memory_grow_dynamic_local,
|
||||
// vmcalls::local_dynamic_memory_grow as usize,
|
||||
// );
|
||||
// exec_engine.add_global_mapping(
|
||||
// &intrinsics.memory_grow_static_local,
|
||||
// vmcalls::local_static_memory_grow as usize,
|
||||
// );
|
||||
// exec_engine.add_global_mapping(
|
||||
// &intrinsics.memory_grow_dynamic_import,
|
||||
// vmcalls::imported_dynamic_memory_grow as usize,
|
||||
// );
|
||||
// exec_engine.add_global_mapping(
|
||||
// &intrinsics.memory_grow_static_import,
|
||||
// vmcalls::imported_static_memory_grow as usize,
|
||||
// );
|
||||
// exec_engine.add_global_mapping(
|
||||
// &intrinsics.memory_size_dynamic_local,
|
||||
// vmcalls::local_dynamic_memory_size as usize,
|
||||
// );
|
||||
// exec_engine.add_global_mapping(
|
||||
// &intrinsics.memory_size_static_local,
|
||||
// vmcalls::local_static_memory_size as usize,
|
||||
// );
|
||||
// exec_engine.add_global_mapping(
|
||||
// &intrinsics.memory_size_dynamic_import,
|
||||
// vmcalls::imported_dynamic_memory_size as usize,
|
||||
// );
|
||||
// exec_engine.add_global_mapping(
|
||||
// &intrinsics.memory_size_static_import,
|
||||
// vmcalls::imported_static_memory_size as usize,
|
||||
// );
|
||||
|
||||
// unsafe {
|
||||
// let func: JitFunction<unsafe extern fn(*mut u8, i32) -> i32> = exec_engine.get_function("fn0").unwrap();
|
||||
// let result = func.call(0 as _, 0);
|
||||
// println!("result: {}", result);
|
||||
// }
|
||||
}
|
||||
|
Reference in New Issue
Block a user