Skip to main content

wasm_encoder/core/
linking.rs

1use crate::{CustomSection, Encode, Section, SectionId, encode_section};
2use alloc::borrow::Cow;
3use alloc::vec;
4use alloc::vec::Vec;
5
6const VERSION: u32 = 2;
7
8/// An encoder for the [linking custom
9/// section](https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md#linking-metadata-section).
10///
11/// This section is a non-standard convention that is supported by the LLVM
12/// toolchain. It, along with associated "reloc.*" custom sections, allows you
13/// to treat a Wasm module as a low-level object file that can be linked with
14/// other Wasm object files to produce a final, complete Wasm module.
15///
16/// The linking section must come before the reloc sections.
17///
18/// # Example
19///
20/// ```
21/// use wasm_encoder::{LinkingSection, Module, SymbolTable};
22///
23/// // Create a new linking section.
24/// let mut linking = LinkingSection::new();
25///
26/// // Define a symbol table.
27/// let mut sym_tab = SymbolTable::new();
28///
29/// // Define a function symbol in the symbol table.
30/// let flags = SymbolTable::WASM_SYM_BINDING_LOCAL | SymbolTable::WASM_SYM_EXPORTED;
31/// let func_index = 42;
32/// let sym_name = "my_exported_func";
33/// sym_tab.function(flags, func_index, Some(sym_name));
34///
35/// // Add the symbol table to our linking section.
36/// linking.symbol_table(&sym_tab);
37///
38/// // Add the linking section to a new Wasm module and get the encoded bytes.
39/// let mut module = Module::new();
40/// module.section(&linking);
41/// let wasm_bytes = module.finish();
42/// ```
43#[derive(Clone, Debug)]
44pub struct LinkingSection {
45    bytes: Vec<u8>,
46}
47
48impl LinkingSection {
49    /// Construct a new encoder for the linking custom section.
50    pub fn new() -> Self {
51        Self::default()
52    }
53
54    // TODO: `fn segment_info` for the `WASM_SEGMENT_INFO` linking subsection.
55
56    // TODO: `fn init_funcs` for the `WASM_INIT_FUNCS` linking subsection.
57
58    // TODO: `fn comdat_info` for the `WASM_COMDAT_INFO` linking subsection.
59
60    /// Add a symbol table subsection.
61    pub fn symbol_table(&mut self, symbol_table: &SymbolTable) -> &mut Self {
62        symbol_table.encode(&mut self.bytes);
63        self
64    }
65
66    /// Add a target architecture subsection, e.g. `"wasm32"` or `"wasm64"`.
67    pub fn target_arch(&mut self, arch: &str) -> &mut Self {
68        self.bytes.push(WASM_TARGET_ARCH);
69        let mut subsection = Vec::new();
70        arch.encode(&mut subsection);
71        subsection.as_slice().encode(&mut self.bytes);
72        self
73    }
74}
75
76impl Default for LinkingSection {
77    fn default() -> Self {
78        let mut bytes = Vec::new();
79        VERSION.encode(&mut bytes);
80        Self { bytes }
81    }
82}
83
84impl Encode for LinkingSection {
85    fn encode(&self, sink: &mut Vec<u8>) {
86        CustomSection {
87            name: "linking".into(),
88            data: Cow::Borrowed(&self.bytes),
89        }
90        .encode(sink);
91    }
92}
93
94impl Section for LinkingSection {
95    fn id(&self) -> u8 {
96        SectionId::Custom.into()
97    }
98}
99
100#[allow(unused)]
101const WASM_SEGMENT_INFO: u8 = 5;
102#[allow(unused)]
103const WASM_INIT_FUNCS: u8 = 6;
104#[allow(unused)]
105const WASM_COMDAT_INFO: u8 = 7;
106const WASM_SYMBOL_TABLE: u8 = 8;
107const WASM_TARGET_ARCH: u8 = 9;
108
109/// A subsection of the [linking custom section][crate::LinkingSection] that
110/// provides extra information about the symbols present in this Wasm object
111/// file.
112#[derive(Clone, Debug, Default)]
113pub struct SymbolTable {
114    bytes: Vec<u8>,
115    num_added: u32,
116}
117
118const SYMTAB_FUNCTION: u32 = 0;
119const SYMTAB_DATA: u32 = 1;
120const SYMTAB_GLOBAL: u32 = 2;
121#[allow(unused)]
122const SYMTAB_SECTION: u32 = 3;
123#[allow(unused)]
124const SYMTAB_TAG: u32 = 4;
125const SYMTAB_TABLE: u32 = 5;
126
127impl SymbolTable {
128    /// Construct a new symbol table subsection encoder.
129    pub fn new() -> Self {
130        SymbolTable {
131            bytes: vec![],
132            num_added: 0,
133        }
134    }
135
136    /// Define a function symbol in this symbol table.
137    ///
138    /// The `name` must be omitted if `index` references an imported table and
139    /// the `WASM_SYM_EXPLICIT_NAME` flag is not set.
140    pub fn function(&mut self, flags: u32, index: u32, name: Option<&str>) -> &mut Self {
141        SYMTAB_FUNCTION.encode(&mut self.bytes);
142        flags.encode(&mut self.bytes);
143        index.encode(&mut self.bytes);
144        if let Some(name) = name {
145            name.encode(&mut self.bytes);
146        }
147        self.num_added += 1;
148        self
149    }
150
151    /// Define a global symbol in this symbol table.
152    ///
153    /// The `name` must be omitted if `index` references an imported table and
154    /// the `WASM_SYM_EXPLICIT_NAME` flag is not set.
155    pub fn global(&mut self, flags: u32, index: u32, name: Option<&str>) -> &mut Self {
156        SYMTAB_GLOBAL.encode(&mut self.bytes);
157        flags.encode(&mut self.bytes);
158        index.encode(&mut self.bytes);
159        if let Some(name) = name {
160            name.encode(&mut self.bytes);
161        }
162        self.num_added += 1;
163        self
164    }
165
166    // TODO: tags
167
168    /// Define a table symbol in this symbol table.
169    ///
170    /// The `name` must be omitted if `index` references an imported table and
171    /// the `WASM_SYM_EXPLICIT_NAME` flag is not set.
172    pub fn table(&mut self, flags: u32, index: u32, name: Option<&str>) -> &mut Self {
173        SYMTAB_TABLE.encode(&mut self.bytes);
174        flags.encode(&mut self.bytes);
175        index.encode(&mut self.bytes);
176        if let Some(name) = name {
177            name.encode(&mut self.bytes);
178        }
179        self.num_added += 1;
180        self
181    }
182
183    /// Add a data symbol to this symbol table.
184    pub fn data(
185        &mut self,
186        flags: u32,
187        name: &str,
188        definition: Option<DataSymbolDefinition>,
189    ) -> &mut Self {
190        SYMTAB_DATA.encode(&mut self.bytes);
191        flags.encode(&mut self.bytes);
192        name.encode(&mut self.bytes);
193        if let Some(def) = definition {
194            def.index.encode(&mut self.bytes);
195            def.offset.encode(&mut self.bytes);
196            def.size.encode(&mut self.bytes);
197        }
198        self.num_added += 1;
199        self
200    }
201
202    // TODO: sections
203
204    /// This is a weak symbol.
205    ///
206    /// This flag is mutually exclusive with `WASM_SYM_BINDING_LOCAL`.
207    ///
208    /// When linking multiple modules defining the same symbol, all weak
209    /// definitions are discarded if any strong definitions exist; then if
210    /// multiple weak definitions exist all but one (unspecified) are discarded;
211    /// and finally it is an error if more than one definition remains.
212    pub const WASM_SYM_BINDING_WEAK: u32 = 0x1;
213
214    /// This is a local symbol.
215    ///
216    /// This flag is mutually exclusive with `WASM_SYM_BINDING_WEAK`.
217    ///
218    /// Local symbols are not to be exported, or linked to other
219    /// modules/sections. The names of all non-local symbols must be unique, but
220    /// the names of local symbols are not considered for uniqueness. A local
221    /// function or global symbol cannot reference an import.
222    pub const WASM_SYM_BINDING_LOCAL: u32 = 0x02;
223
224    /// This is a hidden symbol.
225    ///
226    /// Hidden symbols are not to be exported when performing the final link,
227    /// but may be linked to other modules.
228    pub const WASM_SYM_VISIBILITY_HIDDEN: u32 = 0x04;
229
230    /// This symbol is not defined.
231    ///
232    /// For non-data symbols, this must match whether the symbol is an import or
233    /// is defined; for data symbols, determines whether a segment is specified.
234    pub const WASM_SYM_UNDEFINED: u32 = 0x10;
235
236    /// This symbol is intended to be exported from the wasm module to the host
237    /// environment.
238    ///
239    /// This differs from the visibility flags in that it effects the static
240    /// linker.
241    pub const WASM_SYM_EXPORTED: u32 = 0x20;
242
243    /// This symbol uses an explicit symbol name, rather than reusing the name
244    /// from a wasm import.
245    ///
246    /// This allows it to remap imports from foreign WebAssembly modules into
247    /// local symbols with different names.
248    pub const WASM_SYM_EXPLICIT_NAME: u32 = 0x40;
249
250    /// This symbol is intended to be included in the linker output, regardless
251    /// of whether it is used by the program.
252    pub const WASM_SYM_NO_STRIP: u32 = 0x80;
253}
254
255impl Encode for SymbolTable {
256    fn encode(&self, sink: &mut Vec<u8>) {
257        sink.push(WASM_SYMBOL_TABLE);
258        encode_section(sink, self.num_added, &self.bytes);
259    }
260}
261
262/// The definition of a data symbol within a symbol table.
263#[derive(Clone, Debug)]
264pub struct DataSymbolDefinition {
265    /// The index of the data segment that this symbol is in.
266    pub index: u32,
267    /// The offset of this symbol within its segment.
268    pub offset: u32,
269    /// The byte size (which can be zero) of this data symbol.
270    ///
271    /// Note that `offset + size` must be less than or equal to the segment's
272    /// size.
273    pub size: u32,
274}