5 use rustc_data_structures::fx::FxHashMap;
8 // https://webassembly.github.io/spec/core/binary/modules.html#binary-importsec
9 const WASM_IMPORT_SECTION_ID: u8 = 2;
10 const WASM_CUSTOM_SECTION_ID: u8 = 0;
12 const WASM_EXTERNAL_KIND_FUNCTION: u8 = 0;
13 const WASM_EXTERNAL_KIND_TABLE: u8 = 1;
14 const WASM_EXTERNAL_KIND_MEMORY: u8 = 2;
15 const WASM_EXTERNAL_KIND_GLOBAL: u8 = 3;
17 /// Rewrite the module imports are listed from in a wasm module given the field
18 /// name to module name mapping in `import_map`.
20 /// LLVM 6 which we're using right now doesn't have the ability to configure the
21 /// module a wasm symbol is import from. Rather all imported symbols come from
22 /// the bland `"env"` module unconditionally. Furthermore we'd *also* need
23 /// support in LLD for preserving these import modules, which it unfortunately
24 /// currently does not.
26 /// This function is intended as a hack for now where we manually rewrite the
27 /// wasm output by LLVM to have the correct import modules listed. The
28 /// `#[link(wasm_import_module = "...")]` attribute in Rust translates to the
29 /// module that each symbol is imported from, so here we manually go through the
30 /// wasm file, decode it, rewrite imports, and then rewrite the wasm module.
32 /// Support for this was added to LLVM in
33 /// https://github.com/llvm-mirror/llvm/commit/0f32e1365, although support still
34 /// needs to be added, tracked at https://bugs.llvm.org/show_bug.cgi?id=37168
35 pub fn rewrite_imports(path: &Path, import_map: &FxHashMap<String, String>) {
36 if import_map.is_empty() {
40 let wasm = fs::read(path).expect("failed to read wasm output");
41 let mut ret = WasmEncoder::new();
42 ret.data.extend(&wasm[..8]);
44 // skip the 8 byte wasm/version header
45 for (id, raw) in WasmSections(WasmDecoder::new(&wasm[8..])) {
47 if id == WASM_IMPORT_SECTION_ID {
48 info!("rewriting import section");
49 let data = rewrite_import_section(
50 &mut WasmDecoder::new(raw),
55 info!("carry forward section {}, {} bytes long", id, raw.len());
60 fs::write(path, &ret.data).expect("failed to write wasm output");
62 fn rewrite_import_section(
63 wasm: &mut WasmDecoder<'_>,
64 import_map: &FxHashMap<String, String>,
68 let mut dst = WasmEncoder::new();
71 info!("rewriting {} imports", n);
73 rewrite_import_entry(wasm, &mut dst, import_map);
78 fn rewrite_import_entry(wasm: &mut WasmDecoder<'_>,
79 dst: &mut WasmEncoder,
80 import_map: &FxHashMap<String, String>) {
81 // More info about the binary format here is available at:
82 // https://webassembly.github.io/spec/core/binary/modules.html#import-section
84 // Note that you can also find the whole point of existence of this
85 // function here, where we map the `module` name to a different one if
86 // we've got one listed.
87 let module = wasm.str();
88 let field = wasm.str();
89 let new_module = if module == "env" {
90 import_map.get(field).map(|s| &**s).unwrap_or(module)
94 info!("import rewrite ({} => {}) / {}", module, new_module, field);
97 let kind = wasm.byte();
100 WASM_EXTERNAL_KIND_FUNCTION => dst.u32(wasm.u32()),
101 WASM_EXTERNAL_KIND_TABLE => {
102 dst.byte(wasm.byte()); // element_type
103 dst.limits(wasm.limits());
105 WASM_EXTERNAL_KIND_MEMORY => dst.limits(wasm.limits()),
106 WASM_EXTERNAL_KIND_GLOBAL => {
107 dst.byte(wasm.byte()); // content_type
108 dst.bool(wasm.bool()); // mutable
110 b => panic!("unknown kind: {}", b),
115 /// Adds or augment the existing `producers` section to encode information about
116 /// the Rust compiler used to produce the wasm file.
117 pub fn add_producer_section(
124 values: Vec<FieldValue<'a>>,
127 #[derive(Copy, Clone)]
128 struct FieldValue<'a> {
133 let wasm = fs::read(path).expect("failed to read wasm output");
134 let mut ret = WasmEncoder::new();
135 ret.data.extend(&wasm[..8]);
137 // skip the 8 byte wasm/version header
138 let rustc_value = FieldValue {
140 version: rustc_version,
142 let rust_value = FieldValue {
144 version: rust_version,
146 let mut fields = Vec::new();
147 let mut wrote_rustc = false;
148 let mut wrote_rust = false;
150 // Move all sections from the original wasm file to our output, skipping
151 // everything except the producers section
152 for (id, raw) in WasmSections(WasmDecoder::new(&wasm[8..])) {
153 if id != WASM_CUSTOM_SECTION_ID {
158 let mut decoder = WasmDecoder::new(raw);
159 if decoder.str() != "producers" {
165 // Read off the producers section into our fields outside the loop,
166 // we'll re-encode the producers section when we're done (to handle an
167 // entirely missing producers section as well).
168 info!("rewriting existing producers section");
170 for _ in 0..decoder.u32() {
171 let name = decoder.str();
172 let mut values = Vec::new();
173 for _ in 0..decoder.u32() {
174 let name = decoder.str();
175 let version = decoder.str();
176 values.push(FieldValue { name, version });
179 if name == "language" {
180 values.push(rust_value);
182 } else if name == "processed-by" {
183 values.push(rustc_value);
186 fields.push(Field { name, values });
193 values: vec![rust_value],
198 name: "processed-by",
199 values: vec![rustc_value],
203 // Append the producers section to the end of the wasm file.
204 let mut section = WasmEncoder::new();
205 section.str("producers");
206 section.u32(fields.len() as u32);
207 for field in fields {
208 section.str(field.name);
209 section.u32(field.values.len() as u32);
210 for value in field.values {
211 section.str(value.name);
212 section.str(value.version);
215 ret.byte(WASM_CUSTOM_SECTION_ID);
216 ret.bytes(§ion.data);
218 fs::write(path, &ret.data).expect("failed to write wasm output");
221 struct WasmSections<'a>(WasmDecoder<'a>);
223 impl<'a> Iterator for WasmSections<'a> {
224 type Item = (u8, &'a [u8]);
226 fn next(&mut self) -> Option<(u8, &'a [u8])> {
227 if self.0.data.is_empty() {
231 // see https://webassembly.github.io/spec/core/binary/modules.html#sections
232 let id = self.0.byte();
233 let section_len = self.0.u32();
234 info!("new section {} / {} bytes", id, section_len);
235 let section = self.0.skip(section_len as usize);
240 struct WasmDecoder<'a> {
244 impl<'a> WasmDecoder<'a> {
245 fn new(data: &'a [u8]) -> WasmDecoder<'a> {
249 fn byte(&mut self) -> u8 {
253 fn u32(&mut self) -> u32 {
254 let (n, l1) = leb128::read_u32_leb128(self.data);
255 self.data = &self.data[l1..];
259 fn skip(&mut self, amt: usize) -> &'a [u8] {
260 let (data, rest) = self.data.split_at(amt);
265 fn str(&mut self) -> &'a str {
266 let len = self.u32();
267 str::from_utf8(self.skip(len as usize)).unwrap()
270 fn bool(&mut self) -> bool {
274 fn limits(&mut self) -> (u32, Option<u32>) {
275 let has_max = self.bool();
276 (self.u32(), if has_max { Some(self.u32()) } else { None })
285 fn new() -> WasmEncoder {
286 WasmEncoder { data: Vec::new() }
289 fn u32(&mut self, val: u32) {
290 leb128::write_u32_leb128(&mut self.data, val);
293 fn byte(&mut self, val: u8) {
297 fn bytes(&mut self, val: &[u8]) {
298 self.u32(val.len() as u32);
299 self.data.extend_from_slice(val);
302 fn str(&mut self, val: &str) {
303 self.bytes(val.as_bytes())
306 fn bool(&mut self, b: bool) {
310 fn limits(&mut self, limits: (u32, Option<u32>)) {
311 self.bool(limits.1.is_some());
313 if let Some(c) = limits.1 {