owasm_utils/
pack.rs

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