Skip to content

Commit 7f6386a

Browse files
authored
Add a new wasm-tools component unbundle command (#1725)
* Add a new `wasm-tools component unbundle` command This commit adds a new subcommand to `wasm-tools component` named `unbundle` which is tasked with extracting core wasm modules out of a component. This is intended to showcase an example implementation of doing this and the main motivation is to extract core wasm modules out of a component for runtimes such as Wasmtime. This enables sharing machine code for WebAssembly modules between components that use the same core wasm module. This new subcommand is primarily built on `wasm_encoder::reencode` with a simple size-based heuristic for which modules to extract. * Test module output as well
1 parent fa041b5 commit 7f6386a

23 files changed

Lines changed: 832 additions & 25 deletions

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ that can be use programmatically as well:
148148
| `wasm-tools component embed` | | | Embed a `component-type` custom section in a core wasm binary |
149149
| `wasm-tools metadata show` | [wasm-metadata] | | Show name and producer metadata in a component or module |
150150
| `wasm-tools metadata add` | | | Add name or producer metadata to a component or module |
151+
| `wasm-tools metadata unbundle` | | | Extract core wasm modules from a component |
151152
| `wasm-tools addr2line` | | | Translate wasm offsets to filename/line numbers with DWARF |
152153
| `wasm-tools completion` | | | Generate shell completion scripts for `wasm-tools` |
153154
| `wasm-tools json-from-wast` | | | Convert a `*.wast` file into JSON commands |

crates/wasm-encoder/src/component/names.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ impl ComponentNameSection {
104104
self.component_decls(INSTANCE_SORT, names)
105105
}
106106

107+
/// Appends a raw subsection with the given id and data.
108+
pub fn raw(&mut self, id: u8, data: &[u8]) {
109+
self.bytes.push(id);
110+
data.encode(&mut self.bytes);
111+
}
112+
107113
fn component_decls(&mut self, kind: u8, names: &NameMap) {
108114
self.subsection_header(Subsection::Decls, 1 + names.size());
109115
self.bytes.push(kind);

crates/wasm-encoder/src/core/names.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,12 @@ impl NameSection {
180180
names.encode(&mut self.bytes);
181181
}
182182

183+
/// Appends a raw subsection with the given id and data.
184+
pub fn raw(&mut self, id: u8, data: &[u8]) {
185+
self.bytes.push(id);
186+
data.encode(&mut self.bytes);
187+
}
188+
183189
fn subsection_header(&mut self, id: Subsection, len: usize) {
184190
self.bytes.push(id as u8);
185191
len.encode(&mut self.bytes);

crates/wasm-encoder/src/reencode.rs

Lines changed: 127 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ pub trait Reencode {
4545
utils::type_index(self, ty)
4646
}
4747

48+
fn type_index_unpacked(
49+
&mut self,
50+
ty: wasmparser::UnpackedIndex,
51+
) -> Result<u32, Error<Self::Error>> {
52+
utils::type_index_unpacked(self, ty)
53+
}
54+
4855
fn external_index(&mut self, kind: wasmparser::ExternalKind, index: u32) -> u32 {
4956
match kind {
5057
wasmparser::ExternalKind::Func => self.function_index(index),
@@ -463,6 +470,21 @@ pub trait Reencode {
463470
) -> Result<(), Error<Self::Error>> {
464471
utils::parse_core_module(self, module, parser, data)
465472
}
473+
474+
fn custom_name_section(
475+
&mut self,
476+
section: wasmparser::NameSectionReader<'_>,
477+
) -> Result<crate::NameSection, Error<Self::Error>> {
478+
utils::custom_name_section(self, section)
479+
}
480+
481+
fn parse_custom_name_subsection(
482+
&mut self,
483+
names: &mut crate::NameSection,
484+
section: wasmparser::Name<'_>,
485+
) -> Result<(), Error<Self::Error>> {
486+
utils::parse_custom_name_subsection(self, names, section)
487+
}
466488
}
467489

468490
/// An error when re-encoding from `wasmparser` to `wasm-encoder`.
@@ -860,7 +882,14 @@ pub mod utils {
860882
module: &mut crate::Module,
861883
section: wasmparser::CustomSectionReader<'_>,
862884
) -> Result<(), Error<T::Error>> {
863-
module.section(&reencoder.custom_section(section));
885+
match section.as_known() {
886+
wasmparser::KnownCustom::Name(name) => {
887+
module.section(&reencoder.custom_name_section(name)?);
888+
}
889+
_ => {
890+
module.section(&reencoder.custom_section(section));
891+
}
892+
}
864893
Ok(())
865894
}
866895

@@ -915,6 +944,15 @@ pub mod utils {
915944
ty
916945
}
917946

947+
pub fn type_index_unpacked<T: ?Sized + Reencode>(
948+
reencoder: &mut T,
949+
ty: wasmparser::UnpackedIndex,
950+
) -> Result<u32, Error<T::Error>> {
951+
ty.as_module_index()
952+
.map(|ty| reencoder.type_index(ty))
953+
.ok_or(Error::CanonicalizedHeapTypeReference)
954+
}
955+
918956
pub fn tag_type<T: ?Sized + Reencode>(
919957
reencoder: &mut T,
920958
tag_ty: wasmparser::TagType,
@@ -986,11 +1024,7 @@ pub mod utils {
9861024
is_final: sub_ty.is_final,
9871025
supertype_idx: sub_ty
9881026
.supertype_idx
989-
.map(|i| {
990-
i.as_module_index()
991-
.map(|ty| reencoder.type_index(ty))
992-
.ok_or(Error::CanonicalizedHeapTypeReference)
993-
})
1027+
.map(|i| reencoder.type_index_unpacked(i.unpack()))
9941028
.transpose()?,
9951029
composite_type: reencoder.composite_type(sub_ty.composite_type)?,
9961030
})
@@ -1101,11 +1135,9 @@ pub mod utils {
11011135
heap_type: wasmparser::HeapType,
11021136
) -> Result<crate::HeapType, Error<T::Error>> {
11031137
Ok(match heap_type {
1104-
wasmparser::HeapType::Concrete(i) => crate::HeapType::Concrete(
1105-
i.as_module_index()
1106-
.map(|ty| reencoder.type_index(ty))
1107-
.ok_or(Error::CanonicalizedHeapTypeReference)?,
1108-
),
1138+
wasmparser::HeapType::Concrete(i) => {
1139+
crate::HeapType::Concrete(reencoder.type_index_unpacked(i)?)
1140+
}
11091141
wasmparser::HeapType::Abstract { shared, ty } => crate::HeapType::Abstract {
11101142
shared,
11111143
ty: reencoder.abstract_heap_type(ty),
@@ -1580,6 +1612,90 @@ pub mod utils {
15801612
module.section(&crate::RawSection { id, data: contents });
15811613
Ok(())
15821614
}
1615+
1616+
pub fn custom_name_section<T: ?Sized + Reencode>(
1617+
reencoder: &mut T,
1618+
section: wasmparser::NameSectionReader<'_>,
1619+
) -> Result<crate::NameSection, Error<T::Error>> {
1620+
let mut ret = crate::NameSection::new();
1621+
for subsection in section {
1622+
reencoder.parse_custom_name_subsection(&mut ret, subsection?)?;
1623+
}
1624+
Ok(ret)
1625+
}
1626+
1627+
pub fn parse_custom_name_subsection<T: ?Sized + Reencode>(
1628+
reencoder: &mut T,
1629+
names: &mut crate::NameSection,
1630+
section: wasmparser::Name<'_>,
1631+
) -> Result<(), Error<T::Error>> {
1632+
match section {
1633+
wasmparser::Name::Module { name, .. } => {
1634+
names.module(name);
1635+
}
1636+
wasmparser::Name::Function(map) => {
1637+
names.functions(&name_map(map, |i| reencoder.function_index(i))?);
1638+
}
1639+
wasmparser::Name::Type(map) => {
1640+
names.types(&name_map(map, |i| reencoder.type_index(i))?);
1641+
}
1642+
wasmparser::Name::Local(map) => {
1643+
names.locals(&indirect_name_map(map, |i| reencoder.function_index(i))?);
1644+
}
1645+
wasmparser::Name::Label(map) => {
1646+
names.labels(&indirect_name_map(map, |i| reencoder.function_index(i))?);
1647+
}
1648+
wasmparser::Name::Table(map) => {
1649+
names.tables(&name_map(map, |i| reencoder.table_index(i))?);
1650+
}
1651+
wasmparser::Name::Memory(map) => {
1652+
names.memories(&name_map(map, |i| reencoder.memory_index(i))?);
1653+
}
1654+
wasmparser::Name::Global(map) => {
1655+
names.globals(&name_map(map, |i| reencoder.global_index(i))?);
1656+
}
1657+
wasmparser::Name::Element(map) => {
1658+
names.elements(&name_map(map, |i| reencoder.element_index(i))?);
1659+
}
1660+
wasmparser::Name::Data(map) => {
1661+
names.data(&name_map(map, |i| reencoder.data_index(i))?);
1662+
}
1663+
wasmparser::Name::Tag(map) => {
1664+
names.tags(&name_map(map, |i| reencoder.tag_index(i))?);
1665+
}
1666+
wasmparser::Name::Field(map) => {
1667+
names.fields(&indirect_name_map(map, |i| reencoder.type_index(i))?);
1668+
}
1669+
wasmparser::Name::Unknown { ty, data, .. } => {
1670+
names.raw(ty, data);
1671+
}
1672+
}
1673+
Ok(())
1674+
}
1675+
1676+
pub fn name_map(
1677+
map: wasmparser::NameMap<'_>,
1678+
mut map_index: impl FnMut(u32) -> u32,
1679+
) -> wasmparser::Result<crate::NameMap> {
1680+
let mut ret = crate::NameMap::new();
1681+
for naming in map {
1682+
let naming = naming?;
1683+
ret.append(map_index(naming.index), naming.name);
1684+
}
1685+
Ok(ret)
1686+
}
1687+
1688+
pub fn indirect_name_map(
1689+
map: wasmparser::IndirectNameMap<'_>,
1690+
mut map_index: impl FnMut(u32) -> u32,
1691+
) -> wasmparser::Result<crate::IndirectNameMap> {
1692+
let mut ret = crate::IndirectNameMap::new();
1693+
for naming in map {
1694+
let naming = naming?;
1695+
ret.append(map_index(naming.index), &name_map(naming.names, |i| i)?);
1696+
}
1697+
Ok(ret)
1698+
}
15831699
}
15841700

15851701
impl From<wasmparser::MemArg> for crate::MemArg {

0 commit comments

Comments
 (0)