Skip to content

Commit b4da51c

Browse files
authored
Register target features in custom section when using xforms (#3967)
1 parent 9b37613 commit b4da51c

File tree

7 files changed

+94
-4
lines changed

7 files changed

+94
-4
lines changed

Diff for: CHANGELOG.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,12 @@
4545
* Fix MSRV compilation.
4646
[#3927](https://github.com/rustwasm/wasm-bindgen/pull/3927)
4747

48-
* Fixed `clippy::empty_docs` lint.
48+
* Fix `clippy::empty_docs` lint.
4949
[#3946](https://github.com/rustwasm/wasm-bindgen/pull/3946)
5050

51+
* Fix missing target features in module when enabling reference types or multi-value transformation.
52+
[#3967](https://github.com/rustwasm/wasm-bindgen/pull/3967)
53+
5154
--------------------------------------------------------------------------------
5255

5356
## [0.2.92](https://github.com/rustwasm/wasm-bindgen/compare/0.2.91...0.2.92)

Diff for: crates/externref-xform/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ rust-version = "1.57"
1515
[dependencies]
1616
anyhow = "1.0"
1717
walrus = "0.20.2"
18+
wasm-bindgen-wasm-conventions = { path = "../wasm-conventions", version = "=0.2.92" }
1819

1920
[dev-dependencies]
2021
rayon = "1.0"

Diff for: crates/externref-xform/src/lib.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
//! goal at least is to have valid wasm modules coming in that don't use
1616
//! `externref` and valid wasm modules going out which use `externref` at the fringes.
1717
18-
use anyhow::{anyhow, bail, Error};
18+
use anyhow::{anyhow, bail, Context as _, Error};
1919
use std::cmp;
2020
use std::collections::{BTreeMap, HashMap, HashSet};
2121
use std::mem;
@@ -101,6 +101,10 @@ impl Context {
101101
/// large the function table is so we know what indexes to hand out when
102102
/// we're appending entries.
103103
pub fn prepare(&mut self, module: &mut Module) -> Result<(), Error> {
104+
// Insert reference types to the target features section.
105+
wasm_bindgen_wasm_conventions::insert_target_feature(module, "reference-types")
106+
.context("failed to parse `target_features` custom section")?;
107+
104108
// Figure out what the maximum index of functions pointers are. We'll
105109
// be adding new entries to the function table later (maybe) so
106110
// precalculate this ahead of time.

Diff for: crates/multi-value-xform/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ rust-version = "1.57"
1515
[dependencies]
1616
anyhow = "1.0"
1717
walrus = "0.20.2"
18+
wasm-bindgen-wasm-conventions = { path = "../wasm-conventions", version = "=0.2.92" }
1819

1920
[dev-dependencies]
2021
rayon = "1.0"

Diff for: crates/multi-value-xform/src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@
9292
9393
#![deny(missing_docs, missing_debug_implementations)]
9494

95+
use anyhow::Context;
96+
9597
/// Run the transformation.
9698
///
9799
/// See the module-level docs for details on the transformation.
@@ -117,6 +119,10 @@ pub fn run(
117119
shadow_stack_pointer: walrus::GlobalId,
118120
to_xform: &[(walrus::FunctionId, usize, Vec<walrus::ValType>)],
119121
) -> Result<Vec<walrus::FunctionId>, anyhow::Error> {
122+
// Insert multi-value to the target features section.
123+
wasm_bindgen_wasm_conventions::insert_target_feature(module, "multivalue")
124+
.context("failed to parse `target_features` custom section")?;
125+
120126
let mut wrappers = Vec::new();
121127
for (func, return_pointer_index, results) in to_xform {
122128
wrappers.push(xform_one(

Diff for: crates/wasm-conventions/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,8 @@ edition = "2018"
1111
rust-version = "1.57"
1212

1313
[dependencies]
14+
leb128 = "0.2"
1415
walrus = "0.20.2"
16+
# Matching the version `walrus` depends on.
17+
wasmparser = "0.80"
1518
anyhow = "1.0"

Diff for: crates/wasm-conventions/src/lib.rs

+74-2
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@
66
//! * The shadow stack pointer
77
//! * The canonical linear memory that contains the shadow stack
88
9-
use anyhow::{anyhow, bail, Result};
9+
use std::io::Cursor;
10+
11+
use anyhow::{anyhow, bail, Context, Result};
1012
use walrus::{
1113
ir::Value, ElementId, FunctionBuilder, FunctionId, FunctionKind, GlobalId, GlobalKind,
12-
InitExpr, MemoryId, Module, ValType,
14+
InitExpr, MemoryId, Module, RawCustomSection, ValType,
1315
};
16+
use wasmparser::BinaryReader;
1417

1518
/// Get a Wasm module's canonical linear memory.
1619
pub fn get_memory(module: &Module) -> Result<MemoryId> {
@@ -148,3 +151,72 @@ pub fn get_or_insert_start_builder(module: &mut Module) -> &mut FunctionBuilder
148151
.unwrap_local_mut()
149152
.builder_mut()
150153
}
154+
155+
pub fn insert_target_feature(module: &mut Module, new_feature: &str) -> Result<()> {
156+
// Taken from <https://github.com/bytecodealliance/wasm-tools/blob/f1898f46bb9d96f0f09682415cb6ccfd6a4dca79/crates/wasmparser/src/limits.rs#L27>.
157+
anyhow::ensure!(new_feature.len() <= 100_000, "feature name too long");
158+
159+
// Try to find an existing section.
160+
let section = module
161+
.customs
162+
.iter_mut()
163+
.find(|(_, custom)| custom.name() == "target_features");
164+
165+
// If one exists, check if the target feature is already present.
166+
let section = if let Some((_, section)) = section {
167+
let section: &mut RawCustomSection = section
168+
.as_any_mut()
169+
.downcast_mut()
170+
.context("failed to read section")?;
171+
let mut reader = BinaryReader::new(&section.data);
172+
// The first integer contains the target feature count.
173+
let count = reader.read_var_u32()?;
174+
175+
// Try to find if the target feature is already present.
176+
for _ in 0..count {
177+
// First byte is the prefix.
178+
let prefix_index = reader.current_position();
179+
let prefix = reader.read_u8()? as u8;
180+
// Read the feature.
181+
let length = reader.read_var_u32()?;
182+
let feature = reader.read_bytes(length as usize)?;
183+
184+
// If we found the target feature, we are done here.
185+
if feature == new_feature.as_bytes() {
186+
// Make sure we set any existing prefix to "enabled".
187+
if prefix == b'-' {
188+
section.data[prefix_index] = b'+';
189+
}
190+
191+
return Ok(());
192+
}
193+
}
194+
195+
section
196+
} else {
197+
let mut data = Vec::new();
198+
leb128::write::unsigned(&mut data, 0).unwrap();
199+
let id = module.customs.add(RawCustomSection {
200+
name: String::from("target_features"),
201+
data,
202+
});
203+
module.customs.get_mut(id).unwrap()
204+
};
205+
206+
// If we couldn't find the target feature, insert it.
207+
208+
// The first byte contains an integer describing the target feature count, which we increase by one.
209+
let mut data = Cursor::new(&section.data);
210+
let count = leb128::read::unsigned(&mut data).unwrap();
211+
let mut new_count = Vec::new();
212+
leb128::write::unsigned(&mut new_count, count + 1).unwrap();
213+
section.data.splice(0..data.position() as usize, new_count);
214+
// Then we insert the "enabled" prefix at the end.
215+
section.data.push(b'+');
216+
// The next byte contains the length of the target feature string.
217+
leb128::write::unsigned(&mut section.data, new_feature.len() as u64).unwrap();
218+
// Lastly the target feature string is inserted.
219+
section.data.extend(new_feature.as_bytes());
220+
221+
Ok(())
222+
}

0 commit comments

Comments
 (0)