wasmparser/readers/core/
linking.rs

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