Skip to content

Commit

Permalink
Compiling version running on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
syrusakbary committed Jun 11, 2020
1 parent 803c896 commit f9aabee
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 27 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ api-docs-repo/
/package/
/dist/
/wapm-cli/
/src/windows-installer/WasmerInstaller.exe

# Generated by tests on Android
/avd
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions lib/compiler-cranelift/src/translator/unwind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ pub fn compiled_function_unwind_info(
match unwind_info {
Some(UnwindInfo::WindowsX64(unwind)) => {
let size = unwind.emit_size();
let mut data = Vec::with_capacity(size);
unwind.emit(&mut data);
let mut data: Vec<u8> = vec![0; size];
unwind.emit(&mut data[..]);
Ok(Some(CompiledFunctionUnwindInfo::WindowsX64(data)))
}
Some(UnwindInfo::SystemV(unwind)) => {
Expand Down
1 change: 1 addition & 0 deletions lib/engine-jit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ region = "2.1"
serde = { version = "1.0", features = ["derive", "rc"] }
serde_bytes = { version = "0.11" }
bincode = "1.2"
cfg-if = "0.1"

[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3", features = ["winnt", "impl-default"] }
Expand Down
74 changes: 49 additions & 25 deletions lib/engine-jit/src/code_memory.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
//! Memory management for executable code.
use crate::unwind::UnwindRegistry;
use region;
use std::mem::ManuallyDrop;
use std::ptr;
use std::{cmp, mem};
use wasm_common::entity::PrimaryMap;
use wasm_common::LocalFunctionIndex;
use wasmer_compiler::{FunctionBody, SectionBody};
use wasmer_compiler::{CompiledFunctionUnwindInfo, FunctionBody, SectionBody};
use wasmer_runtime::{Mmap, VMFunctionBody};

/// The optimal alignment for functions.
Expand All @@ -16,18 +18,19 @@ const ARCH_FUNCTION_ALIGNMENT: usize = 16;

struct CodeMemoryEntry {
mmap: ManuallyDrop<Mmap>,
registry: ManuallyDrop<UnwindRegistry>,
}

impl CodeMemoryEntry {
fn new() -> Self {
Self {
mmap: ManuallyDrop::new(Mmap::new()),
}
let mmap = ManuallyDrop::new(Mmap::new());
let registry = ManuallyDrop::new(UnwindRegistry::new(mmap.as_ptr() as usize));
Self { mmap, registry }
}
fn with_capacity(cap: usize) -> Result<Self, String> {
Ok(Self {
mmap: ManuallyDrop::new(Mmap::with_at_least(cap)?),
})
let mmap = ManuallyDrop::new(Mmap::with_at_least(cap)?);
let registry = ManuallyDrop::new(UnwindRegistry::new(mmap.as_ptr() as usize));
Ok(Self { mmap, registry })
}
}

Expand Down Expand Up @@ -84,9 +87,9 @@ impl CodeMemory {
) -> Result<&mut [VMFunctionBody], String> {
let size = Self::function_allocation_size(func);

let (buf, start) = self.allocate(size, ARCH_FUNCTION_ALIGNMENT)?;
let (buf, registry, start) = self.allocate(size, ARCH_FUNCTION_ALIGNMENT)?;

let (_, _, vmfunc) = Self::copy_function(func, start as u32, buf);
let (_, _, vmfunc) = Self::copy_function(func, start as u32, buf, registry);
assert!(vmfunc as *mut _ as *mut u8 as usize % ARCH_FUNCTION_ALIGNMENT == 0);

Ok(vmfunc)
Expand All @@ -98,7 +101,7 @@ impl CodeMemory {
section: &SectionBody,
) -> Result<&mut [u8], String> {
let section = section.as_slice();
let (buf, _) = self.allocate(section.len(), ARCH_FUNCTION_ALIGNMENT)?;
let (buf, _, _) = self.allocate(section.len(), ARCH_FUNCTION_ALIGNMENT)?;
buf.copy_from_slice(section);
Ok(buf)
}
Expand All @@ -115,14 +118,14 @@ impl CodeMemory {
+ Self::function_allocation_size(func)
});

let (mut buf, start) = self.allocate(total_len, ARCH_FUNCTION_ALIGNMENT)?;
let (mut buf, registry, start) = self.allocate(total_len, ARCH_FUNCTION_ALIGNMENT)?;
let mut result = Vec::with_capacity(compilation.len());
let mut start = start as u32;
let mut padding = 0usize;

for func in compilation.values() {
let (next_start, next_buf, vmfunc) =
Self::copy_function(func, start + padding as u32, &mut buf[padding..]);
Self::copy_function(func, start + padding as u32, &mut buf[padding..], registry);
assert!(vmfunc as *mut _ as *mut u8 as usize % ARCH_FUNCTION_ALIGNMENT == 0);

result.push(vmfunc);
Expand All @@ -140,7 +143,7 @@ impl CodeMemory {
self.push_current(0)
.expect("failed to push current memory map");

for CodeMemoryEntry { mmap: m } in &mut self.entries[self.published..] {
for CodeMemoryEntry { mmap: m, .. } in &mut self.entries[self.published..] {
if !m.is_empty() {
unsafe {
region::protect(m.as_mut_ptr(), m.len(), region::Protection::READ_EXECUTE)
Expand All @@ -166,7 +169,7 @@ impl CodeMemory {
&mut self,
size: usize,
alignment: usize,
) -> Result<(&mut [u8], usize), String> {
) -> Result<(&mut [u8], &mut UnwindRegistry, usize), String> {
assert!(alignment > 0);

let align_padding = get_align_padding_size(self.position, alignment);
Expand All @@ -189,17 +192,21 @@ impl CodeMemory {

Ok((
&mut self.current.mmap.as_mut_slice()[old_position..self.position],
&mut self.current.registry,
old_position,
))
}

/// Calculates the allocation size of the given compiled function.
fn function_allocation_size(func: &FunctionBody) -> usize {
if let Some(unwind_info) = &func.unwind_info {
// Account for necessary unwind information alignment padding (32-bit)
((func.body.len() + 3) & !3) + unwind_info.len()
} else {
func.body.len()
match &func.unwind_info {
Some(CompiledFunctionUnwindInfo::WindowsX64(info)) => {
// Windows unwind information is required to be emitted into code memory
// This is because it must be a positive relative offset from the start of the memory
// Account for necessary unwind information alignment padding (32-bit alignment)
((func.body.len() + 3) & !3) + info.len()
}
_ => func.body.len(),
}
}

Expand All @@ -210,19 +217,36 @@ impl CodeMemory {
func: &FunctionBody,
func_start: u32,
buf: &'a mut [u8],
registry: &mut UnwindRegistry,
) -> (u32, &'a mut [u8], &'a mut [VMFunctionBody]) {
let func_end = func_start + (func.body.len() as u32);
let func_len = func.body.len();
let mut func_end = func_start + (func_len as u32);

let (body, remainder) = buf.split_at_mut(func.body.len());
let (body, mut remainder) = buf.split_at_mut(func_len);
body.copy_from_slice(&func.body);
let vmfunc = Self::view_as_mut_vmfunc_slice(body);

if func.unwind_info.is_none() {
return (func_end, remainder, vmfunc);
if let Some(CompiledFunctionUnwindInfo::WindowsX64(info)) = &func.unwind_info {
// Windows unwind information is written following the function body
// Keep unwind information 32-bit aligned (round up to the nearest 4 byte boundary)
let unwind_start = (func_end + 3) & !3;
let unwind_size = info.len();
let padding = (unwind_start - func_end) as usize;

let (slice, r) = remainder.split_at_mut(padding + unwind_size);
slice[padding..].copy_from_slice(&info);

func_end = unwind_start + (unwind_size as u32);
remainder = r;
}

if let Some(info) = &func.unwind_info {
registry
.register(func_start, func_len as u32, info)
.expect("failed to register unwind information");
}
let unwind_info = func.unwind_info.as_ref().unwrap();

return (func_end, remainder, vmfunc);
(func_end, remainder, vmfunc)
}

/// Convert mut a slice from u8 to VMFunctionBody.
Expand Down
1 change: 1 addition & 0 deletions lib/engine-jit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ mod code_memory;
mod engine;
mod link;
mod serialize;
mod unwind;

pub use crate::artifact::JITArtifact;
pub use crate::code_memory::CodeMemory;
Expand Down
14 changes: 14 additions & 0 deletions lib/engine-jit/src/unwind/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
cfg_if::cfg_if! {
if #[cfg(all(windows, target_arch = "x86_64"))] {
mod windows_x64;
pub use self::windows_x64::*;
} else if #[cfg(all(windows, target_arch = "x86"))] {
mod windows_x32;
pub use self::windows_x32::*;
} else if #[cfg(unix)] {
mod systemv;
pub use self::systemv::*;
} else {
compile_error!("unsupported target platform for unwind");
}
}
95 changes: 95 additions & 0 deletions lib/engine-jit/src/unwind/windows_x64.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//! Module for Windows x64 ABI unwind registry.
use wasmer_compiler::CompiledFunctionUnwindInfo;
use winapi::um::winnt;

/// Represents a registry of function unwind information for Windows x64 ABI.
pub struct UnwindRegistry {
base_address: usize,
functions: Vec<winnt::RUNTIME_FUNCTION>,
published: bool,
}

impl UnwindRegistry {
/// Creates a new unwind registry with the given base address.
pub fn new(base_address: usize) -> Self {
Self {
base_address,
functions: Vec::new(),
published: false,
}
}

/// Registers a function given the start offset, length, and unwind information.
pub fn register(
&mut self,
func_start: u32,
func_len: u32,
info: &CompiledFunctionUnwindInfo,
) -> Result<(), String> {
if self.published {
return Err("unwind registry has already been published".to_string());
}

match info {
CompiledFunctionUnwindInfo::WindowsX64(_) => {
let mut entry = winnt::RUNTIME_FUNCTION::default();

entry.BeginAddress = func_start;
entry.EndAddress = func_start + func_len;

// The unwind information should be immediately following the function
// with padding for 4 byte alignment
unsafe {
*entry.u.UnwindInfoAddress_mut() = (entry.EndAddress + 3) & !3;
}

self.functions.push(entry);

Ok(())
}
_ => Err("unsupported unwind information".to_string()),
}
}

/// Publishes all registered functions.
pub fn publish(&mut self) -> Result<(), String> {
if self.published {
return Err("unwind registry has already been published".to_string());
}

self.published = true;

if !self.functions.is_empty() {
// Windows heap allocations are 32-bit aligned, but assert just in case
assert_eq!(
(self.functions.as_mut_ptr() as u64) % 4,
0,
"function table allocation was not aligned"
);

unsafe {
if winnt::RtlAddFunctionTable(
self.functions.as_mut_ptr(),
self.functions.len() as u32,
self.base_address as u64,
) == 0
{
return Err("failed to register function table".to_string());
}
}
}

Ok(())
}
}

impl Drop for UnwindRegistry {
fn drop(&mut self) {
if self.published {
unsafe {
winnt::RtlDeleteFunctionTable(self.functions.as_mut_ptr());
}
}
}
}

0 comments on commit f9aabee

Please sign in to comment.