pwasm_utils/
pack.rs

1use crate::std::{borrow::ToOwned, fmt, vec::Vec};
2
3use super::{gas::update_call_index, TargetRuntime};
4use parity_wasm::{
5	builder,
6	elements::{
7		self, DataSection, DataSegment, External, ImportCountType, InitExpr, Instruction, Internal,
8		Section,
9	},
10};
11
12/// Pack error.
13///
14/// Pack has number of assumptions of passed module structure.
15/// When they are violated, pack_instance returns one of these.
16#[derive(Debug)]
17pub enum Error {
18	MalformedModule,
19	NoTypeSection,
20	NoExportSection,
21	NoCodeSection,
22	InvalidCreateSignature(&'static str),
23	NoCreateSymbol(&'static str),
24	InvalidCreateMember(&'static str),
25	NoImportSection,
26}
27
28impl fmt::Display for Error {
29	fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
30		match *self {
31			Error::MalformedModule => write!(f, "Module internal references are inconsistent"),
32			Error::NoTypeSection => write!(f, "No type section in the module"),
33			Error::NoExportSection => write!(f, "No export section in the module"),
34			Error::NoCodeSection => write!(f, "No code section inthe module"),
35			Error::InvalidCreateSignature(sym) => {
36				write!(f, "Exported symbol `{}` has invalid signature, should be () -> ()", sym)
37			},
38			Error::InvalidCreateMember(sym) => {
39				write!(f, "Exported symbol `{}` should be a function", sym)
40			},
41			Error::NoCreateSymbol(sym) => write!(f, "No exported `{}` symbol", sym),
42			Error::NoImportSection => write!(f, "No import section in the module"),
43		}
44	}
45}
46
47/// If a pwasm module has an exported function matching "create" symbol we want to pack it into "constructor".
48/// `raw_module` is the actual contract code
49/// `ctor_module` is the constructor which should return `raw_module`
50pub fn pack_instance(
51	raw_module: Vec<u8>,
52	mut ctor_module: elements::Module,
53	target: &TargetRuntime,
54) -> Result<elements::Module, Error> {
55	// Total number of constructor module import functions
56	let ctor_import_functions = ctor_module.import_section().map(|x| x.functions()).unwrap_or(0);
57
58	// We need to find an internal ID of function which is exported as `symbols().create`
59	// in order to find it in the Code section of the module
60	let mut create_func_id = {
61		let found_entry = ctor_module
62			.export_section()
63			.ok_or(Error::NoExportSection)?
64			.entries()
65			.iter()
66			.find(|entry| target.symbols().create == entry.field())
67			.ok_or_else(|| Error::NoCreateSymbol(target.symbols().create))?;
68
69		let function_index: usize = match found_entry.internal() {
70			Internal::Function(index) => *index as usize,
71			_ => return Err(Error::InvalidCreateMember(target.symbols().create)),
72		};
73
74		// Calculates a function index within module's function section
75		let function_internal_index = function_index - ctor_import_functions;
76
77		// Constructor should be of signature `func()` (void), fail otherwise
78		let type_id = ctor_module
79			.function_section()
80			.ok_or(Error::NoCodeSection)?
81			.entries()
82			.get(function_index - ctor_import_functions)
83			.ok_or(Error::MalformedModule)?
84			.type_ref();
85
86		let elements::Type::Function(func) = ctor_module
87			.type_section()
88			.ok_or(Error::NoTypeSection)?
89			.types()
90			.get(type_id as usize)
91			.ok_or(Error::MalformedModule)?;
92
93		// Deploy should have no arguments and also should return nothing
94		if !func.params().is_empty() {
95			return Err(Error::InvalidCreateSignature(target.symbols().create))
96		}
97		if !func.results().is_empty() {
98			return Err(Error::InvalidCreateSignature(target.symbols().create))
99		}
100
101		function_internal_index
102	};
103
104	let ret_function_id = {
105		let mut id = 0;
106		let mut found = false;
107		for entry in ctor_module.import_section().ok_or(Error::NoImportSection)?.entries().iter() {
108			if let External::Function(_) = *entry.external() {
109				if entry.field() == target.symbols().ret {
110					found = true;
111					break
112				} else {
113					id += 1;
114				}
115			}
116		}
117		if !found {
118			let mut mbuilder = builder::from_module(ctor_module);
119			let import_sig = mbuilder
120				.push_signature(builder::signature().param().i32().param().i32().build_sig());
121
122			mbuilder.push_import(
123				builder::import()
124					.module("env")
125					.field(target.symbols().ret)
126					.external()
127					.func(import_sig)
128					.build(),
129			);
130
131			ctor_module = mbuilder.build();
132
133			let ret_func = ctor_module.import_count(ImportCountType::Function) as u32 - 1;
134
135			for section in ctor_module.sections_mut() {
136				match section {
137					elements::Section::Code(code_section) => {
138						for func_body in code_section.bodies_mut() {
139							update_call_index(func_body.code_mut(), ret_func);
140						}
141					},
142					elements::Section::Export(export_section) => {
143						for export in export_section.entries_mut() {
144							if let elements::Internal::Function(func_index) = export.internal_mut()
145							{
146								if *func_index >= ret_func {
147									*func_index += 1
148								}
149							}
150						}
151					},
152					elements::Section::Element(elements_section) => {
153						for segment in elements_section.entries_mut() {
154							// update all indirect call addresses initial values
155							for func_index in segment.members_mut() {
156								if *func_index >= ret_func {
157									*func_index += 1
158								}
159							}
160						}
161					},
162					_ => {},
163				}
164			}
165
166			create_func_id += 1;
167			ret_func
168		} else {
169			id
170		}
171	};
172
173	// If new function is put in ctor module, it will have this callable index
174	let last_function_index = ctor_module.functions_space();
175
176	// We ensure here that module has the DataSection
177	if !ctor_module
178		.sections()
179		.iter()
180		.any(|section| matches!(*section, Section::Data(_)))
181	{
182		// DataSection has to be the last non-custom section according the to the spec
183		ctor_module
184			.sections_mut()
185			.push(Section::Data(DataSection::with_entries(vec![])));
186	}
187
188	// Code data address is an address where we put the contract's code (raw_module)
189	let mut code_data_address = 0i32;
190
191	for section in ctor_module.sections_mut() {
192		if let Section::Data(data_section) = section {
193			let (index, offset) = if let Some(entry) = data_section.entries().iter().last() {
194				let init_expr = entry
195					.offset()
196					.as_ref()
197					.expect("parity-wasm is compiled without bulk-memory operations")
198					.code();
199				if let Instruction::I32Const(offst) = init_expr[0] {
200					let len = entry.value().len() as i32;
201					let offst = offst as i32;
202					(entry.index(), offst + (len + 4) - len % 4)
203				} else {
204					(0, 0)
205				}
206			} else {
207				(0, 0)
208			};
209			let code_data = DataSegment::new(
210				index,
211				Some(InitExpr::new(vec![Instruction::I32Const(offset), Instruction::End])),
212				raw_module.clone(),
213			);
214			data_section.entries_mut().push(code_data);
215			code_data_address = offset;
216		}
217	}
218
219	let mut new_module = builder::from_module(ctor_module)
220		.function()
221		.signature()
222		.build()
223		.body()
224		.with_instructions(elements::Instructions::new(vec![
225			Instruction::Call((create_func_id + ctor_import_functions) as u32),
226			Instruction::I32Const(code_data_address),
227			Instruction::I32Const(raw_module.len() as i32),
228			Instruction::Call(ret_function_id as u32),
229			Instruction::End,
230		]))
231		.build()
232		.build()
233		.build();
234
235	for section in new_module.sections_mut() {
236		if let Section::Export(export_section) = section {
237			for entry in export_section.entries_mut().iter_mut() {
238				if target.symbols().create == entry.field() {
239					// change `create` symbol export name into default `call` symbol name.
240					*entry.field_mut() = target.symbols().call.to_owned();
241					*entry.internal_mut() =
242						elements::Internal::Function(last_function_index as u32);
243				}
244			}
245		}
246	}
247
248	Ok(new_module)
249}
250
251#[cfg(test)]
252mod test {
253	use super::{super::optimize, *};
254
255	fn test_packer(mut module: elements::Module, target_runtime: &TargetRuntime) {
256		let mut ctor_module = module.clone();
257		optimize(&mut module, vec![target_runtime.symbols().call])
258			.expect("Optimizer to finish without errors");
259		optimize(&mut ctor_module, vec![target_runtime.symbols().create])
260			.expect("Optimizer to finish without errors");
261
262		let raw_module = parity_wasm::serialize(module).unwrap();
263		let ctor_module =
264			pack_instance(raw_module.clone(), ctor_module, target_runtime).expect("Packing failed");
265
266		let data_section =
267			ctor_module.data_section().expect("Packed module has to have a data section");
268		let data_segment = data_section
269			.entries()
270			.iter()
271			.last()
272			.expect("Packed module has to have a data section with at least one entry");
273		assert!(
274			data_segment.value() == AsRef::<[u8]>::as_ref(&raw_module),
275			"Last data segment should be equal to the raw module"
276		);
277	}
278
279	#[test]
280	fn no_data_section() {
281		let target_runtime = TargetRuntime::pwasm();
282
283		test_packer(
284			builder::module()
285				.import()
286				.module("env")
287				.field("memory")
288				.external()
289				.memory(1, Some(1))
290				.build()
291				.function()
292				.signature()
293				.params()
294				.i32()
295				.i32()
296				.build()
297				.build()
298				.body()
299				.build()
300				.build()
301				.function()
302				.signature()
303				.build()
304				.body()
305				.with_instructions(elements::Instructions::new(vec![elements::Instruction::End]))
306				.build()
307				.build()
308				.function()
309				.signature()
310				.build()
311				.body()
312				.with_instructions(elements::Instructions::new(vec![elements::Instruction::End]))
313				.build()
314				.build()
315				.export()
316				.field(target_runtime.symbols().call)
317				.internal()
318				.func(1)
319				.build()
320				.export()
321				.field(target_runtime.symbols().create)
322				.internal()
323				.func(2)
324				.build()
325				.build(),
326			&target_runtime,
327		);
328	}
329
330	#[test]
331	fn with_data_section() {
332		let target_runtime = TargetRuntime::pwasm();
333
334		test_packer(
335			builder::module()
336				.import()
337				.module("env")
338				.field("memory")
339				.external()
340				.memory(1, Some(1))
341				.build()
342				.data()
343				.offset(elements::Instruction::I32Const(16))
344				.value(vec![0u8])
345				.build()
346				.function()
347				.signature()
348				.params()
349				.i32()
350				.i32()
351				.build()
352				.build()
353				.body()
354				.build()
355				.build()
356				.function()
357				.signature()
358				.build()
359				.body()
360				.with_instructions(elements::Instructions::new(vec![elements::Instruction::End]))
361				.build()
362				.build()
363				.function()
364				.signature()
365				.build()
366				.body()
367				.with_instructions(elements::Instructions::new(vec![elements::Instruction::End]))
368				.build()
369				.build()
370				.export()
371				.field(target_runtime.symbols().call)
372				.internal()
373				.func(1)
374				.build()
375				.export()
376				.field(target_runtime.symbols().create)
377				.internal()
378				.func(2)
379				.build()
380				.build(),
381			&target_runtime,
382		);
383	}
384}