Skip to content

Commit 7df6f41

Browse files
committed
rustc: Add a #[wasm_custom_section] attribute
This commit is an implementation of adding custom sections to wasm artifacts in rustc. The intention here is to expose the ability of the wasm binary format to contain custom sections with arbitrary user-defined data. Currently neither our version of LLVM nor LLD supports this so the implementation is currently custom to rustc itself. The implementation here is to attach a `#[wasm_custom_section = "foo"]` attribute to any `const` which has a type like `[u8; N]`. Other types of constants aren't supported yet but may be added one day! This should hopefully be enough to get off the ground with *some* custom section support. The current semantics are that any constant tagged with `#[wasm_custom_section]` section will be *appended* to the corresponding section in the final output wasm artifact (and this affects dependencies linked in as well, not just the final crate). This means that whatever is interpreting the contents must be able to interpret binary-concatenated sections (or each constant needs to be in its own custom section). To test this change the existing `run-make` test suite was moved to a `run-make-fulldeps` folder and a new `run-make` test suite was added which applies to all targets by default. This test suite currently only has one test which only runs for the wasm target (using a node.js script to use `WebAssembly` in JS to parse the wasm output).
1 parent 5092c6b commit 7df6f41

File tree

575 files changed

+522
-17
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

575 files changed

+522
-17
lines changed

src/bootstrap/builder.rs

+1
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ impl<'a> Builder<'a> {
313313
test::RunPassFullDepsPretty, test::RunFailFullDepsPretty,
314314
test::Crate, test::CrateLibrustc, test::CrateRustdoc, test::Linkcheck,
315315
test::Cargotest, test::Cargo, test::Rls, test::ErrorIndex, test::Distcheck,
316+
test::RunMakeFullDeps,
316317
test::Nomicon, test::Reference, test::RustdocBook, test::RustByExample,
317318
test::TheBook, test::UnstableBook,
318319
test::Rustfmt, test::Miri, test::Clippy, test::RustdocJS, test::RustdocTheme,

src/bootstrap/compile.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -915,7 +915,7 @@ impl Step for Assemble {
915915
}
916916
}
917917

918-
let lld_install = if build.config.lld_enabled && target_compiler.stage > 0 {
918+
let lld_install = if build.config.lld_enabled {
919919
Some(builder.ensure(native::Lld {
920920
target: target_compiler.host,
921921
}))

src/bootstrap/test.rs

+12-7
Original file line numberDiff line numberDiff line change
@@ -759,12 +759,18 @@ test!(RunFailFullDepsPretty {
759759
host: true
760760
});
761761

762-
host_test!(RunMake {
762+
default_test!(RunMake {
763763
path: "src/test/run-make",
764764
mode: "run-make",
765765
suite: "run-make"
766766
});
767767

768+
host_test!(RunMakeFullDeps {
769+
path: "src/test/run-make-fulldeps",
770+
mode: "run-make",
771+
suite: "run-make-fulldeps"
772+
});
773+
768774
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
769775
struct Compiletest {
770776
compiler: Compiler,
@@ -827,8 +833,7 @@ impl Step for Compiletest {
827833
// FIXME: Does pretty need librustc compiled? Note that there are
828834
// fulldeps test suites with mode = pretty as well.
829835
mode == "pretty" ||
830-
mode == "rustdoc" ||
831-
mode == "run-make" {
836+
mode == "rustdoc" {
832837
builder.ensure(compile::Rustc { compiler, target });
833838
}
834839

@@ -849,7 +854,7 @@ impl Step for Compiletest {
849854
cmd.arg("--rustc-path").arg(builder.rustc(compiler));
850855

851856
// Avoid depending on rustdoc when we don't need it.
852-
if mode == "rustdoc" || mode == "run-make" {
857+
if mode == "rustdoc" || (mode == "run-make" && suite.ends_with("fulldeps")) {
853858
cmd.arg("--rustdoc-path").arg(builder.rustdoc(compiler.host));
854859
}
855860

@@ -931,7 +936,7 @@ impl Step for Compiletest {
931936

932937
// Only pass correct values for these flags for the `run-make` suite as it
933938
// requires that a C++ compiler was configured which isn't always the case.
934-
if suite == "run-make" {
939+
if suite == "run-make-fulldeps" {
935940
let llvm_components = output(Command::new(&llvm_config).arg("--components"));
936941
let llvm_cxxflags = output(Command::new(&llvm_config).arg("--cxxflags"));
937942
cmd.arg("--cc").arg(build.cc(target))
@@ -944,12 +949,12 @@ impl Step for Compiletest {
944949
}
945950
}
946951
}
947-
if suite == "run-make" && !build.config.llvm_enabled {
952+
if suite == "run-make-fulldeps" && !build.config.llvm_enabled {
948953
println!("Ignoring run-make test suite as they generally don't work without LLVM");
949954
return;
950955
}
951956

952-
if suite != "run-make" {
957+
if suite != "run-make-fulldeps" {
953958
cmd.arg("--cc").arg("")
954959
.arg("--cxx").arg("")
955960
.arg("--cflags").arg("")

src/librustc/dep_graph/dep_node.rs

+2
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,8 @@ define_dep_nodes!( <'tcx>
650650

651651
[] GetSymbolExportLevel(DefId),
652652

653+
[] WasmCustomSections(CrateNum),
654+
653655
[input] Features,
654656

655657
[] ProgramClausesFor(DefId),

src/librustc/hir/check_attr.rs

+13
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ enum Target {
2525
Struct,
2626
Union,
2727
Enum,
28+
Const,
2829
Other,
2930
}
3031

@@ -35,6 +36,7 @@ impl Target {
3536
hir::ItemStruct(..) => Target::Struct,
3637
hir::ItemUnion(..) => Target::Union,
3738
hir::ItemEnum(..) => Target::Enum,
39+
hir::ItemConst(..) => Target::Const,
3840
_ => Target::Other,
3941
}
4042
}
@@ -60,6 +62,17 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
6062
if name == "inline" {
6163
self.check_inline(attr, item, target)
6264
}
65+
66+
if name == "wasm_custom_section" {
67+
if target != Target::Const {
68+
self.tcx.sess.span_err(attr.span, "only allowed on consts");
69+
}
70+
71+
if attr.value_str().is_none() {
72+
self.tcx.sess.span_err(attr.span, "must be of the form \
73+
#[wasm_custom_section = \"foo\"]");
74+
}
75+
}
6376
}
6477
}
6578

src/librustc/middle/dead.rs

+5
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,11 @@ fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt,
318318
return true;
319319
}
320320

321+
// These constants are special for wasm
322+
if attr::contains_name(attrs, "wasm_custom_section") {
323+
return true;
324+
}
325+
321326
tcx.lint_level_at_node(lint::builtin::DEAD_CODE, id).0 == lint::Allow
322327
}
323328

src/librustc/ty/maps/config.rs

+6
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::instance_def_size_estimate<'tcx>
678678
}
679679
}
680680

681+
impl<'tcx> QueryDescription<'tcx> for queries::wasm_custom_sections<'tcx> {
682+
fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
683+
format!("custom wasm sections for a crate")
684+
}
685+
}
686+
681687
impl<'tcx> QueryDescription<'tcx> for queries::generics_of<'tcx> {
682688
#[inline]
683689
fn cache_on_disk(def_id: Self::Key) -> bool {

src/librustc/ty/maps/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,8 @@ define_maps! { <'tcx>
424424
[] fn features_query: features_node(CrateNum) -> Lrc<feature_gate::Features>,
425425

426426
[] fn program_clauses_for: ProgramClausesFor(DefId) -> Lrc<Vec<Clause<'tcx>>>,
427+
428+
[] fn wasm_custom_sections: WasmCustomSections(CrateNum) -> Lrc<Vec<DefId>>,
427429
}
428430

429431
//////////////////////////////////////////////////////////////////////

src/librustc/ty/maps/plumbing.rs

+1
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
940940
DepKind::Features => { force!(features_query, LOCAL_CRATE); }
941941

942942
DepKind::ProgramClausesFor => { force!(program_clauses_for, def_id!()); }
943+
DepKind::WasmCustomSections => { force!(wasm_custom_sections, krate!()); }
943944
}
944945

945946
true

src/librustc_metadata/cstore_impl.rs

+2
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,8 @@ provide! { <'tcx> tcx, def_id, other, cdata,
271271

272272
Arc::new(cdata.exported_symbols())
273273
}
274+
275+
wasm_custom_sections => { Lrc::new(cdata.wasm_custom_sections()) }
274276
}
275277

276278
pub fn provide<'tcx>(providers: &mut Providers<'tcx>) {

src/librustc_metadata/decoder.rs

+10
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,16 @@ impl<'a, 'tcx> CrateMetadata {
10671067
.collect()
10681068
}
10691069

1070+
pub fn wasm_custom_sections(&self) -> Vec<DefId> {
1071+
let sections = self.root
1072+
.wasm_custom_sections
1073+
.decode(self)
1074+
.map(|def_index| self.local_def_id(def_index))
1075+
.collect::<Vec<_>>();
1076+
info!("loaded wasm sections {:?}", sections);
1077+
return sections
1078+
}
1079+
10701080
pub fn get_macro(&self, id: DefIndex) -> (InternedString, MacroDef) {
10711081
let entry = self.entry(id);
10721082
match entry.kind {

src/librustc_metadata/encoder.rs

+12
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
435435
&exported_symbols);
436436
let exported_symbols_bytes = self.position() - i;
437437

438+
// encode wasm custom sections
439+
let wasm_custom_sections = self.tcx.wasm_custom_sections(LOCAL_CRATE);
440+
let wasm_custom_sections = self.tracked(
441+
IsolatedEncoder::encode_wasm_custom_sections,
442+
&wasm_custom_sections);
443+
438444
// Encode and index the items.
439445
i = self.position();
440446
let items = self.encode_info_for_items();
@@ -478,6 +484,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
478484
def_path_table,
479485
impls,
480486
exported_symbols,
487+
wasm_custom_sections,
481488
index,
482489
});
483490

@@ -1444,6 +1451,11 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
14441451
.cloned())
14451452
}
14461453

1454+
fn encode_wasm_custom_sections(&mut self, statics: &[DefId]) -> LazySeq<DefIndex> {
1455+
info!("encoding custom wasm section constants {:?}", statics);
1456+
self.lazy_seq(statics.iter().map(|id| id.index))
1457+
}
1458+
14471459
fn encode_dylib_dependency_formats(&mut self, _: ()) -> LazySeq<Option<LinkagePreference>> {
14481460
match self.tcx.sess.dependency_formats.borrow().get(&config::CrateTypeDylib) {
14491461
Some(arr) => {

src/librustc_metadata/schema.rs

+1
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ pub struct CrateRoot {
204204
pub def_path_table: Lazy<hir::map::definitions::DefPathTable>,
205205
pub impls: LazySeq<TraitImpls>,
206206
pub exported_symbols: LazySeq<(ExportedSymbol, SymbolExportLevel)>,
207+
pub wasm_custom_sections: LazySeq<DefIndex>,
207208

208209
pub index: LazySeq<index::Index>,
209210
}

src/librustc_trans/attributes.rs

+31-1
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
1212
use std::ffi::{CStr, CString};
1313

14-
use rustc::hir::TransFnAttrFlags;
14+
use rustc::hir::{self, TransFnAttrFlags};
1515
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
16+
use rustc::hir::itemlikevisit::ItemLikeVisitor;
1617
use rustc::session::config::Sanitizer;
18+
use rustc::ty::TyCtxt;
1719
use rustc::ty::maps::Providers;
1820
use rustc_data_structures::sync::Lrc;
1921

@@ -161,4 +163,32 @@ pub fn provide(providers: &mut Providers) {
161163
.collect())
162164
}
163165
};
166+
167+
providers.wasm_custom_sections = |tcx, cnum| {
168+
assert_eq!(cnum, LOCAL_CRATE);
169+
let mut finder = WasmSectionFinder { tcx, list: Vec::new() };
170+
tcx.hir.krate().visit_all_item_likes(&mut finder);
171+
Lrc::new(finder.list)
172+
};
173+
}
174+
175+
struct WasmSectionFinder<'a, 'tcx: 'a> {
176+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
177+
list: Vec<DefId>,
178+
}
179+
180+
impl<'a, 'tcx: 'a> ItemLikeVisitor<'tcx> for WasmSectionFinder<'a, 'tcx> {
181+
fn visit_item(&mut self, i: &'tcx hir::Item) {
182+
match i.node {
183+
hir::ItemConst(..) => {}
184+
_ => return,
185+
}
186+
if i.attrs.iter().any(|i| i.check_name("wasm_custom_section")) {
187+
self.list.push(self.tcx.hir.local_def_id(i.id));
188+
}
189+
}
190+
191+
fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) {}
192+
193+
fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) {}
164194
}

src/librustc_trans/back/link.rs

+6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use back::wasm;
1112
use cc::windows_registry;
1213
use super::archive::{ArchiveBuilder, ArchiveConfig};
1314
use super::bytecode::RLIB_BYTECODE_EXTENSION;
@@ -810,6 +811,11 @@ fn link_natively(sess: &Session,
810811
Err(e) => sess.fatal(&format!("failed to run dsymutil: {}", e)),
811812
}
812813
}
814+
815+
if sess.opts.target_triple == "wasm32-unknown-unknown" {
816+
wasm::add_custom_sections(&out_filename,
817+
&trans.crate_info.wasm_custom_sections);
818+
}
813819
}
814820

815821
fn exec_linker(sess: &Session, cmd: &mut Command, tmpdir: &Path)

src/librustc_trans/back/wasm.rs

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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::collections::BTreeMap;
14+
15+
use serialize::leb128;
16+
17+
pub fn add_custom_sections(path: &Path, sections: &BTreeMap<String, Vec<u8>>) {
18+
let mut wasm = fs::read(path).expect("failed to read wasm output");
19+
20+
// see https://webassembly.github.io/spec/core/binary/modules.html#custom-section
21+
for (section, bytes) in sections {
22+
// write the `id` identifier, 0 for a custom section
23+
let len = wasm.len();
24+
leb128::write_u32_leb128(&mut wasm, len, 0);
25+
26+
// figure out how long our name descriptor will be
27+
let mut name = Vec::new();
28+
leb128::write_u32_leb128(&mut name, 0, section.len() as u32);
29+
name.extend_from_slice(section.as_bytes());
30+
31+
// write the length of the payload
32+
let len = wasm.len();
33+
let total_len = bytes.len() + name.len();
34+
leb128::write_u32_leb128(&mut wasm, len, total_len as u32);
35+
36+
// write out the name section
37+
wasm.extend(name);
38+
39+
// and now the payload itself
40+
wasm.extend_from_slice(bytes);
41+
}
42+
43+
fs::write(path, &wasm).expect("failed to write wasm output");
44+
}

0 commit comments

Comments
 (0)