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(bits: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(bits: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: &'a str = reader.read_string()?;
97 let alignment: u32 = reader.read_var_u32()?;
98 let flags: SegmentFlags = 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: u32 = reader.read_var_u32()?;
122 let symbol_index: u32 = 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: &'a str = reader.read_string()?;
147 let flags: u32 = reader.read_var_u32()?;
148 // FIXME(#188) ideally shouldn't need to skip here
149 let symbols: BinaryReader<'a> = reader.skip(|reader: &mut BinaryReader<'a>| {
150 let count: u32 = 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(reader: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: ComdatSymbolKind = reader.read()?;
176 let index: u32 = 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: usize = 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: u8 => Err(BinaryReader::invalid_leading_byte_error(
209 byte:k,
210 desc:"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: u32 = reader.read_var_u32()?;
352 let offset: u32 = reader.read_var_u32()?;
353 let size: u32 = 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: &[u8] = reader.remaining_buffer();
388 let offset: usize = 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: u8 => 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}
458