| use std::fs; |
| use std::path::Path; |
| use std::str; |
| |
| use serialize::leb128; |
| |
| // https://webassembly.github.io/spec/core/binary/modules.html#binary-importsec |
| const WASM_CUSTOM_SECTION_ID: u8 = 0; |
| |
| /// Adds 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<FieldValue<'a>>, |
| } |
| |
| #[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() |
| } |
| } |
| |
| struct WasmEncoder { |
| data: Vec<u8>, |
| } |
| |
| 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()) |
| } |
| } |