]> git.lizzy.rs Git - rust.git/commitdiff
Encode a custom "producers" section in wasm files
authorAlex Crichton <alex@alexcrichton.com>
Mon, 19 Nov 2018 20:05:21 +0000 (12:05 -0800)
committerAlex Crichton <alex@alexcrichton.com>
Mon, 19 Nov 2018 20:09:19 +0000 (12:09 -0800)
This commit implements WebAssembly/tool-conventions#65 for wasm files
produced by the Rust compiler. This adds a bit of metadata to wasm
modules to indicate that the file's language includes Rust and the
file's "processed-by" tools includes rustc.

The thinking with this section is to eventually have telemetry in
browsers tracking all this.

src/librustc_codegen_llvm/back/link.rs
src/librustc_codegen_llvm/back/wasm.rs

index 20f05d110877a242184bbaa6a358a327ee0b21e9..fa9a852ad013593f0484fbb167f07730e19574ac 100644 (file)
@@ -692,6 +692,11 @@ fn escape_string(s: &[u8]) -> String {
 
     if sess.opts.target_triple.triple() == "wasm32-unknown-unknown" {
         wasm::rewrite_imports(&out_filename, &codegen_results.crate_info.wasm_imports);
+        wasm::add_producer_section(
+            &out_filename,
+            &sess.edition().to_string(),
+            option_env!("CFG_VERSION").unwrap_or("unknown"),
+        );
     }
 }
 
index 7101255173cafd08d5bd88aecb4237b57dbc8530..1a5c65f3c43974e42ddfae1ff49502c367ea2a11 100644 (file)
@@ -17,6 +17,7 @@
 
 // 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;
@@ -121,6 +122,112 @@ fn rewrite_import_entry(wasm: &mut WasmDecoder,
     }
 }
 
+/// 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<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(&section.data);
+
+    fs::write(path, &ret.data).expect("failed to write wasm output");
+}
+
 struct WasmSections<'a>(WasmDecoder<'a>);
 
 impl<'a> Iterator for WasmSections<'a> {