casper_wasm_utils/
ext.rs

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