Skip to content

Commit 089a504

Browse files
committed
Encode a custom "producers" section in wasm files
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.
1 parent 9e8a982 commit 089a504

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed

src/librustc_codegen_llvm/back/link.rs

+5
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,11 @@ fn link_natively(sess: &Session,
692692

693693
if sess.opts.target_triple.triple() == "wasm32-unknown-unknown" {
694694
wasm::rewrite_imports(&out_filename, &codegen_results.crate_info.wasm_imports);
695+
wasm::add_producer_section(
696+
&out_filename,
697+
&sess.edition().to_string(),
698+
option_env!("CFG_VERSION").unwrap_or("unknown"),
699+
);
695700
}
696701
}
697702

src/librustc_codegen_llvm/back/wasm.rs

+107
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use serialize::leb128;
1717

1818
// https://webassembly.github.io/spec/core/binary/modules.html#binary-importsec
1919
const WASM_IMPORT_SECTION_ID: u8 = 2;
20+
const WASM_CUSTOM_SECTION_ID: u8 = 0;
2021

2122
const WASM_EXTERNAL_KIND_FUNCTION: u8 = 0;
2223
const WASM_EXTERNAL_KIND_TABLE: u8 = 1;
@@ -121,6 +122,112 @@ pub fn rewrite_imports(path: &Path, import_map: &FxHashMap<String, String>) {
121122
}
122123
}
123124

125+
/// Add or augment the existing `producers` section to encode information about
126+
/// the Rust compiler used to produce the wasm file.
127+
pub fn add_producer_section(
128+
path: &Path,
129+
rust_version: &str,
130+
rustc_version: &str,
131+
) {
132+
struct Field<'a> {
133+
name: &'a str,
134+
values: Vec<FieldValue<'a>>,
135+
}
136+
137+
#[derive(Copy, Clone)]
138+
struct FieldValue<'a> {
139+
name: &'a str,
140+
version: &'a str,
141+
}
142+
143+
let wasm = fs::read(path).expect("failed to read wasm output");
144+
let mut ret = WasmEncoder::new();
145+
ret.data.extend(&wasm[..8]);
146+
147+
// skip the 8 byte wasm/version header
148+
let rustc_value = FieldValue {
149+
name: "rustc",
150+
version: rustc_version,
151+
};
152+
let rust_value = FieldValue {
153+
name: "Rust",
154+
version: rust_version,
155+
};
156+
let mut fields = Vec::new();
157+
let mut wrote_rustc = false;
158+
let mut wrote_rust = false;
159+
160+
// Move all sections from the original wasm file to our output, skipping
161+
// everything except the producers section
162+
for (id, raw) in WasmSections(WasmDecoder::new(&wasm[8..])) {
163+
if id != WASM_CUSTOM_SECTION_ID {
164+
ret.byte(id);
165+
ret.bytes(raw);
166+
continue
167+
}
168+
let mut decoder = WasmDecoder::new(raw);
169+
if decoder.str() != "producers" {
170+
ret.byte(id);
171+
ret.bytes(raw);
172+
continue
173+
}
174+
175+
// Read off the producers section into our fields outside the loop,
176+
// we'll re-encode the producers section when we're done (to handle an
177+
// entirely missing producers section as well).
178+
info!("rewriting existing producers section");
179+
180+
for _ in 0..decoder.u32() {
181+
let name = decoder.str();
182+
let mut values = Vec::new();
183+
for _ in 0..decoder.u32() {
184+
let name = decoder.str();
185+
let version = decoder.str();
186+
values.push(FieldValue { name, version });
187+
}
188+
189+
if name == "language" {
190+
values.push(rust_value);
191+
wrote_rust = true;
192+
} else if name == "processed-by" {
193+
values.push(rustc_value);
194+
wrote_rustc = true;
195+
}
196+
fields.push(Field { name, values });
197+
}
198+
}
199+
200+
if !wrote_rust {
201+
fields.push(Field {
202+
name: "language",
203+
values: vec![rust_value],
204+
});
205+
}
206+
if !wrote_rustc {
207+
fields.push(Field {
208+
name: "processed-by",
209+
values: vec![rustc_value],
210+
});
211+
}
212+
213+
// Append the producers section to the end of the wasm file.
214+
let mut section = WasmEncoder::new();
215+
section.str("producers");
216+
section.u32(fields.len() as u32);
217+
for field in fields {
218+
section.str(field.name);
219+
section.u32(field.values.len() as u32);
220+
for value in field.values {
221+
section.str(value.name);
222+
section.str(value.version);
223+
}
224+
}
225+
ret.byte(WASM_CUSTOM_SECTION_ID);
226+
ret.bytes(&section.data);
227+
228+
fs::write(path, &ret.data).expect("failed to write wasm output");
229+
}
230+
124231
struct WasmSections<'a>(WasmDecoder<'a>);
125232

126233
impl<'a> Iterator for WasmSections<'a> {

0 commit comments

Comments
 (0)