tinywasm_wasmparser/readers/core/
linking.rs

1use alloc::format;
2
3use crate::std::ops::Range;
4use crate::{
5    BinaryReader, BinaryReaderError, FromReader, Result, SectionLimited, Subsection, Subsections,
6};
7
8bitflags::bitflags! {
9    /// Flags for WebAssembly symbols.
10    ///
11    /// These flags correspond to those described in
12    /// <https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md>
13    /// with the `WASM_SYM_*` prefix.
14    #[repr(transparent)]
15    #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
16    pub struct SymbolFlags: u32 {
17        /* N.B.:
18            Newly added flags should be keep in sync with `print_dylink0_flags`
19            in `crates/wasmprinter/src/lib.rs`.
20        */
21        /// This is a weak symbol.
22        const BINDING_WEAK = 1 << 0;
23        /// This is a local symbol (this is exclusive with [BINDING_WEAK]).
24        const BINDING_LOCAL = 1 << 1;
25        /// This is a hidden symbol.
26        const VISIBILITY_HIDDEN = 1 << 2;
27        /// This symbol is not defined.
28        const UNDEFINED = 1 << 4;
29        /// This symbol is intended to be exported from the wasm module to the host environment.
30        const EXPORTED = 1 << 5;
31        /// This symbol uses an explicit symbol name, rather than reusing the name from a wasm import.
32        const EXPLICIT_NAME = 1 << 6;
33        /// This symbol is intended to be included in the linker output, regardless of whether it is used by the program.
34        const NO_STRIP = 1 << 7;
35        /// This symbol resides in thread local storage.
36        const TLS = 1 << 8;
37        /// This symbol represents an absolute address.
38        const ABSOLUTE = 1 << 9;
39    }
40
41    /// Flags for WebAssembly segments.
42    ///
43    /// These flags are defined by implementation at the time of writing:
44    /// <https://github.com/llvm/llvm-project/blob/llvmorg-17.0.6/llvm/include/llvm/BinaryFormat/Wasm.h#L391-L394>
45    #[repr(transparent)]
46    #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
47    pub struct SegmentFlags: u32 {
48        /// The segment contains only null-terminated strings, which allows the linker to perform merging.
49        const STRINGS = 0x1;
50        /// The segment contains thread-local data.
51        const TLS = 0x2;
52    }
53}
54
55impl<'a> FromReader<'a> for SymbolFlags {
56    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
57        Ok(Self::from_bits_retain(reader.read_var_u32()?))
58    }
59}
60
61impl<'a> FromReader<'a> for SegmentFlags {
62    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
63        Ok(Self::from_bits_retain(reader.read_var_u32()?))
64    }
65}
66
67/// A reader for the `linking` custom section of a WebAssembly module.
68///
69/// This format is currently defined upstream at
70/// <https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md>.
71#[derive(Debug, Clone)]
72pub struct LinkingSectionReader<'a> {
73    /// The version of linking metadata contained in this section.
74    version: u32,
75    /// The subsections in this section.
76    subsections: Subsections<'a, Linking<'a>>,
77    /// The range of the entire section, including the version.
78    range: Range<usize>,
79}
80
81/// Represents a reader for segments from the linking custom section.
82pub type SegmentMap<'a> = SectionLimited<'a, Segment<'a>>;
83
84/// Represents extra metadata about the data segments.
85#[derive(Debug, Copy, Clone)]
86pub struct Segment<'a> {
87    /// The name for the segment.
88    pub name: &'a str,
89    /// The required alignment of the segment, encoded as a power of 2.
90    pub alignment: u32,
91    /// The flags for the segment.
92    pub flags: SegmentFlags,
93}
94
95impl<'a> FromReader<'a> for Segment<'a> {
96    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
97        let name = reader.read_string()?;
98        let alignment = reader.read_var_u32()?;
99        let flags = reader.read()?;
100        Ok(Self {
101            name,
102            alignment,
103            flags,
104        })
105    }
106}
107
108/// Represents a reader for init functions from the linking custom section.
109pub type InitFuncMap<'a> = SectionLimited<'a, InitFunc>;
110
111/// Represents an init function in the linking custom section.
112#[derive(Debug, Copy, Clone)]
113pub struct InitFunc {
114    /// The priority of the init function.
115    pub priority: u32,
116    /// The symbol index of init function (*not* the function index).
117    pub symbol_index: u32,
118}
119
120impl<'a> FromReader<'a> for InitFunc {
121    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
122        let priority = reader.read_var_u32()?;
123        let symbol_index = reader.read_var_u32()?;
124        Ok(Self {
125            priority,
126            symbol_index,
127        })
128    }
129}
130
131/// Represents a reader for COMDAT data from the linking custom section.
132pub type ComdatMap<'a> = SectionLimited<'a, Comdat<'a>>;
133
134/// Represents [COMDAT](https://llvm.org/docs/LangRef.html#comdats) data in the linking custom section.
135#[derive(Debug, Clone)]
136pub struct Comdat<'a> {
137    /// The name of this comdat.
138    pub name: &'a str,
139    /// The flags.
140    pub flags: u32,
141    /// The member symbols of this comdat.
142    pub symbols: SectionLimited<'a, ComdatSymbol>,
143}
144
145impl<'a> FromReader<'a> for Comdat<'a> {
146    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
147        let name = reader.read_string()?;
148        let flags = reader.read_var_u32()?;
149        let symbols = SectionLimited::new(reader.remaining_buffer(), reader.original_position())?;
150        Ok(Self {
151            name,
152            flags,
153            symbols,
154        })
155    }
156}
157
158/// Represents a symbol that is part of a comdat.
159#[derive(Debug, Copy, Clone)]
160pub struct ComdatSymbol {
161    /// The kind of the symbol.
162    pub kind: ComdatSymbolKind,
163    /// The index of the symbol. Must not be an import.
164    pub index: u32,
165}
166
167impl<'a> FromReader<'a> for ComdatSymbol {
168    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
169        let kind = reader.read()?;
170        let index = reader.read_var_u32()?;
171        Ok(Self { kind, index })
172    }
173}
174
175/// Represents a symbol kind.
176#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
177pub enum ComdatSymbolKind {
178    /// The symbol is a data segment.
179    Data,
180    /// The symbol is a function.
181    Func,
182    /// The symbol is a global.
183    Global,
184    /// The symbol is an event.
185    Event,
186    /// The symbol is a table.
187    Table,
188    /// The symbol is a section.
189    Section,
190}
191
192impl<'a> FromReader<'a> for ComdatSymbolKind {
193    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
194        let offset = reader.original_position();
195        match reader.read_u8()? {
196            0 => Ok(Self::Data),
197            1 => Ok(Self::Func),
198            2 => Ok(Self::Global),
199            3 => Ok(Self::Event),
200            4 => Ok(Self::Table),
201            5 => Ok(Self::Section),
202            k => Err(BinaryReader::invalid_leading_byte_error(
203                k,
204                "comdat symbol kind",
205                offset,
206            )),
207        }
208    }
209}
210
211/// Represents a reader for symbol info from the linking custom section.
212pub type SymbolInfoMap<'a> = SectionLimited<'a, SymbolInfo<'a>>;
213
214/// Represents extra information about symbols in the linking custom section.
215///
216/// The symbol flags correspond to those described in
217/// <https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md>
218/// with the `WASM_SYM_*` prefix.
219#[derive(Debug, Copy, Clone)]
220pub enum SymbolInfo<'a> {
221    /// The symbol is a function.
222    Func {
223        /// The flags for the symbol.
224        flags: SymbolFlags,
225        /// The index of the function corresponding to this symbol.
226        index: u32,
227        /// The name for the function, if it is defined or uses an explicit name.
228        name: Option<&'a str>,
229    },
230    /// The symbol is a data symbol.
231    Data {
232        /// The flags for the symbol.
233        flags: SymbolFlags,
234        /// The name for the symbol.
235        name: &'a str,
236        /// The definition of the data symbol, if it is defined.
237        symbol: Option<DefinedDataSymbol>,
238    },
239    /// The symbol is a global.
240    Global {
241        /// The flags for the symbol.
242        flags: SymbolFlags,
243        /// The index of the global corresponding to this symbol.
244        index: u32,
245        /// The name for the global, if it is defined or uses an explicit name.
246        name: Option<&'a str>,
247    },
248    /// The symbol is a section.
249    Section {
250        /// The flags for the symbol.
251        flags: SymbolFlags,
252        /// The index of the function corresponding to this symbol.
253        section: u32,
254    },
255    /// The symbol is an event.
256    Event {
257        /// The flags for the symbol.
258        flags: SymbolFlags,
259        /// The index of the event corresponding to this symbol.
260        index: u32,
261        /// The name for the event, if it is defined or uses an explicit name.
262        name: Option<&'a str>,
263    },
264    /// The symbol is a table.
265    Table {
266        /// The flags for the symbol.
267        flags: SymbolFlags,
268        /// The index of the table corresponding to this symbol.
269        index: u32,
270        /// The name for the table, if it is defined or uses an explicit name.
271        name: Option<&'a str>,
272    },
273}
274
275impl<'a> FromReader<'a> for SymbolInfo<'a> {
276    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
277        let offset = reader.original_position();
278        let kind = reader.read_u8()?;
279        let flags: SymbolFlags = reader.read()?;
280
281        let defined = !flags.contains(SymbolFlags::UNDEFINED);
282        let explicit_name = flags.contains(SymbolFlags::EXPLICIT_NAME);
283
284        const SYMTAB_FUNCTION: u8 = 0;
285        const SYMTAB_DATA: u8 = 1;
286        const SYMTAB_GLOBAL: u8 = 2;
287        const SYMTAB_SECTION: u8 = 3;
288        const SYMTAB_EVENT: u8 = 4;
289        const SYMTAB_TABLE: u8 = 5;
290
291        // https://github.com/WebAssembly/wabt/blob/1.0.34/src/binary-writer.cc#L1226
292        match kind {
293            SYMTAB_FUNCTION | SYMTAB_GLOBAL | SYMTAB_EVENT | SYMTAB_TABLE => {
294                let index = reader.read_var_u32()?;
295                let name = match defined || explicit_name {
296                    true => Some(reader.read_string()?),
297                    false => None,
298                };
299                Ok(match kind {
300                    SYMTAB_FUNCTION => Self::Func { flags, index, name },
301                    SYMTAB_GLOBAL => Self::Global { flags, index, name },
302                    SYMTAB_EVENT => Self::Event { flags, index, name },
303                    SYMTAB_TABLE => Self::Table { flags, index, name },
304                    _ => unreachable!(),
305                })
306            }
307            SYMTAB_DATA => {
308                let name = reader.read_string()?;
309                let data = match defined {
310                    true => Some(reader.read()?),
311                    false => None,
312                };
313                Ok(Self::Data {
314                    flags,
315                    name,
316                    symbol: data,
317                })
318            }
319            SYMTAB_SECTION => {
320                let section = reader.read_var_u32()?;
321                Ok(Self::Section { flags, section })
322            }
323            k => Err(BinaryReader::invalid_leading_byte_error(
324                k,
325                "symbol kind",
326                offset,
327            )),
328        }
329    }
330}
331
332/// Represents the metadata about a data symbol defined in the wasm file.
333#[derive(Debug, Copy, Clone)]
334pub struct DefinedDataSymbol {
335    /// The index of the data segment.
336    pub index: u32,
337    /// The offset within the segment. Must be <= the segment's size.
338    pub offset: u32,
339    /// The size of the data, which can be zero. `offset + size` must be <= the segment's size.
340    pub size: u32,
341}
342
343impl<'a> FromReader<'a> for DefinedDataSymbol {
344    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
345        let index = reader.read_var_u32()?;
346        let offset = reader.read_var_u32()?;
347        let size = reader.read_var_u32()?;
348        Ok(Self {
349            index,
350            offset,
351            size,
352        })
353    }
354}
355
356/// Represents a subsection read from the linking custom section.
357#[derive(Debug, Clone)]
358pub enum Linking<'a> {
359    /// Extra metadata about the data segments.
360    SegmentInfo(SegmentMap<'a>),
361    /// A list of constructor functions to be called at startup.
362    InitFuncs(InitFuncMap<'a>),
363    /// The [COMDAT](https://llvm.org/docs/LangRef.html#comdats) groups of associated linking objects.
364    ComdatInfo(ComdatMap<'a>),
365    /// Extra information about the symbols present in the module.
366    SymbolTable(SymbolInfoMap<'a>),
367    /// An unknown [linking subsection](https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md#linking-metadata-section).
368    Unknown {
369        /// The identifier for this subsection.
370        ty: u8,
371        /// The contents of this subsection.
372        data: &'a [u8],
373        /// The range of bytes, relative to the start of the original data
374        /// stream, that the contents of this subsection reside in.
375        range: Range<usize>,
376    },
377}
378
379impl<'a> Subsection<'a> for Linking<'a> {
380    fn from_reader(id: u8, reader: BinaryReader<'a>) -> Result<Self> {
381        let data = reader.remaining_buffer();
382        let offset = reader.original_position();
383        Ok(match id {
384            5 => Self::SegmentInfo(SegmentMap::new(data, offset)?),
385            6 => Self::InitFuncs(InitFuncMap::new(data, offset)?),
386            7 => Self::ComdatInfo(ComdatMap::new(data, offset)?),
387            8 => Self::SymbolTable(SymbolInfoMap::new(data, offset)?),
388            ty => Self::Unknown {
389                ty,
390                data,
391                range: offset..offset + data.len(),
392            },
393        })
394    }
395}
396
397impl<'a> LinkingSectionReader<'a> {
398    /// Creates a new reader for the linking section contents starting at
399    /// `offset` within the original wasm file.
400    pub fn new(data: &'a [u8], offset: usize) -> Result<Self> {
401        let mut reader = BinaryReader::new_with_offset(data, offset);
402        let range = reader.range();
403
404        let version = reader.read_var_u32()?;
405        if version != 2 {
406            return Err(BinaryReaderError::new(
407                format!("unsupported linking section version: {}", version),
408                offset,
409            ));
410        }
411
412        let subsections = Subsections::new(reader.remaining_buffer(), reader.original_position());
413        Ok(Self {
414            version,
415            subsections,
416            range,
417        })
418    }
419
420    /// Returns the version of linking metadata contained in this section.
421    pub fn version(&self) -> u32 {
422        self.version
423    }
424
425    /// Returns the original byte offset of this section.
426    pub fn original_position(&self) -> usize {
427        self.subsections.original_position()
428    }
429
430    /// Returns the range, as byte offsets, of this section within the original
431    /// wasm binary.
432    pub fn range(&self) -> Range<usize> {
433        self.range.clone()
434    }
435
436    /// Returns the iterator for advancing through the subsections.
437    ///
438    /// You can also use [`IntoIterator::into_iter`] directly on this type.
439    pub fn subsections(&self) -> Subsections<'a, Linking<'a>> {
440        self.subsections.clone()
441    }
442}
443
444impl<'a> IntoIterator for LinkingSectionReader<'a> {
445    type Item = Result<Linking<'a>>;
446    type IntoIter = Subsections<'a, Linking<'a>>;
447
448    fn into_iter(self) -> Self::IntoIter {
449        self.subsections
450    }
451}