Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/wizer/src/rewrite.rs
2459 views
1
//! Final rewrite pass.
2
3
use crate::{FuncRenames, SnapshotVal, Wizer, info::ModuleContext, snapshot::Snapshot};
4
use std::cell::Cell;
5
use std::convert::TryFrom;
6
use wasm_encoder::reencode::{Reencode, RoundtripReencoder};
7
use wasm_encoder::{ConstExpr, SectionId};
8
9
impl Wizer {
10
/// Given the initialized snapshot, rewrite the Wasm so that it is already
11
/// initialized.
12
///
13
pub(crate) fn rewrite(
14
&self,
15
module: &mut ModuleContext<'_>,
16
snapshot: &Snapshot,
17
renames: &FuncRenames,
18
) -> Vec<u8> {
19
log::debug!("Rewriting input Wasm to pre-initialized state");
20
21
let mut encoder = wasm_encoder::Module::new();
22
let has_wasi_initialize = module.has_wasi_initialize();
23
24
// Encode the initialized data segments from the snapshot rather
25
// than the original, uninitialized data segments.
26
let add_data_segments = |data_section: &mut wasm_encoder::DataSection| {
27
for seg in &snapshot.data_segments {
28
let offset = if seg.is64 {
29
ConstExpr::i64_const(seg.offset.cast_signed())
30
} else {
31
ConstExpr::i32_const(u32::try_from(seg.offset).unwrap().cast_signed())
32
};
33
data_section.active(seg.memory_index, &offset, seg.data.iter().copied());
34
}
35
};
36
37
// There are multiple places were we potentially need to check whether
38
// we've added the data section already and if we haven't yet, then do
39
// so. For example, the original Wasm might not have a data section at
40
// all, and so we have to potentially add it at the end of iterating
41
// over the original sections. This closure encapsulates all that
42
// add-it-if-we-haven't-already logic in one place.
43
let added_data_section = Cell::new(false);
44
45
let add_data_section = |encoder: &mut wasm_encoder::Module| {
46
if added_data_section.get() {
47
return;
48
}
49
added_data_section.set(true);
50
let mut data_section = wasm_encoder::DataSection::new();
51
add_data_segments(&mut data_section);
52
encoder.section(&data_section);
53
};
54
55
for section in module.raw_sections() {
56
match section {
57
// Some tools expect the name custom section to come last, even
58
// though custom sections are allowed in any order. Therefore,
59
// make sure we've added our data section by now.
60
s if is_name_section(s) => {
61
add_data_section(&mut encoder);
62
encoder.section(s);
63
}
64
65
// For the memory section, we update the minimum size of each
66
// defined memory to the snapshot's initialized size for that
67
// memory.
68
s if s.id == u8::from(SectionId::Memory) => {
69
let mut memories = wasm_encoder::MemorySection::new();
70
assert_eq!(module.defined_memories_len(), snapshot.memory_mins.len());
71
for ((_, mem), new_min) in module
72
.defined_memories()
73
.zip(snapshot.memory_mins.iter().copied())
74
{
75
let mut mem = RoundtripReencoder.memory_type(mem).unwrap();
76
mem.minimum = new_min;
77
memories.memory(mem);
78
}
79
encoder.section(&memories);
80
}
81
82
// Encode the initialized global values from the snapshot,
83
// rather than the original values.
84
s if s.id == u8::from(SectionId::Global) => {
85
let original_globals = wasmparser::GlobalSectionReader::new(
86
wasmparser::BinaryReader::new(s.data, 0),
87
)
88
.unwrap();
89
let mut globals = wasm_encoder::GlobalSection::new();
90
let mut snapshot = snapshot.globals.iter();
91
for ((_, glob_ty, export_name), global) in
92
module.defined_globals().zip(original_globals)
93
{
94
let global = global.unwrap();
95
if export_name.is_some() {
96
// This is a mutable global and it was present in
97
// the snapshot, so translate the snapshot value to
98
// a constant expression and insert it.
99
assert!(glob_ty.mutable);
100
let (_, val) = snapshot.next().unwrap();
101
let init = match val {
102
SnapshotVal::I32(x) => ConstExpr::i32_const(*x),
103
SnapshotVal::I64(x) => ConstExpr::i64_const(*x),
104
SnapshotVal::F32(x) => {
105
ConstExpr::f32_const(wasm_encoder::Ieee32::new(*x))
106
}
107
SnapshotVal::F64(x) => {
108
ConstExpr::f64_const(wasm_encoder::Ieee64::new(*x))
109
}
110
SnapshotVal::V128(x) => ConstExpr::v128_const(x.cast_signed()),
111
};
112
let glob_ty = RoundtripReencoder.global_type(glob_ty).unwrap();
113
globals.global(glob_ty, &init);
114
} else {
115
// This global isn't mutable so preserve its value
116
// as-is.
117
assert!(!glob_ty.mutable);
118
RoundtripReencoder
119
.parse_global(&mut globals, global)
120
.unwrap();
121
};
122
}
123
encoder.section(&globals);
124
}
125
126
// Remove exports for the wizer initialization
127
// function and WASI reactor _initialize function,
128
// then perform any requested renames.
129
s if s.id == u8::from(SectionId::Export) => {
130
let mut exports = wasm_encoder::ExportSection::new();
131
for export in module.exports() {
132
if (export.name == self.get_init_func() && !self.get_keep_init_func())
133
|| (has_wasi_initialize && export.name == "_initialize")
134
{
135
continue;
136
}
137
138
if !renames.rename_src_to_dst.contains_key(export.name)
139
&& renames.rename_dsts.contains(export.name)
140
{
141
// A rename overwrites this export, and it is not
142
// renamed to another export, so skip it.
143
continue;
144
}
145
146
let field = renames
147
.rename_src_to_dst
148
.get(export.name)
149
.map_or(export.name, |f| f.as_str());
150
151
let kind = RoundtripReencoder.export_kind(export.kind).unwrap();
152
exports.export(field, kind, export.index);
153
}
154
encoder.section(&exports);
155
}
156
157
// Skip the `start` function -- it's already been run!
158
s if s.id == u8::from(SectionId::Start) => {
159
continue;
160
}
161
162
// Add the data segments that are being added for the snapshot
163
// to the data count section, if present.
164
s if s.id == u8::from(SectionId::DataCount) => {
165
let mut data = wasmparser::BinaryReader::new(s.data, 0);
166
let prev = data.read_var_u32().unwrap();
167
assert!(data.eof());
168
encoder.section(&wasm_encoder::DataCountSection {
169
count: prev + u32::try_from(snapshot.data_segments.len()).unwrap(),
170
});
171
}
172
173
s if s.id == u8::from(SectionId::Data) => {
174
let mut section = wasm_encoder::DataSection::new();
175
let data = wasmparser::BinaryReader::new(s.data, 0);
176
for data in wasmparser::DataSectionReader::new(data).unwrap() {
177
let data = data.unwrap();
178
match data.kind {
179
// Active data segments, by definition in wasm, are
180
// truncated after instantiation. That means that
181
// for the snapshot all active data segments, which
182
// are already applied, are all turned into empty
183
// passive segments instead.
184
wasmparser::DataKind::Active { .. } => {
185
section.passive([]);
186
}
187
188
// Passive segments are plumbed through as-is.
189
wasmparser::DataKind::Passive => {
190
section.passive(data.data.iter().copied());
191
}
192
}
193
}
194
195
// Append all the initializer data segments before adding
196
// the section.
197
add_data_segments(&mut section);
198
encoder.section(&section);
199
added_data_section.set(true);
200
}
201
202
s => {
203
encoder.section(s);
204
}
205
}
206
}
207
208
// Make sure that we've added our data section to the module.
209
add_data_section(&mut encoder);
210
encoder.finish()
211
}
212
}
213
214
fn is_name_section(s: &wasm_encoder::RawSection) -> bool {
215
s.id == u8::from(SectionId::Custom) && {
216
let mut reader = wasmparser::BinaryReader::new(s.data, 0);
217
matches!(reader.read_string(), Ok("name"))
218
}
219
}
220
221