Skip to main content

wasmparser/readers/core/
linking.rs

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