swasm_utils/
ext.rs

1use std::string::String;
2use std::vec::Vec;
3use std::borrow::ToOwned;
4
5use swasm::{elements, builder};
6use optimizer::{import_section, export_section};
7use byteorder::{LittleEndian, ByteOrder};
8
9type Insertion = (usize, u32, u32, String);
10
11pub fn update_call_index(instructions: &mut elements::Instructions, original_imports: usize, inserts: &[Insertion]) {
12	use swasm::elements::Instruction::*;
13	for instruction in instructions.elements_mut().iter_mut() {
14		if let &mut Call(ref mut call_index) = instruction {
15			if let Some(pos) = inserts.iter().position(|x| x.1 == *call_index) {
16				*call_index = (original_imports + pos) as u32;
17			} else if *call_index as usize > original_imports {
18				*call_index += inserts.len() as u32;
19			}
20		}
21	}
22}
23
24pub fn memory_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::MemorySection> {
25	for section in module.sections_mut() {
26	   if let &mut elements::Section::Memory(ref mut sect) = section {
27		   return Some(sect);
28		}
29	}
30	None
31}
32
33pub fn externalize_mem(mut module: elements::Module, adjust_pages: Option<u32>, max_pages: u32) -> elements::Module {
34	let mut entry = memory_section(&mut module)
35		.expect("Memory section to exist")
36		.entries_mut()
37		.pop()
38		.expect("Own memory entry to exist in memory section");
39
40	if let Some(adjust_pages) = adjust_pages {
41		assert!(adjust_pages <= max_pages);
42		entry = elements::MemoryType::new(adjust_pages, Some(max_pages));
43	}
44
45	if entry.limits().maximum().is_none() {
46		entry = elements::MemoryType::new(entry.limits().initial(), Some(max_pages));
47	}
48
49	let mut builder = builder::from_module(module);
50	builder.push_import(
51		elements::ImportEntry::new(
52			"env".to_owned(),
53			"memory".to_owned(),
54			elements::External::Memory(entry),
55		)
56	);
57
58	builder.build()
59}
60
61fn foreach_public_func_name<F>(mut module: elements::Module, f: F) -> elements::Module
62where F: Fn(&mut String)
63{
64	import_section(&mut module).map(|is| {
65		for entry in is.entries_mut() {
66			if let elements::External::Function(_) = *entry.external() {
67				f(entry.field_mut())
68			}
69		}
70	});
71
72	export_section(&mut module).map(|es| {
73		for entry in es.entries_mut() {
74			if let elements::Internal::Function(_) = *entry.internal() {
75				f(entry.field_mut())
76			}
77		}
78	});
79
80	module
81}
82
83pub fn underscore_funcs(module: elements::Module) -> elements::Module {
84	foreach_public_func_name(module, |n| n.insert(0, '_'))
85}
86
87pub fn ununderscore_funcs(module: elements::Module) -> elements::Module {
88	foreach_public_func_name(module, |n| { n.remove(0); })
89}
90
91pub fn shrink_unknown_stack(
92	mut module: elements::Module,
93	// for example, `shrink_amount = (1MB - 64KB)` will limit stack to 64KB
94	shrink_amount: u32,
95) -> (elements::Module, u32) {
96	let mut new_stack_top = 0;
97	for section in module.sections_mut() {
98		match section {
99			&mut elements::Section::Data(ref mut data_section) => {
100				for ref mut data_segment in data_section.entries_mut() {
101					if data_segment.offset().code() == &[elements::Instruction::I32Const(4), elements::Instruction::End] {
102						assert_eq!(data_segment.value().len(), 4);
103						let current_val = LittleEndian::read_u32(data_segment.value());
104						let new_val = current_val - shrink_amount;
105						LittleEndian::write_u32(data_segment.value_mut(), new_val);
106						new_stack_top = new_val;
107					}
108				}
109			},
110			_ => continue
111		}
112	}
113	(module, new_stack_top)
114}
115
116pub fn externalize(
117	module: elements::Module,
118	replaced_funcs: Vec<&str>,
119) -> elements::Module {
120   // Save import functions number for later
121	let import_funcs_total = module
122		.import_section().expect("Import section to exist")
123		.entries()
124		.iter()
125		.filter(|e| if let &elements::External::Function(_) = e.external() { true } else { false })
126		.count();
127
128	// First, we find functions indices that are to be rewired to externals
129	//   Triple is (function_index (callable), type_index, function_name)
130	let mut replaces: Vec<Insertion> = replaced_funcs
131		.into_iter()
132		.filter_map(|f| {
133			let export = module
134				.export_section().expect("Export section to exist")
135				.entries().iter().enumerate()
136				.find(|&(_, entry)| entry.field() == f)
137				.expect("All functions of interest to exist");
138
139			if let &elements::Internal::Function(func_idx) = export.1.internal() {
140				let type_ref = module
141					.function_section().expect("Functions section to exist")
142					.entries()[func_idx as usize - import_funcs_total]
143					.type_ref();
144
145				Some((export.0, func_idx, type_ref, export.1.field().to_owned()))
146			} else {
147				None
148			}
149		})
150		.collect();
151
152	replaces.sort_by_key(|e| e.0);
153
154	// Second, we duplicate them as import definitions
155	let mut mbuilder = builder::from_module(module);
156	for &(_, _, type_ref, ref field) in replaces.iter() {
157		mbuilder.push_import(
158			builder::import()
159				.module("env")
160				.field(field)
161				.external().func(type_ref)
162				.build()
163		);
164	}
165
166	// Back to mutable access
167	let mut module = mbuilder.build();
168
169	// Third, rewire all calls to imported functions and update all other calls indices
170	for section in module.sections_mut() {
171		match section {
172			&mut elements::Section::Code(ref mut code_section) => {
173				for ref mut func_body in code_section.bodies_mut() {
174					update_call_index(func_body.code_mut(), import_funcs_total, &replaces);
175				}
176			},
177			&mut elements::Section::Export(ref mut export_section) => {
178				for ref mut export in export_section.entries_mut() {
179					if let &mut elements::Internal::Function(ref mut func_index) = export.internal_mut() {
180						if *func_index >= import_funcs_total as u32 { *func_index += replaces.len() as u32; }
181					}
182				}
183			},
184			&mut elements::Section::Element(ref mut elements_section) => {
185				for ref mut segment in elements_section.entries_mut() {
186					// update all indirect call addresses initial values
187					for func_index in segment.members_mut() {
188						if *func_index >= import_funcs_total as u32 { *func_index += replaces.len() as u32; }
189					}
190				}
191			},
192			_ => { }
193		}
194	}
195
196	module
197
198}