use std::fs; use std::path::Path; use std::str; use rustc_data_structures::fx::FxHashMap; use serialize::leb128; // https://webassembly.github.io/spec/core/binary/modules.html#binary-importsec const WASM_IMPORT_SECTION_ID: u8 = 2; const WASM_CUSTOM_SECTION_ID: u8 = 0; const WASM_EXTERNAL_KIND_FUNCTION: u8 = 0; const WASM_EXTERNAL_KIND_TABLE: u8 = 1; const WASM_EXTERNAL_KIND_MEMORY: u8 = 2; const WASM_EXTERNAL_KIND_GLOBAL: u8 = 3; /// Rewrite the module imports are listed from in a wasm module given the field /// name to module name mapping in `import_map`. /// /// LLVM 6 which we're using right now doesn't have the ability to configure the /// module a wasm symbol is import from. Rather all imported symbols come from /// the bland `"env"` module unconditionally. Furthermore we'd *also* need /// support in LLD for preserving these import modules, which it unfortunately /// currently does not. /// /// This function is intended as a hack for now where we manually rewrite the /// wasm output by LLVM to have the correct import modules listed. The /// `#[link(wasm_import_module = "...")]` attribute in Rust translates to the /// module that each symbol is imported from, so here we manually go through the /// wasm file, decode it, rewrite imports, and then rewrite the wasm module. /// /// Support for this was added to LLVM in /// https://github.com/llvm-mirror/llvm/commit/0f32e1365, although support still /// needs to be added, tracked at https://bugs.llvm.org/show_bug.cgi?id=37168 pub fn rewrite_imports(path: &Path, import_map: &FxHashMap) { if import_map.is_empty() { return } let wasm = fs::read(path).expect("failed to read wasm output"); let mut ret = WasmEncoder::new(); ret.data.extend(&wasm[..8]); // skip the 8 byte wasm/version header for (id, raw) in WasmSections(WasmDecoder::new(&wasm[8..])) { ret.byte(id); if id == WASM_IMPORT_SECTION_ID { info!("rewriting import section"); let data = rewrite_import_section( &mut WasmDecoder::new(raw), import_map, ); ret.bytes(&data); } else { info!("carry forward section {}, {} bytes long", id, raw.len()); ret.bytes(raw); } } fs::write(path, &ret.data).expect("failed to write wasm output"); fn rewrite_import_section( wasm: &mut WasmDecoder, import_map: &FxHashMap, ) -> Vec { let mut dst = WasmEncoder::new(); let n = wasm.u32(); dst.u32(n); info!("rewriting {} imports", n); for _ in 0..n { rewrite_import_entry(wasm, &mut dst, import_map); } return dst.data } fn rewrite_import_entry(wasm: &mut WasmDecoder, dst: &mut WasmEncoder, import_map: &FxHashMap) { // More info about the binary format here is available at: // https://webassembly.github.io/spec/core/binary/modules.html#import-section // // Note that you can also find the whole point of existence of this // function here, where we map the `module` name to a different one if // we've got one listed. let module = wasm.str(); let field = wasm.str(); let new_module = if module == "env" { import_map.get(field).map(|s| &**s).unwrap_or(module) } else { module }; info!("import rewrite ({} => {}) / {}", module, new_module, field); dst.str(new_module); dst.str(field); let kind = wasm.byte(); dst.byte(kind); match kind { WASM_EXTERNAL_KIND_FUNCTION => dst.u32(wasm.u32()), WASM_EXTERNAL_KIND_TABLE => { dst.byte(wasm.byte()); // element_type dst.limits(wasm.limits()); } WASM_EXTERNAL_KIND_MEMORY => dst.limits(wasm.limits()), WASM_EXTERNAL_KIND_GLOBAL => { dst.byte(wasm.byte()); // content_type dst.bool(wasm.bool()); // mutable } b => panic!("unknown kind: {}", b), } } } /// Add or augment the existing `producers` section to encode information about /// the Rust compiler used to produce the wasm file. pub fn add_producer_section( path: &Path, rust_version: &str, rustc_version: &str, ) { struct Field<'a> { name: &'a str, values: Vec>, } #[derive(Copy, Clone)] struct FieldValue<'a> { name: &'a str, version: &'a str, } let wasm = fs::read(path).expect("failed to read wasm output"); let mut ret = WasmEncoder::new(); ret.data.extend(&wasm[..8]); // skip the 8 byte wasm/version header let rustc_value = FieldValue { name: "rustc", version: rustc_version, }; let rust_value = FieldValue { name: "Rust", version: rust_version, }; let mut fields = Vec::new(); let mut wrote_rustc = false; let mut wrote_rust = false; // Move all sections from the original wasm file to our output, skipping // everything except the producers section for (id, raw) in WasmSections(WasmDecoder::new(&wasm[8..])) { if id != WASM_CUSTOM_SECTION_ID { ret.byte(id); ret.bytes(raw); continue } let mut decoder = WasmDecoder::new(raw); if decoder.str() != "producers" { ret.byte(id); ret.bytes(raw); continue } // Read off the producers section into our fields outside the loop, // we'll re-encode the producers section when we're done (to handle an // entirely missing producers section as well). info!("rewriting existing producers section"); for _ in 0..decoder.u32() { let name = decoder.str(); let mut values = Vec::new(); for _ in 0..decoder.u32() { let name = decoder.str(); let version = decoder.str(); values.push(FieldValue { name, version }); } if name == "language" { values.push(rust_value); wrote_rust = true; } else if name == "processed-by" { values.push(rustc_value); wrote_rustc = true; } fields.push(Field { name, values }); } } if !wrote_rust { fields.push(Field { name: "language", values: vec![rust_value], }); } if !wrote_rustc { fields.push(Field { name: "processed-by", values: vec![rustc_value], }); } // Append the producers section to the end of the wasm file. let mut section = WasmEncoder::new(); section.str("producers"); section.u32(fields.len() as u32); for field in fields { section.str(field.name); section.u32(field.values.len() as u32); for value in field.values { section.str(value.name); section.str(value.version); } } ret.byte(WASM_CUSTOM_SECTION_ID); ret.bytes(§ion.data); fs::write(path, &ret.data).expect("failed to write wasm output"); } struct WasmSections<'a>(WasmDecoder<'a>); impl<'a> Iterator for WasmSections<'a> { type Item = (u8, &'a [u8]); fn next(&mut self) -> Option<(u8, &'a [u8])> { if self.0.data.is_empty() { return None } // see https://webassembly.github.io/spec/core/binary/modules.html#sections let id = self.0.byte(); let section_len = self.0.u32(); info!("new section {} / {} bytes", id, section_len); let section = self.0.skip(section_len as usize); Some((id, section)) } } struct WasmDecoder<'a> { data: &'a [u8], } impl<'a> WasmDecoder<'a> { fn new(data: &'a [u8]) -> WasmDecoder<'a> { WasmDecoder { data } } fn byte(&mut self) -> u8 { self.skip(1)[0] } fn u32(&mut self) -> u32 { let (n, l1) = leb128::read_u32_leb128(self.data); self.data = &self.data[l1..]; return n } fn skip(&mut self, amt: usize) -> &'a [u8] { let (data, rest) = self.data.split_at(amt); self.data = rest; data } fn str(&mut self) -> &'a str { let len = self.u32(); str::from_utf8(self.skip(len as usize)).unwrap() } fn bool(&mut self) -> bool { self.byte() == 1 } fn limits(&mut self) -> (u32, Option) { let has_max = self.bool(); (self.u32(), if has_max { Some(self.u32()) } else { None }) } } struct WasmEncoder { data: Vec, } impl WasmEncoder { fn new() -> WasmEncoder { WasmEncoder { data: Vec::new() } } fn u32(&mut self, val: u32) { leb128::write_u32_leb128(&mut self.data, val); } fn byte(&mut self, val: u8) { self.data.push(val); } fn bytes(&mut self, val: &[u8]) { self.u32(val.len() as u32); self.data.extend_from_slice(val); } fn str(&mut self, val: &str) { self.bytes(val.as_bytes()) } fn bool(&mut self, b: bool) { self.byte(b as u8); } fn limits(&mut self, limits: (u32, Option)) { self.bool(limits.1.is_some()); self.u32(limits.0); if let Some(c) = limits.1 { self.u32(c); } } }