Skip to main content

rucc_object/
wasm.rs

1//! The relocatable wasm object, which the `object` crate reads and does not write.
2//!
3//! Design: section 9.3 of the WebAssembly notes, and the object format of the tool conventions
4//! (`Linking.md`) as LLD 23 reads it. The relocation numbers are the ones in LLVM's
5//! `WasmRelocs.def`, because the conventions text does not list all of them.
6//!
7//! # What the caller gives and what this decides
8//!
9//! The caller gives the function bodies as bytes, the data as segments, and one list of symbols.
10//! A field in a body or a segment that names something the linker places is a [`Fixup`]: the
11//! caller reserves its bytes and says what it refers to, and this module writes the field. So the
12//! caller never needs to know an index or an address. That is the point of the split, because
13//! the numbers depend on the whole file. A function index counts the imported functions first,
14//! and a data address counts every segment before the one it is in.
15//!
16//! A function symbol is defined when a [`Function`] names it, and imported when none does. A data
17//! symbol is defined when it has a place. Globals, tables and tags are always imported, because
18//! the only ones a C object refers to are the stack pointer, the function table and the tag of
19//! `longjmp`, and the linker makes all three. This module sets the `UNDEFINED` and
20//! `EXPLICIT_NAME` flags itself from these facts, so a symbol cannot say that it is defined and
21//! have no definition.
22//!
23//! # The fields the linker patches
24//!
25//! Every relocated field in code is a LEB128 at its widest, five bytes, and every one in data is
26//! four bytes. The linker then patches in place and does not move code. The value written here
27//! is the one the field would have if this object were the whole program, which is what clang
28//! writes too, so `llvm-objdump` shows the same thing for both compilers.
29
30use std::fmt;
31
32/// `\0asm`, then version 1.
33const MAGIC: [u8; 8] = [0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00];
34
35/// The version of the `linking` section that LLD reads.
36const LINKING_VERSION: u32 = 2;
37
38/// The width of a padded LEB128 field for a 32-bit value.
39pub const PADDED: usize = 5;
40
41/// The size of a wasm page.
42const PAGE: u64 = 65_536;
43
44/// A symbol is weak.
45pub const WEAK: u32 = 0x1;
46/// A symbol is local to the object: `static`, or a name the compiler made.
47pub const LOCAL: u32 = 0x2;
48/// A symbol is hidden. Every non-static symbol is, unless `-fvisibility=default` says otherwise.
49pub const HIDDEN: u32 = 0x4;
50/// A symbol is not defined in this object. This module sets it.
51const UNDEFINED: u32 = 0x10;
52/// A symbol is exported from the final module.
53pub const EXPORTED: u32 = 0x20;
54/// An import has a field name that is not the symbol name. This module sets it.
55const EXPLICIT_NAME: u32 = 0x40;
56/// The linker keeps a symbol that nothing refers to.
57pub const NO_STRIP: u32 = 0x80;
58
59/// A segment holds strings that the linker can merge.
60pub const STRINGS: u32 = 0x1;
61/// The linker keeps a segment that nothing refers to.
62pub const RETAIN: u32 = 0x4;
63
64/// A value type of the core specification.
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
66pub enum ValType {
67    I32,
68    I64,
69    F32,
70    F64,
71}
72
73impl ValType {
74    /// The byte the binary format gives the type.
75    #[must_use]
76    pub fn byte(self) -> u8 {
77        match self {
78            ValType::I32 => 0x7f,
79            ValType::I64 => 0x7e,
80            ValType::F32 => 0x7d,
81            ValType::F64 => 0x7c,
82        }
83    }
84}
85
86impl fmt::Display for ValType {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        f.write_str(match self {
89            ValType::I32 => "i32",
90            ValType::I64 => "i64",
91            ValType::F32 => "f32",
92            ValType::F64 => "f64",
93        })
94    }
95}
96
97/// The type of a function.
98#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
99pub struct FuncType {
100    pub params: Vec<ValType>,
101    pub results: Vec<ValType>,
102}
103
104/// What a symbol is.
105#[derive(Debug, Clone, PartialEq, Eq)]
106pub enum SymbolKind {
107    /// A function of the type at this index. It is imported when no [`Function`] defines it.
108    Function { ty: u32, import: Option<Import> },
109    /// A data object. It is defined when it has a place.
110    Data { place: Option<Place> },
111    /// A global, always imported.
112    Global { ty: ValType, mutable: bool, import: Option<Import> },
113    /// A table of function references, always imported.
114    Table { import: Option<Import> },
115    /// An exception tag of the function type at this index, always imported.
116    Tag { ty: u32, import: Option<Import> },
117}
118
119/// The module and the field of an import when they are not `env` and the symbol name, which is
120/// what `import_module` and `import_name` ask for.
121#[derive(Debug, Clone, PartialEq, Eq)]
122pub struct Import {
123    pub module: String,
124    pub field: String,
125}
126
127/// Where a data symbol is: its segment, its offset in that segment and its size.
128#[derive(Debug, Clone, Copy, PartialEq, Eq)]
129pub struct Place {
130    pub segment: u32,
131    pub offset: u32,
132    pub size: u32,
133}
134
135/// One entry of the symbol table.
136#[derive(Debug, Clone, PartialEq, Eq)]
137pub struct Symbol {
138    pub name: String,
139    pub kind: SymbolKind,
140    /// Any of [`WEAK`], [`LOCAL`], [`HIDDEN`], [`EXPORTED`] and [`NO_STRIP`].
141    pub flags: u32,
142}
143
144/// What a relocated field holds.
145#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
146pub enum RelocKind {
147    /// The index of a function, in `call`.
148    FunctionIndexLeb,
149    /// The table slot of a function, in `i32.const`.
150    TableIndexSleb,
151    /// The table slot of a function, in data.
152    TableIndexI32,
153    /// A data address, in the offset of a load or a store.
154    MemoryAddrLeb,
155    /// A data address, in `i32.const`.
156    MemoryAddrSleb,
157    /// A data address, in data.
158    MemoryAddrI32,
159    /// The index of a type, in `call_indirect`.
160    TypeIndexLeb,
161    /// The index of a global, in `global.get` and `global.set`.
162    GlobalIndexLeb,
163    /// The index of a tag, in `throw` and `try_table`.
164    TagIndexLeb,
165    /// The number of a table, in `call_indirect`.
166    TableNumberLeb,
167}
168
169impl RelocKind {
170    /// The number LLVM gives the relocation.
171    #[must_use]
172    pub fn number(self) -> u8 {
173        match self {
174            RelocKind::FunctionIndexLeb => 0,
175            RelocKind::TableIndexSleb => 1,
176            RelocKind::TableIndexI32 => 2,
177            RelocKind::MemoryAddrLeb => 3,
178            RelocKind::MemoryAddrSleb => 4,
179            RelocKind::MemoryAddrI32 => 5,
180            RelocKind::TypeIndexLeb => 6,
181            RelocKind::GlobalIndexLeb => 7,
182            RelocKind::TagIndexLeb => 10,
183            RelocKind::TableNumberLeb => 20,
184        }
185    }
186
187    /// The number of bytes the field takes.
188    #[must_use]
189    pub fn width(self) -> usize {
190        match self {
191            RelocKind::TableIndexI32 | RelocKind::MemoryAddrI32 => 4,
192            _ => PADDED,
193        }
194    }
195
196    /// Whether the entry carries an addend. Only the address relocations do.
197    fn has_addend(self) -> bool {
198        matches!(
199            self,
200            RelocKind::MemoryAddrLeb | RelocKind::MemoryAddrSleb | RelocKind::MemoryAddrI32
201        )
202    }
203
204    /// Whether the entry names a type rather than a symbol.
205    fn names_type(self) -> bool {
206        self == RelocKind::TypeIndexLeb
207    }
208}
209
210/// A field that names something the linker places.
211#[derive(Debug, Clone, Copy, PartialEq, Eq)]
212pub struct Fixup {
213    /// Where the field starts, from the start of the code of its function or of the bytes of its
214    /// segment. The caller reserves [`RelocKind::width`] bytes there.
215    pub at: u32,
216    pub kind: RelocKind,
217    /// A symbol index, or a type index for [`RelocKind::TypeIndexLeb`].
218    pub target: u32,
219    /// Added to a data address. Zero for the other kinds.
220    pub addend: i32,
221}
222
223/// A function that this object defines.
224#[derive(Debug, Clone, PartialEq, Eq, Default)]
225pub struct Function {
226    /// The index of its symbol, which is a function symbol.
227    pub symbol: u32,
228    /// The locals after the parameters, as runs of one type.
229    pub locals: Vec<(u32, ValType)>,
230    /// The instructions, with the `end` that closes the body.
231    pub code: Vec<u8>,
232    pub fixups: Vec<Fixup>,
233    /// A name for the export section of this object, from `export_name`.
234    pub export: Option<String>,
235}
236
237/// One data segment. Each data object has its own, which is how the linker can drop one.
238#[derive(Debug, Clone, PartialEq, Eq, Default)]
239pub struct Segment {
240    /// `.data.<symbol>`, `.rodata.<symbol>` or `.bss.<symbol>`, as clang names them.
241    pub name: String,
242    /// The alignment, as its log2.
243    pub align: u32,
244    /// Any of [`STRINGS`] and [`RETAIN`].
245    pub flags: u32,
246    pub bytes: Vec<u8>,
247    pub fixups: Vec<Fixup>,
248}
249
250/// The `producers` section: the language and the compiler.
251#[derive(Debug, Clone, PartialEq, Eq, Default)]
252pub struct Producers {
253    /// `C11`, `C17` or `C23`.
254    pub language: Option<String>,
255    /// The name and the version of each tool that made the object.
256    pub processed_by: Vec<(String, String)>,
257}
258
259/// Everything in one object.
260#[derive(Debug, Clone, PartialEq, Eq, Default)]
261pub struct Module {
262    pub types: Vec<FuncType>,
263    pub symbols: Vec<Symbol>,
264    pub functions: Vec<Function>,
265    /// The function symbols that are second names of a function in this object, each as the
266    /// symbol of the second name and the symbol of the function. This is the `alias` attribute of
267    /// GCC on a function. The two symbols have the same function index, and each one keeps its
268    /// own name and flags. A data symbol needs no entry here, because a second name of a variable
269    /// is a data symbol with the same place.
270    pub aliases: Vec<(u32, u32)>,
271    pub segments: Vec<Segment>,
272    /// The constructors, as a priority and a symbol index. 65535 is the priority of one that gave
273    /// none.
274    pub inits: Vec<(u32, u32)>,
275    /// The LLVM names of the features the code uses.
276    pub features: Vec<String>,
277    /// The LLVM names of the features the object must not be linked with.
278    pub disallowed: Vec<String>,
279    pub producers: Producers,
280}
281
282impl Module {
283    /// The index of `ty` in the type list, added when it is not there.
284    ///
285    /// # Panics
286    ///
287    /// When the list has 2^32 types, which no object has.
288    pub fn intern(&mut self, ty: FuncType) -> u32 {
289        let index = match self.types.iter().position(|t| *t == ty) {
290            Some(index) => index,
291            None => {
292                self.types.push(ty);
293                self.types.len() - 1
294            }
295        };
296        u32::try_from(index).expect("fewer than 2^32 types")
297    }
298
299    /// The index of a new symbol.
300    ///
301    /// # Panics
302    ///
303    /// When the table has 2^32 symbols, which no object has.
304    pub fn symbol(&mut self, name: impl Into<String>, kind: SymbolKind, flags: u32) -> u32 {
305        self.symbols.push(Symbol { name: name.into(), kind, flags });
306        u32::try_from(self.symbols.len() - 1).expect("fewer than 2^32 symbols")
307    }
308}
309
310/// Why an object could not be written. Each one is a mistake in the caller, not in the program.
311#[derive(Debug, Clone, PartialEq, Eq)]
312pub enum Error {
313    /// A fixup names a symbol or a type that is not there, or a symbol of the wrong kind.
314    Target { fixup: Fixup, why: &'static str },
315    /// A fixup is outside the bytes it patches.
316    Outside { fixup: Fixup },
317    /// A function names a symbol that is not a function symbol, or two functions name one.
318    Definition { symbol: u32 },
319    /// A data symbol is outside its segment.
320    Place { symbol: u32 },
321    /// An alias is not a function symbol, is defined twice, or names a function that this object
322    /// does not define.
323    Alias { symbol: u32 },
324}
325
326impl fmt::Display for Error {
327    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328        match self {
329            Error::Target { fixup, why } => write!(
330                f,
331                "the fixup at {} names {} {}, which {why}",
332                fixup.at,
333                if fixup.kind.names_type() { "type" } else { "symbol" },
334                fixup.target
335            ),
336            Error::Outside { fixup } => {
337                write!(f, "the fixup at {} is outside the bytes it patches", fixup.at)
338            }
339            Error::Definition { symbol } => {
340                write!(f, "symbol {symbol} is not a function that one definition names")
341            }
342            Error::Place { symbol } => write!(f, "symbol {symbol} is outside its segment"),
343            Error::Alias { symbol } => {
344                write!(f, "symbol {symbol} is not a second name of a function that is defined here")
345            }
346        }
347    }
348}
349
350impl std::error::Error for Error {}
351
352/// What [`write()`] gives: the bytes, and the names a linker can find in them.
353#[derive(Debug, Clone, PartialEq, Eq)]
354pub struct Written {
355    pub bytes: Vec<u8>,
356    /// Every symbol that is defined and not local, in symbol table order. This is what the index
357    /// of an archive lists for the member.
358    pub defines: Vec<String>,
359}
360
361/// Push `value` as an unsigned LEB128 at its shortest.
362pub fn uleb(out: &mut Vec<u8>, mut value: u64) {
363    loop {
364        let byte = (value & 0x7f) as u8;
365        value >>= 7;
366        if value == 0 {
367            out.push(byte);
368            return;
369        }
370        out.push(byte | 0x80);
371    }
372}
373
374/// Push `value` as a signed LEB128 at its shortest.
375pub fn sleb(out: &mut Vec<u8>, mut value: i64) {
376    loop {
377        let byte = (value & 0x7f) as u8;
378        value >>= 7;
379        let done = (value == 0 && byte & 0x40 == 0) || (value == -1 && byte & 0x40 != 0);
380        if done {
381            out.push(byte);
382            return;
383        }
384        out.push(byte | 0x80);
385    }
386}
387
388/// `value` as an unsigned LEB128 of five bytes.
389#[must_use]
390pub fn uleb_padded(value: u32) -> [u8; PADDED] {
391    let mut out = [0; PADDED];
392    let mut rest = value;
393    for (n, byte) in out.iter_mut().enumerate() {
394        *byte = (rest & 0x7f) as u8;
395        rest >>= 7;
396        if n + 1 < PADDED {
397            *byte |= 0x80;
398        }
399    }
400    out
401}
402
403/// `value` as a signed LEB128 of five bytes.
404#[must_use]
405pub fn sleb_padded(value: i32) -> [u8; PADDED] {
406    let mut out = [0; PADDED];
407    let mut rest = value;
408    for (n, byte) in out.iter_mut().enumerate() {
409        *byte = (rest & 0x7f) as u8;
410        rest >>= 7;
411        if n + 1 < PADDED {
412            *byte |= 0x80;
413        }
414    }
415    out
416}
417
418/// Push a name: its length and its bytes.
419pub fn name(out: &mut Vec<u8>, text: &str) {
420    uleb(out, text.len() as u64);
421    out.extend_from_slice(text.as_bytes());
422}
423
424/// The numbers that the fixups need, worked out once from the whole module.
425struct Layout {
426    /// The function index of each symbol that is a function.
427    function: Vec<Option<u32>>,
428    /// The global, table and tag index of each symbol of that kind.
429    other: Vec<Option<u32>>,
430    /// The address each segment starts at, if this object were the whole program.
431    segment: Vec<u32>,
432    /// The table slot of each function that a table index fixup names. Slot 0 stays empty, as in
433    /// the linker's output, so that a null function pointer traps.
434    slot: Vec<Option<u32>>,
435    /// Which function symbols a definition names.
436    defined: Vec<bool>,
437}
438
439impl Layout {
440    fn of(module: &Module) -> Result<Layout, Error> {
441        let count = module.symbols.len();
442        let mut defined = vec![false; count];
443        for function in &module.functions {
444            let index = function.symbol as usize;
445            let is_function = matches!(
446                module.symbols.get(index).map(|s| &s.kind),
447                Some(SymbolKind::Function { .. })
448            );
449            if !is_function || defined[index] {
450                return Err(Error::Definition { symbol: function.symbol });
451            }
452            defined[index] = true;
453        }
454        // An alias is defined when the function that it names is defined, by a definition or by
455        // an alias that comes before it in the list.
456        for &(alias, target) in &module.aliases {
457            let is_function = matches!(
458                module.symbols.get(alias as usize).map(|s| &s.kind),
459                Some(SymbolKind::Function { .. })
460            );
461            let named = defined.get(target as usize) == Some(&true);
462            if !is_function || defined[alias as usize] || !named {
463                return Err(Error::Alias { symbol: alias });
464            }
465            defined[alias as usize] = true;
466        }
467
468        // The imported functions come first in the index space, in symbol order, then the
469        // definitions in the order they are written.
470        let mut function = vec![None; count];
471        let mut next = 0u32;
472        for (index, symbol) in module.symbols.iter().enumerate() {
473            if matches!(symbol.kind, SymbolKind::Function { .. }) && !defined[index] {
474                function[index] = Some(next);
475                next += 1;
476            }
477        }
478        for f in &module.functions {
479            function[f.symbol as usize] = Some(next);
480            next += 1;
481        }
482        for &(alias, target) in &module.aliases {
483            function[alias as usize] = function[target as usize];
484        }
485
486        // Globals, tables and tags each have their own index space, in symbol order.
487        let mut other = vec![None; count];
488        let (mut globals, mut tables, mut tags) = (0u32, 0u32, 0u32);
489        for (index, symbol) in module.symbols.iter().enumerate() {
490            let counter = match symbol.kind {
491                SymbolKind::Global { .. } => &mut globals,
492                SymbolKind::Table { .. } => &mut tables,
493                SymbolKind::Tag { .. } => &mut tags,
494                _ => continue,
495            };
496            other[index] = Some(*counter);
497            *counter += 1;
498        }
499
500        let mut segment = Vec::with_capacity(module.segments.len());
501        let mut address = 0u64;
502        for s in &module.segments {
503            let align = 1u64 << s.align.min(31);
504            address = address.div_ceil(align) * align;
505            segment.push(u32::try_from(address).unwrap_or(u32::MAX));
506            address += s.bytes.len() as u64;
507        }
508        for (index, symbol) in module.symbols.iter().enumerate() {
509            if let SymbolKind::Data { place: Some(place) } = symbol.kind {
510                let fits = module.segments.get(place.segment as usize).is_some_and(|s| {
511                    u64::from(place.offset) + u64::from(place.size) <= s.bytes.len() as u64
512                });
513                if !fits {
514                    return Err(Error::Place { symbol: u32::try_from(index).unwrap_or(u32::MAX) });
515                }
516            }
517        }
518
519        let mut slot = vec![None; count];
520        let mut slots = 1u32;
521        let fixups = module
522            .functions
523            .iter()
524            .flat_map(|f| &f.fixups)
525            .chain(module.segments.iter().flat_map(|s| &s.fixups));
526        for fixup in fixups {
527            if !matches!(fixup.kind, RelocKind::TableIndexSleb | RelocKind::TableIndexI32) {
528                continue;
529            }
530            if let Some(entry @ None) = slot.get_mut(fixup.target as usize) {
531                *entry = Some(slots);
532                slots += 1;
533            }
534        }
535
536        Ok(Layout { function, other, segment, slot, defined })
537    }
538
539    /// Whether the symbol at `index` is defined in this object.
540    fn is_defined(&self, module: &Module, index: usize) -> bool {
541        match module.symbols[index].kind {
542            SymbolKind::Function { .. } => self.defined[index],
543            SymbolKind::Data { place } => place.is_some(),
544            SymbolKind::Global { .. } | SymbolKind::Table { .. } | SymbolKind::Tag { .. } => false,
545        }
546    }
547
548    /// The value a fixup's field gets, or why it cannot have one.
549    fn value(&self, module: &Module, fixup: Fixup) -> Result<i64, Error> {
550        let fail = |why| Err(Error::Target { fixup, why });
551        if fixup.kind.names_type() {
552            return if (fixup.target as usize) < module.types.len() {
553                Ok(i64::from(fixup.target))
554            } else {
555                fail("is not in the type list")
556            };
557        }
558        let index = fixup.target as usize;
559        let Some(symbol) = module.symbols.get(index) else {
560            return fail("is not in the symbol table");
561        };
562        match (fixup.kind, &symbol.kind) {
563            (RelocKind::FunctionIndexLeb, SymbolKind::Function { .. }) => {
564                Ok(i64::from(self.function[index].unwrap_or(0)))
565            }
566            (RelocKind::TableIndexSleb | RelocKind::TableIndexI32, SymbolKind::Function { .. }) => {
567                Ok(i64::from(self.slot[index].unwrap_or(0)))
568            }
569            (
570                RelocKind::MemoryAddrLeb | RelocKind::MemoryAddrSleb | RelocKind::MemoryAddrI32,
571                SymbolKind::Data { place },
572            ) => {
573                let base = place.map_or(0, |p| {
574                    i64::from(self.segment[p.segment as usize]) + i64::from(p.offset)
575                });
576                Ok(base + i64::from(fixup.addend))
577            }
578            (RelocKind::GlobalIndexLeb, SymbolKind::Global { .. })
579            | (RelocKind::TableNumberLeb, SymbolKind::Table { .. })
580            | (RelocKind::TagIndexLeb, SymbolKind::Tag { .. }) => {
581                Ok(i64::from(self.other[index].unwrap_or(0)))
582            }
583            _ => fail("is not of the kind the relocation wants"),
584        }
585    }
586}
587
588/// Write the field of `fixup` into `bytes`, which start at the start of the function code or the
589/// segment the fixup is in.
590fn patch(bytes: &mut [u8], fixup: Fixup, value: i64) -> Result<(), Error> {
591    let at = fixup.at as usize;
592    let Some(field) = bytes.get_mut(at..at + fixup.kind.width()) else {
593        return Err(Error::Outside { fixup });
594    };
595    // The values are indices and 32-bit addresses, so they fit. An address past 4 GiB in an
596    // object for a 32-bit memory is a bug in the layout above.
597    let value32 = value as i32;
598    match fixup.kind {
599        RelocKind::TableIndexI32 | RelocKind::MemoryAddrI32 => {
600            field.copy_from_slice(&value32.to_le_bytes());
601        }
602        RelocKind::TableIndexSleb | RelocKind::MemoryAddrSleb => {
603            field.copy_from_slice(&sleb_padded(value32));
604        }
605        _ => field.copy_from_slice(&uleb_padded(value as u32)),
606    }
607    Ok(())
608}
609
610/// One relocation entry, with its offset already from the start of the section payload.
611struct Entry {
612    offset: u32,
613    fixup: Fixup,
614}
615
616/// Push one section: its id, its size and its payload.
617fn section(out: &mut Vec<u8>, id: u8, payload: &[u8]) {
618    out.push(id);
619    uleb(out, payload.len() as u64);
620    out.extend_from_slice(payload);
621}
622
623/// Push one custom section with this name.
624fn custom(out: &mut Vec<u8>, label: &str, payload: &[u8]) {
625    let mut body = Vec::with_capacity(label.len() + 1 + payload.len());
626    name(&mut body, label);
627    body.extend_from_slice(payload);
628    section(out, 0, &body);
629}
630
631/// The module and the field a symbol is imported from.
632fn import_of(symbol: &Symbol) -> (String, String) {
633    let import = match &symbol.kind {
634        SymbolKind::Function { import, .. }
635        | SymbolKind::Global { import, .. }
636        | SymbolKind::Table { import }
637        | SymbolKind::Tag { import, .. } => import.as_ref(),
638        SymbolKind::Data { .. } => None,
639    };
640    match import {
641        Some(i) => (i.module.clone(), i.field.clone()),
642        None => ("env".to_owned(), symbol.name.clone()),
643    }
644}
645
646/// A length or a count, as the LEB128 functions take it.
647fn len32(n: usize) -> u64 {
648    n as u64
649}
650
651/// The object for `module`.
652///
653/// # Errors
654///
655/// When a fixup or a symbol refers to something that is not in the module. See [`Error`].
656pub fn write(module: &Module) -> Result<Written, Error> {
657    let layout = Layout::of(module)?;
658    let mut out = Vec::from(MAGIC);
659    // The index of each section in the file, which a reloc section names.
660    let mut sections = 0u32;
661
662    if !module.types.is_empty() {
663        let mut payload = Vec::new();
664        uleb(&mut payload, len32(module.types.len()));
665        for ty in &module.types {
666            payload.push(0x60);
667            uleb(&mut payload, len32(ty.params.len()));
668            payload.extend(ty.params.iter().map(|t| t.byte()));
669            uleb(&mut payload, len32(ty.results.len()));
670            payload.extend(ty.results.iter().map(|t| t.byte()));
671        }
672        section(&mut out, 1, &payload);
673        sections += 1;
674    }
675
676    // The imports: the memory, then each global, table, tag and function that the object
677    // refers to and does not define, in symbol order inside each kind. The memory always comes,
678    // because the linker defines it and every object shares it.
679    {
680        let data: u64 = layout.segment.last().map_or(0, |start| {
681            u64::from(*start) + module.segments.last().map_or(0, |s| s.bytes.len() as u64)
682        });
683        let mut entries: Vec<Vec<u8>> = Vec::new();
684        let mut memory = Vec::new();
685        name(&mut memory, "env");
686        name(&mut memory, "__linear_memory");
687        memory.push(0x02);
688        memory.push(0x00);
689        uleb(&mut memory, data.div_ceil(PAGE));
690        entries.push(memory);
691        let kinds: [fn(&SymbolKind) -> bool; 4] = [
692            |k| matches!(k, SymbolKind::Global { .. }),
693            |k| matches!(k, SymbolKind::Table { .. }),
694            |k| matches!(k, SymbolKind::Tag { .. }),
695            |k| matches!(k, SymbolKind::Function { .. }),
696        ];
697        for wanted in kinds {
698            for (index, symbol) in module.symbols.iter().enumerate() {
699                if !wanted(&symbol.kind) || layout.is_defined(module, index) {
700                    continue;
701                }
702                let (from, field) = import_of(symbol);
703                let mut entry = Vec::new();
704                name(&mut entry, &from);
705                name(&mut entry, &field);
706                match symbol.kind {
707                    SymbolKind::Function { ty, .. } => {
708                        entry.push(0x00);
709                        uleb(&mut entry, u64::from(ty));
710                    }
711                    SymbolKind::Table { .. } => {
712                        entry.extend_from_slice(&[0x01, 0x70, 0x00]);
713                        uleb(&mut entry, len32(layout.slot.iter().flatten().count()));
714                    }
715                    SymbolKind::Global { ty, mutable, .. } => {
716                        entry.extend_from_slice(&[0x03, ty.byte(), u8::from(mutable)]);
717                    }
718                    SymbolKind::Tag { ty, .. } => {
719                        entry.extend_from_slice(&[0x04, 0x00]);
720                        uleb(&mut entry, u64::from(ty));
721                    }
722                    SymbolKind::Data { .. } => unreachable!("data is never imported"),
723                }
724                entries.push(entry);
725            }
726        }
727        let mut payload = Vec::new();
728        uleb(&mut payload, len32(entries.len()));
729        for entry in entries {
730            payload.extend_from_slice(&entry);
731        }
732        section(&mut out, 2, &payload);
733        sections += 1;
734    }
735
736    if !module.functions.is_empty() {
737        let mut payload = Vec::new();
738        uleb(&mut payload, len32(module.functions.len()));
739        for f in &module.functions {
740            let SymbolKind::Function { ty, .. } = module.symbols[f.symbol as usize].kind else {
741                unreachable!("the layout checked that a definition names a function symbol");
742            };
743            uleb(&mut payload, u64::from(ty));
744        }
745        section(&mut out, 3, &payload);
746        sections += 1;
747    }
748
749    let exports: Vec<&Function> = module.functions.iter().filter(|f| f.export.is_some()).collect();
750    if !exports.is_empty() {
751        let mut payload = Vec::new();
752        uleb(&mut payload, len32(exports.len()));
753        for f in exports {
754            name(&mut payload, f.export.as_deref().unwrap_or_default());
755            payload.push(0x00);
756            uleb(&mut payload, u64::from(layout.function[f.symbol as usize].unwrap_or(0)));
757        }
758        section(&mut out, 7, &payload);
759        sections += 1;
760    }
761
762    // The code. Each body is its size, its locals and its instructions, and a fixup's offset is
763    // counted from the start of the payload, which is where the linker counts from.
764    let mut code_relocs = Vec::new();
765    let mut code_section = None;
766    if !module.functions.is_empty() {
767        let mut payload = Vec::new();
768        uleb(&mut payload, len32(module.functions.len()));
769        for f in &module.functions {
770            let mut body = Vec::new();
771            uleb(&mut body, len32(f.locals.len()));
772            for (count, ty) in &f.locals {
773                uleb(&mut body, u64::from(*count));
774                body.push(ty.byte());
775            }
776            let start = body.len();
777            body.extend_from_slice(&f.code);
778            for fixup in &f.fixups {
779                let value = layout.value(module, *fixup)?;
780                patch(&mut body[start..], *fixup, value)?;
781            }
782            uleb(&mut payload, len32(body.len()));
783            let base = payload.len() + start;
784            for fixup in &f.fixups {
785                let offset = u32::try_from(base + fixup.at as usize).unwrap_or(u32::MAX);
786                code_relocs.push(Entry { offset, fixup: *fixup });
787            }
788            payload.extend_from_slice(&body);
789        }
790        section(&mut out, 10, &payload);
791        code_section = Some(sections);
792        sections += 1;
793    }
794
795    // The data. Each segment is active in memory 0 at the address it would have if this were the
796    // whole program. The linker moves it.
797    let mut data_relocs = Vec::new();
798    let mut data_section = None;
799    if !module.segments.is_empty() {
800        let mut payload = Vec::new();
801        uleb(&mut payload, len32(module.segments.len()));
802        for (s, start) in module.segments.iter().zip(&layout.segment) {
803            payload.extend_from_slice(&[0x00, 0x41]);
804            sleb(&mut payload, i64::from(*start as i32));
805            payload.push(0x0b);
806            uleb(&mut payload, len32(s.bytes.len()));
807            let base = payload.len();
808            let mut bytes = s.bytes.clone();
809            for fixup in &s.fixups {
810                let value = layout.value(module, *fixup)?;
811                patch(&mut bytes, *fixup, value)?;
812                let offset = u32::try_from(base + fixup.at as usize).unwrap_or(u32::MAX);
813                data_relocs.push(Entry { offset, fixup: *fixup });
814            }
815            payload.extend_from_slice(&bytes);
816        }
817        section(&mut out, 11, &payload);
818        data_section = Some(sections);
819    }
820
821    custom(&mut out, "linking", &linking(module, &layout));
822    for (label, target, mut entries) in
823        [("reloc.CODE", code_section, code_relocs), ("reloc.DATA", data_section, data_relocs)]
824    {
825        let Some(target) = target else { continue };
826        if entries.is_empty() {
827            continue;
828        }
829        entries.sort_by_key(|e| e.offset);
830        let mut payload = Vec::new();
831        uleb(&mut payload, u64::from(target));
832        uleb(&mut payload, len32(entries.len()));
833        for entry in &entries {
834            payload.push(entry.fixup.kind.number());
835            uleb(&mut payload, u64::from(entry.offset));
836            uleb(&mut payload, u64::from(entry.fixup.target));
837            if entry.fixup.kind.has_addend() {
838                sleb(&mut payload, i64::from(entry.fixup.addend));
839            }
840        }
841        custom(&mut out, label, &payload);
842    }
843
844    if let Some(payload) = producers(&module.producers) {
845        custom(&mut out, "producers", &payload);
846    }
847    if let Some(payload) = target_features(module) {
848        custom(&mut out, "target_features", &payload);
849    }
850
851    let defines = module
852        .symbols
853        .iter()
854        .enumerate()
855        .filter(|(index, s)| layout.is_defined(module, *index) && s.flags & LOCAL == 0)
856        .map(|(_, s)| s.name.clone())
857        .collect();
858    Ok(Written { bytes: out, defines })
859}
860
861/// The payload of the `producers` section, or nothing when there is nothing to say. The `-S` text
862/// writes the same bytes into a custom section, so the two outputs cannot disagree about it.
863#[must_use]
864pub fn producers(producers: &Producers) -> Option<Vec<u8>> {
865    let fields: Vec<(&str, Vec<(&str, &str)>)> = [
866        ("language", producers.language.iter().map(|l| (l.as_str(), "")).collect::<Vec<_>>()),
867        (
868            "processed-by",
869            producers.processed_by.iter().map(|(n, v)| (n.as_str(), v.as_str())).collect(),
870        ),
871    ]
872    .into_iter()
873    .filter(|(_, values)| !values.is_empty())
874    .collect();
875    if fields.is_empty() {
876        return None;
877    }
878    let mut payload = Vec::new();
879    uleb(&mut payload, len32(fields.len()));
880    for (field, values) in fields {
881        name(&mut payload, field);
882        uleb(&mut payload, len32(values.len()));
883        for (value, version) in values {
884            name(&mut payload, value);
885            name(&mut payload, version);
886        }
887    }
888    Some(payload)
889}
890
891/// The payload of the `target_features` section, or nothing when the module names no feature.
892/// The `-S` text writes these bytes too.
893#[must_use]
894pub fn target_features(module: &Module) -> Option<Vec<u8>> {
895    if module.features.is_empty() && module.disallowed.is_empty() {
896        return None;
897    }
898    let mut payload = Vec::new();
899    uleb(&mut payload, len32(module.features.len() + module.disallowed.len()));
900    for (prefix, list) in [(b'+', &module.features), (b'-', &module.disallowed)] {
901        for feature in list {
902            payload.push(prefix);
903            name(&mut payload, feature);
904        }
905    }
906    Some(payload)
907}
908
909/// The payload of the `linking` section: the version, then the symbol table, the segment names
910/// and the constructors, in that fixed order so that two objects compare byte for byte.
911fn linking(module: &Module, layout: &Layout) -> Vec<u8> {
912    let mut payload = Vec::new();
913    uleb(&mut payload, u64::from(LINKING_VERSION));
914
915    let mut table = Vec::new();
916    uleb(&mut table, len32(module.symbols.len()));
917    for (index, symbol) in module.symbols.iter().enumerate() {
918        let defined = layout.is_defined(module, index);
919        let mut flags = symbol.flags & !(UNDEFINED | EXPLICIT_NAME);
920        if !defined {
921            flags |= UNDEFINED;
922        }
923        let explicit = !defined
924            && !matches!(symbol.kind, SymbolKind::Data { .. })
925            && import_of(symbol).1 != symbol.name;
926        if explicit {
927            flags |= EXPLICIT_NAME;
928        }
929        let kind = match symbol.kind {
930            SymbolKind::Function { .. } => 0,
931            SymbolKind::Data { .. } => 1,
932            SymbolKind::Global { .. } => 2,
933            SymbolKind::Tag { .. } => 4,
934            SymbolKind::Table { .. } => 5,
935        };
936        table.push(kind);
937        uleb(&mut table, u64::from(flags));
938        match symbol.kind {
939            SymbolKind::Data { place } => {
940                name(&mut table, &symbol.name);
941                if let Some(place) = place {
942                    uleb(&mut table, u64::from(place.segment));
943                    uleb(&mut table, u64::from(place.offset));
944                    uleb(&mut table, u64::from(place.size));
945                }
946            }
947            _ => {
948                let number = match symbol.kind {
949                    SymbolKind::Function { .. } => layout.function[index],
950                    _ => layout.other[index],
951                };
952                uleb(&mut table, u64::from(number.unwrap_or(0)));
953                if defined || explicit {
954                    name(&mut table, &symbol.name);
955                }
956            }
957        }
958    }
959    subsection(&mut payload, 8, &table);
960
961    if !module.segments.is_empty() {
962        let mut info = Vec::new();
963        uleb(&mut info, len32(module.segments.len()));
964        for s in &module.segments {
965            name(&mut info, &s.name);
966            uleb(&mut info, u64::from(s.align));
967            uleb(&mut info, u64::from(s.flags));
968        }
969        subsection(&mut payload, 5, &info);
970    }
971
972    if !module.inits.is_empty() {
973        let mut inits = Vec::new();
974        uleb(&mut inits, len32(module.inits.len()));
975        for (priority, symbol) in &module.inits {
976            uleb(&mut inits, u64::from(*priority));
977            uleb(&mut inits, u64::from(*symbol));
978        }
979        subsection(&mut payload, 6, &inits);
980    }
981    payload
982}
983
984fn subsection(out: &mut Vec<u8>, id: u8, payload: &[u8]) {
985    out.push(id);
986    uleb(out, len32(payload.len()));
987    out.extend_from_slice(payload);
988}