| 1 | use crate::prelude::*; |
| 2 | use crate::{ |
| 3 | BinaryReader, BinaryReaderError, FromReader, Result, SectionLimited, Subsection, Subsections, |
| 4 | }; |
| 5 | use core::ops::Range; |
| 6 | |
| 7 | bitflags::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 | |
| 54 | impl<'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 | |
| 60 | impl<'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)] |
| 71 | pub 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. |
| 81 | pub type SegmentMap<'a> = SectionLimited<'a, Segment<'a>>; |
| 82 | |
| 83 | /// Represents extra metadata about the data segments. |
| 84 | #[derive (Debug, Copy, Clone)] |
| 85 | pub 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 | |
| 94 | impl<'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. |
| 108 | pub type InitFuncMap<'a> = SectionLimited<'a, InitFunc>; |
| 109 | |
| 110 | /// Represents an init function in the linking custom section. |
| 111 | #[derive (Debug, Copy, Clone)] |
| 112 | pub 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 | |
| 119 | impl<'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. |
| 131 | pub 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)] |
| 135 | pub 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 | |
| 144 | impl<'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)] |
| 166 | pub 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 | |
| 173 | impl<'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)] |
| 183 | pub 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 | |
| 198 | impl<'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. |
| 218 | pub 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)] |
| 226 | pub 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 | |
| 281 | impl<'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)] |
| 340 | pub 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 | |
| 349 | impl<'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)] |
| 364 | pub 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 | |
| 385 | impl<'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 | |
| 403 | impl<'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 | |
| 450 | impl<'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 | |