wasmer_emscripten/
utils.rs1use super::env;
2use super::env::{get_emscripten_data, get_emscripten_funcs};
3use crate::storage::align_memory;
4use crate::EmEnv;
5use libc::stat;
6use std::ffi::CStr;
7use std::mem::size_of;
8use std::os::raw::c_char;
9use std::path::PathBuf;
10use std::slice;
11use wasmer::{FunctionEnvMut, GlobalInit, MemoryView, Module, Pages, WasmPtr};
12
13pub fn is_emscripten_module(module: &Module) -> bool {
15 for import in module.imports().functions() {
16 let name = import.name();
17 let module = import.module();
18 if (name == "_emscripten_memcpy_big"
19 || name == "emscripten_memcpy_big"
20 || name == "__map_file")
21 && module == "env"
22 {
23 return true;
24 }
25 }
26 false
27}
28
29pub fn get_emscripten_table_size(module: &Module) -> Result<(u32, Option<u32>), String> {
30 if let Some(import) = module.imports().tables().next() {
31 let ty = import.ty();
32 Ok((ty.minimum, ty.maximum))
33 } else {
34 Err("Emscripten requires at least one imported table".to_string())
35 }
36}
37
38pub fn get_emscripten_memory_size(module: &Module) -> Result<(Pages, Option<Pages>, bool), String> {
39 if let Some(import) = module.imports().memories().next() {
40 let ty = import.ty();
41 Ok((ty.minimum, ty.maximum, ty.shared))
42 } else {
43 Err("Emscripten requires at least one imported memory".to_string())
44 }
45}
46
47pub fn get_emscripten_metadata(module: &Module) -> Result<Option<(u32, u32)>, String> {
52 let max_idx = match module
53 .info()
54 .global_initializers
55 .iter()
56 .map(|(k, _)| k)
57 .max()
58 {
59 Some(x) => x,
60 None => return Ok(None),
61 };
62
63 let snd_max_idx = match module
64 .info()
65 .global_initializers
66 .iter()
67 .map(|(k, _)| k)
68 .filter(|k| *k != max_idx)
69 .max()
70 {
71 Some(x) => x,
72 None => return Ok(None),
73 };
74
75 if let (GlobalInit::I32Const(dynamic_base), GlobalInit::I32Const(dynamictop_ptr)) = (
76 &module.info().global_initializers[max_idx],
77 &module.info().global_initializers[snd_max_idx],
78 ) {
79 let dynamic_base = (*dynamic_base as u32).checked_sub(32).ok_or_else(|| {
80 format!(
81 "emscripten unexpected dynamic_base {}",
82 *dynamic_base as u32
83 )
84 })?;
85 let dynamictop_ptr = (*dynamictop_ptr as u32).checked_sub(32).ok_or_else(|| {
86 format!(
87 "emscripten unexpected dynamictop_ptr {}",
88 *dynamictop_ptr as u32
89 )
90 })?;
91 Ok(Some((
92 align_memory(dynamic_base),
93 align_memory(dynamictop_ptr),
94 )))
95 } else {
96 Ok(None)
97 }
98}
99
100pub unsafe fn write_to_buf(
101 ctx: FunctionEnvMut<EmEnv>,
102 string: *const c_char,
103 buf: u32,
104 max: u32,
105) -> u32 {
106 let memory = ctx.data().memory(0);
107 let buf_addr = emscripten_memory_pointer!(memory.view(&ctx), buf) as *mut c_char;
108
109 for i in 0..max {
110 *buf_addr.add(i as _) = *string.add(i as _);
111 }
112
113 buf
114}
115
116pub unsafe fn copy_cstr_into_wasm(ctx: &mut FunctionEnvMut<EmEnv>, cstr: *const c_char) -> u32 {
118 let s = CStr::from_ptr(cstr).to_str().unwrap();
119 let cstr_len = s.len();
120 let space_offset = env::call_malloc(ctx, (cstr_len as u32) + 1);
121 let memory = ctx.data().memory(0);
122 let raw_memory = emscripten_memory_pointer!(memory.view(&ctx), space_offset) as *mut c_char;
123 let slice = slice::from_raw_parts_mut(raw_memory, cstr_len);
124
125 for (byte, loc) in s.bytes().zip(slice.iter_mut()) {
126 *loc = byte as _;
127 }
128
129 *raw_memory.add(cstr_len) = 0;
132
133 space_offset
134}
135
136pub unsafe fn allocate_on_stack<'a, T: Copy>(
139 mut ctx: &mut FunctionEnvMut<'a, EmEnv>,
140 count: u32,
141) -> (u32, &'a mut [T]) {
142 let stack_alloc_ref = get_emscripten_funcs(ctx).stack_alloc_ref().unwrap().clone();
143 let offset = stack_alloc_ref
144 .call(&mut ctx, count * (size_of::<T>() as u32))
145 .unwrap();
146
147 let memory = ctx.data().memory(0);
148 let addr = emscripten_memory_pointer!(memory.view(&ctx), offset) as *mut T;
149 let slice = slice::from_raw_parts_mut(addr, count as usize);
150
151 (offset, slice)
152}
153
154pub unsafe fn allocate_cstr_on_stack<'a>(
157 ctx: &'a mut FunctionEnvMut<'a, EmEnv>,
158 s: &str,
159) -> (u32, &'a [u8]) {
160 let (offset, slice) = allocate_on_stack(ctx, (s.len() + 1) as u32);
161
162 use std::iter;
163 for (byte, loc) in s.bytes().chain(iter::once(0)).zip(slice.iter_mut()) {
164 *loc = byte;
165 }
166
167 (offset, slice)
168}
169
170#[cfg(not(target_os = "windows"))]
171pub unsafe fn copy_terminated_array_of_cstrs(
172 mut _ctx: FunctionEnvMut<EmEnv>,
173 cstrs: *mut *mut c_char,
174) -> u32 {
175 let _total_num = {
176 let mut ptr = cstrs;
177 let mut counter = 0;
178 while !(*ptr).is_null() {
179 counter += 1;
180 ptr = ptr.add(1);
181 }
182 counter
183 };
184 debug!(
185 "emscripten::copy_terminated_array_of_cstrs::total_num: {}",
186 _total_num
187 );
188 0
189}
190
191#[repr(C)]
192pub struct GuestStat {
193 st_dev: u32,
194 __st_dev_padding: u32,
195 __st_ino_truncated: u32,
196 st_mode: u32,
197 st_nlink: u32,
198 st_uid: u32,
199 st_gid: u32,
200 st_rdev: u32,
201 __st_rdev_padding: u32,
202 st_size: u32,
203 st_blksize: u32,
204 st_blocks: u32,
205 st_atime: u64,
206 st_mtime: u64,
207 st_ctime: u64,
208 st_ino: u32,
209}
210
211#[allow(clippy::cast_ptr_alignment)]
212pub unsafe fn copy_stat_into_wasm(ctx: FunctionEnvMut<EmEnv>, buf: u32, stat: &stat) {
213 let memory = ctx.data().memory(0);
214 let stat_ptr = emscripten_memory_pointer!(memory.view(&ctx), buf) as *mut GuestStat;
215 (*stat_ptr).st_dev = stat.st_dev as _;
216 (*stat_ptr).__st_dev_padding = 0;
217 (*stat_ptr).__st_ino_truncated = stat.st_ino as _;
218 (*stat_ptr).st_mode = stat.st_mode as _;
219 (*stat_ptr).st_nlink = stat.st_nlink as _;
220 (*stat_ptr).st_uid = stat.st_uid as _;
221 (*stat_ptr).st_gid = stat.st_gid as _;
222 (*stat_ptr).st_rdev = stat.st_rdev as _;
223 (*stat_ptr).__st_rdev_padding = 0;
224 (*stat_ptr).st_size = stat.st_size as _;
225 (*stat_ptr).st_blksize = 4096;
226 #[cfg(not(target_os = "windows"))]
227 {
228 (*stat_ptr).st_blocks = stat.st_blocks as _;
229 }
230 #[cfg(target_os = "windows")]
231 {
232 (*stat_ptr).st_blocks = 0;
233 }
234 (*stat_ptr).st_atime = stat.st_atime as _;
235 (*stat_ptr).st_mtime = stat.st_mtime as _;
236 (*stat_ptr).st_ctime = stat.st_ctime as _;
237 (*stat_ptr).st_ino = stat.st_ino as _;
238}
239
240#[allow(dead_code)] pub fn read_string_from_wasm(memory: &MemoryView, offset: u32) -> String {
242 WasmPtr::<u8>::new(offset)
243 .read_utf8_string_with_nul(memory)
244 .unwrap()
245}
246
247pub fn get_cstr_path(ctx: FunctionEnvMut<EmEnv>, path: *const i8) -> Option<std::ffi::CString> {
250 use std::collections::VecDeque;
251
252 let path_str =
253 unsafe { std::ffi::CStr::from_ptr(path as *const _).to_str().unwrap() }.to_string();
254 let data = get_emscripten_data(&ctx);
255 let path = PathBuf::from(path_str);
256 let mut prefix_added = false;
257 let mut components = path.components().collect::<VecDeque<_>>();
258 if components.len() == 1 {
261 components.push_front(std::path::Component::CurDir);
262 prefix_added = true;
263 }
264 let mut cumulative_path = PathBuf::new();
265 for c in components.into_iter() {
266 cumulative_path.push(c);
267 if let Some(val) = data
268 .as_ref()
269 .unwrap()
270 .mapped_dirs
271 .get(&cumulative_path.to_string_lossy().to_string())
272 {
273 let rest_of_path = if !prefix_added {
274 path.strip_prefix(cumulative_path).ok()?
275 } else {
276 &path
277 };
278 let rebased_path = val.join(rest_of_path);
279 return std::ffi::CString::new(rebased_path.to_string_lossy().as_bytes()).ok();
280 }
281 }
282 None
283}
284
285pub fn get_current_directory(ctx: FunctionEnvMut<EmEnv>) -> Option<PathBuf> {
288 if let Some(val) = get_emscripten_data(&ctx)
289 .as_ref()
290 .unwrap()
291 .mapped_dirs
292 .get(".")
293 {
294 return Some(val.clone());
295 }
296 std::env::current_dir()
297 .map(|cwd| {
298 if let Some(val) = get_emscripten_data(&ctx)
299 .as_ref()
300 .unwrap()
301 .mapped_dirs
302 .get(&cwd.to_string_lossy().to_string())
303 {
304 val.clone()
305 } else {
306 cwd
307 }
308 })
309 .ok()
310}