]> git.lizzy.rs Git - rust.git/blob - src/librustc_codegen_llvm/back/wasm.rs
7101255173cafd08d5bd88aecb4237b57dbc8530
[rust.git] / src / librustc_codegen_llvm / back / wasm.rs
1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use std::fs;
12 use std::path::Path;
13 use std::str;
14
15 use rustc_data_structures::fx::FxHashMap;
16 use serialize::leb128;
17
18 // https://webassembly.github.io/spec/core/binary/modules.html#binary-importsec
19 const WASM_IMPORT_SECTION_ID: u8 = 2;
20
21 const WASM_EXTERNAL_KIND_FUNCTION: u8 = 0;
22 const WASM_EXTERNAL_KIND_TABLE: u8 = 1;
23 const WASM_EXTERNAL_KIND_MEMORY: u8 = 2;
24 const WASM_EXTERNAL_KIND_GLOBAL: u8 = 3;
25
26 /// Rewrite the module imports are listed from in a wasm module given the field
27 /// name to module name mapping in `import_map`.
28 ///
29 /// LLVM 6 which we're using right now doesn't have the ability to configure the
30 /// module a wasm symbol is import from. Rather all imported symbols come from
31 /// the bland `"env"` module unconditionally. Furthermore we'd *also* need
32 /// support in LLD for preserving these import modules, which it unfortunately
33 /// currently does not.
34 ///
35 /// This function is intended as a hack for now where we manually rewrite the
36 /// wasm output by LLVM to have the correct import modules listed. The
37 /// `#[link(wasm_import_module = "...")]` attribute in Rust translates to the
38 /// module that each symbol is imported from, so here we manually go through the
39 /// wasm file, decode it, rewrite imports, and then rewrite the wasm module.
40 ///
41 /// Support for this was added to LLVM in
42 /// https://github.com/llvm-mirror/llvm/commit/0f32e1365, although support still
43 /// needs to be added, tracked at https://bugs.llvm.org/show_bug.cgi?id=37168
44 pub fn rewrite_imports(path: &Path, import_map: &FxHashMap<String, String>) {
45     if import_map.is_empty() {
46         return
47     }
48
49     let wasm = fs::read(path).expect("failed to read wasm output");
50     let mut ret = WasmEncoder::new();
51     ret.data.extend(&wasm[..8]);
52
53     // skip the 8 byte wasm/version header
54     for (id, raw) in WasmSections(WasmDecoder::new(&wasm[8..])) {
55         ret.byte(id);
56         if id == WASM_IMPORT_SECTION_ID {
57             info!("rewriting import section");
58             let data = rewrite_import_section(
59                 &mut WasmDecoder::new(raw),
60                 import_map,
61             );
62             ret.bytes(&data);
63         } else {
64             info!("carry forward section {}, {} bytes long", id, raw.len());
65             ret.bytes(raw);
66         }
67     }
68
69     fs::write(path, &ret.data).expect("failed to write wasm output");
70
71     fn rewrite_import_section(
72         wasm: &mut WasmDecoder,
73         import_map: &FxHashMap<String, String>,
74     )
75         -> Vec<u8>
76     {
77         let mut dst = WasmEncoder::new();
78         let n = wasm.u32();
79         dst.u32(n);
80         info!("rewriting {} imports", n);
81         for _ in 0..n {
82             rewrite_import_entry(wasm, &mut dst, import_map);
83         }
84         return dst.data
85     }
86
87     fn rewrite_import_entry(wasm: &mut WasmDecoder,
88                             dst: &mut WasmEncoder,
89                             import_map: &FxHashMap<String, String>) {
90         // More info about the binary format here is available at:
91         // https://webassembly.github.io/spec/core/binary/modules.html#import-section
92         //
93         // Note that you can also find the whole point of existence of this
94         // function here, where we map the `module` name to a different one if
95         // we've got one listed.
96         let module = wasm.str();
97         let field = wasm.str();
98         let new_module = if module == "env" {
99             import_map.get(field).map(|s| &**s).unwrap_or(module)
100         } else {
101             module
102         };
103         info!("import rewrite ({} => {}) / {}", module, new_module, field);
104         dst.str(new_module);
105         dst.str(field);
106         let kind = wasm.byte();
107         dst.byte(kind);
108         match kind {
109             WASM_EXTERNAL_KIND_FUNCTION => dst.u32(wasm.u32()),
110             WASM_EXTERNAL_KIND_TABLE => {
111                 dst.byte(wasm.byte()); // element_type
112                 dst.limits(wasm.limits());
113             }
114             WASM_EXTERNAL_KIND_MEMORY => dst.limits(wasm.limits()),
115             WASM_EXTERNAL_KIND_GLOBAL => {
116                 dst.byte(wasm.byte()); // content_type
117                 dst.bool(wasm.bool()); // mutable
118             }
119             b => panic!("unknown kind: {}", b),
120         }
121     }
122 }
123
124 struct WasmSections<'a>(WasmDecoder<'a>);
125
126 impl<'a> Iterator for WasmSections<'a> {
127     type Item = (u8, &'a [u8]);
128
129     fn next(&mut self) -> Option<(u8, &'a [u8])> {
130         if self.0.data.is_empty() {
131             return None
132         }
133
134         // see https://webassembly.github.io/spec/core/binary/modules.html#sections
135         let id = self.0.byte();
136         let section_len = self.0.u32();
137         info!("new section {} / {} bytes", id, section_len);
138         let section = self.0.skip(section_len as usize);
139         Some((id, section))
140     }
141 }
142
143 struct WasmDecoder<'a> {
144     data: &'a [u8],
145 }
146
147 impl<'a> WasmDecoder<'a> {
148     fn new(data: &'a [u8]) -> WasmDecoder<'a> {
149         WasmDecoder { data }
150     }
151
152     fn byte(&mut self) -> u8 {
153         self.skip(1)[0]
154     }
155
156     fn u32(&mut self) -> u32 {
157         let (n, l1) = leb128::read_u32_leb128(self.data);
158         self.data = &self.data[l1..];
159         return n
160     }
161
162     fn skip(&mut self, amt: usize) -> &'a [u8] {
163         let (data, rest) = self.data.split_at(amt);
164         self.data = rest;
165         data
166     }
167
168     fn str(&mut self) -> &'a str {
169         let len = self.u32();
170         str::from_utf8(self.skip(len as usize)).unwrap()
171     }
172
173     fn bool(&mut self) -> bool {
174         self.byte() == 1
175     }
176
177     fn limits(&mut self) -> (u32, Option<u32>) {
178         let has_max = self.bool();
179         (self.u32(), if has_max { Some(self.u32()) } else { None })
180     }
181 }
182
183 struct WasmEncoder {
184     data: Vec<u8>,
185 }
186
187 impl WasmEncoder {
188     fn new() -> WasmEncoder {
189         WasmEncoder { data: Vec::new() }
190     }
191
192     fn u32(&mut self, val: u32) {
193         leb128::write_u32_leb128(&mut self.data, val);
194     }
195
196     fn byte(&mut self, val: u8) {
197         self.data.push(val);
198     }
199
200     fn bytes(&mut self, val: &[u8]) {
201         self.u32(val.len() as u32);
202         self.data.extend_from_slice(val);
203     }
204
205     fn str(&mut self, val: &str) {
206         self.bytes(val.as_bytes())
207     }
208
209     fn bool(&mut self, b: bool) {
210         self.byte(b as u8);
211     }
212
213     fn limits(&mut self, limits: (u32, Option<u32>)) {
214         self.bool(limits.1.is_some());
215         self.u32(limits.0);
216         if let Some(c) = limits.1 {
217             self.u32(c);
218         }
219     }
220 }