zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob 6f20e86b (215353B) - Raw


      1 const Wasm = @This();
      2 
      3 const std = @import("std");
      4 const builtin = @import("builtin");
      5 const mem = std.mem;
      6 const Allocator = std.mem.Allocator;
      7 const assert = std.debug.assert;
      8 const fs = std.fs;
      9 const leb = std.leb;
     10 const log = std.log.scoped(.link);
     11 
     12 pub const Atom = @import("Wasm/Atom.zig");
     13 const Dwarf = @import("Dwarf.zig");
     14 const Module = @import("../Module.zig");
     15 const InternPool = @import("../InternPool.zig");
     16 const Compilation = @import("../Compilation.zig");
     17 const CodeGen = @import("../arch/wasm/CodeGen.zig");
     18 const codegen = @import("../codegen.zig");
     19 const link = @import("../link.zig");
     20 const lldMain = @import("../main.zig").lldMain;
     21 const trace = @import("../tracy.zig").trace;
     22 const build_options = @import("build_options");
     23 const wasi_libc = @import("../wasi_libc.zig");
     24 const Cache = std.Build.Cache;
     25 const Type = @import("../type.zig").Type;
     26 const Value = @import("../value.zig").Value;
     27 const TypedValue = @import("../TypedValue.zig");
     28 const LlvmObject = @import("../codegen/llvm.zig").Object;
     29 const Air = @import("../Air.zig");
     30 const Liveness = @import("../Liveness.zig");
     31 const Symbol = @import("Wasm/Symbol.zig");
     32 const Object = @import("Wasm/Object.zig");
     33 const Archive = @import("Wasm/Archive.zig");
     34 const types = @import("Wasm/types.zig");
     35 pub const Relocation = types.Relocation;
     36 
     37 pub const base_tag: link.File.Tag = .wasm;
     38 
     39 base: link.File,
     40 entry_name: ?[]const u8,
     41 import_symbols: bool,
     42 export_symbol_names: []const []const u8,
     43 global_base: ?u64,
     44 initial_memory: ?u64,
     45 max_memory: ?u64,
     46 /// Output name of the file
     47 name: []const u8,
     48 /// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
     49 llvm_object: ?*LlvmObject = null,
     50 /// When importing objects from the host environment, a name must be supplied.
     51 /// LLVM uses "env" by default when none is given. This would be a good default for Zig
     52 /// to support existing code.
     53 /// TODO: Allow setting this through a flag?
     54 host_name: []const u8 = "env",
     55 /// List of all `Decl` that are currently alive.
     56 /// Each index maps to the corresponding `Atom.Index`.
     57 decls: std.AutoHashMapUnmanaged(InternPool.DeclIndex, Atom.Index) = .{},
     58 /// Mapping between an `Atom` and its type index representing the Wasm
     59 /// type of the function signature.
     60 atom_types: std.AutoHashMapUnmanaged(Atom.Index, u32) = .{},
     61 /// List of all symbols generated by Zig code.
     62 symbols: std.ArrayListUnmanaged(Symbol) = .{},
     63 /// List of symbol indexes which are free to be used.
     64 symbols_free_list: std.ArrayListUnmanaged(u32) = .{},
     65 /// Maps atoms to their segment index
     66 atoms: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{},
     67 /// List of all atoms.
     68 managed_atoms: std.ArrayListUnmanaged(Atom) = .{},
     69 /// Represents the index into `segments` where the 'code' section
     70 /// lives.
     71 code_section_index: ?u32 = null,
     72 /// The index of the segment representing the custom '.debug_info' section.
     73 debug_info_index: ?u32 = null,
     74 /// The index of the segment representing the custom '.debug_line' section.
     75 debug_line_index: ?u32 = null,
     76 /// The index of the segment representing the custom '.debug_loc' section.
     77 debug_loc_index: ?u32 = null,
     78 /// The index of the segment representing the custom '.debug_ranges' section.
     79 debug_ranges_index: ?u32 = null,
     80 /// The index of the segment representing the custom '.debug_pubnames' section.
     81 debug_pubnames_index: ?u32 = null,
     82 /// The index of the segment representing the custom '.debug_pubtypes' section.
     83 debug_pubtypes_index: ?u32 = null,
     84 /// The index of the segment representing the custom '.debug_pubtypes' section.
     85 debug_str_index: ?u32 = null,
     86 /// The index of the segment representing the custom '.debug_pubtypes' section.
     87 debug_abbrev_index: ?u32 = null,
     88 /// The count of imported functions. This number will be appended
     89 /// to the function indexes as their index starts at the lowest non-extern function.
     90 imported_functions_count: u32 = 0,
     91 /// The count of imported wasm globals. This number will be appended
     92 /// to the global indexes when sections are merged.
     93 imported_globals_count: u32 = 0,
     94 /// The count of imported tables. This number will be appended
     95 /// to the table indexes when sections are merged.
     96 imported_tables_count: u32 = 0,
     97 /// Map of symbol locations, represented by its `types.Import`
     98 imports: std.AutoHashMapUnmanaged(SymbolLoc, types.Import) = .{},
     99 /// Represents non-synthetic section entries.
    100 /// Used for code, data and custom sections.
    101 segments: std.ArrayListUnmanaged(Segment) = .{},
    102 /// Maps a data segment key (such as .rodata) to the index into `segments`.
    103 data_segments: std.StringArrayHashMapUnmanaged(u32) = .{},
    104 /// A table of `types.Segment` which provide meta data
    105 /// about a data symbol such as its name where the key is
    106 /// the segment index, which can be found from `data_segments`
    107 segment_info: std.AutoArrayHashMapUnmanaged(u32, types.Segment) = .{},
    108 /// Deduplicated string table for strings used by symbols, imports and exports.
    109 string_table: StringTable = .{},
    110 /// Debug information for wasm
    111 dwarf: ?Dwarf = null,
    112 
    113 // Output sections
    114 /// Output type section
    115 func_types: std.ArrayListUnmanaged(std.wasm.Type) = .{},
    116 /// Output function section where the key is the original
    117 /// function index and the value is function.
    118 /// This allows us to map multiple symbols to the same function.
    119 functions: std.AutoArrayHashMapUnmanaged(struct { file: ?u16, index: u32 }, struct { func: std.wasm.Func, sym_index: u32 }) = .{},
    120 /// Output global section
    121 wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .{},
    122 /// Memory section
    123 memories: std.wasm.Memory = .{ .limits = .{
    124     .min = 0,
    125     .max = undefined,
    126     .flags = 0,
    127 } },
    128 /// Output table section
    129 tables: std.ArrayListUnmanaged(std.wasm.Table) = .{},
    130 /// Output export section
    131 exports: std.ArrayListUnmanaged(types.Export) = .{},
    132 /// List of initialization functions. These must be called in order of priority
    133 /// by the (synthetic) __wasm_call_ctors function.
    134 init_funcs: std.ArrayListUnmanaged(InitFuncLoc) = .{},
    135 /// Index to a function defining the entry of the wasm file
    136 entry: ?u32 = null,
    137 
    138 /// Indirect function table, used to call function pointers
    139 /// When this is non-zero, we must emit a table entry,
    140 /// as well as an 'elements' section.
    141 ///
    142 /// Note: Key is symbol location, value represents the index into the table
    143 function_table: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{},
    144 
    145 /// All object files and their data which are linked into the final binary
    146 objects: std.ArrayListUnmanaged(Object) = .{},
    147 /// All archive files that are lazy loaded.
    148 /// e.g. when an undefined symbol references a symbol from the archive.
    149 archives: std.ArrayListUnmanaged(Archive) = .{},
    150 
    151 /// A map of global names (read: offset into string table) to their symbol location
    152 globals: std.AutoHashMapUnmanaged(u32, SymbolLoc) = .{},
    153 /// The list of GOT symbols and their location
    154 got_symbols: std.ArrayListUnmanaged(SymbolLoc) = .{},
    155 /// Maps discarded symbols and their positions to the location of the symbol
    156 /// it was resolved to
    157 discarded: std.AutoHashMapUnmanaged(SymbolLoc, SymbolLoc) = .{},
    158 /// List of all symbol locations which have been resolved by the linker and will be emit
    159 /// into the final binary.
    160 resolved_symbols: std.AutoArrayHashMapUnmanaged(SymbolLoc, void) = .{},
    161 /// Symbols that remain undefined after symbol resolution.
    162 /// Note: The key represents an offset into the string table, rather than the actual string.
    163 undefs: std.AutoArrayHashMapUnmanaged(u32, SymbolLoc) = .{},
    164 /// Maps a symbol's location to an atom. This can be used to find meta
    165 /// data of a symbol, such as its size, or its offset to perform a relocation.
    166 /// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped.
    167 symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, Atom.Index) = .{},
    168 /// Maps a symbol's location to its export name, which may differ from the decl's name
    169 /// which does the exporting.
    170 /// Note: The value represents the offset into the string table, rather than the actual string.
    171 export_names: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{},
    172 
    173 /// Represents the symbol index of the error name table
    174 /// When this is `null`, no code references an error using runtime `@errorName`.
    175 /// During initializion, a symbol with corresponding atom will be created that is
    176 /// used to perform relocations to the pointer of this table.
    177 /// The actual table is populated during `flush`.
    178 error_table_symbol: ?u32 = null,
    179 
    180 // Debug section atoms. These are only set when the current compilation
    181 // unit contains Zig code. The lifetime of these atoms are extended
    182 // until the end of the compiler's lifetime. Meaning they're not freed
    183 // during `flush()` in incremental-mode.
    184 debug_info_atom: ?Atom.Index = null,
    185 debug_line_atom: ?Atom.Index = null,
    186 debug_loc_atom: ?Atom.Index = null,
    187 debug_ranges_atom: ?Atom.Index = null,
    188 debug_abbrev_atom: ?Atom.Index = null,
    189 debug_str_atom: ?Atom.Index = null,
    190 debug_pubnames_atom: ?Atom.Index = null,
    191 debug_pubtypes_atom: ?Atom.Index = null,
    192 
    193 /// List of atom indexes of functions that are generated by the backend,
    194 /// rather than by the linker.
    195 synthetic_functions: std.ArrayListUnmanaged(Atom.Index) = .{},
    196 
    197 /// Map for storing anonymous declarations. Each anonymous decl maps to its Atom's index.
    198 anon_decls: std.AutoArrayHashMapUnmanaged(InternPool.Index, Atom.Index) = .{},
    199 
    200 import_table: bool,
    201 export_table: bool,
    202 
    203 pub const Alignment = types.Alignment;
    204 
    205 pub const Segment = struct {
    206     alignment: Alignment,
    207     size: u32,
    208     offset: u32,
    209     flags: u32,
    210 
    211     pub const Flag = enum(u32) {
    212         WASM_DATA_SEGMENT_IS_PASSIVE = 0x01,
    213         WASM_DATA_SEGMENT_HAS_MEMINDEX = 0x02,
    214     };
    215 
    216     pub fn isPassive(segment: Segment) bool {
    217         return segment.flags & @intFromEnum(Flag.WASM_DATA_SEGMENT_IS_PASSIVE) != 0;
    218     }
    219 
    220     /// For a given segment, determines if it needs passive initialization
    221     fn needsPassiveInitialization(segment: Segment, import_mem: bool, name: []const u8) bool {
    222         if (import_mem and !std.mem.eql(u8, name, ".bss")) {
    223             return true;
    224         }
    225         return segment.isPassive();
    226     }
    227 };
    228 
    229 pub const Export = struct {
    230     sym_index: ?u32 = null,
    231 };
    232 
    233 pub const SymbolLoc = struct {
    234     /// The index of the symbol within the specified file
    235     index: u32,
    236     /// The index of the object file where the symbol resides.
    237     /// When this is `null` the symbol comes from a non-object file.
    238     file: ?u16,
    239 
    240     /// From a given location, returns the corresponding symbol in the wasm binary
    241     pub fn getSymbol(loc: SymbolLoc, wasm_bin: *const Wasm) *Symbol {
    242         if (wasm_bin.discarded.get(loc)) |new_loc| {
    243             return new_loc.getSymbol(wasm_bin);
    244         }
    245         if (loc.file) |object_index| {
    246             const object = wasm_bin.objects.items[object_index];
    247             return &object.symtable[loc.index];
    248         }
    249         return &wasm_bin.symbols.items[loc.index];
    250     }
    251 
    252     /// From a given location, returns the name of the symbol.
    253     pub fn getName(loc: SymbolLoc, wasm_bin: *const Wasm) []const u8 {
    254         if (wasm_bin.discarded.get(loc)) |new_loc| {
    255             return new_loc.getName(wasm_bin);
    256         }
    257         if (loc.file) |object_index| {
    258             const object = wasm_bin.objects.items[object_index];
    259             return object.string_table.get(object.symtable[loc.index].name);
    260         }
    261         return wasm_bin.string_table.get(wasm_bin.symbols.items[loc.index].name);
    262     }
    263 
    264     /// From a given symbol location, returns the final location.
    265     /// e.g. when a symbol was resolved and replaced by the symbol
    266     /// in a different file, this will return said location.
    267     /// If the symbol wasn't replaced by another, this will return
    268     /// the given location itwasm.
    269     pub fn finalLoc(loc: SymbolLoc, wasm_bin: *const Wasm) SymbolLoc {
    270         if (wasm_bin.discarded.get(loc)) |new_loc| {
    271             return new_loc.finalLoc(wasm_bin);
    272         }
    273         return loc;
    274     }
    275 };
    276 
    277 // Contains the location of the function symbol, as well as
    278 /// the priority itself of the initialization function.
    279 pub const InitFuncLoc = struct {
    280     /// object file index in the list of objects.
    281     /// Unlike `SymbolLoc` this cannot be `null` as we never define
    282     /// our own ctors.
    283     file: u16,
    284     /// Symbol index within the corresponding object file.
    285     index: u32,
    286     /// The priority in which the constructor must be called.
    287     priority: u32,
    288 
    289     /// From a given `InitFuncLoc` returns the corresponding function symbol
    290     fn getSymbol(loc: InitFuncLoc, wasm: *const Wasm) *Symbol {
    291         return getSymbolLoc(loc).getSymbol(wasm);
    292     }
    293 
    294     /// Turns the given `InitFuncLoc` into a `SymbolLoc`
    295     fn getSymbolLoc(loc: InitFuncLoc) SymbolLoc {
    296         return .{ .file = loc.file, .index = loc.index };
    297     }
    298 
    299     /// Returns true when `lhs` has a higher priority (e.i. value closer to 0) than `rhs`.
    300     fn lessThan(ctx: void, lhs: InitFuncLoc, rhs: InitFuncLoc) bool {
    301         _ = ctx;
    302         return lhs.priority < rhs.priority;
    303     }
    304 };
    305 /// Generic string table that duplicates strings
    306 /// and converts them into offsets instead.
    307 pub const StringTable = struct {
    308     /// Table that maps string offsets, which is used to de-duplicate strings.
    309     /// Rather than having the offset map to the data, the `StringContext` holds all bytes of the string.
    310     /// The strings are stored as a contigious array where each string is zero-terminated.
    311     string_table: std.HashMapUnmanaged(
    312         u32,
    313         void,
    314         std.hash_map.StringIndexContext,
    315         std.hash_map.default_max_load_percentage,
    316     ) = .{},
    317     /// Holds the actual data of the string table.
    318     string_data: std.ArrayListUnmanaged(u8) = .{},
    319 
    320     /// Accepts a string and searches for a corresponding string.
    321     /// When found, de-duplicates the string and returns the existing offset instead.
    322     /// When the string is not found in the `string_table`, a new entry will be inserted
    323     /// and the new offset to its data will be returned.
    324     pub fn put(table: *StringTable, allocator: Allocator, string: []const u8) !u32 {
    325         const gop = try table.string_table.getOrPutContextAdapted(
    326             allocator,
    327             string,
    328             std.hash_map.StringIndexAdapter{ .bytes = &table.string_data },
    329             .{ .bytes = &table.string_data },
    330         );
    331         if (gop.found_existing) {
    332             const off = gop.key_ptr.*;
    333             log.debug("reusing string '{s}' at offset 0x{x}", .{ string, off });
    334             return off;
    335         }
    336 
    337         try table.string_data.ensureUnusedCapacity(allocator, string.len + 1);
    338         const offset = @as(u32, @intCast(table.string_data.items.len));
    339 
    340         log.debug("writing new string '{s}' at offset 0x{x}", .{ string, offset });
    341 
    342         table.string_data.appendSliceAssumeCapacity(string);
    343         table.string_data.appendAssumeCapacity(0);
    344 
    345         gop.key_ptr.* = offset;
    346 
    347         return offset;
    348     }
    349 
    350     /// From a given offset, returns its corresponding string value.
    351     /// Asserts offset does not exceed bounds.
    352     pub fn get(table: StringTable, off: u32) []const u8 {
    353         assert(off < table.string_data.items.len);
    354         return mem.sliceTo(@as([*:0]const u8, @ptrCast(table.string_data.items.ptr + off)), 0);
    355     }
    356 
    357     /// Returns the offset of a given string when it exists.
    358     /// Will return null if the given string does not yet exist within the string table.
    359     pub fn getOffset(table: *StringTable, string: []const u8) ?u32 {
    360         return table.string_table.getKeyAdapted(
    361             string,
    362             std.hash_map.StringIndexAdapter{ .bytes = &table.string_data },
    363         );
    364     }
    365 
    366     /// Frees all resources of the string table. Any references pointing
    367     /// to the strings will be invalid.
    368     pub fn deinit(table: *StringTable, allocator: Allocator) void {
    369         table.string_data.deinit(allocator);
    370         table.string_table.deinit(allocator);
    371         table.* = undefined;
    372     }
    373 };
    374 
    375 pub fn open(
    376     arena: Allocator,
    377     comp: *Compilation,
    378     emit: Compilation.Emit,
    379     options: link.File.OpenOptions,
    380 ) !*Wasm {
    381     // TODO: restore saved linker state, don't truncate the file, and
    382     // participate in incremental compilation.
    383     return createEmpty(arena, comp, emit, options);
    384 }
    385 
    386 pub fn createEmpty(
    387     arena: Allocator,
    388     comp: *Compilation,
    389     emit: Compilation.Emit,
    390     options: link.File.OpenOptions,
    391 ) !*Wasm {
    392     const gpa = comp.gpa;
    393     const target = comp.root_mod.resolved_target.result;
    394     assert(target.ofmt == .wasm);
    395 
    396     const use_lld = build_options.have_llvm and comp.config.use_lld;
    397     const use_llvm = comp.config.use_llvm;
    398     const output_mode = comp.config.output_mode;
    399     const shared_memory = comp.config.shared_memory;
    400     const wasi_exec_model = comp.config.wasi_exec_model;
    401 
    402     // If using LLD to link, this code should produce an object file so that it
    403     // can be passed to LLD.
    404     // If using LLVM to generate the object file for the zig compilation unit,
    405     // we need a place to put the object file so that it can be subsequently
    406     // handled.
    407     const zcu_object_sub_path = if (!use_lld and !use_llvm)
    408         null
    409     else
    410         try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path});
    411 
    412     const wasm = try arena.create(Wasm);
    413     wasm.* = .{
    414         .base = .{
    415             .tag = .wasm,
    416             .comp = comp,
    417             .emit = emit,
    418             .zcu_object_sub_path = zcu_object_sub_path,
    419             .gc_sections = options.gc_sections orelse (output_mode != .Obj),
    420             .print_gc_sections = options.print_gc_sections,
    421             .stack_size = options.stack_size orelse std.wasm.page_size * 16, // 1MB
    422             .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
    423             .file = null,
    424             .disable_lld_caching = options.disable_lld_caching,
    425             .build_id = options.build_id,
    426             .rpath_list = options.rpath_list,
    427         },
    428         .name = undefined,
    429         .import_table = options.import_table,
    430         .export_table = options.export_table,
    431         .import_symbols = options.import_symbols,
    432         .export_symbol_names = options.export_symbol_names,
    433         .global_base = options.global_base,
    434         .initial_memory = options.initial_memory,
    435         .max_memory = options.max_memory,
    436 
    437         .entry_name = switch (options.entry) {
    438             .disabled => null,
    439             .default => if (output_mode != .Exe) null else defaultEntrySymbolName(wasi_exec_model),
    440             .enabled => defaultEntrySymbolName(wasi_exec_model),
    441             .named => |name| name,
    442         },
    443     };
    444     if (use_llvm and comp.config.have_zcu) {
    445         wasm.llvm_object = try LlvmObject.create(arena, comp);
    446     }
    447     errdefer wasm.base.destroy();
    448 
    449     if (use_lld and (use_llvm or !comp.config.have_zcu)) {
    450         // LLVM emits the object file (if any); LLD links it into the final product.
    451         return wasm;
    452     }
    453 
    454     // What path should this Wasm linker code output to?
    455     // If using LLD to link, this code should produce an object file so that it
    456     // can be passed to LLD.
    457     const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path;
    458 
    459     const file = try emit.directory.handle.createFile(sub_path, .{
    460         .truncate = true,
    461         .read = true,
    462         .mode = if (fs.has_executable_bit)
    463             if (target.os.tag == .wasi and output_mode == .Exe)
    464                 fs.File.default_mode | 0b001_000_000
    465             else
    466                 fs.File.default_mode
    467         else
    468             0,
    469     });
    470     wasm.base.file = file;
    471     wasm.name = sub_path;
    472 
    473     // create stack pointer symbol
    474     {
    475         const loc = try wasm.createSyntheticSymbol("__stack_pointer", .global);
    476         const symbol = loc.getSymbol(wasm);
    477         // For object files we will import the stack pointer symbol
    478         if (output_mode == .Obj) {
    479             symbol.setUndefined(true);
    480             symbol.index = @intCast(wasm.imported_globals_count);
    481             wasm.imported_globals_count += 1;
    482             try wasm.imports.putNoClobber(
    483                 gpa,
    484                 loc,
    485                 .{
    486                     .module_name = try wasm.string_table.put(gpa, wasm.host_name),
    487                     .name = symbol.name,
    488                     .kind = .{ .global = .{ .valtype = .i32, .mutable = true } },
    489                 },
    490             );
    491         } else {
    492             symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
    493             symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
    494             const global = try wasm.wasm_globals.addOne(gpa);
    495             global.* = .{
    496                 .global_type = .{
    497                     .valtype = .i32,
    498                     .mutable = true,
    499                 },
    500                 .init = .{ .i32_const = 0 },
    501             };
    502         }
    503     }
    504 
    505     // create indirect function pointer symbol
    506     {
    507         const loc = try wasm.createSyntheticSymbol("__indirect_function_table", .table);
    508         const symbol = loc.getSymbol(wasm);
    509         const table: std.wasm.Table = .{
    510             .limits = .{ .flags = 0, .min = 0, .max = undefined }, // will be overwritten during `mapFunctionTable`
    511             .reftype = .funcref,
    512         };
    513         if (output_mode == .Obj or options.import_table) {
    514             symbol.setUndefined(true);
    515             symbol.index = @intCast(wasm.imported_tables_count);
    516             wasm.imported_tables_count += 1;
    517             try wasm.imports.put(gpa, loc, .{
    518                 .module_name = try wasm.string_table.put(gpa, wasm.host_name),
    519                 .name = symbol.name,
    520                 .kind = .{ .table = table },
    521             });
    522         } else {
    523             symbol.index = @as(u32, @intCast(wasm.imported_tables_count + wasm.tables.items.len));
    524             try wasm.tables.append(gpa, table);
    525             if (wasm.export_table) {
    526                 symbol.setFlag(.WASM_SYM_EXPORTED);
    527             } else {
    528                 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
    529             }
    530         }
    531     }
    532 
    533     // create __wasm_call_ctors
    534     {
    535         const loc = try wasm.createSyntheticSymbol("__wasm_call_ctors", .function);
    536         const symbol = loc.getSymbol(wasm);
    537         symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
    538         // we do not know the function index until after we merged all sections.
    539         // Therefore we set `symbol.index` and create its corresponding references
    540         // at the end during `initializeCallCtorsFunction`.
    541     }
    542 
    543     // shared-memory symbols for TLS support
    544     if (shared_memory) {
    545         {
    546             const loc = try wasm.createSyntheticSymbol("__tls_base", .global);
    547             const symbol = loc.getSymbol(wasm);
    548             symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
    549             symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
    550             try wasm.wasm_globals.append(gpa, .{
    551                 .global_type = .{ .valtype = .i32, .mutable = true },
    552                 .init = .{ .i32_const = undefined },
    553             });
    554         }
    555         {
    556             const loc = try wasm.createSyntheticSymbol("__tls_size", .global);
    557             const symbol = loc.getSymbol(wasm);
    558             symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
    559             symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
    560             try wasm.wasm_globals.append(gpa, .{
    561                 .global_type = .{ .valtype = .i32, .mutable = false },
    562                 .init = .{ .i32_const = undefined },
    563             });
    564         }
    565         {
    566             const loc = try wasm.createSyntheticSymbol("__tls_align", .global);
    567             const symbol = loc.getSymbol(wasm);
    568             symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
    569             symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
    570             try wasm.wasm_globals.append(gpa, .{
    571                 .global_type = .{ .valtype = .i32, .mutable = false },
    572                 .init = .{ .i32_const = undefined },
    573             });
    574         }
    575         {
    576             const loc = try wasm.createSyntheticSymbol("__wasm_init_tls", .function);
    577             const symbol = loc.getSymbol(wasm);
    578             symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
    579         }
    580     }
    581 
    582     return wasm;
    583 }
    584 
    585 /// For a given name, creates a new global synthetic symbol.
    586 /// Leaves index undefined and the default flags (0).
    587 fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !SymbolLoc {
    588     const gpa = wasm.base.comp.gpa;
    589     const name_offset = try wasm.string_table.put(gpa, name);
    590     return wasm.createSyntheticSymbolOffset(name_offset, tag);
    591 }
    592 
    593 fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) !SymbolLoc {
    594     const sym_index = @as(u32, @intCast(wasm.symbols.items.len));
    595     const loc: SymbolLoc = .{ .index = sym_index, .file = null };
    596     const gpa = wasm.base.comp.gpa;
    597     try wasm.symbols.append(gpa, .{
    598         .name = name_offset,
    599         .flags = 0,
    600         .tag = tag,
    601         .index = undefined,
    602         .virtual_address = undefined,
    603     });
    604     try wasm.resolved_symbols.putNoClobber(gpa, loc, {});
    605     try wasm.globals.put(gpa, name_offset, loc);
    606     return loc;
    607 }
    608 
    609 /// Initializes symbols and atoms for the debug sections
    610 /// Initialization is only done when compiling Zig code.
    611 /// When Zig is invoked as a linker instead, the atoms
    612 /// and symbols come from the object files instead.
    613 pub fn initDebugSections(wasm: *Wasm) !void {
    614     if (wasm.dwarf == null) return; // not compiling Zig code, so no need to pre-initialize debug sections
    615     assert(wasm.debug_info_index == null);
    616     // this will create an Atom and set the index for us.
    617     wasm.debug_info_atom = try wasm.createDebugSectionForIndex(&wasm.debug_info_index, ".debug_info");
    618     wasm.debug_line_atom = try wasm.createDebugSectionForIndex(&wasm.debug_line_index, ".debug_line");
    619     wasm.debug_loc_atom = try wasm.createDebugSectionForIndex(&wasm.debug_loc_index, ".debug_loc");
    620     wasm.debug_abbrev_atom = try wasm.createDebugSectionForIndex(&wasm.debug_abbrev_index, ".debug_abbrev");
    621     wasm.debug_ranges_atom = try wasm.createDebugSectionForIndex(&wasm.debug_ranges_index, ".debug_ranges");
    622     wasm.debug_str_atom = try wasm.createDebugSectionForIndex(&wasm.debug_str_index, ".debug_str");
    623     wasm.debug_pubnames_atom = try wasm.createDebugSectionForIndex(&wasm.debug_pubnames_index, ".debug_pubnames");
    624     wasm.debug_pubtypes_atom = try wasm.createDebugSectionForIndex(&wasm.debug_pubtypes_index, ".debug_pubtypes");
    625 }
    626 
    627 fn parseInputFiles(wasm: *Wasm, files: []const []const u8) !void {
    628     for (files) |path| {
    629         if (try wasm.parseObjectFile(path)) continue;
    630         if (try wasm.parseArchive(path, false)) continue; // load archives lazily
    631         log.warn("Unexpected file format at path: '{s}'", .{path});
    632     }
    633 }
    634 
    635 /// Parses the object file from given path. Returns true when the given file was an object
    636 /// file and parsed successfully. Returns false when file is not an object file.
    637 /// May return an error instead when parsing failed.
    638 fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {
    639     const file = try fs.cwd().openFile(path, .{});
    640     errdefer file.close();
    641 
    642     const gpa = wasm.base.comp.gpa;
    643     var object = Object.create(gpa, file, path, null) catch |err| switch (err) {
    644         error.InvalidMagicByte, error.NotObjectFile => return false,
    645         else => |e| return e,
    646     };
    647     errdefer object.deinit(gpa);
    648     try wasm.objects.append(gpa, object);
    649     return true;
    650 }
    651 
    652 /// For a given `InternPool.DeclIndex` returns its corresponding `Atom.Index`.
    653 /// When the index was not found, a new `Atom` will be created, and its index will be returned.
    654 /// The newly created Atom is empty with default fields as specified by `Atom.empty`.
    655 pub fn getOrCreateAtomForDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) !Atom.Index {
    656     const gpa = wasm.base.comp.gpa;
    657     const gop = try wasm.decls.getOrPut(gpa, decl_index);
    658     if (!gop.found_existing) {
    659         const atom_index = try wasm.createAtom();
    660         gop.value_ptr.* = atom_index;
    661         const atom = wasm.getAtom(atom_index);
    662         const symbol = atom.symbolLoc().getSymbol(wasm);
    663         const mod = wasm.base.comp.module.?;
    664         const decl = mod.declPtr(decl_index);
    665         const full_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
    666         symbol.name = try wasm.string_table.put(gpa, full_name);
    667     }
    668     return gop.value_ptr.*;
    669 }
    670 
    671 /// Creates a new empty `Atom` and returns its `Atom.Index`
    672 fn createAtom(wasm: *Wasm) !Atom.Index {
    673     const gpa = wasm.base.comp.gpa;
    674     const index: Atom.Index = @intCast(wasm.managed_atoms.items.len);
    675     const atom = try wasm.managed_atoms.addOne(gpa);
    676     atom.* = Atom.empty;
    677     atom.sym_index = try wasm.allocateSymbol();
    678     try wasm.symbol_atom.putNoClobber(gpa, .{ .file = null, .index = atom.sym_index }, index);
    679 
    680     return index;
    681 }
    682 
    683 pub inline fn getAtom(wasm: *const Wasm, index: Atom.Index) Atom {
    684     return wasm.managed_atoms.items[index];
    685 }
    686 
    687 pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom {
    688     return &wasm.managed_atoms.items[index];
    689 }
    690 
    691 /// Parses an archive file and will then parse each object file
    692 /// that was found in the archive file.
    693 /// Returns false when the file is not an archive file.
    694 /// May return an error instead when parsing failed.
    695 ///
    696 /// When `force_load` is `true`, it will for link all object files in the archive.
    697 /// When false, it will only link with object files that contain symbols that
    698 /// are referenced by other object files or Zig code.
    699 fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
    700     const gpa = wasm.base.comp.gpa;
    701 
    702     const file = try fs.cwd().openFile(path, .{});
    703     errdefer file.close();
    704 
    705     var archive: Archive = .{
    706         .file = file,
    707         .name = path,
    708     };
    709     archive.parse(gpa) catch |err| switch (err) {
    710         error.EndOfStream, error.NotArchive => {
    711             archive.deinit(gpa);
    712             return false;
    713         },
    714         else => |e| return e,
    715     };
    716 
    717     if (!force_load) {
    718         errdefer archive.deinit(gpa);
    719         try wasm.archives.append(gpa, archive);
    720         return true;
    721     }
    722     defer archive.deinit(gpa);
    723 
    724     // In this case we must force link all embedded object files within the archive
    725     // We loop over all symbols, and then group them by offset as the offset
    726     // notates where the object file starts.
    727     var offsets = std.AutoArrayHashMap(u32, void).init(gpa);
    728     defer offsets.deinit();
    729     for (archive.toc.values()) |symbol_offsets| {
    730         for (symbol_offsets.items) |sym_offset| {
    731             try offsets.put(sym_offset, {});
    732         }
    733     }
    734 
    735     for (offsets.keys()) |file_offset| {
    736         const object = try wasm.objects.addOne(gpa);
    737         object.* = try archive.parseObject(gpa, file_offset);
    738     }
    739 
    740     return true;
    741 }
    742 
    743 fn requiresTLSReloc(wasm: *const Wasm) bool {
    744     for (wasm.got_symbols.items) |loc| {
    745         if (loc.getSymbol(wasm).isTLS()) {
    746             return true;
    747         }
    748     }
    749     return false;
    750 }
    751 
    752 fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
    753     const gpa = wasm.base.comp.gpa;
    754     const object: Object = wasm.objects.items[object_index];
    755     log.debug("Resolving symbols in object: '{s}'", .{object.name});
    756 
    757     for (object.symtable, 0..) |symbol, i| {
    758         const sym_index = @as(u32, @intCast(i));
    759         const location: SymbolLoc = .{
    760             .file = object_index,
    761             .index = sym_index,
    762         };
    763         const sym_name = object.string_table.get(symbol.name);
    764         if (mem.eql(u8, sym_name, "__indirect_function_table")) {
    765             continue;
    766         }
    767         const sym_name_index = try wasm.string_table.put(gpa, sym_name);
    768 
    769         if (symbol.isLocal()) {
    770             if (symbol.isUndefined()) {
    771                 log.err("Local symbols are not allowed to reference imports", .{});
    772                 log.err("  symbol '{s}' defined in '{s}'", .{ sym_name, object.name });
    773                 return error.UndefinedLocal;
    774             }
    775             try wasm.resolved_symbols.putNoClobber(gpa, location, {});
    776             continue;
    777         }
    778 
    779         const maybe_existing = try wasm.globals.getOrPut(gpa, sym_name_index);
    780         if (!maybe_existing.found_existing) {
    781             maybe_existing.value_ptr.* = location;
    782             try wasm.resolved_symbols.putNoClobber(gpa, location, {});
    783 
    784             if (symbol.isUndefined()) {
    785                 try wasm.undefs.putNoClobber(gpa, sym_name_index, location);
    786             }
    787             continue;
    788         }
    789 
    790         const existing_loc = maybe_existing.value_ptr.*;
    791         const existing_sym: *Symbol = existing_loc.getSymbol(wasm);
    792 
    793         const existing_file_path = if (existing_loc.file) |file| blk: {
    794             break :blk wasm.objects.items[file].name;
    795         } else wasm.name;
    796 
    797         if (!existing_sym.isUndefined()) outer: {
    798             if (!symbol.isUndefined()) inner: {
    799                 if (symbol.isWeak()) {
    800                     break :inner; // ignore the new symbol (discard it)
    801                 }
    802                 if (existing_sym.isWeak()) {
    803                     break :outer; // existing is weak, while new one isn't. Replace it.
    804                 }
    805                 // both are defined and weak, we have a symbol collision.
    806                 log.err("symbol '{s}' defined multiple times", .{sym_name});
    807                 log.err("  first definition in '{s}'", .{existing_file_path});
    808                 log.err("  next definition in '{s}'", .{object.name});
    809                 return error.SymbolCollision;
    810             }
    811 
    812             try wasm.discarded.put(gpa, location, existing_loc);
    813             continue; // Do not overwrite defined symbols with undefined symbols
    814         }
    815 
    816         if (symbol.tag != existing_sym.tag) {
    817             log.err("symbol '{s}' mismatching type '{s}", .{ sym_name, @tagName(symbol.tag) });
    818             log.err("  first definition in '{s}'", .{existing_file_path});
    819             log.err("  next definition in '{s}'", .{object.name});
    820             return error.SymbolMismatchingType;
    821         }
    822 
    823         if (existing_sym.isUndefined() and symbol.isUndefined()) {
    824             // only verify module/import name for function symbols
    825             if (symbol.tag == .function) {
    826                 const existing_name = if (existing_loc.file) |file_index| blk: {
    827                     const obj = wasm.objects.items[file_index];
    828                     const name_index = obj.findImport(symbol.tag.externalType(), existing_sym.index).module_name;
    829                     break :blk obj.string_table.get(name_index);
    830                 } else blk: {
    831                     const name_index = wasm.imports.get(existing_loc).?.module_name;
    832                     break :blk wasm.string_table.get(name_index);
    833                 };
    834 
    835                 const module_index = object.findImport(symbol.tag.externalType(), symbol.index).module_name;
    836                 const module_name = object.string_table.get(module_index);
    837                 if (!mem.eql(u8, existing_name, module_name)) {
    838                     log.err("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{
    839                         sym_name,
    840                         existing_name,
    841                         module_name,
    842                     });
    843                     log.err("  first definition in '{s}'", .{existing_file_path});
    844                     log.err("  next definition in '{s}'", .{object.name});
    845                     return error.ModuleNameMismatch;
    846                 }
    847             }
    848 
    849             // both undefined so skip overwriting existing symbol and discard the new symbol
    850             try wasm.discarded.put(gpa, location, existing_loc);
    851             continue;
    852         }
    853 
    854         if (existing_sym.tag == .global) {
    855             const existing_ty = wasm.getGlobalType(existing_loc);
    856             const new_ty = wasm.getGlobalType(location);
    857             if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) {
    858                 log.err("symbol '{s}' mismatching global types", .{sym_name});
    859                 log.err("  first definition in '{s}'", .{existing_file_path});
    860                 log.err("  next definition in '{s}'", .{object.name});
    861                 return error.GlobalTypeMismatch;
    862             }
    863         }
    864 
    865         if (existing_sym.tag == .function) {
    866             const existing_ty = wasm.getFunctionSignature(existing_loc);
    867             const new_ty = wasm.getFunctionSignature(location);
    868             if (!existing_ty.eql(new_ty)) {
    869                 log.err("symbol '{s}' mismatching function signatures.", .{sym_name});
    870                 log.err("  expected signature {}, but found signature {}", .{ existing_ty, new_ty });
    871                 log.err("  first definition in '{s}'", .{existing_file_path});
    872                 log.err("  next definition in '{s}'", .{object.name});
    873                 return error.FunctionSignatureMismatch;
    874             }
    875         }
    876 
    877         // when both symbols are weak, we skip overwriting unless the existing
    878         // symbol is weak and the new one isn't, in which case we *do* overwrite it.
    879         if (existing_sym.isWeak() and symbol.isWeak()) blk: {
    880             if (existing_sym.isUndefined() and !symbol.isUndefined()) break :blk;
    881             try wasm.discarded.put(gpa, location, existing_loc);
    882             continue;
    883         }
    884 
    885         // simply overwrite with the new symbol
    886         log.debug("Overwriting symbol '{s}'", .{sym_name});
    887         log.debug("  old definition in '{s}'", .{existing_file_path});
    888         log.debug("  new definition in '{s}'", .{object.name});
    889         try wasm.discarded.putNoClobber(gpa, existing_loc, location);
    890         maybe_existing.value_ptr.* = location;
    891         try wasm.globals.put(gpa, sym_name_index, location);
    892         try wasm.resolved_symbols.put(gpa, location, {});
    893         assert(wasm.resolved_symbols.swapRemove(existing_loc));
    894         if (existing_sym.isUndefined()) {
    895             _ = wasm.undefs.swapRemove(sym_name_index);
    896         }
    897     }
    898 }
    899 
    900 fn resolveSymbolsInArchives(wasm: *Wasm) !void {
    901     const gpa = wasm.base.comp.gpa;
    902     if (wasm.archives.items.len == 0) return;
    903 
    904     log.debug("Resolving symbols in archives", .{});
    905     var index: u32 = 0;
    906     undef_loop: while (index < wasm.undefs.count()) {
    907         const sym_name_index = wasm.undefs.keys()[index];
    908 
    909         for (wasm.archives.items) |archive| {
    910             const sym_name = wasm.string_table.get(sym_name_index);
    911             log.debug("Detected symbol '{s}' in archive '{s}', parsing objects..", .{ sym_name, archive.name });
    912             const offset = archive.toc.get(sym_name) orelse {
    913                 // symbol does not exist in this archive
    914                 continue;
    915             };
    916 
    917             // Symbol is found in unparsed object file within current archive.
    918             // Parse object and and resolve symbols again before we check remaining
    919             // undefined symbols.
    920             const object_file_index: u16 = @intCast(wasm.objects.items.len);
    921             const object = try archive.parseObject(gpa, offset.items[0]);
    922             try wasm.objects.append(gpa, object);
    923             try wasm.resolveSymbolsInObject(object_file_index);
    924 
    925             // continue loop for any remaining undefined symbols that still exist
    926             // after resolving last object file
    927             continue :undef_loop;
    928         }
    929         index += 1;
    930     }
    931 }
    932 
    933 /// Writes an unsigned 32-bit integer as a LEB128-encoded 'i32.const' value.
    934 fn writeI32Const(writer: anytype, val: u32) !void {
    935     try writer.writeByte(std.wasm.opcode(.i32_const));
    936     try leb.writeILEB128(writer, @as(i32, @bitCast(val)));
    937 }
    938 
    939 fn setupInitMemoryFunction(wasm: *Wasm) !void {
    940     const comp = wasm.base.comp;
    941     const gpa = comp.gpa;
    942     const shared_memory = comp.config.shared_memory;
    943     const import_memory = comp.config.import_memory;
    944 
    945     // Passive segments are used to avoid memory being reinitialized on each
    946     // thread's instantiation. These passive segments are initialized and
    947     // dropped in __wasm_init_memory, which is registered as the start function
    948     // We also initialize bss segments (using memory.fill) as part of this
    949     // function.
    950     if (!wasm.hasPassiveInitializationSegments()) {
    951         return;
    952     }
    953 
    954     const flag_address: u32 = if (shared_memory) address: {
    955         // when we have passive initialization segments and shared memory
    956         // `setupMemory` will create this symbol and set its virtual address.
    957         const loc = wasm.findGlobalSymbol("__wasm_init_memory_flag").?;
    958         break :address loc.getSymbol(wasm).virtual_address;
    959     } else 0;
    960 
    961     var function_body = std.ArrayList(u8).init(gpa);
    962     defer function_body.deinit();
    963     const writer = function_body.writer();
    964 
    965     // we have 0 locals
    966     try leb.writeULEB128(writer, @as(u32, 0));
    967 
    968     if (shared_memory) {
    969         // destination blocks
    970         // based on values we jump to corresponding label
    971         try writer.writeByte(std.wasm.opcode(.block)); // $drop
    972         try writer.writeByte(std.wasm.block_empty); // block type
    973 
    974         try writer.writeByte(std.wasm.opcode(.block)); // $wait
    975         try writer.writeByte(std.wasm.block_empty); // block type
    976 
    977         try writer.writeByte(std.wasm.opcode(.block)); // $init
    978         try writer.writeByte(std.wasm.block_empty); // block type
    979 
    980         // atomically check
    981         try writeI32Const(writer, flag_address);
    982         try writeI32Const(writer, 0);
    983         try writeI32Const(writer, 1);
    984         try writer.writeByte(std.wasm.opcode(.atomics_prefix));
    985         try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.i32_atomic_rmw_cmpxchg));
    986         try leb.writeULEB128(writer, @as(u32, 2)); // alignment
    987         try leb.writeULEB128(writer, @as(u32, 0)); // offset
    988 
    989         // based on the value from the atomic check, jump to the label.
    990         try writer.writeByte(std.wasm.opcode(.br_table));
    991         try leb.writeULEB128(writer, @as(u32, 2)); // length of the table (we have 3 blocks but because of the mandatory default the length is 2).
    992         try leb.writeULEB128(writer, @as(u32, 0)); // $init
    993         try leb.writeULEB128(writer, @as(u32, 1)); // $wait
    994         try leb.writeULEB128(writer, @as(u32, 2)); // $drop
    995         try writer.writeByte(std.wasm.opcode(.end));
    996     }
    997 
    998     var it = wasm.data_segments.iterator();
    999     var segment_index: u32 = 0;
   1000     while (it.next()) |entry| : (segment_index += 1) {
   1001         const segment: Segment = wasm.segments.items[entry.value_ptr.*];
   1002         if (segment.needsPassiveInitialization(import_memory, entry.key_ptr.*)) {
   1003             // For passive BSS segments we can simple issue a memory.fill(0).
   1004             // For non-BSS segments we do a memory.init.  Both these
   1005             // instructions take as their first argument the destination
   1006             // address.
   1007             try writeI32Const(writer, segment.offset);
   1008 
   1009             if (shared_memory and std.mem.eql(u8, entry.key_ptr.*, ".tdata")) {
   1010                 // When we initialize the TLS segment we also set the `__tls_base`
   1011                 // global.  This allows the runtime to use this static copy of the
   1012                 // TLS data for the first/main thread.
   1013                 try writeI32Const(writer, segment.offset);
   1014                 try writer.writeByte(std.wasm.opcode(.global_set));
   1015                 const loc = wasm.findGlobalSymbol("__tls_base").?;
   1016                 try leb.writeULEB128(writer, loc.getSymbol(wasm).index);
   1017             }
   1018 
   1019             try writeI32Const(writer, 0);
   1020             try writeI32Const(writer, segment.size);
   1021             try writer.writeByte(std.wasm.opcode(.misc_prefix));
   1022             if (std.mem.eql(u8, entry.key_ptr.*, ".bss")) {
   1023                 // fill bss segment with zeroes
   1024                 try leb.writeULEB128(writer, std.wasm.miscOpcode(.memory_fill));
   1025             } else {
   1026                 // initialize the segment
   1027                 try leb.writeULEB128(writer, std.wasm.miscOpcode(.memory_init));
   1028                 try leb.writeULEB128(writer, segment_index);
   1029             }
   1030             try writer.writeByte(0); // memory index immediate
   1031         }
   1032     }
   1033 
   1034     if (shared_memory) {
   1035         // we set the init memory flag to value '2'
   1036         try writeI32Const(writer, flag_address);
   1037         try writeI32Const(writer, 2);
   1038         try writer.writeByte(std.wasm.opcode(.atomics_prefix));
   1039         try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.i32_atomic_store));
   1040         try leb.writeULEB128(writer, @as(u32, 2)); // alignment
   1041         try leb.writeULEB128(writer, @as(u32, 0)); // offset
   1042 
   1043         // notify any waiters for segment initialization completion
   1044         try writeI32Const(writer, flag_address);
   1045         try writer.writeByte(std.wasm.opcode(.i32_const));
   1046         try leb.writeILEB128(writer, @as(i32, -1)); // number of waiters
   1047         try writer.writeByte(std.wasm.opcode(.atomics_prefix));
   1048         try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.memory_atomic_notify));
   1049         try leb.writeULEB128(writer, @as(u32, 2)); // alignment
   1050         try leb.writeULEB128(writer, @as(u32, 0)); // offset
   1051         try writer.writeByte(std.wasm.opcode(.drop));
   1052 
   1053         // branch and drop segments
   1054         try writer.writeByte(std.wasm.opcode(.br));
   1055         try leb.writeULEB128(writer, @as(u32, 1));
   1056 
   1057         // wait for thread to initialize memory segments
   1058         try writer.writeByte(std.wasm.opcode(.end)); // end $wait
   1059         try writeI32Const(writer, flag_address);
   1060         try writeI32Const(writer, 1); // expected flag value
   1061         try writer.writeByte(std.wasm.opcode(.i64_const));
   1062         try leb.writeILEB128(writer, @as(i64, -1)); // timeout
   1063         try writer.writeByte(std.wasm.opcode(.atomics_prefix));
   1064         try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.memory_atomic_wait32));
   1065         try leb.writeULEB128(writer, @as(u32, 2)); // alignment
   1066         try leb.writeULEB128(writer, @as(u32, 0)); // offset
   1067         try writer.writeByte(std.wasm.opcode(.drop));
   1068 
   1069         try writer.writeByte(std.wasm.opcode(.end)); // end $drop
   1070     }
   1071 
   1072     it.reset();
   1073     segment_index = 0;
   1074     while (it.next()) |entry| : (segment_index += 1) {
   1075         const name = entry.key_ptr.*;
   1076         const segment: Segment = wasm.segments.items[entry.value_ptr.*];
   1077         if (segment.needsPassiveInitialization(import_memory, name) and
   1078             !std.mem.eql(u8, name, ".bss"))
   1079         {
   1080             // The TLS region should not be dropped since its is needed
   1081             // during the initialization of each thread (__wasm_init_tls).
   1082             if (shared_memory and std.mem.eql(u8, name, ".tdata")) {
   1083                 continue;
   1084             }
   1085 
   1086             try writer.writeByte(std.wasm.opcode(.misc_prefix));
   1087             try leb.writeULEB128(writer, std.wasm.miscOpcode(.data_drop));
   1088             try leb.writeULEB128(writer, segment_index);
   1089         }
   1090     }
   1091 
   1092     // End of the function body
   1093     try writer.writeByte(std.wasm.opcode(.end));
   1094 
   1095     try wasm.createSyntheticFunction(
   1096         "__wasm_init_memory",
   1097         std.wasm.Type{ .params = &.{}, .returns = &.{} },
   1098         &function_body,
   1099     );
   1100 }
   1101 
   1102 /// Constructs a synthetic function that performs runtime relocations for
   1103 /// TLS symbols. This function is called by `__wasm_init_tls`.
   1104 fn setupTLSRelocationsFunction(wasm: *Wasm) !void {
   1105     const comp = wasm.base.comp;
   1106     const gpa = comp.gpa;
   1107     const shared_memory = comp.config.shared_memory;
   1108 
   1109     // When we have TLS GOT entries and shared memory is enabled,
   1110     // we must perform runtime relocations or else we don't create the function.
   1111     if (!shared_memory or !wasm.requiresTLSReloc()) {
   1112         return;
   1113     }
   1114 
   1115     // const loc = try wasm.createSyntheticSymbol("__wasm_apply_global_tls_relocs");
   1116     var function_body = std.ArrayList(u8).init(gpa);
   1117     defer function_body.deinit();
   1118     const writer = function_body.writer();
   1119 
   1120     // locals (we have none)
   1121     try writer.writeByte(0);
   1122     for (wasm.got_symbols.items, 0..) |got_loc, got_index| {
   1123         const sym: *Symbol = got_loc.getSymbol(wasm);
   1124         if (!sym.isTLS()) continue; // only relocate TLS symbols
   1125         if (sym.tag == .data and sym.isDefined()) {
   1126             // get __tls_base
   1127             try writer.writeByte(std.wasm.opcode(.global_get));
   1128             try leb.writeULEB128(writer, wasm.findGlobalSymbol("__tls_base").?.getSymbol(wasm).index);
   1129 
   1130             // add the virtual address of the symbol
   1131             try writer.writeByte(std.wasm.opcode(.i32_const));
   1132             try leb.writeULEB128(writer, sym.virtual_address);
   1133         } else if (sym.tag == .function) {
   1134             @panic("TODO: relocate GOT entry of function");
   1135         } else continue;
   1136 
   1137         try writer.writeByte(std.wasm.opcode(.i32_add));
   1138         try writer.writeByte(std.wasm.opcode(.global_set));
   1139         try leb.writeULEB128(writer, wasm.imported_globals_count + @as(u32, @intCast(wasm.wasm_globals.items.len + got_index)));
   1140     }
   1141     try writer.writeByte(std.wasm.opcode(.end));
   1142 
   1143     try wasm.createSyntheticFunction(
   1144         "__wasm_apply_global_tls_relocs",
   1145         std.wasm.Type{ .params = &.{}, .returns = &.{} },
   1146         &function_body,
   1147     );
   1148 }
   1149 
   1150 fn validateFeatures(
   1151     wasm: *const Wasm,
   1152     to_emit: *[@typeInfo(types.Feature.Tag).Enum.fields.len]bool,
   1153     emit_features_count: *u32,
   1154 ) !void {
   1155     const comp = wasm.base.comp;
   1156     const target = comp.root_mod.resolved_target.result;
   1157     const shared_memory = comp.config.shared_memory;
   1158     const cpu_features = target.cpu.features;
   1159     const infer = cpu_features.isEmpty(); // when the user did not define any features, we infer them from linked objects.
   1160     const known_features_count = @typeInfo(types.Feature.Tag).Enum.fields.len;
   1161 
   1162     var allowed = [_]bool{false} ** known_features_count;
   1163     var used = [_]u17{0} ** known_features_count;
   1164     var disallowed = [_]u17{0} ** known_features_count;
   1165     var required = [_]u17{0} ** known_features_count;
   1166 
   1167     // when false, we fail linking. We only verify this after a loop to catch all invalid features.
   1168     var valid_feature_set = true;
   1169     // will be set to true when there's any TLS segment found in any of the object files
   1170     var has_tls = false;
   1171 
   1172     // When the user has given an explicit list of features to enable,
   1173     // we extract them and insert each into the 'allowed' list.
   1174     if (!infer) {
   1175         inline for (@typeInfo(std.Target.wasm.Feature).Enum.fields) |feature_field| {
   1176             if (cpu_features.isEnabled(feature_field.value)) {
   1177                 allowed[feature_field.value] = true;
   1178                 emit_features_count.* += 1;
   1179             }
   1180         }
   1181     }
   1182 
   1183     // extract all the used, disallowed and required features from each
   1184     // linked object file so we can test them.
   1185     for (wasm.objects.items, 0..) |object, object_index| {
   1186         for (object.features) |feature| {
   1187             const value = @as(u16, @intCast(object_index)) << 1 | @as(u1, 1);
   1188             switch (feature.prefix) {
   1189                 .used => {
   1190                     used[@intFromEnum(feature.tag)] = value;
   1191                 },
   1192                 .disallowed => {
   1193                     disallowed[@intFromEnum(feature.tag)] = value;
   1194                 },
   1195                 .required => {
   1196                     required[@intFromEnum(feature.tag)] = value;
   1197                     used[@intFromEnum(feature.tag)] = value;
   1198                 },
   1199             }
   1200         }
   1201 
   1202         for (object.segment_info) |segment| {
   1203             if (segment.isTLS()) {
   1204                 has_tls = true;
   1205             }
   1206         }
   1207     }
   1208 
   1209     // when we infer the features, we allow each feature found in the 'used' set
   1210     // and insert it into the 'allowed' set. When features are not inferred,
   1211     // we validate that a used feature is allowed.
   1212     for (used, 0..) |used_set, used_index| {
   1213         const is_enabled = @as(u1, @truncate(used_set)) != 0;
   1214         if (infer) {
   1215             allowed[used_index] = is_enabled;
   1216             emit_features_count.* += @intFromBool(is_enabled);
   1217         } else if (is_enabled and !allowed[used_index]) {
   1218             log.err("feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))});
   1219             log.err("  defined in '{s}'", .{wasm.objects.items[used_set >> 1].name});
   1220             valid_feature_set = false;
   1221         }
   1222     }
   1223 
   1224     if (!valid_feature_set) {
   1225         return error.InvalidFeatureSet;
   1226     }
   1227 
   1228     if (shared_memory) {
   1229         const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)];
   1230         if (@as(u1, @truncate(disallowed_feature)) != 0) {
   1231             log.err(
   1232                 "shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled",
   1233                 .{wasm.objects.items[disallowed_feature >> 1].name},
   1234             );
   1235             valid_feature_set = false;
   1236         }
   1237 
   1238         for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
   1239             if (!allowed[@intFromEnum(feature)]) {
   1240                 log.err("feature '{}' is not used but is required for shared-memory", .{feature});
   1241             }
   1242         }
   1243     }
   1244 
   1245     if (has_tls) {
   1246         for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
   1247             if (!allowed[@intFromEnum(feature)]) {
   1248                 log.err("feature '{}' is not used but is required for thread-local storage", .{feature});
   1249             }
   1250         }
   1251     }
   1252     // For each linked object, validate the required and disallowed features
   1253     for (wasm.objects.items) |object| {
   1254         var object_used_features = [_]bool{false} ** known_features_count;
   1255         for (object.features) |feature| {
   1256             if (feature.prefix == .disallowed) continue; // already defined in 'disallowed' set.
   1257             // from here a feature is always used
   1258             const disallowed_feature = disallowed[@intFromEnum(feature.tag)];
   1259             if (@as(u1, @truncate(disallowed_feature)) != 0) {
   1260                 log.err("feature '{}' is disallowed, but used by linked object", .{feature.tag});
   1261                 log.err("  disallowed by '{s}'", .{wasm.objects.items[disallowed_feature >> 1].name});
   1262                 log.err("  used in '{s}'", .{object.name});
   1263                 valid_feature_set = false;
   1264             }
   1265 
   1266             object_used_features[@intFromEnum(feature.tag)] = true;
   1267         }
   1268 
   1269         // validate the linked object file has each required feature
   1270         for (required, 0..) |required_feature, feature_index| {
   1271             const is_required = @as(u1, @truncate(required_feature)) != 0;
   1272             if (is_required and !object_used_features[feature_index]) {
   1273                 log.err("feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))});
   1274                 log.err("  required by '{s}'", .{wasm.objects.items[required_feature >> 1].name});
   1275                 log.err("  missing in '{s}'", .{object.name});
   1276                 valid_feature_set = false;
   1277             }
   1278         }
   1279     }
   1280 
   1281     if (!valid_feature_set) {
   1282         return error.InvalidFeatureSet;
   1283     }
   1284 
   1285     to_emit.* = allowed;
   1286 }
   1287 
   1288 /// Creates synthetic linker-symbols, but only if they are being referenced from
   1289 /// any object file. For instance, the `__heap_base` symbol will only be created,
   1290 /// if one or multiple undefined references exist. When none exist, the symbol will
   1291 /// not be created, ensuring we don't unneccesarily emit unreferenced symbols.
   1292 fn resolveLazySymbols(wasm: *Wasm) !void {
   1293     const comp = wasm.base.comp;
   1294     const gpa = comp.gpa;
   1295     const shared_memory = comp.config.shared_memory;
   1296 
   1297     if (wasm.string_table.getOffset("__heap_base")) |name_offset| {
   1298         if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| {
   1299             const loc = try wasm.createSyntheticSymbolOffset(name_offset, .data);
   1300             try wasm.discarded.putNoClobber(gpa, kv.value, loc);
   1301             _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations.
   1302         }
   1303     }
   1304 
   1305     if (wasm.string_table.getOffset("__heap_end")) |name_offset| {
   1306         if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| {
   1307             const loc = try wasm.createSyntheticSymbolOffset(name_offset, .data);
   1308             try wasm.discarded.putNoClobber(gpa, kv.value, loc);
   1309             _ = wasm.resolved_symbols.swapRemove(loc);
   1310         }
   1311     }
   1312 
   1313     if (!shared_memory) {
   1314         if (wasm.string_table.getOffset("__tls_base")) |name_offset| {
   1315             if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| {
   1316                 const loc = try wasm.createSyntheticSymbolOffset(name_offset, .global);
   1317                 try wasm.discarded.putNoClobber(gpa, kv.value, loc);
   1318                 _ = wasm.resolved_symbols.swapRemove(kv.value);
   1319                 const symbol = loc.getSymbol(wasm);
   1320                 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
   1321                 symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len);
   1322                 try wasm.wasm_globals.append(gpa, .{
   1323                     .global_type = .{ .valtype = .i32, .mutable = true },
   1324                     .init = .{ .i32_const = undefined },
   1325                 });
   1326             }
   1327         }
   1328     }
   1329     if (wasm.string_table.getOffset("__zig_errors_len")) |name_offset| {
   1330         if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| {
   1331             const loc = try wasm.createSyntheticSymbolOffset(name_offset, .data);
   1332             try wasm.discarded.putNoClobber(gpa, kv.value, loc);
   1333             _ = wasm.resolved_symbols.swapRemove(kv.value);
   1334         }
   1335     }
   1336 }
   1337 
   1338 // Tries to find a global symbol by its name. Returns null when not found,
   1339 /// and its location when it is found.
   1340 pub fn findGlobalSymbol(wasm: *Wasm, name: []const u8) ?SymbolLoc {
   1341     const offset = wasm.string_table.getOffset(name) orelse return null;
   1342     return wasm.globals.get(offset);
   1343 }
   1344 
   1345 fn checkUndefinedSymbols(wasm: *const Wasm) !void {
   1346     const comp = wasm.base.comp;
   1347     if (comp.config.output_mode == .Obj) return;
   1348     if (wasm.import_symbols) return;
   1349 
   1350     var found_undefined_symbols = false;
   1351     for (wasm.undefs.values()) |undef| {
   1352         const symbol = undef.getSymbol(wasm);
   1353         if (symbol.tag == .data) {
   1354             found_undefined_symbols = true;
   1355             const file_name = if (undef.file) |file_index| name: {
   1356                 break :name wasm.objects.items[file_index].name;
   1357             } else wasm.name;
   1358             const symbol_name = undef.getName(wasm);
   1359             log.err("could not resolve undefined symbol '{s}'", .{symbol_name});
   1360             log.err("  defined in '{s}'", .{file_name});
   1361         }
   1362     }
   1363     if (found_undefined_symbols) {
   1364         return error.UndefinedSymbol;
   1365     }
   1366 }
   1367 
   1368 pub fn deinit(wasm: *Wasm) void {
   1369     const gpa = wasm.base.comp.gpa;
   1370     if (wasm.llvm_object) |llvm_object| llvm_object.deinit();
   1371 
   1372     for (wasm.func_types.items) |*func_type| {
   1373         func_type.deinit(gpa);
   1374     }
   1375     for (wasm.segment_info.values()) |segment_info| {
   1376         gpa.free(segment_info.name);
   1377     }
   1378     for (wasm.objects.items) |*object| {
   1379         object.deinit(gpa);
   1380     }
   1381 
   1382     for (wasm.archives.items) |*archive| {
   1383         archive.deinit(gpa);
   1384     }
   1385 
   1386     // For decls and anon decls we free the memory of its atoms.
   1387     // The memory of atoms parsed from object files is managed by
   1388     // the object file itself, and therefore we can skip those.
   1389     {
   1390         var it = wasm.decls.valueIterator();
   1391         while (it.next()) |atom_index_ptr| {
   1392             const atom = wasm.getAtomPtr(atom_index_ptr.*);
   1393             for (atom.locals.items) |local_index| {
   1394                 const local_atom = wasm.getAtomPtr(local_index);
   1395                 local_atom.deinit(gpa);
   1396             }
   1397             atom.deinit(gpa);
   1398         }
   1399     }
   1400     {
   1401         for (wasm.anon_decls.values()) |atom_index| {
   1402             const atom = wasm.getAtomPtr(atom_index);
   1403             for (atom.locals.items) |local_index| {
   1404                 const local_atom = wasm.getAtomPtr(local_index);
   1405                 local_atom.deinit(gpa);
   1406             }
   1407             atom.deinit(gpa);
   1408         }
   1409     }
   1410     for (wasm.synthetic_functions.items) |atom_index| {
   1411         const atom = wasm.getAtomPtr(atom_index);
   1412         atom.deinit(gpa);
   1413     }
   1414 
   1415     wasm.decls.deinit(gpa);
   1416     wasm.anon_decls.deinit(gpa);
   1417     wasm.atom_types.deinit(gpa);
   1418     wasm.symbols.deinit(gpa);
   1419     wasm.symbols_free_list.deinit(gpa);
   1420     wasm.globals.deinit(gpa);
   1421     wasm.resolved_symbols.deinit(gpa);
   1422     wasm.undefs.deinit(gpa);
   1423     wasm.discarded.deinit(gpa);
   1424     wasm.symbol_atom.deinit(gpa);
   1425     wasm.export_names.deinit(gpa);
   1426     wasm.atoms.deinit(gpa);
   1427     wasm.managed_atoms.deinit(gpa);
   1428     wasm.segments.deinit(gpa);
   1429     wasm.data_segments.deinit(gpa);
   1430     wasm.segment_info.deinit(gpa);
   1431     wasm.objects.deinit(gpa);
   1432     wasm.archives.deinit(gpa);
   1433 
   1434     // free output sections
   1435     wasm.imports.deinit(gpa);
   1436     wasm.func_types.deinit(gpa);
   1437     wasm.functions.deinit(gpa);
   1438     wasm.wasm_globals.deinit(gpa);
   1439     wasm.function_table.deinit(gpa);
   1440     wasm.tables.deinit(gpa);
   1441     wasm.init_funcs.deinit(gpa);
   1442     wasm.exports.deinit(gpa);
   1443 
   1444     wasm.string_table.deinit(gpa);
   1445     wasm.synthetic_functions.deinit(gpa);
   1446 
   1447     if (wasm.dwarf) |*dwarf| {
   1448         dwarf.deinit();
   1449     }
   1450 }
   1451 
   1452 /// Allocates a new symbol and returns its index.
   1453 /// Will re-use slots when a symbol was freed at an earlier stage.
   1454 pub fn allocateSymbol(wasm: *Wasm) !u32 {
   1455     const gpa = wasm.base.comp.gpa;
   1456 
   1457     try wasm.symbols.ensureUnusedCapacity(gpa, 1);
   1458     const symbol: Symbol = .{
   1459         .name = std.math.maxInt(u32), // will be set after updateDecl as well as during atom creation for decls
   1460         .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
   1461         .tag = .undefined, // will be set after updateDecl
   1462         .index = std.math.maxInt(u32), // will be set during atom parsing
   1463         .virtual_address = std.math.maxInt(u32), // will be set during atom allocation
   1464     };
   1465     if (wasm.symbols_free_list.popOrNull()) |index| {
   1466         wasm.symbols.items[index] = symbol;
   1467         return index;
   1468     }
   1469     const index = @as(u32, @intCast(wasm.symbols.items.len));
   1470     wasm.symbols.appendAssumeCapacity(symbol);
   1471     return index;
   1472 }
   1473 
   1474 pub fn updateFunc(wasm: *Wasm, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
   1475     if (build_options.skip_non_native and builtin.object_format != .wasm) {
   1476         @panic("Attempted to compile for object format that was disabled by build configuration");
   1477     }
   1478     if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(mod, func_index, air, liveness);
   1479 
   1480     const tracy = trace(@src());
   1481     defer tracy.end();
   1482 
   1483     const gpa = wasm.base.comp.gpa;
   1484     const func = mod.funcInfo(func_index);
   1485     const decl_index = func.owner_decl;
   1486     const decl = mod.declPtr(decl_index);
   1487     const atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
   1488     const atom = wasm.getAtomPtr(atom_index);
   1489     atom.clear();
   1490 
   1491     // var decl_state: ?Dwarf.DeclState = if (wasm.dwarf) |*dwarf| try dwarf.initDeclState(mod, decl_index) else null;
   1492     // defer if (decl_state) |*ds| ds.deinit();
   1493 
   1494     var code_writer = std.ArrayList(u8).init(gpa);
   1495     defer code_writer.deinit();
   1496     // const result = try codegen.generateFunction(
   1497     //     &wasm.base,
   1498     //     decl.srcLoc(mod),
   1499     //     func,
   1500     //     air,
   1501     //     liveness,
   1502     //     &code_writer,
   1503     //     if (decl_state) |*ds| .{ .dwarf = ds } else .none,
   1504     // );
   1505     const result = try codegen.generateFunction(
   1506         &wasm.base,
   1507         decl.srcLoc(mod),
   1508         func_index,
   1509         air,
   1510         liveness,
   1511         &code_writer,
   1512         .none,
   1513     );
   1514 
   1515     const code = switch (result) {
   1516         .ok => code_writer.items,
   1517         .fail => |em| {
   1518             decl.analysis = .codegen_failure;
   1519             try mod.failed_decls.put(mod.gpa, decl_index, em);
   1520             return;
   1521         },
   1522     };
   1523 
   1524     // if (wasm.dwarf) |*dwarf| {
   1525     //     try dwarf.commitDeclState(
   1526     //         mod,
   1527     //         decl_index,
   1528     //         // Actual value will be written after relocation.
   1529     //         // For Wasm, this is the offset relative to the code section
   1530     //         // which isn't known until flush().
   1531     //         0,
   1532     //         code.len,
   1533     //         &decl_state.?,
   1534     //     );
   1535     // }
   1536     return wasm.finishUpdateDecl(decl_index, code, .function);
   1537 }
   1538 
   1539 // Generate code for the Decl, storing it in memory to be later written to
   1540 // the file on flush().
   1541 pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: InternPool.DeclIndex) !void {
   1542     if (build_options.skip_non_native and builtin.object_format != .wasm) {
   1543         @panic("Attempted to compile for object format that was disabled by build configuration");
   1544     }
   1545     if (wasm.llvm_object) |llvm_object| return llvm_object.updateDecl(mod, decl_index);
   1546 
   1547     const tracy = trace(@src());
   1548     defer tracy.end();
   1549 
   1550     const decl = mod.declPtr(decl_index);
   1551     if (decl.val.getFunction(mod)) |_| {
   1552         return;
   1553     } else if (decl.val.getExternFunc(mod)) |_| {
   1554         return;
   1555     }
   1556 
   1557     const gpa = wasm.base.comp.gpa;
   1558     const atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
   1559     const atom = wasm.getAtomPtr(atom_index);
   1560     atom.clear();
   1561 
   1562     if (decl.isExtern(mod)) {
   1563         const variable = decl.getOwnedVariable(mod).?;
   1564         const name = mod.intern_pool.stringToSlice(decl.name);
   1565         const lib_name = mod.intern_pool.stringToSliceUnwrap(variable.lib_name);
   1566         return wasm.addOrUpdateImport(name, atom.sym_index, lib_name, null);
   1567     }
   1568     const val = if (decl.val.getVariable(mod)) |variable| Value.fromInterned(variable.init) else decl.val;
   1569 
   1570     var code_writer = std.ArrayList(u8).init(gpa);
   1571     defer code_writer.deinit();
   1572 
   1573     const res = try codegen.generateSymbol(
   1574         &wasm.base,
   1575         decl.srcLoc(mod),
   1576         .{ .ty = decl.ty, .val = val },
   1577         &code_writer,
   1578         .none,
   1579         .{ .parent_atom_index = atom.sym_index },
   1580     );
   1581 
   1582     const code = switch (res) {
   1583         .ok => code_writer.items,
   1584         .fail => |em| {
   1585             decl.analysis = .codegen_failure;
   1586             try mod.failed_decls.put(mod.gpa, decl_index, em);
   1587             return;
   1588         },
   1589     };
   1590 
   1591     return wasm.finishUpdateDecl(decl_index, code, .data);
   1592 }
   1593 
   1594 pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl_index: InternPool.DeclIndex) !void {
   1595     if (wasm.llvm_object) |_| return;
   1596     if (wasm.dwarf) |*dw| {
   1597         const tracy = trace(@src());
   1598         defer tracy.end();
   1599 
   1600         const decl = mod.declPtr(decl_index);
   1601         const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
   1602 
   1603         log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl });
   1604         try dw.updateDeclLineNumber(mod, decl_index);
   1605     }
   1606 }
   1607 
   1608 fn finishUpdateDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex, code: []const u8, symbol_tag: Symbol.Tag) !void {
   1609     const gpa = wasm.base.comp.gpa;
   1610     const mod = wasm.base.comp.module.?;
   1611     const decl = mod.declPtr(decl_index);
   1612     const atom_index = wasm.decls.get(decl_index).?;
   1613     const atom = wasm.getAtomPtr(atom_index);
   1614     const symbol = &wasm.symbols.items[atom.sym_index];
   1615     const full_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
   1616     symbol.name = try wasm.string_table.put(gpa, full_name);
   1617     symbol.tag = symbol_tag;
   1618     try atom.code.appendSlice(gpa, code);
   1619     try wasm.resolved_symbols.put(gpa, atom.symbolLoc(), {});
   1620 
   1621     atom.size = @intCast(code.len);
   1622     if (code.len == 0) return;
   1623     atom.alignment = decl.getAlignment(mod);
   1624 }
   1625 
   1626 /// From a given symbol location, returns its `wasm.GlobalType`.
   1627 /// Asserts the Symbol represents a global.
   1628 fn getGlobalType(wasm: *const Wasm, loc: SymbolLoc) std.wasm.GlobalType {
   1629     const symbol = loc.getSymbol(wasm);
   1630     assert(symbol.tag == .global);
   1631     const is_undefined = symbol.isUndefined();
   1632     if (loc.file) |file_index| {
   1633         const obj: Object = wasm.objects.items[file_index];
   1634         if (is_undefined) {
   1635             return obj.findImport(.global, symbol.index).kind.global;
   1636         }
   1637         const import_global_count = obj.importedCountByKind(.global);
   1638         return obj.globals[symbol.index - import_global_count].global_type;
   1639     }
   1640     if (is_undefined) {
   1641         return wasm.imports.get(loc).?.kind.global;
   1642     }
   1643     return wasm.wasm_globals.items[symbol.index].global_type;
   1644 }
   1645 
   1646 /// From a given symbol location, returns its `wasm.Type`.
   1647 /// Asserts the Symbol represents a function.
   1648 fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type {
   1649     const symbol = loc.getSymbol(wasm);
   1650     assert(symbol.tag == .function);
   1651     const is_undefined = symbol.isUndefined();
   1652     if (loc.file) |file_index| {
   1653         const obj: Object = wasm.objects.items[file_index];
   1654         if (is_undefined) {
   1655             const ty_index = obj.findImport(.function, symbol.index).kind.function;
   1656             return obj.func_types[ty_index];
   1657         }
   1658         const import_function_count = obj.importedCountByKind(.function);
   1659         const type_index = obj.functions[symbol.index - import_function_count].type_index;
   1660         return obj.func_types[type_index];
   1661     }
   1662     if (is_undefined) {
   1663         const ty_index = wasm.imports.get(loc).?.kind.function;
   1664         return wasm.func_types.items[ty_index];
   1665     }
   1666     return wasm.func_types.items[wasm.functions.get(.{ .file = loc.file, .index = symbol.index }).?.func.type_index];
   1667 }
   1668 
   1669 /// Lowers a constant typed value to a local symbol and atom.
   1670 /// Returns the symbol index of the local
   1671 /// The given `decl` is the parent decl whom owns the constant.
   1672 pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: InternPool.DeclIndex) !u32 {
   1673     const gpa = wasm.base.comp.gpa;
   1674     const mod = wasm.base.comp.module.?;
   1675     assert(tv.ty.zigTypeTag(mod) != .Fn); // cannot create local symbols for functions
   1676     const decl = mod.declPtr(decl_index);
   1677 
   1678     const parent_atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
   1679     const parent_atom = wasm.getAtom(parent_atom_index);
   1680     const local_index = parent_atom.locals.items.len;
   1681     const fqn = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
   1682     const name = try std.fmt.allocPrintZ(gpa, "__unnamed_{s}_{d}", .{
   1683         fqn, local_index,
   1684     });
   1685     defer gpa.free(name);
   1686 
   1687     switch (try wasm.lowerConst(name, tv, decl.srcLoc(mod))) {
   1688         .ok => |atom_index| {
   1689             try wasm.getAtomPtr(parent_atom_index).locals.append(gpa, atom_index);
   1690             return wasm.getAtom(atom_index).getSymbolIndex().?;
   1691         },
   1692         .fail => |em| {
   1693             decl.analysis = .codegen_failure;
   1694             try mod.failed_decls.put(mod.gpa, decl_index, em);
   1695             return error.CodegenFail;
   1696         },
   1697     }
   1698 }
   1699 
   1700 const LowerConstResult = union(enum) {
   1701     ok: Atom.Index,
   1702     fail: *Module.ErrorMsg,
   1703 };
   1704 
   1705 fn lowerConst(wasm: *Wasm, name: []const u8, tv: TypedValue, src_loc: Module.SrcLoc) !LowerConstResult {
   1706     const gpa = wasm.base.comp.gpa;
   1707     const mod = wasm.base.comp.module.?;
   1708 
   1709     // Create and initialize a new local symbol and atom
   1710     const atom_index = try wasm.createAtom();
   1711     var value_bytes = std.ArrayList(u8).init(gpa);
   1712     defer value_bytes.deinit();
   1713 
   1714     const code = code: {
   1715         const atom = wasm.getAtomPtr(atom_index);
   1716         atom.alignment = tv.ty.abiAlignment(mod);
   1717         wasm.symbols.items[atom.sym_index] = .{
   1718             .name = try wasm.string_table.put(gpa, name),
   1719             .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
   1720             .tag = .data,
   1721             .index = undefined,
   1722             .virtual_address = undefined,
   1723         };
   1724         try wasm.resolved_symbols.putNoClobber(gpa, atom.symbolLoc(), {});
   1725 
   1726         const result = try codegen.generateSymbol(
   1727             &wasm.base,
   1728             src_loc,
   1729             tv,
   1730             &value_bytes,
   1731             .none,
   1732             .{
   1733                 .parent_atom_index = atom.sym_index,
   1734                 .addend = null,
   1735             },
   1736         );
   1737         break :code switch (result) {
   1738             .ok => value_bytes.items,
   1739             .fail => |em| {
   1740                 return .{ .fail = em };
   1741             },
   1742         };
   1743     };
   1744 
   1745     const atom = wasm.getAtomPtr(atom_index);
   1746     atom.size = @intCast(code.len);
   1747     try atom.code.appendSlice(gpa, code);
   1748     return .{ .ok = atom_index };
   1749 }
   1750 
   1751 /// Returns the symbol index from a symbol of which its flag is set global,
   1752 /// such as an exported or imported symbol.
   1753 /// If the symbol does not yet exist, creates a new one symbol instead
   1754 /// and then returns the index to it.
   1755 pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8, lib_name: ?[]const u8) !u32 {
   1756     _ = lib_name;
   1757     const gpa = wasm.base.comp.gpa;
   1758     const name_index = try wasm.string_table.put(gpa, name);
   1759     const gop = try wasm.globals.getOrPut(gpa, name_index);
   1760     if (gop.found_existing) {
   1761         return gop.value_ptr.*.index;
   1762     }
   1763 
   1764     var symbol: Symbol = .{
   1765         .name = name_index,
   1766         .flags = 0,
   1767         .index = undefined, // index to type will be set after merging function symbols
   1768         .tag = .function,
   1769         .virtual_address = undefined,
   1770     };
   1771     symbol.setGlobal(true);
   1772     symbol.setUndefined(true);
   1773 
   1774     const sym_index = if (wasm.symbols_free_list.popOrNull()) |index| index else blk: {
   1775         const index: u32 = @intCast(wasm.symbols.items.len);
   1776         try wasm.symbols.ensureUnusedCapacity(gpa, 1);
   1777         wasm.symbols.items.len += 1;
   1778         break :blk index;
   1779     };
   1780     wasm.symbols.items[sym_index] = symbol;
   1781     gop.value_ptr.* = .{ .index = sym_index, .file = null };
   1782     try wasm.resolved_symbols.put(gpa, gop.value_ptr.*, {});
   1783     try wasm.undefs.putNoClobber(gpa, name_index, gop.value_ptr.*);
   1784     return sym_index;
   1785 }
   1786 
   1787 /// For a given decl, find the given symbol index's atom, and create a relocation for the type.
   1788 /// Returns the given pointer address
   1789 pub fn getDeclVAddr(
   1790     wasm: *Wasm,
   1791     decl_index: InternPool.DeclIndex,
   1792     reloc_info: link.File.RelocInfo,
   1793 ) !u64 {
   1794     const target = wasm.base.comp.root_mod.resolved_target.result;
   1795     const gpa = wasm.base.comp.gpa;
   1796     const mod = wasm.base.comp.module.?;
   1797     const decl = mod.declPtr(decl_index);
   1798 
   1799     const target_atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
   1800     const target_symbol_index = wasm.getAtom(target_atom_index).sym_index;
   1801 
   1802     assert(reloc_info.parent_atom_index != 0);
   1803     const atom_index = wasm.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?;
   1804     const atom = wasm.getAtomPtr(atom_index);
   1805     const is_wasm32 = target.cpu.arch == .wasm32;
   1806     if (decl.ty.zigTypeTag(mod) == .Fn) {
   1807         assert(reloc_info.addend == 0); // addend not allowed for function relocations
   1808         // We found a function pointer, so add it to our table,
   1809         // as function pointers are not allowed to be stored inside the data section.
   1810         // They are instead stored in a function table which are called by index.
   1811         try wasm.addTableFunction(target_symbol_index);
   1812         try atom.relocs.append(gpa, .{
   1813             .index = target_symbol_index,
   1814             .offset = @intCast(reloc_info.offset),
   1815             .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64,
   1816         });
   1817     } else {
   1818         try atom.relocs.append(gpa, .{
   1819             .index = target_symbol_index,
   1820             .offset = @intCast(reloc_info.offset),
   1821             .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,
   1822             .addend = @intCast(reloc_info.addend),
   1823         });
   1824     }
   1825     // we do not know the final address at this point,
   1826     // as atom allocation will determine the address and relocations
   1827     // will calculate and rewrite this. Therefore, we simply return the symbol index
   1828     // that was targeted.
   1829     return target_symbol_index;
   1830 }
   1831 
   1832 pub fn lowerAnonDecl(
   1833     wasm: *Wasm,
   1834     decl_val: InternPool.Index,
   1835     explicit_alignment: Alignment,
   1836     src_loc: Module.SrcLoc,
   1837 ) !codegen.Result {
   1838     const gpa = wasm.base.comp.gpa;
   1839     const gop = try wasm.anon_decls.getOrPut(gpa, decl_val);
   1840     if (!gop.found_existing) {
   1841         const mod = wasm.base.comp.module.?;
   1842         const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val));
   1843         const tv: TypedValue = .{ .ty = ty, .val = Value.fromInterned(decl_val) };
   1844         var name_buf: [32]u8 = undefined;
   1845         const name = std.fmt.bufPrint(&name_buf, "__anon_{d}", .{
   1846             @intFromEnum(decl_val),
   1847         }) catch unreachable;
   1848 
   1849         switch (try wasm.lowerConst(name, tv, src_loc)) {
   1850             .ok => |atom_index| wasm.anon_decls.values()[gop.index] = atom_index,
   1851             .fail => |em| return .{ .fail = em },
   1852         }
   1853     }
   1854 
   1855     const atom = wasm.getAtomPtr(wasm.anon_decls.values()[gop.index]);
   1856     atom.alignment = switch (atom.alignment) {
   1857         .none => explicit_alignment,
   1858         else => switch (explicit_alignment) {
   1859             .none => atom.alignment,
   1860             else => atom.alignment.maxStrict(explicit_alignment),
   1861         },
   1862     };
   1863     return .ok;
   1864 }
   1865 
   1866 pub fn getAnonDeclVAddr(wasm: *Wasm, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 {
   1867     const gpa = wasm.base.comp.gpa;
   1868     const target = wasm.base.comp.root_mod.resolved_target.result;
   1869     const atom_index = wasm.anon_decls.get(decl_val).?;
   1870     const target_symbol_index = wasm.getAtom(atom_index).getSymbolIndex().?;
   1871 
   1872     const parent_atom_index = wasm.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?;
   1873     const parent_atom = wasm.getAtomPtr(parent_atom_index);
   1874     const is_wasm32 = target.cpu.arch == .wasm32;
   1875     const mod = wasm.base.comp.module.?;
   1876     const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val));
   1877     if (ty.zigTypeTag(mod) == .Fn) {
   1878         assert(reloc_info.addend == 0); // addend not allowed for function relocations
   1879         // We found a function pointer, so add it to our table,
   1880         // as function pointers are not allowed to be stored inside the data section.
   1881         // They are instead stored in a function table which are called by index.
   1882         try wasm.addTableFunction(target_symbol_index);
   1883         try parent_atom.relocs.append(gpa, .{
   1884             .index = target_symbol_index,
   1885             .offset = @intCast(reloc_info.offset),
   1886             .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64,
   1887         });
   1888     } else {
   1889         try parent_atom.relocs.append(gpa, .{
   1890             .index = target_symbol_index,
   1891             .offset = @intCast(reloc_info.offset),
   1892             .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,
   1893             .addend = @intCast(reloc_info.addend),
   1894         });
   1895     }
   1896 
   1897     // we do not know the final address at this point,
   1898     // as atom allocation will determine the address and relocations
   1899     // will calculate and rewrite this. Therefore, we simply return the symbol index
   1900     // that was targeted.
   1901     return target_symbol_index;
   1902 }
   1903 
   1904 pub fn deleteDeclExport(
   1905     wasm: *Wasm,
   1906     decl_index: InternPool.DeclIndex,
   1907     name: InternPool.NullTerminatedString,
   1908 ) void {
   1909     _ = name;
   1910     if (wasm.llvm_object) |_| return;
   1911     const atom_index = wasm.decls.get(decl_index) orelse return;
   1912     const sym_index = wasm.getAtom(atom_index).sym_index;
   1913     const loc: SymbolLoc = .{ .file = null, .index = sym_index };
   1914     const symbol = loc.getSymbol(wasm);
   1915     const symbol_name = wasm.string_table.get(symbol.name);
   1916     log.debug("Deleting export for decl '{s}'", .{symbol_name});
   1917     if (wasm.export_names.fetchRemove(loc)) |kv| {
   1918         assert(wasm.globals.remove(kv.value));
   1919     } else {
   1920         assert(wasm.globals.remove(symbol.name));
   1921     }
   1922 }
   1923 
   1924 pub fn updateExports(
   1925     wasm: *Wasm,
   1926     mod: *Module,
   1927     exported: Module.Exported,
   1928     exports: []const *Module.Export,
   1929 ) !void {
   1930     if (build_options.skip_non_native and builtin.object_format != .wasm) {
   1931         @panic("Attempted to compile for object format that was disabled by build configuration");
   1932     }
   1933     if (wasm.llvm_object) |llvm_object| return llvm_object.updateExports(mod, exported, exports);
   1934 
   1935     const decl_index = switch (exported) {
   1936         .decl_index => |i| i,
   1937         .value => |val| {
   1938             _ = val;
   1939             @panic("TODO: implement Wasm linker code for exporting a constant value");
   1940         },
   1941     };
   1942     const decl = mod.declPtr(decl_index);
   1943     const atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
   1944     const atom = wasm.getAtom(atom_index);
   1945     const atom_sym = atom.symbolLoc().getSymbol(wasm).*;
   1946     const gpa = mod.gpa;
   1947 
   1948     for (exports) |exp| {
   1949         if (mod.intern_pool.stringToSliceUnwrap(exp.opts.section)) |section| {
   1950             try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create(
   1951                 gpa,
   1952                 decl.srcLoc(mod),
   1953                 "Unimplemented: ExportOptions.section '{s}'",
   1954                 .{section},
   1955             ));
   1956             continue;
   1957         }
   1958 
   1959         const exported_decl_index = switch (exp.exported) {
   1960             .value => {
   1961                 try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create(
   1962                     gpa,
   1963                     decl.srcLoc(mod),
   1964                     "Unimplemented: exporting a named constant value",
   1965                     .{},
   1966                 ));
   1967                 continue;
   1968             },
   1969             .decl_index => |i| i,
   1970         };
   1971         const exported_atom_index = try wasm.getOrCreateAtomForDecl(exported_decl_index);
   1972         const exported_atom = wasm.getAtom(exported_atom_index);
   1973         const export_name = try wasm.string_table.put(gpa, mod.intern_pool.stringToSlice(exp.opts.name));
   1974         const sym_loc = exported_atom.symbolLoc();
   1975         const symbol = sym_loc.getSymbol(wasm);
   1976         symbol.setGlobal(true);
   1977         symbol.setUndefined(false);
   1978         symbol.index = atom_sym.index;
   1979         symbol.tag = atom_sym.tag;
   1980         symbol.name = atom_sym.name;
   1981 
   1982         switch (exp.opts.linkage) {
   1983             .Internal => {
   1984                 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
   1985                 symbol.setFlag(.WASM_SYM_BINDING_WEAK);
   1986             },
   1987             .Weak => {
   1988                 symbol.setFlag(.WASM_SYM_BINDING_WEAK);
   1989             },
   1990             .Strong => {}, // symbols are strong by default
   1991             .LinkOnce => {
   1992                 try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create(
   1993                     gpa,
   1994                     decl.srcLoc(mod),
   1995                     "Unimplemented: LinkOnce",
   1996                     .{},
   1997                 ));
   1998                 continue;
   1999             },
   2000         }
   2001 
   2002         if (wasm.globals.get(export_name)) |existing_loc| {
   2003             if (existing_loc.index == atom.sym_index) continue;
   2004             const existing_sym: Symbol = existing_loc.getSymbol(wasm).*;
   2005 
   2006             if (!existing_sym.isUndefined()) blk: {
   2007                 if (symbol.isWeak()) {
   2008                     try wasm.discarded.put(gpa, existing_loc, sym_loc);
   2009                     continue; // to-be-exported symbol is weak, so we keep the existing symbol
   2010                 }
   2011 
   2012                 // new symbol is not weak while existing is, replace existing symbol
   2013                 if (existing_sym.isWeak()) {
   2014                     break :blk;
   2015                 }
   2016                 // When both the to-be-exported symbol and the already existing symbol
   2017                 // are strong symbols, we have a linker error.
   2018                 // In the other case we replace one with the other.
   2019                 try mod.failed_exports.put(gpa, exp, try Module.ErrorMsg.create(
   2020                     gpa,
   2021                     decl.srcLoc(mod),
   2022                     \\LinkError: symbol '{}' defined multiple times
   2023                     \\  first definition in '{s}'
   2024                     \\  next definition in '{s}'
   2025                 ,
   2026                     .{ exp.opts.name.fmt(&mod.intern_pool), wasm.name, wasm.name },
   2027                 ));
   2028                 continue;
   2029             }
   2030 
   2031             // in this case the existing symbol must be replaced either because it's weak or undefined.
   2032             try wasm.discarded.put(gpa, existing_loc, sym_loc);
   2033             _ = wasm.imports.remove(existing_loc);
   2034             _ = wasm.undefs.swapRemove(existing_sym.name);
   2035         }
   2036 
   2037         // Ensure the symbol will be exported using the given name
   2038         if (!mod.intern_pool.stringEqlSlice(exp.opts.name, sym_loc.getName(wasm))) {
   2039             try wasm.export_names.put(gpa, sym_loc, export_name);
   2040         }
   2041 
   2042         try wasm.globals.put(
   2043             gpa,
   2044             export_name,
   2045             sym_loc,
   2046         );
   2047     }
   2048 }
   2049 
   2050 pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void {
   2051     if (wasm.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);
   2052     const gpa = wasm.base.comp.gpa;
   2053     const mod = wasm.base.comp.module.?;
   2054     const decl = mod.declPtr(decl_index);
   2055     const atom_index = wasm.decls.get(decl_index).?;
   2056     const atom = wasm.getAtomPtr(atom_index);
   2057     atom.prev = null;
   2058     wasm.symbols_free_list.append(gpa, atom.sym_index) catch {};
   2059     _ = wasm.decls.remove(decl_index);
   2060     wasm.symbols.items[atom.sym_index].tag = .dead;
   2061     for (atom.locals.items) |local_atom_index| {
   2062         const local_atom = wasm.getAtom(local_atom_index);
   2063         const local_symbol = &wasm.symbols.items[local_atom.sym_index];
   2064         local_symbol.tag = .dead; // also for any local symbol
   2065         wasm.symbols_free_list.append(gpa, local_atom.sym_index) catch {};
   2066         assert(wasm.resolved_symbols.swapRemove(local_atom.symbolLoc()));
   2067         assert(wasm.symbol_atom.remove(local_atom.symbolLoc()));
   2068     }
   2069 
   2070     if (decl.isExtern(mod)) {
   2071         _ = wasm.imports.remove(atom.symbolLoc());
   2072     }
   2073     _ = wasm.resolved_symbols.swapRemove(atom.symbolLoc());
   2074     _ = wasm.symbol_atom.remove(atom.symbolLoc());
   2075 
   2076     // if (wasm.dwarf) |*dwarf| {
   2077     //     dwarf.freeDecl(decl_index);
   2078     // }
   2079 
   2080 }
   2081 
   2082 /// Appends a new entry to the indirect function table
   2083 pub fn addTableFunction(wasm: *Wasm, symbol_index: u32) !void {
   2084     const gpa = wasm.base.comp.gpa;
   2085     const index: u32 = @intCast(wasm.function_table.count());
   2086     try wasm.function_table.put(gpa, .{ .file = null, .index = symbol_index }, index);
   2087 }
   2088 
   2089 /// Assigns indexes to all indirect functions.
   2090 /// Starts at offset 1, where the value `0` represents an unresolved function pointer
   2091 /// or null-pointer
   2092 fn mapFunctionTable(wasm: *Wasm) void {
   2093     var it = wasm.function_table.iterator();
   2094     var index: u32 = 1;
   2095     while (it.next()) |entry| {
   2096         const symbol = entry.key_ptr.*.getSymbol(wasm);
   2097         if (symbol.isAlive()) {
   2098             entry.value_ptr.* = index;
   2099             index += 1;
   2100         } else {
   2101             wasm.function_table.removeByPtr(entry.key_ptr);
   2102         }
   2103     }
   2104 
   2105     if (wasm.import_table or wasm.base.comp.config.output_mode == .Obj) {
   2106         const sym_loc = wasm.findGlobalSymbol("__indirect_function_table").?;
   2107         const import = wasm.imports.getPtr(sym_loc).?;
   2108         import.kind.table.limits.min = index - 1; // we start at index 1.
   2109     } else if (index > 1) {
   2110         log.debug("Appending indirect function table", .{});
   2111         const sym_loc = wasm.findGlobalSymbol("__indirect_function_table").?;
   2112         const symbol = sym_loc.getSymbol(wasm);
   2113         const table = &wasm.tables.items[symbol.index - wasm.imported_tables_count];
   2114         table.limits = .{ .min = index, .max = index, .flags = 0x1 };
   2115     }
   2116 }
   2117 
   2118 /// Either creates a new import, or updates one if existing.
   2119 /// When `type_index` is non-null, we assume an external function.
   2120 /// In all other cases, a data-symbol will be created instead.
   2121 pub fn addOrUpdateImport(
   2122     wasm: *Wasm,
   2123     /// Name of the import
   2124     name: []const u8,
   2125     /// Symbol index that is external
   2126     symbol_index: u32,
   2127     /// Optional library name (i.e. `extern "c" fn foo() void`
   2128     lib_name: ?[:0]const u8,
   2129     /// The index of the type that represents the function signature
   2130     /// when the extern is a function. When this is null, a data-symbol
   2131     /// is asserted instead.
   2132     type_index: ?u32,
   2133 ) !void {
   2134     const gpa = wasm.base.comp.gpa;
   2135     assert(symbol_index != 0);
   2136     // For the import name, we use the decl's name, rather than the fully qualified name
   2137     // Also mangle the name when the lib name is set and not equal to "C" so imports with the same
   2138     // name but different module can be resolved correctly.
   2139     const mangle_name = lib_name != null and
   2140         !std.mem.eql(u8, lib_name.?, "c");
   2141     const full_name = if (mangle_name) full_name: {
   2142         break :full_name try std.fmt.allocPrint(gpa, "{s}|{s}", .{ name, lib_name.? });
   2143     } else name;
   2144     defer if (mangle_name) gpa.free(full_name);
   2145 
   2146     const decl_name_index = try wasm.string_table.put(gpa, full_name);
   2147     const symbol: *Symbol = &wasm.symbols.items[symbol_index];
   2148     symbol.setUndefined(true);
   2149     symbol.setGlobal(true);
   2150     symbol.name = decl_name_index;
   2151     if (mangle_name) {
   2152         // we specified a specific name for the symbol that does not match the import name
   2153         symbol.setFlag(.WASM_SYM_EXPLICIT_NAME);
   2154     }
   2155     const global_gop = try wasm.globals.getOrPut(gpa, decl_name_index);
   2156     if (!global_gop.found_existing) {
   2157         const loc: SymbolLoc = .{ .file = null, .index = symbol_index };
   2158         global_gop.value_ptr.* = loc;
   2159         try wasm.resolved_symbols.put(gpa, loc, {});
   2160         try wasm.undefs.putNoClobber(gpa, decl_name_index, loc);
   2161     } else if (global_gop.value_ptr.*.index != symbol_index) {
   2162         // We are not updating a symbol, but found an existing global
   2163         // symbol with the same name. This means we always favor the
   2164         // existing symbol, regardless whether it's defined or not.
   2165         // We can also skip storing the import as we will not output
   2166         // this symbol.
   2167         return wasm.discarded.put(
   2168             gpa,
   2169             .{ .file = null, .index = symbol_index },
   2170             global_gop.value_ptr.*,
   2171         );
   2172     }
   2173 
   2174     if (type_index) |ty_index| {
   2175         const gop = try wasm.imports.getOrPut(gpa, .{ .index = symbol_index, .file = null });
   2176         const module_name = if (lib_name) |l_name| blk: {
   2177             break :blk l_name;
   2178         } else wasm.host_name;
   2179         if (!gop.found_existing) {
   2180             gop.value_ptr.* = .{
   2181                 .module_name = try wasm.string_table.put(gpa, module_name),
   2182                 .name = try wasm.string_table.put(gpa, name),
   2183                 .kind = .{ .function = ty_index },
   2184             };
   2185         }
   2186     } else {
   2187         // non-functions will not be imported from the runtime, but only resolved during link-time
   2188         symbol.tag = .data;
   2189     }
   2190 }
   2191 
   2192 /// Kind represents the type of an Atom, which is only
   2193 /// used to parse a decl into an Atom to define in which section
   2194 /// or segment it should be placed.
   2195 const Kind = union(enum) {
   2196     /// Represents the segment the data symbol should
   2197     /// be inserted into.
   2198     /// TODO: Add TLS segments
   2199     data: enum {
   2200         read_only,
   2201         uninitialized,
   2202         initialized,
   2203     },
   2204     function: void,
   2205 
   2206     /// Returns the segment name the data kind represents.
   2207     /// Asserts `kind` has its active tag set to `data`.
   2208     fn segmentName(kind: Kind) []const u8 {
   2209         switch (kind.data) {
   2210             .read_only => return ".rodata.",
   2211             .uninitialized => return ".bss.",
   2212             .initialized => return ".data.",
   2213         }
   2214     }
   2215 };
   2216 
   2217 /// Parses an Atom and inserts its metadata into the corresponding sections.
   2218 fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
   2219     const comp = wasm.base.comp;
   2220     const gpa = comp.gpa;
   2221     const shared_memory = comp.config.shared_memory;
   2222     const import_memory = comp.config.import_memory;
   2223     const atom = wasm.getAtomPtr(atom_index);
   2224     const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm);
   2225     const do_garbage_collect = wasm.base.gc_sections;
   2226 
   2227     if (symbol.isDead() and do_garbage_collect) {
   2228         // Prevent unreferenced symbols from being parsed.
   2229         return;
   2230     }
   2231 
   2232     const final_index: u32 = switch (kind) {
   2233         .function => result: {
   2234             const index: u32 = @intCast(wasm.functions.count() + wasm.imported_functions_count);
   2235             const type_index = wasm.atom_types.get(atom_index).?;
   2236             try wasm.functions.putNoClobber(
   2237                 gpa,
   2238                 .{ .file = null, .index = index },
   2239                 .{ .func = .{ .type_index = type_index }, .sym_index = atom.sym_index },
   2240             );
   2241             symbol.tag = .function;
   2242             symbol.index = index;
   2243 
   2244             if (wasm.code_section_index == null) {
   2245                 wasm.code_section_index = @intCast(wasm.segments.items.len);
   2246                 try wasm.segments.append(gpa, .{
   2247                     .alignment = atom.alignment,
   2248                     .size = atom.size,
   2249                     .offset = 0,
   2250                     .flags = 0,
   2251                 });
   2252             }
   2253 
   2254             break :result wasm.code_section_index.?;
   2255         },
   2256         .data => result: {
   2257             const segment_name = try std.mem.concat(gpa, u8, &.{
   2258                 kind.segmentName(),
   2259                 wasm.string_table.get(symbol.name),
   2260             });
   2261             errdefer gpa.free(segment_name);
   2262             const segment_info: types.Segment = .{
   2263                 .name = segment_name,
   2264                 .alignment = atom.alignment,
   2265                 .flags = 0,
   2266             };
   2267             symbol.tag = .data;
   2268 
   2269             // when creating an object file, or importing memory and the data belongs in the .bss segment
   2270             // we set the entire region of it to zeroes.
   2271             // We do not have to do this when exporting the memory (the default) because the runtime
   2272             // will do it for us, and we do not emit the bss segment at all.
   2273             if ((wasm.base.comp.config.output_mode == .Obj or import_memory) and kind.data == .uninitialized) {
   2274                 @memset(atom.code.items, 0);
   2275             }
   2276 
   2277             const should_merge = wasm.base.comp.config.output_mode != .Obj;
   2278             const gop = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(should_merge));
   2279             if (gop.found_existing) {
   2280                 const index = gop.value_ptr.*;
   2281                 wasm.segments.items[index].size += atom.size;
   2282 
   2283                 symbol.index = @intCast(wasm.segment_info.getIndex(index).?);
   2284                 // segment info already exists, so free its memory
   2285                 gpa.free(segment_name);
   2286                 break :result index;
   2287             } else {
   2288                 const index: u32 = @intCast(wasm.segments.items.len);
   2289                 var flags: u32 = 0;
   2290                 if (shared_memory) {
   2291                     flags |= @intFromEnum(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE);
   2292                 }
   2293                 try wasm.segments.append(gpa, .{
   2294                     .alignment = atom.alignment,
   2295                     .size = 0,
   2296                     .offset = 0,
   2297                     .flags = flags,
   2298                 });
   2299                 gop.value_ptr.* = index;
   2300 
   2301                 const info_index: u32 = @intCast(wasm.segment_info.count());
   2302                 try wasm.segment_info.put(gpa, index, segment_info);
   2303                 symbol.index = info_index;
   2304                 break :result index;
   2305             }
   2306         },
   2307     };
   2308 
   2309     const segment: *Segment = &wasm.segments.items[final_index];
   2310     segment.alignment = segment.alignment.max(atom.alignment);
   2311 
   2312     try wasm.appendAtomAtIndex(final_index, atom_index);
   2313 }
   2314 
   2315 /// From a given index, append the given `Atom` at the back of the linked list.
   2316 /// Simply inserts it into the map of atoms when it doesn't exist yet.
   2317 pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom_index: Atom.Index) !void {
   2318     const gpa = wasm.base.comp.gpa;
   2319     const atom = wasm.getAtomPtr(atom_index);
   2320     if (wasm.atoms.getPtr(index)) |last_index_ptr| {
   2321         atom.prev = last_index_ptr.*;
   2322         last_index_ptr.* = atom_index;
   2323     } else {
   2324         try wasm.atoms.putNoClobber(gpa, index, atom_index);
   2325     }
   2326 }
   2327 
   2328 /// Allocates debug atoms into their respective debug sections
   2329 /// to merge them with maybe-existing debug atoms from object files.
   2330 fn allocateDebugAtoms(wasm: *Wasm) !void {
   2331     if (wasm.dwarf == null) return;
   2332 
   2333     const allocAtom = struct {
   2334         fn f(bin: *Wasm, maybe_index: *?u32, atom_index: Atom.Index) !void {
   2335             const index = maybe_index.* orelse idx: {
   2336                 const index = @as(u32, @intCast(bin.segments.items.len));
   2337                 try bin.appendDummySegment();
   2338                 maybe_index.* = index;
   2339                 break :idx index;
   2340             };
   2341             const atom = bin.getAtomPtr(atom_index);
   2342             atom.size = @as(u32, @intCast(atom.code.items.len));
   2343             bin.symbols.items[atom.sym_index].index = index;
   2344             try bin.appendAtomAtIndex(index, atom_index);
   2345         }
   2346     }.f;
   2347 
   2348     try allocAtom(wasm, &wasm.debug_info_index, wasm.debug_info_atom.?);
   2349     try allocAtom(wasm, &wasm.debug_line_index, wasm.debug_line_atom.?);
   2350     try allocAtom(wasm, &wasm.debug_loc_index, wasm.debug_loc_atom.?);
   2351     try allocAtom(wasm, &wasm.debug_str_index, wasm.debug_str_atom.?);
   2352     try allocAtom(wasm, &wasm.debug_ranges_index, wasm.debug_ranges_atom.?);
   2353     try allocAtom(wasm, &wasm.debug_abbrev_index, wasm.debug_abbrev_atom.?);
   2354     try allocAtom(wasm, &wasm.debug_pubnames_index, wasm.debug_pubnames_atom.?);
   2355     try allocAtom(wasm, &wasm.debug_pubtypes_index, wasm.debug_pubtypes_atom.?);
   2356 }
   2357 
   2358 fn allocateAtoms(wasm: *Wasm) !void {
   2359     // first sort the data segments
   2360     try sortDataSegments(wasm);
   2361     try allocateDebugAtoms(wasm);
   2362 
   2363     var it = wasm.atoms.iterator();
   2364     while (it.next()) |entry| {
   2365         const segment = &wasm.segments.items[entry.key_ptr.*];
   2366         var atom_index = entry.value_ptr.*;
   2367         if (entry.key_ptr.* == wasm.code_section_index) {
   2368             // Code section is allocated upon writing as they are required to be ordered
   2369             // to synchronise with the function section.
   2370             continue;
   2371         }
   2372         var offset: u32 = 0;
   2373         while (true) {
   2374             const atom = wasm.getAtomPtr(atom_index);
   2375             const symbol_loc = atom.symbolLoc();
   2376             // Ensure we get the original symbol, so we verify the correct symbol on whether
   2377             // it is dead or not and ensure an atom is removed when dead.
   2378             // This is required as we may have parsed aliases into atoms.
   2379             const sym = if (symbol_loc.file) |object_index| sym: {
   2380                 const object = wasm.objects.items[object_index];
   2381                 break :sym object.symtable[symbol_loc.index];
   2382             } else wasm.symbols.items[symbol_loc.index];
   2383 
   2384             // Dead symbols must be unlinked from the linked-list to prevent them
   2385             // from being emit into the binary.
   2386             if (sym.isDead()) {
   2387                 if (entry.value_ptr.* == atom_index and atom.prev != null) {
   2388                     // When the atom is dead and is also the first atom retrieved from wasm.atoms(index) we update
   2389                     // the entry to point it to the previous atom to ensure we do not start with a dead symbol that
   2390                     // was removed and therefore do not emit any code at all.
   2391                     entry.value_ptr.* = atom.prev.?;
   2392                 }
   2393                 atom_index = atom.prev orelse break;
   2394                 atom.prev = null;
   2395                 continue;
   2396             }
   2397             offset = @intCast(atom.alignment.forward(offset));
   2398             atom.offset = offset;
   2399             log.debug("Atom '{s}' allocated from 0x{x:0>8} to 0x{x:0>8} size={d}", .{
   2400                 symbol_loc.getName(wasm),
   2401                 offset,
   2402                 offset + atom.size,
   2403                 atom.size,
   2404             });
   2405             offset += atom.size;
   2406             atom_index = atom.prev orelse break;
   2407         }
   2408         segment.size = @intCast(segment.alignment.forward(offset));
   2409     }
   2410 }
   2411 
   2412 /// For each data symbol, sets the virtual address.
   2413 fn allocateVirtualAddresses(wasm: *Wasm) void {
   2414     for (wasm.resolved_symbols.keys()) |loc| {
   2415         const symbol = loc.getSymbol(wasm);
   2416         if (symbol.tag != .data or symbol.isDead()) {
   2417             // Only data symbols have virtual addresses.
   2418             // Dead symbols do not get allocated, so we don't need to set their virtual address either.
   2419             continue;
   2420         }
   2421         const atom_index = wasm.symbol_atom.get(loc) orelse {
   2422             // synthetic symbol that does not contain an atom
   2423             continue;
   2424         };
   2425 
   2426         const atom = wasm.getAtom(atom_index);
   2427         const merge_segment = wasm.base.comp.config.output_mode != .Obj;
   2428         const segment_info = if (atom.file) |object_index| blk: {
   2429             break :blk wasm.objects.items[object_index].segment_info;
   2430         } else wasm.segment_info.values();
   2431         const segment_name = segment_info[symbol.index].outputName(merge_segment);
   2432         const segment_index = wasm.data_segments.get(segment_name).?;
   2433         const segment = wasm.segments.items[segment_index];
   2434 
   2435         // TLS symbols have their virtual address set relative to their own TLS segment,
   2436         // rather than the entire Data section.
   2437         if (symbol.hasFlag(.WASM_SYM_TLS)) {
   2438             symbol.virtual_address = atom.offset;
   2439         } else {
   2440             symbol.virtual_address = atom.offset + segment.offset;
   2441         }
   2442     }
   2443 }
   2444 
   2445 fn sortDataSegments(wasm: *Wasm) !void {
   2446     const gpa = wasm.base.comp.gpa;
   2447     var new_mapping: std.StringArrayHashMapUnmanaged(u32) = .{};
   2448     try new_mapping.ensureUnusedCapacity(gpa, wasm.data_segments.count());
   2449     errdefer new_mapping.deinit(gpa);
   2450 
   2451     const keys = try gpa.dupe([]const u8, wasm.data_segments.keys());
   2452     defer gpa.free(keys);
   2453 
   2454     const SortContext = struct {
   2455         fn sort(_: void, lhs: []const u8, rhs: []const u8) bool {
   2456             return order(lhs) < order(rhs);
   2457         }
   2458 
   2459         fn order(name: []const u8) u8 {
   2460             if (mem.startsWith(u8, name, ".rodata")) return 0;
   2461             if (mem.startsWith(u8, name, ".data")) return 1;
   2462             if (mem.startsWith(u8, name, ".text")) return 2;
   2463             return 3;
   2464         }
   2465     };
   2466 
   2467     mem.sort([]const u8, keys, {}, SortContext.sort);
   2468     for (keys) |key| {
   2469         const segment_index = wasm.data_segments.get(key).?;
   2470         new_mapping.putAssumeCapacity(key, segment_index);
   2471     }
   2472     wasm.data_segments.deinit(gpa);
   2473     wasm.data_segments = new_mapping;
   2474 }
   2475 
   2476 /// Obtains all initfuncs from each object file, verifies its function signature,
   2477 /// and then appends it to our final `init_funcs` list.
   2478 /// After all functions have been inserted, the functions will be ordered based
   2479 /// on their priority.
   2480 /// NOTE: This function must be called before we merged any other section.
   2481 /// This is because all init funcs in the object files contain references to the
   2482 /// original functions and their types. We need to know the type to verify it doesn't
   2483 /// contain any parameters.
   2484 fn setupInitFunctions(wasm: *Wasm) !void {
   2485     const gpa = wasm.base.comp.gpa;
   2486     for (wasm.objects.items, 0..) |object, file_index| {
   2487         try wasm.init_funcs.ensureUnusedCapacity(gpa, object.init_funcs.len);
   2488         for (object.init_funcs) |init_func| {
   2489             const symbol = object.symtable[init_func.symbol_index];
   2490             const ty: std.wasm.Type = if (symbol.isUndefined()) ty: {
   2491                 const imp: types.Import = object.findImport(.function, symbol.index);
   2492                 break :ty object.func_types[imp.kind.function];
   2493             } else ty: {
   2494                 const func_index = symbol.index - object.importedCountByKind(.function);
   2495                 const func = object.functions[func_index];
   2496                 break :ty object.func_types[func.type_index];
   2497             };
   2498             if (ty.params.len != 0) {
   2499                 log.err("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)});
   2500                 return error.InvalidInitFunc;
   2501             }
   2502             log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)});
   2503             wasm.init_funcs.appendAssumeCapacity(.{
   2504                 .index = init_func.symbol_index,
   2505                 .file = @as(u16, @intCast(file_index)),
   2506                 .priority = init_func.priority,
   2507             });
   2508             try wasm.mark(.{ .index = init_func.symbol_index, .file = @intCast(file_index) });
   2509         }
   2510     }
   2511 
   2512     // sort the initfunctions based on their priority
   2513     mem.sort(InitFuncLoc, wasm.init_funcs.items, {}, InitFuncLoc.lessThan);
   2514 
   2515     if (wasm.init_funcs.items.len > 0) {
   2516         const loc = wasm.findGlobalSymbol("__wasm_call_ctors").?;
   2517         try wasm.mark(loc);
   2518     }
   2519 }
   2520 
   2521 /// Generates an atom containing the global error set' size.
   2522 /// This will only be generated if the symbol exists.
   2523 fn setupErrorsLen(wasm: *Wasm) !void {
   2524     const gpa = wasm.base.comp.gpa;
   2525     const loc = wasm.findGlobalSymbol("__zig_errors_len") orelse return;
   2526 
   2527     const errors_len = wasm.base.comp.module.?.global_error_set.count();
   2528     // overwrite existing atom if it already exists (maybe the error set has increased)
   2529     // if not, allcoate a new atom.
   2530     const atom_index = if (wasm.symbol_atom.get(loc)) |index| blk: {
   2531         const atom = wasm.getAtomPtr(index);
   2532         atom.deinit(gpa);
   2533         break :blk index;
   2534     } else new_atom: {
   2535         const atom_index: Atom.Index = @intCast(wasm.managed_atoms.items.len);
   2536         try wasm.symbol_atom.put(gpa, loc, atom_index);
   2537         try wasm.managed_atoms.append(gpa, undefined);
   2538         break :new_atom atom_index;
   2539     };
   2540     const atom = wasm.getAtomPtr(atom_index);
   2541     atom.* = Atom.empty;
   2542     atom.sym_index = loc.index;
   2543     atom.size = 2;
   2544     try atom.code.writer(gpa).writeInt(u16, @intCast(errors_len), .little);
   2545 
   2546     try wasm.parseAtom(atom_index, .{ .data = .read_only });
   2547 }
   2548 
   2549 /// Creates a function body for the `__wasm_call_ctors` symbol.
   2550 /// Loops over all constructors found in `init_funcs` and calls them
   2551 /// respectively based on their priority which was sorted by `setupInitFunctions`.
   2552 /// NOTE: This function must be called after we merged all sections to ensure the
   2553 /// references to the function stored in the symbol have been finalized so we end
   2554 /// up calling the resolved function.
   2555 fn initializeCallCtorsFunction(wasm: *Wasm) !void {
   2556     const gpa = wasm.base.comp.gpa;
   2557     // No code to emit, so also no ctors to call
   2558     if (wasm.code_section_index == null) {
   2559         // Make sure to remove it from the resolved symbols so we do not emit
   2560         // it within any section. TODO: Remove this once we implement garbage collection.
   2561         const loc = wasm.findGlobalSymbol("__wasm_call_ctors").?;
   2562         assert(wasm.resolved_symbols.swapRemove(loc));
   2563         return;
   2564     }
   2565 
   2566     var function_body = std.ArrayList(u8).init(gpa);
   2567     defer function_body.deinit();
   2568     const writer = function_body.writer();
   2569 
   2570     // Create the function body
   2571     {
   2572         // Write locals count (we have none)
   2573         try leb.writeULEB128(writer, @as(u32, 0));
   2574 
   2575         // call constructors
   2576         for (wasm.init_funcs.items) |init_func_loc| {
   2577             const symbol = init_func_loc.getSymbol(wasm);
   2578             const func = wasm.functions.values()[symbol.index - wasm.imported_functions_count].func;
   2579             const ty = wasm.func_types.items[func.type_index];
   2580 
   2581             // Call function by its function index
   2582             try writer.writeByte(std.wasm.opcode(.call));
   2583             try leb.writeULEB128(writer, symbol.index);
   2584 
   2585             // drop all returned values from the stack as __wasm_call_ctors has no return value
   2586             for (ty.returns) |_| {
   2587                 try writer.writeByte(std.wasm.opcode(.drop));
   2588             }
   2589         }
   2590 
   2591         // End function body
   2592         try writer.writeByte(std.wasm.opcode(.end));
   2593     }
   2594 
   2595     try wasm.createSyntheticFunction(
   2596         "__wasm_call_ctors",
   2597         std.wasm.Type{ .params = &.{}, .returns = &.{} },
   2598         &function_body,
   2599     );
   2600 }
   2601 
   2602 fn createSyntheticFunction(
   2603     wasm: *Wasm,
   2604     symbol_name: []const u8,
   2605     func_ty: std.wasm.Type,
   2606     function_body: *std.ArrayList(u8),
   2607 ) !void {
   2608     const gpa = wasm.base.comp.gpa;
   2609     const loc = wasm.findGlobalSymbol(symbol_name) orelse
   2610         try wasm.createSyntheticSymbol(symbol_name, .function);
   2611     const symbol = loc.getSymbol(wasm);
   2612     if (symbol.isDead()) {
   2613         return;
   2614     }
   2615     const ty_index = try wasm.putOrGetFuncType(func_ty);
   2616     // create function with above type
   2617     const func_index = wasm.imported_functions_count + @as(u32, @intCast(wasm.functions.count()));
   2618     try wasm.functions.putNoClobber(
   2619         gpa,
   2620         .{ .file = null, .index = func_index },
   2621         .{ .func = .{ .type_index = ty_index }, .sym_index = loc.index },
   2622     );
   2623     symbol.index = func_index;
   2624 
   2625     // create the atom that will be output into the final binary
   2626     const atom_index = @as(Atom.Index, @intCast(wasm.managed_atoms.items.len));
   2627     const atom = try wasm.managed_atoms.addOne(gpa);
   2628     atom.* = .{
   2629         .size = @as(u32, @intCast(function_body.items.len)),
   2630         .offset = 0,
   2631         .sym_index = loc.index,
   2632         .file = null,
   2633         .alignment = .@"1",
   2634         .prev = null,
   2635         .code = function_body.moveToUnmanaged(),
   2636         .original_offset = 0,
   2637     };
   2638     try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom_index);
   2639     try wasm.symbol_atom.putNoClobber(gpa, loc, atom_index);
   2640 }
   2641 
   2642 /// Unlike `createSyntheticFunction` this function is to be called by
   2643 /// the codegeneration backend. This will not allocate the created Atom yet,
   2644 /// but will instead be appended to `synthetic_functions` list and will be
   2645 /// parsed at the end of code generation.
   2646 /// Returns the index of the symbol.
   2647 pub fn createFunction(
   2648     wasm: *Wasm,
   2649     symbol_name: []const u8,
   2650     func_ty: std.wasm.Type,
   2651     function_body: *std.ArrayList(u8),
   2652     relocations: *std.ArrayList(Relocation),
   2653 ) !u32 {
   2654     const gpa = wasm.base.comp.gpa;
   2655     const loc = try wasm.createSyntheticSymbol(symbol_name, .function);
   2656 
   2657     const atom_index: Atom.Index = @intCast(wasm.managed_atoms.items.len);
   2658     const atom = try wasm.managed_atoms.addOne(gpa);
   2659     atom.* = .{
   2660         .size = @intCast(function_body.items.len),
   2661         .offset = 0,
   2662         .sym_index = loc.index,
   2663         .file = null,
   2664         .alignment = .@"1",
   2665         .prev = null,
   2666         .code = function_body.moveToUnmanaged(),
   2667         .relocs = relocations.moveToUnmanaged(),
   2668         .original_offset = 0,
   2669     };
   2670     const symbol = loc.getSymbol(wasm);
   2671     symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); // ensure function does not get exported
   2672 
   2673     const section_index = wasm.code_section_index orelse idx: {
   2674         const index = @as(u32, @intCast(wasm.segments.items.len));
   2675         try wasm.appendDummySegment();
   2676         break :idx index;
   2677     };
   2678     try wasm.appendAtomAtIndex(section_index, atom_index);
   2679     try wasm.symbol_atom.putNoClobber(gpa, loc, atom_index);
   2680     try wasm.atom_types.put(gpa, atom_index, try wasm.putOrGetFuncType(func_ty));
   2681     try wasm.synthetic_functions.append(gpa, atom_index);
   2682 
   2683     return loc.index;
   2684 }
   2685 
   2686 /// If required, sets the function index in the `start` section.
   2687 fn setupStartSection(wasm: *Wasm) !void {
   2688     if (wasm.findGlobalSymbol("__wasm_init_memory")) |loc| {
   2689         wasm.entry = loc.getSymbol(wasm).index;
   2690     }
   2691 }
   2692 
   2693 fn initializeTLSFunction(wasm: *Wasm) !void {
   2694     const comp = wasm.base.comp;
   2695     const gpa = comp.gpa;
   2696     const shared_memory = comp.config.shared_memory;
   2697 
   2698     if (!shared_memory) return;
   2699 
   2700     var function_body = std.ArrayList(u8).init(gpa);
   2701     defer function_body.deinit();
   2702     const writer = function_body.writer();
   2703 
   2704     // locals
   2705     try writer.writeByte(0);
   2706 
   2707     // If there's a TLS segment, initialize it during runtime using the bulk-memory feature
   2708     if (wasm.data_segments.getIndex(".tdata")) |data_index| {
   2709         const segment_index = wasm.data_segments.entries.items(.value)[data_index];
   2710         const segment = wasm.segments.items[segment_index];
   2711 
   2712         const param_local: u32 = 0;
   2713 
   2714         try writer.writeByte(std.wasm.opcode(.local_get));
   2715         try leb.writeULEB128(writer, param_local);
   2716 
   2717         const tls_base_loc = wasm.findGlobalSymbol("__tls_base").?;
   2718         try writer.writeByte(std.wasm.opcode(.global_set));
   2719         try leb.writeULEB128(writer, tls_base_loc.getSymbol(wasm).index);
   2720 
   2721         // load stack values for the bulk-memory operation
   2722         {
   2723             try writer.writeByte(std.wasm.opcode(.local_get));
   2724             try leb.writeULEB128(writer, param_local);
   2725 
   2726             try writer.writeByte(std.wasm.opcode(.i32_const));
   2727             try leb.writeULEB128(writer, @as(u32, 0)); //segment offset
   2728 
   2729             try writer.writeByte(std.wasm.opcode(.i32_const));
   2730             try leb.writeULEB128(writer, @as(u32, segment.size)); //segment offset
   2731         }
   2732 
   2733         // perform the bulk-memory operation to initialize the data segment
   2734         try writer.writeByte(std.wasm.opcode(.misc_prefix));
   2735         try leb.writeULEB128(writer, std.wasm.miscOpcode(.memory_init));
   2736         // segment immediate
   2737         try leb.writeULEB128(writer, @as(u32, @intCast(data_index)));
   2738         // memory index immediate (always 0)
   2739         try leb.writeULEB128(writer, @as(u32, 0));
   2740     }
   2741 
   2742     // If we have to perform any TLS relocations, call the corresponding function
   2743     // which performs all runtime TLS relocations. This is a synthetic function,
   2744     // generated by the linker.
   2745     if (wasm.findGlobalSymbol("__wasm_apply_global_tls_relocs")) |loc| {
   2746         try writer.writeByte(std.wasm.opcode(.call));
   2747         try leb.writeULEB128(writer, loc.getSymbol(wasm).index);
   2748     }
   2749 
   2750     try writer.writeByte(std.wasm.opcode(.end));
   2751 
   2752     try wasm.createSyntheticFunction(
   2753         "__wasm_init_tls",
   2754         std.wasm.Type{ .params = &.{.i32}, .returns = &.{} },
   2755         &function_body,
   2756     );
   2757 }
   2758 
   2759 fn setupImports(wasm: *Wasm) !void {
   2760     const gpa = wasm.base.comp.gpa;
   2761     log.debug("Merging imports", .{});
   2762     var discarded_it = wasm.discarded.keyIterator();
   2763     while (discarded_it.next()) |discarded| {
   2764         if (discarded.file == null) {
   2765             // remove an import if it was resolved
   2766             if (wasm.imports.remove(discarded.*)) {
   2767                 log.debug("Removed symbol '{s}' as an import", .{
   2768                     discarded.getName(wasm),
   2769                 });
   2770             }
   2771         }
   2772     }
   2773 
   2774     for (wasm.resolved_symbols.keys()) |symbol_loc| {
   2775         const file_index = symbol_loc.file orelse {
   2776             // imports generated by Zig code are already in the `import` section
   2777             continue;
   2778         };
   2779 
   2780         const symbol = symbol_loc.getSymbol(wasm);
   2781         if (symbol.isDead() or
   2782             !symbol.requiresImport() or
   2783             std.mem.eql(u8, symbol_loc.getName(wasm), "__indirect_function_table"))
   2784         {
   2785             continue;
   2786         }
   2787 
   2788         log.debug("Symbol '{s}' will be imported from the host", .{symbol_loc.getName(wasm)});
   2789         const object = wasm.objects.items[file_index];
   2790         const import = object.findImport(symbol.tag.externalType(), symbol.index);
   2791 
   2792         // We copy the import to a new import to ensure the names contain references
   2793         // to the internal string table, rather than of the object file.
   2794         const new_imp: types.Import = .{
   2795             .module_name = try wasm.string_table.put(gpa, object.string_table.get(import.module_name)),
   2796             .name = try wasm.string_table.put(gpa, object.string_table.get(import.name)),
   2797             .kind = import.kind,
   2798         };
   2799         // TODO: De-duplicate imports when they contain the same names and type
   2800         try wasm.imports.putNoClobber(gpa, symbol_loc, new_imp);
   2801     }
   2802 
   2803     // Assign all indexes of the imports to their representing symbols
   2804     var function_index: u32 = 0;
   2805     var global_index: u32 = 0;
   2806     var table_index: u32 = 0;
   2807     var it = wasm.imports.iterator();
   2808     while (it.next()) |entry| {
   2809         const symbol = entry.key_ptr.*.getSymbol(wasm);
   2810         const import: types.Import = entry.value_ptr.*;
   2811         switch (import.kind) {
   2812             .function => {
   2813                 symbol.index = function_index;
   2814                 function_index += 1;
   2815             },
   2816             .global => {
   2817                 symbol.index = global_index;
   2818                 global_index += 1;
   2819             },
   2820             .table => {
   2821                 symbol.index = table_index;
   2822                 table_index += 1;
   2823             },
   2824             else => unreachable,
   2825         }
   2826     }
   2827     wasm.imported_functions_count = function_index;
   2828     wasm.imported_globals_count = global_index;
   2829     wasm.imported_tables_count = table_index;
   2830 
   2831     log.debug("Merged ({d}) functions, ({d}) globals, and ({d}) tables into import section", .{
   2832         function_index,
   2833         global_index,
   2834         table_index,
   2835     });
   2836 }
   2837 
   2838 /// Takes the global, function and table section from each linked object file
   2839 /// and merges it into a single section for each.
   2840 fn mergeSections(wasm: *Wasm) !void {
   2841     const gpa = wasm.base.comp.gpa;
   2842 
   2843     var removed_duplicates = std.ArrayList(SymbolLoc).init(gpa);
   2844     defer removed_duplicates.deinit();
   2845 
   2846     for (wasm.resolved_symbols.keys()) |sym_loc| {
   2847         if (sym_loc.file == null) {
   2848             // Zig code-generated symbols are already within the sections and do not
   2849             // require to be merged
   2850             continue;
   2851         }
   2852 
   2853         const object = &wasm.objects.items[sym_loc.file.?];
   2854         const symbol = &object.symtable[sym_loc.index];
   2855 
   2856         if (symbol.isDead() or
   2857             symbol.isUndefined() or
   2858             (symbol.tag != .function and symbol.tag != .global and symbol.tag != .table))
   2859         {
   2860             // Skip undefined symbols as they go in the `import` section
   2861             // Also skip symbols that do not need to have a section merged.
   2862             continue;
   2863         }
   2864 
   2865         const offset = object.importedCountByKind(symbol.tag.externalType());
   2866         const index = symbol.index - offset;
   2867         switch (symbol.tag) {
   2868             .function => {
   2869                 const gop = try wasm.functions.getOrPut(
   2870                     gpa,
   2871                     .{ .file = sym_loc.file, .index = symbol.index },
   2872                 );
   2873                 if (gop.found_existing) {
   2874                     // We found an alias to the same function, discard this symbol in favor of
   2875                     // the original symbol and point the discard function to it. This ensures
   2876                     // we only emit a single function, instead of duplicates.
   2877                     symbol.unmark();
   2878                     try wasm.discarded.putNoClobber(
   2879                         gpa,
   2880                         sym_loc,
   2881                         .{ .file = gop.key_ptr.*.file, .index = gop.value_ptr.*.sym_index },
   2882                     );
   2883                     try removed_duplicates.append(sym_loc);
   2884                     continue;
   2885                 }
   2886                 gop.value_ptr.* = .{ .func = object.functions[index], .sym_index = sym_loc.index };
   2887                 symbol.index = @as(u32, @intCast(gop.index)) + wasm.imported_functions_count;
   2888             },
   2889             .global => {
   2890                 const original_global = object.globals[index];
   2891                 symbol.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count;
   2892                 try wasm.wasm_globals.append(gpa, original_global);
   2893             },
   2894             .table => {
   2895                 const original_table = object.tables[index];
   2896                 symbol.index = @as(u32, @intCast(wasm.tables.items.len)) + wasm.imported_tables_count;
   2897                 try wasm.tables.append(gpa, original_table);
   2898             },
   2899             else => unreachable,
   2900         }
   2901     }
   2902 
   2903     // For any removed duplicates, remove them from the resolved symbols list
   2904     for (removed_duplicates.items) |sym_loc| {
   2905         assert(wasm.resolved_symbols.swapRemove(sym_loc));
   2906     }
   2907 
   2908     log.debug("Merged ({d}) functions", .{wasm.functions.count()});
   2909     log.debug("Merged ({d}) globals", .{wasm.wasm_globals.items.len});
   2910     log.debug("Merged ({d}) tables", .{wasm.tables.items.len});
   2911 }
   2912 
   2913 /// Merges function types of all object files into the final
   2914 /// 'types' section, while assigning the type index to the representing
   2915 /// section (import, export, function).
   2916 fn mergeTypes(wasm: *Wasm) !void {
   2917     const gpa = wasm.base.comp.gpa;
   2918     // A map to track which functions have already had their
   2919     // type inserted. If we do this for the same function multiple times,
   2920     // it will be overwritten with the incorrect type.
   2921     var dirty = std.AutoHashMap(u32, void).init(gpa);
   2922     try dirty.ensureUnusedCapacity(@as(u32, @intCast(wasm.functions.count())));
   2923     defer dirty.deinit();
   2924 
   2925     for (wasm.resolved_symbols.keys()) |sym_loc| {
   2926         if (sym_loc.file == null) {
   2927             // zig code-generated symbols are already present in final type section
   2928             continue;
   2929         }
   2930         const object = wasm.objects.items[sym_loc.file.?];
   2931         const symbol = object.symtable[sym_loc.index];
   2932         if (symbol.tag != .function or symbol.isDead()) {
   2933             // Only functions have types. Only retrieve the type of referenced functions.
   2934             continue;
   2935         }
   2936 
   2937         if (symbol.isUndefined()) {
   2938             log.debug("Adding type from extern function '{s}'", .{sym_loc.getName(wasm)});
   2939             const import: *types.Import = wasm.imports.getPtr(sym_loc) orelse continue;
   2940             const original_type = object.func_types[import.kind.function];
   2941             import.kind.function = try wasm.putOrGetFuncType(original_type);
   2942         } else if (!dirty.contains(symbol.index)) {
   2943             log.debug("Adding type from function '{s}'", .{sym_loc.getName(wasm)});
   2944             const func = &wasm.functions.values()[symbol.index - wasm.imported_functions_count].func;
   2945             func.type_index = try wasm.putOrGetFuncType(object.func_types[func.type_index]);
   2946             dirty.putAssumeCapacityNoClobber(symbol.index, {});
   2947         }
   2948     }
   2949     log.debug("Completed merging and deduplicating types. Total count: ({d})", .{wasm.func_types.items.len});
   2950 }
   2951 
   2952 fn setupExports(wasm: *Wasm) !void {
   2953     const comp = wasm.base.comp;
   2954     const gpa = comp.gpa;
   2955     if (comp.config.output_mode == .Obj) return;
   2956     log.debug("Building exports from symbols", .{});
   2957 
   2958     const force_exp_names = wasm.export_symbol_names;
   2959     if (force_exp_names.len > 0) {
   2960         var failed_exports = false;
   2961 
   2962         for (force_exp_names) |exp_name| {
   2963             const loc = wasm.findGlobalSymbol(exp_name) orelse {
   2964                 log.err("could not export '{s}', symbol not found", .{exp_name});
   2965                 failed_exports = true;
   2966                 continue;
   2967             };
   2968 
   2969             const symbol = loc.getSymbol(wasm);
   2970             symbol.setFlag(.WASM_SYM_EXPORTED);
   2971         }
   2972 
   2973         if (failed_exports) {
   2974             return error.MissingSymbol;
   2975         }
   2976     }
   2977 
   2978     for (wasm.resolved_symbols.keys()) |sym_loc| {
   2979         const symbol = sym_loc.getSymbol(wasm);
   2980         if (!symbol.isExported(comp.config.rdynamic)) continue;
   2981 
   2982         const sym_name = sym_loc.getName(wasm);
   2983         const export_name = if (wasm.export_names.get(sym_loc)) |name| name else blk: {
   2984             if (sym_loc.file == null) break :blk symbol.name;
   2985             break :blk try wasm.string_table.put(gpa, sym_name);
   2986         };
   2987         const exp: types.Export = if (symbol.tag == .data) exp: {
   2988             const global_index = @as(u32, @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len));
   2989             try wasm.wasm_globals.append(gpa, .{
   2990                 .global_type = .{ .valtype = .i32, .mutable = false },
   2991                 .init = .{ .i32_const = @as(i32, @intCast(symbol.virtual_address)) },
   2992             });
   2993             break :exp .{
   2994                 .name = export_name,
   2995                 .kind = .global,
   2996                 .index = global_index,
   2997             };
   2998         } else .{
   2999             .name = export_name,
   3000             .kind = symbol.tag.externalType(),
   3001             .index = symbol.index,
   3002         };
   3003         log.debug("Exporting symbol '{s}' as '{s}' at index: ({d})", .{
   3004             sym_name,
   3005             wasm.string_table.get(exp.name),
   3006             exp.index,
   3007         });
   3008         try wasm.exports.append(gpa, exp);
   3009     }
   3010 
   3011     log.debug("Completed building exports. Total count: ({d})", .{wasm.exports.items.len});
   3012 }
   3013 
   3014 fn setupStart(wasm: *Wasm) !void {
   3015     const comp = wasm.base.comp;
   3016     // do not export entry point if user set none or no default was set.
   3017     const entry_name = wasm.entry_name orelse return;
   3018 
   3019     const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {
   3020         log.err("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});
   3021         return error.MissingSymbol;
   3022     };
   3023 
   3024     const symbol = symbol_loc.getSymbol(wasm);
   3025     if (symbol.tag != .function) {
   3026         log.err("Entry symbol '{s}' is not a function", .{entry_name});
   3027         return error.InvalidEntryKind;
   3028     }
   3029 
   3030     // Ensure the symbol is exported so host environment can access it
   3031     if (comp.config.output_mode != .Obj) {
   3032         symbol.setFlag(.WASM_SYM_EXPORTED);
   3033     }
   3034 }
   3035 
   3036 /// Sets up the memory section of the wasm module, as well as the stack.
   3037 fn setupMemory(wasm: *Wasm) !void {
   3038     const comp = wasm.base.comp;
   3039     const shared_memory = comp.config.shared_memory;
   3040     log.debug("Setting up memory layout", .{});
   3041     const page_size = std.wasm.page_size; // 64kb
   3042     const stack_alignment: Alignment = .@"16"; // wasm's stack alignment as specified by tool-convention
   3043     const heap_alignment: Alignment = .@"16"; // wasm's heap alignment as specified by tool-convention
   3044 
   3045     // Always place the stack at the start by default
   3046     // unless the user specified the global-base flag
   3047     var place_stack_first = true;
   3048     var memory_ptr: u64 = if (wasm.global_base) |base| blk: {
   3049         place_stack_first = false;
   3050         break :blk base;
   3051     } else 0;
   3052 
   3053     const is_obj = comp.config.output_mode == .Obj;
   3054 
   3055     if (place_stack_first and !is_obj) {
   3056         memory_ptr = stack_alignment.forward(memory_ptr);
   3057         memory_ptr += wasm.base.stack_size;
   3058         // We always put the stack pointer global at index 0
   3059         wasm.wasm_globals.items[0].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr))));
   3060     }
   3061 
   3062     var offset: u32 = @as(u32, @intCast(memory_ptr));
   3063     var data_seg_it = wasm.data_segments.iterator();
   3064     while (data_seg_it.next()) |entry| {
   3065         const segment = &wasm.segments.items[entry.value_ptr.*];
   3066         memory_ptr = segment.alignment.forward(memory_ptr);
   3067 
   3068         // set TLS-related symbols
   3069         if (mem.eql(u8, entry.key_ptr.*, ".tdata")) {
   3070             if (wasm.findGlobalSymbol("__tls_size")) |loc| {
   3071                 const sym = loc.getSymbol(wasm);
   3072                 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.size);
   3073             }
   3074             if (wasm.findGlobalSymbol("__tls_align")) |loc| {
   3075                 const sym = loc.getSymbol(wasm);
   3076                 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.alignment.toByteUnitsOptional().?);
   3077             }
   3078             if (wasm.findGlobalSymbol("__tls_base")) |loc| {
   3079                 const sym = loc.getSymbol(wasm);
   3080                 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = if (shared_memory)
   3081                     @as(i32, 0)
   3082                 else
   3083                     @as(i32, @intCast(memory_ptr));
   3084             }
   3085         }
   3086 
   3087         memory_ptr += segment.size;
   3088         segment.offset = offset;
   3089         offset += segment.size;
   3090     }
   3091 
   3092     // create the memory init flag which is used by the init memory function
   3093     if (shared_memory and wasm.hasPassiveInitializationSegments()) {
   3094         // align to pointer size
   3095         memory_ptr = mem.alignForward(u64, memory_ptr, 4);
   3096         const loc = try wasm.createSyntheticSymbol("__wasm_init_memory_flag", .data);
   3097         const sym = loc.getSymbol(wasm);
   3098         sym.virtual_address = @as(u32, @intCast(memory_ptr));
   3099         memory_ptr += 4;
   3100     }
   3101 
   3102     if (!place_stack_first and !is_obj) {
   3103         memory_ptr = stack_alignment.forward(memory_ptr);
   3104         memory_ptr += wasm.base.stack_size;
   3105         wasm.wasm_globals.items[0].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr))));
   3106     }
   3107 
   3108     // One of the linked object files has a reference to the __heap_base symbol.
   3109     // We must set its virtual address so it can be used in relocations.
   3110     if (wasm.findGlobalSymbol("__heap_base")) |loc| {
   3111         const symbol = loc.getSymbol(wasm);
   3112         symbol.virtual_address = @intCast(heap_alignment.forward(memory_ptr));
   3113     }
   3114 
   3115     // Setup the max amount of pages
   3116     // For now we only support wasm32 by setting the maximum allowed memory size 2^32-1
   3117     const max_memory_allowed: u64 = (1 << 32) - 1;
   3118 
   3119     if (wasm.initial_memory) |initial_memory| {
   3120         if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) {
   3121             log.err("Initial memory must be {d}-byte aligned", .{page_size});
   3122             return error.MissAlignment;
   3123         }
   3124         if (memory_ptr > initial_memory) {
   3125             log.err("Initial memory too small, must be at least {d} bytes", .{memory_ptr});
   3126             return error.MemoryTooSmall;
   3127         }
   3128         if (initial_memory > max_memory_allowed) {
   3129             log.err("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});
   3130             return error.MemoryTooBig;
   3131         }
   3132         memory_ptr = initial_memory;
   3133     }
   3134     memory_ptr = mem.alignForward(u64, memory_ptr, std.wasm.page_size);
   3135     // In case we do not import memory, but define it ourselves,
   3136     // set the minimum amount of pages on the memory section.
   3137     wasm.memories.limits.min = @as(u32, @intCast(memory_ptr / page_size));
   3138     log.debug("Total memory pages: {d}", .{wasm.memories.limits.min});
   3139 
   3140     if (wasm.findGlobalSymbol("__heap_end")) |loc| {
   3141         const symbol = loc.getSymbol(wasm);
   3142         symbol.virtual_address = @as(u32, @intCast(memory_ptr));
   3143     }
   3144 
   3145     if (wasm.max_memory) |max_memory| {
   3146         if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) {
   3147             log.err("Maximum memory must be {d}-byte aligned", .{page_size});
   3148             return error.MissAlignment;
   3149         }
   3150         if (memory_ptr > max_memory) {
   3151             log.err("Maxmimum memory too small, must be at least {d} bytes", .{memory_ptr});
   3152             return error.MemoryTooSmall;
   3153         }
   3154         if (max_memory > max_memory_allowed) {
   3155             log.err("Maximum memory exceeds maxmium amount {d}", .{max_memory_allowed});
   3156             return error.MemoryTooBig;
   3157         }
   3158         wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size));
   3159         wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX);
   3160         if (shared_memory) {
   3161             wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_IS_SHARED);
   3162         }
   3163         log.debug("Maximum memory pages: {?d}", .{wasm.memories.limits.max});
   3164     }
   3165 }
   3166 
   3167 /// From a given object's index and the index of the segment, returns the corresponding
   3168 /// index of the segment within the final data section. When the segment does not yet
   3169 /// exist, a new one will be initialized and appended. The new index will be returned in that case.
   3170 pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u32 {
   3171     const comp = wasm.base.comp;
   3172     const gpa = comp.gpa;
   3173     const object: Object = wasm.objects.items[object_index];
   3174     const symbol = object.symtable[symbol_index];
   3175     const index: u32 = @intCast(wasm.segments.items.len);
   3176     const shared_memory = comp.config.shared_memory;
   3177 
   3178     switch (symbol.tag) {
   3179         .data => {
   3180             const segment_info = object.segment_info[symbol.index];
   3181             const merge_segment = comp.config.output_mode != .Obj;
   3182             const result = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(merge_segment));
   3183             if (!result.found_existing) {
   3184                 result.value_ptr.* = index;
   3185                 var flags: u32 = 0;
   3186                 if (shared_memory) {
   3187                     flags |= @intFromEnum(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE);
   3188                 }
   3189                 try wasm.segments.append(gpa, .{
   3190                     .alignment = .@"1",
   3191                     .size = 0,
   3192                     .offset = 0,
   3193                     .flags = flags,
   3194                 });
   3195                 try wasm.segment_info.putNoClobber(gpa, index, .{
   3196                     .name = try gpa.dupe(u8, segment_info.name),
   3197                     .alignment = segment_info.alignment,
   3198                     .flags = segment_info.flags,
   3199                 });
   3200                 return index;
   3201             } else return result.value_ptr.*;
   3202         },
   3203         .function => return wasm.code_section_index orelse blk: {
   3204             wasm.code_section_index = index;
   3205             try wasm.appendDummySegment();
   3206             break :blk index;
   3207         },
   3208         .section => {
   3209             const section_name = object.string_table.get(symbol.name);
   3210             if (mem.eql(u8, section_name, ".debug_info")) {
   3211                 return wasm.debug_info_index orelse blk: {
   3212                     wasm.debug_info_index = index;
   3213                     try wasm.appendDummySegment();
   3214                     break :blk index;
   3215                 };
   3216             } else if (mem.eql(u8, section_name, ".debug_line")) {
   3217                 return wasm.debug_line_index orelse blk: {
   3218                     wasm.debug_line_index = index;
   3219                     try wasm.appendDummySegment();
   3220                     break :blk index;
   3221                 };
   3222             } else if (mem.eql(u8, section_name, ".debug_loc")) {
   3223                 return wasm.debug_loc_index orelse blk: {
   3224                     wasm.debug_loc_index = index;
   3225                     try wasm.appendDummySegment();
   3226                     break :blk index;
   3227                 };
   3228             } else if (mem.eql(u8, section_name, ".debug_ranges")) {
   3229                 return wasm.debug_ranges_index orelse blk: {
   3230                     wasm.debug_ranges_index = index;
   3231                     try wasm.appendDummySegment();
   3232                     break :blk index;
   3233                 };
   3234             } else if (mem.eql(u8, section_name, ".debug_pubnames")) {
   3235                 return wasm.debug_pubnames_index orelse blk: {
   3236                     wasm.debug_pubnames_index = index;
   3237                     try wasm.appendDummySegment();
   3238                     break :blk index;
   3239                 };
   3240             } else if (mem.eql(u8, section_name, ".debug_pubtypes")) {
   3241                 return wasm.debug_pubtypes_index orelse blk: {
   3242                     wasm.debug_pubtypes_index = index;
   3243                     try wasm.appendDummySegment();
   3244                     break :blk index;
   3245                 };
   3246             } else if (mem.eql(u8, section_name, ".debug_abbrev")) {
   3247                 return wasm.debug_abbrev_index orelse blk: {
   3248                     wasm.debug_abbrev_index = index;
   3249                     try wasm.appendDummySegment();
   3250                     break :blk index;
   3251                 };
   3252             } else if (mem.eql(u8, section_name, ".debug_str")) {
   3253                 return wasm.debug_str_index orelse blk: {
   3254                     wasm.debug_str_index = index;
   3255                     try wasm.appendDummySegment();
   3256                     break :blk index;
   3257                 };
   3258             } else {
   3259                 log.warn("found unknown section '{s}'", .{section_name});
   3260                 return error.UnexpectedValue;
   3261             }
   3262         },
   3263         else => unreachable,
   3264     }
   3265 }
   3266 
   3267 /// Appends a new segment with default field values
   3268 fn appendDummySegment(wasm: *Wasm) !void {
   3269     const gpa = wasm.base.comp.gpa;
   3270     try wasm.segments.append(gpa, .{
   3271         .alignment = .@"1",
   3272         .size = 0,
   3273         .offset = 0,
   3274         .flags = 0,
   3275     });
   3276 }
   3277 
   3278 /// Returns the symbol index of the error name table.
   3279 ///
   3280 /// When the symbol does not yet exist, it will create a new one instead.
   3281 pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
   3282     if (wasm.error_table_symbol) |symbol| {
   3283         return symbol;
   3284     }
   3285 
   3286     // no error was referenced yet, so create a new symbol and atom for it
   3287     // and then return said symbol's index. The final table will be populated
   3288     // during `flush` when we know all possible error names.
   3289 
   3290     const gpa = wasm.base.comp.gpa;
   3291     const atom_index = try wasm.createAtom();
   3292     const atom = wasm.getAtomPtr(atom_index);
   3293     const slice_ty = Type.slice_const_u8_sentinel_0;
   3294     const mod = wasm.base.comp.module.?;
   3295     atom.alignment = slice_ty.abiAlignment(mod);
   3296     const sym_index = atom.sym_index;
   3297 
   3298     const sym_name = try wasm.string_table.put(gpa, "__zig_err_name_table");
   3299     const symbol = &wasm.symbols.items[sym_index];
   3300     symbol.* = .{
   3301         .name = sym_name,
   3302         .tag = .data,
   3303         .flags = 0,
   3304         .index = 0,
   3305         .virtual_address = undefined,
   3306     };
   3307     symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
   3308     symbol.mark();
   3309 
   3310     try wasm.resolved_symbols.put(gpa, atom.symbolLoc(), {});
   3311 
   3312     log.debug("Error name table was created with symbol index: ({d})", .{sym_index});
   3313     wasm.error_table_symbol = sym_index;
   3314     return sym_index;
   3315 }
   3316 
   3317 /// Populates the error name table, when `error_table_symbol` is not null.
   3318 ///
   3319 /// This creates a table that consists of pointers and length to each error name.
   3320 /// The table is what is being pointed to within the runtime bodies that are generated.
   3321 fn populateErrorNameTable(wasm: *Wasm) !void {
   3322     const gpa = wasm.base.comp.gpa;
   3323     const symbol_index = wasm.error_table_symbol orelse return;
   3324     const atom_index = wasm.symbol_atom.get(.{ .file = null, .index = symbol_index }).?;
   3325 
   3326     // Rather than creating a symbol for each individual error name,
   3327     // we create a symbol for the entire region of error names. We then calculate
   3328     // the pointers into the list using addends which are appended to the relocation.
   3329     const names_atom_index = try wasm.createAtom();
   3330     const names_atom = wasm.getAtomPtr(names_atom_index);
   3331     names_atom.alignment = .@"1";
   3332     const sym_name = try wasm.string_table.put(gpa, "__zig_err_names");
   3333     const names_symbol = &wasm.symbols.items[names_atom.sym_index];
   3334     names_symbol.* = .{
   3335         .name = sym_name,
   3336         .tag = .data,
   3337         .flags = 0,
   3338         .index = 0,
   3339         .virtual_address = undefined,
   3340     };
   3341     names_symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
   3342     names_symbol.mark();
   3343 
   3344     log.debug("Populating error names", .{});
   3345 
   3346     // Addend for each relocation to the table
   3347     var addend: u32 = 0;
   3348     const mod = wasm.base.comp.module.?;
   3349     for (mod.global_error_set.keys()) |error_name_nts| {
   3350         const atom = wasm.getAtomPtr(atom_index);
   3351 
   3352         const error_name = mod.intern_pool.stringToSlice(error_name_nts);
   3353         const len = @as(u32, @intCast(error_name.len + 1)); // names are 0-termianted
   3354 
   3355         const slice_ty = Type.slice_const_u8_sentinel_0;
   3356         const offset = @as(u32, @intCast(atom.code.items.len));
   3357         // first we create the data for the slice of the name
   3358         try atom.code.appendNTimes(gpa, 0, 4); // ptr to name, will be relocated
   3359         try atom.code.writer(gpa).writeInt(u32, len - 1, .little);
   3360         // create relocation to the error name
   3361         try atom.relocs.append(gpa, .{
   3362             .index = names_atom.sym_index,
   3363             .relocation_type = .R_WASM_MEMORY_ADDR_I32,
   3364             .offset = offset,
   3365             .addend = @as(i32, @intCast(addend)),
   3366         });
   3367         atom.size += @as(u32, @intCast(slice_ty.abiSize(mod)));
   3368         addend += len;
   3369 
   3370         // as we updated the error name table, we now store the actual name within the names atom
   3371         try names_atom.code.ensureUnusedCapacity(gpa, len);
   3372         names_atom.code.appendSliceAssumeCapacity(error_name);
   3373         names_atom.code.appendAssumeCapacity(0);
   3374 
   3375         log.debug("Populated error name: '{s}'", .{error_name});
   3376     }
   3377     names_atom.size = addend;
   3378 
   3379     const name_loc = names_atom.symbolLoc();
   3380     try wasm.resolved_symbols.put(gpa, name_loc, {});
   3381     try wasm.symbol_atom.put(gpa, name_loc, names_atom_index);
   3382 
   3383     // link the atoms with the rest of the binary so they can be allocated
   3384     // and relocations will be performed.
   3385     try wasm.parseAtom(atom_index, .{ .data = .read_only });
   3386     try wasm.parseAtom(names_atom_index, .{ .data = .read_only });
   3387 }
   3388 
   3389 /// From a given index variable, creates a new debug section.
   3390 /// This initializes the index, appends a new segment,
   3391 /// and finally, creates a managed `Atom`.
   3392 pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) !Atom.Index {
   3393     const gpa = wasm.base.comp.gpa;
   3394     const new_index: u32 = @intCast(wasm.segments.items.len);
   3395     index.* = new_index;
   3396     try wasm.appendDummySegment();
   3397 
   3398     const atom_index = try wasm.createAtom();
   3399     const atom = wasm.getAtomPtr(atom_index);
   3400     wasm.symbols.items[atom.sym_index] = .{
   3401         .tag = .section,
   3402         .name = try wasm.string_table.put(gpa, name),
   3403         .index = 0,
   3404         .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
   3405     };
   3406 
   3407     atom.alignment = .@"1"; // debug sections are always 1-byte-aligned
   3408     return atom_index;
   3409 }
   3410 
   3411 fn resetState(wasm: *Wasm) void {
   3412     const gpa = wasm.base.comp.gpa;
   3413 
   3414     for (wasm.segment_info.values()) |segment_info| {
   3415         gpa.free(segment_info.name);
   3416     }
   3417 
   3418     var atom_it = wasm.decls.valueIterator();
   3419     while (atom_it.next()) |atom_index| {
   3420         const atom = wasm.getAtomPtr(atom_index.*);
   3421         atom.prev = null;
   3422 
   3423         for (atom.locals.items) |local_atom_index| {
   3424             const local_atom = wasm.getAtomPtr(local_atom_index);
   3425             local_atom.prev = null;
   3426         }
   3427     }
   3428 
   3429     wasm.functions.clearRetainingCapacity();
   3430     wasm.exports.clearRetainingCapacity();
   3431     wasm.segments.clearRetainingCapacity();
   3432     wasm.segment_info.clearRetainingCapacity();
   3433     wasm.data_segments.clearRetainingCapacity();
   3434     wasm.atoms.clearRetainingCapacity();
   3435     wasm.symbol_atom.clearRetainingCapacity();
   3436     wasm.code_section_index = null;
   3437     wasm.debug_info_index = null;
   3438     wasm.debug_line_index = null;
   3439     wasm.debug_loc_index = null;
   3440     wasm.debug_str_index = null;
   3441     wasm.debug_ranges_index = null;
   3442     wasm.debug_abbrev_index = null;
   3443     wasm.debug_pubnames_index = null;
   3444     wasm.debug_pubtypes_index = null;
   3445 }
   3446 
   3447 pub fn flush(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {
   3448     const comp = wasm.base.comp;
   3449     const use_lld = build_options.have_llvm and comp.config.use_lld;
   3450     const use_llvm = comp.config.use_llvm;
   3451 
   3452     if (use_lld) {
   3453         return wasm.linkWithLLD(arena, prog_node);
   3454     } else if (use_llvm) {
   3455         return wasm.linkWithZld(arena, prog_node);
   3456     } else {
   3457         return wasm.flushModule(arena, prog_node);
   3458     }
   3459 }
   3460 
   3461 /// Uses the in-house linker to link one or multiple object -and archive files into a WebAssembly binary.
   3462 fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {
   3463     const tracy = trace(@src());
   3464     defer tracy.end();
   3465 
   3466     const comp = wasm.base.comp;
   3467     const shared_memory = comp.config.shared_memory;
   3468     const import_memory = comp.config.import_memory;
   3469 
   3470     const directory = wasm.base.emit.directory; // Just an alias to make it shorter to type.
   3471     const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.emit.sub_path});
   3472     const opt_zcu = comp.module;
   3473     const use_llvm = comp.config.use_llvm;
   3474 
   3475     // If there is no Zig code to compile, then we should skip flushing the output file because it
   3476     // will not be part of the linker line anyway.
   3477     const module_obj_path: ?[]const u8 = if (opt_zcu != null) blk: {
   3478         assert(use_llvm); // `linkWithZld` should never be called when the Wasm backend is used
   3479         try wasm.flushModule(arena, prog_node);
   3480 
   3481         if (fs.path.dirname(full_out_path)) |dirname| {
   3482             break :blk try fs.path.join(arena, &.{ dirname, wasm.base.zcu_object_sub_path.? });
   3483         } else {
   3484             break :blk wasm.base.zcu_object_sub_path.?;
   3485         }
   3486     } else null;
   3487 
   3488     var sub_prog_node = prog_node.start("Wasm Flush", 0);
   3489     sub_prog_node.activate();
   3490     defer sub_prog_node.end();
   3491 
   3492     const compiler_rt_path: ?[]const u8 = blk: {
   3493         if (comp.compiler_rt_obj) |obj| break :blk obj.full_object_path;
   3494         if (comp.compiler_rt_lib) |lib| break :blk lib.full_object_path;
   3495         break :blk null;
   3496     };
   3497 
   3498     const id_symlink_basename = "zld.id";
   3499 
   3500     var man: Cache.Manifest = undefined;
   3501     defer if (!wasm.base.disable_lld_caching) man.deinit();
   3502     var digest: [Cache.hex_digest_len]u8 = undefined;
   3503 
   3504     const objects = comp.objects;
   3505 
   3506     // NOTE: The following section must be maintained to be equal
   3507     // as the section defined in `linkWithLLD`
   3508     if (!wasm.base.disable_lld_caching) {
   3509         man = comp.cache_parent.obtain();
   3510 
   3511         // We are about to obtain this lock, so here we give other processes a chance first.
   3512         wasm.base.releaseLock();
   3513 
   3514         comptime assert(Compilation.link_hash_implementation_version == 11);
   3515 
   3516         for (objects) |obj| {
   3517             _ = try man.addFile(obj.path, null);
   3518             man.hash.add(obj.must_link);
   3519         }
   3520         for (comp.c_object_table.keys()) |key| {
   3521             _ = try man.addFile(key.status.success.object_path, null);
   3522         }
   3523         try man.addOptionalFile(module_obj_path);
   3524         try man.addOptionalFile(compiler_rt_path);
   3525         man.hash.addOptionalBytes(wasm.entry_name);
   3526         man.hash.add(wasm.base.stack_size);
   3527         man.hash.add(wasm.base.build_id);
   3528         man.hash.add(import_memory);
   3529         man.hash.add(shared_memory);
   3530         man.hash.add(wasm.import_table);
   3531         man.hash.add(wasm.export_table);
   3532         man.hash.addOptional(wasm.initial_memory);
   3533         man.hash.addOptional(wasm.max_memory);
   3534         man.hash.addOptional(wasm.global_base);
   3535         man.hash.add(wasm.export_symbol_names.len);
   3536         // strip does not need to go into the linker hash because it is part of the hash namespace
   3537         for (wasm.export_symbol_names) |symbol_name| {
   3538             man.hash.addBytes(symbol_name);
   3539         }
   3540 
   3541         // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
   3542         _ = try man.hit();
   3543         digest = man.final();
   3544 
   3545         var prev_digest_buf: [digest.len]u8 = undefined;
   3546         const prev_digest: []u8 = Cache.readSmallFile(
   3547             directory.handle,
   3548             id_symlink_basename,
   3549             &prev_digest_buf,
   3550         ) catch |err| blk: {
   3551             log.debug("WASM LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
   3552             // Handle this as a cache miss.
   3553             break :blk prev_digest_buf[0..0];
   3554         };
   3555         if (mem.eql(u8, prev_digest, &digest)) {
   3556             log.debug("WASM LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
   3557             // Hot diggity dog! The output binary is already there.
   3558             wasm.base.lock = man.toOwnedLock();
   3559             return;
   3560         }
   3561         log.debug("WASM LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
   3562 
   3563         // We are about to change the output file to be different, so we invalidate the build hash now.
   3564         directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
   3565             error.FileNotFound => {},
   3566             else => |e| return e,
   3567         };
   3568     }
   3569 
   3570     // Positional arguments to the linker such as object files and static archives.
   3571     var positionals = std.ArrayList([]const u8).init(arena);
   3572     try positionals.ensureUnusedCapacity(objects.len);
   3573 
   3574     const target = comp.root_mod.resolved_target.result;
   3575     const output_mode = comp.config.output_mode;
   3576     const link_mode = comp.config.link_mode;
   3577     const link_libc = comp.config.link_libc;
   3578     const link_libcpp = comp.config.link_libcpp;
   3579     const wasi_exec_model = comp.config.wasi_exec_model;
   3580 
   3581     // When the target os is WASI, we allow linking with WASI-LIBC
   3582     if (target.os.tag == .wasi) {
   3583         const is_exe_or_dyn_lib = output_mode == .Exe or
   3584             (output_mode == .Lib and link_mode == .Dynamic);
   3585         if (is_exe_or_dyn_lib) {
   3586             for (comp.wasi_emulated_libs) |crt_file| {
   3587                 try positionals.append(try comp.get_libc_crt_file(
   3588                     arena,
   3589                     wasi_libc.emulatedLibCRFileLibName(crt_file),
   3590                 ));
   3591             }
   3592 
   3593             if (link_libc) {
   3594                 try positionals.append(try comp.get_libc_crt_file(
   3595                     arena,
   3596                     wasi_libc.execModelCrtFileFullName(wasi_exec_model),
   3597                 ));
   3598                 try positionals.append(try comp.get_libc_crt_file(arena, "libc.a"));
   3599             }
   3600 
   3601             if (link_libcpp) {
   3602                 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
   3603                 try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
   3604             }
   3605         }
   3606     }
   3607 
   3608     if (module_obj_path) |path| {
   3609         try positionals.append(path);
   3610     }
   3611 
   3612     for (objects) |object| {
   3613         try positionals.append(object.path);
   3614     }
   3615 
   3616     for (comp.c_object_table.keys()) |c_object| {
   3617         try positionals.append(c_object.status.success.object_path);
   3618     }
   3619 
   3620     if (comp.compiler_rt_lib) |lib| try positionals.append(lib.full_object_path);
   3621     if (comp.compiler_rt_obj) |obj| try positionals.append(obj.full_object_path);
   3622 
   3623     try wasm.parseInputFiles(positionals.items);
   3624 
   3625     for (wasm.objects.items, 0..) |_, object_index| {
   3626         try wasm.resolveSymbolsInObject(@as(u16, @intCast(object_index)));
   3627     }
   3628 
   3629     var emit_features_count: u32 = 0;
   3630     var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;
   3631     try wasm.validateFeatures(&enabled_features, &emit_features_count);
   3632     try wasm.resolveSymbolsInArchives();
   3633     try wasm.resolveLazySymbols();
   3634     try wasm.checkUndefinedSymbols();
   3635 
   3636     try wasm.setupInitFunctions();
   3637     try wasm.setupStart();
   3638 
   3639     try wasm.markReferences();
   3640     try wasm.setupImports();
   3641     try wasm.mergeSections();
   3642     try wasm.mergeTypes();
   3643     try wasm.allocateAtoms();
   3644     try wasm.setupMemory();
   3645     wasm.allocateVirtualAddresses();
   3646     wasm.mapFunctionTable();
   3647     try wasm.initializeCallCtorsFunction();
   3648     try wasm.setupInitMemoryFunction();
   3649     try wasm.setupTLSRelocationsFunction();
   3650     try wasm.initializeTLSFunction();
   3651     try wasm.setupStartSection();
   3652     try wasm.setupExports();
   3653     try wasm.writeToFile(enabled_features, emit_features_count, arena);
   3654 
   3655     if (!wasm.base.disable_lld_caching) {
   3656         // Update the file with the digest. If it fails we can continue; it only
   3657         // means that the next invocation will have an unnecessary cache miss.
   3658         Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
   3659             log.warn("failed to save linking hash digest symlink: {s}", .{@errorName(err)});
   3660         };
   3661         // Again failure here only means an unnecessary cache miss.
   3662         man.writeManifest() catch |err| {
   3663             log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
   3664         };
   3665         // We hang on to this lock so that the output file path can be used without
   3666         // other processes clobbering it.
   3667         wasm.base.lock = man.toOwnedLock();
   3668     }
   3669 }
   3670 
   3671 pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {
   3672     const tracy = trace(@src());
   3673     defer tracy.end();
   3674 
   3675     const comp = wasm.base.comp;
   3676 
   3677     if (wasm.llvm_object) |llvm_object| {
   3678         try wasm.base.emitLlvmObject(arena, llvm_object, prog_node);
   3679         return;
   3680     }
   3681 
   3682     var sub_prog_node = prog_node.start("Wasm Flush", 0);
   3683     sub_prog_node.activate();
   3684     defer sub_prog_node.end();
   3685 
   3686     // ensure the error names table is populated when an error name is referenced
   3687     try wasm.populateErrorNameTable();
   3688 
   3689     const objects = comp.objects;
   3690 
   3691     // Positional arguments to the linker such as object files and static archives.
   3692     var positionals = std.ArrayList([]const u8).init(arena);
   3693     try positionals.ensureUnusedCapacity(objects.len);
   3694 
   3695     for (objects) |object| {
   3696         positionals.appendAssumeCapacity(object.path);
   3697     }
   3698 
   3699     for (comp.c_object_table.keys()) |c_object| {
   3700         try positionals.append(c_object.status.success.object_path);
   3701     }
   3702 
   3703     if (comp.compiler_rt_lib) |lib| try positionals.append(lib.full_object_path);
   3704     if (comp.compiler_rt_obj) |obj| try positionals.append(obj.full_object_path);
   3705 
   3706     try wasm.parseInputFiles(positionals.items);
   3707 
   3708     for (wasm.objects.items, 0..) |_, object_index| {
   3709         try wasm.resolveSymbolsInObject(@as(u16, @intCast(object_index)));
   3710     }
   3711 
   3712     var emit_features_count: u32 = 0;
   3713     var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;
   3714     try wasm.validateFeatures(&enabled_features, &emit_features_count);
   3715     try wasm.resolveSymbolsInArchives();
   3716     try wasm.resolveLazySymbols();
   3717     try wasm.checkUndefinedSymbols();
   3718 
   3719     // When we finish/error we reset the state of the linker
   3720     // So we can rebuild the binary file on each incremental update
   3721     defer wasm.resetState();
   3722     try wasm.setupInitFunctions();
   3723     try wasm.setupStart();
   3724     try wasm.markReferences();
   3725     try wasm.setupErrorsLen();
   3726     try wasm.setupImports();
   3727     if (comp.module) |mod| {
   3728         var decl_it = wasm.decls.iterator();
   3729         while (decl_it.next()) |entry| {
   3730             const decl = mod.declPtr(entry.key_ptr.*);
   3731             if (decl.isExtern(mod)) continue;
   3732             const atom_index = entry.value_ptr.*;
   3733             const atom = wasm.getAtomPtr(atom_index);
   3734             if (decl.ty.zigTypeTag(mod) == .Fn) {
   3735                 try wasm.parseAtom(atom_index, .function);
   3736             } else if (decl.getOwnedVariable(mod)) |variable| {
   3737                 if (variable.is_const) {
   3738                     try wasm.parseAtom(atom_index, .{ .data = .read_only });
   3739                 } else if (Value.fromInterned(variable.init).isUndefDeep(mod)) {
   3740                     // for safe build modes, we store the atom in the data segment,
   3741                     // whereas for unsafe build modes we store it in bss.
   3742                     const decl_namespace = mod.namespacePtr(decl.src_namespace);
   3743                     const optimize_mode = decl_namespace.file_scope.mod.optimize_mode;
   3744                     const is_initialized = switch (optimize_mode) {
   3745                         .Debug, .ReleaseSafe => true,
   3746                         .ReleaseFast, .ReleaseSmall => false,
   3747                     };
   3748                     try wasm.parseAtom(atom_index, .{ .data = if (is_initialized) .initialized else .uninitialized });
   3749                 } else {
   3750                     // when the decl is all zeroes, we store the atom in the bss segment,
   3751                     // in all other cases it will be in the data segment.
   3752                     const is_zeroes = for (atom.code.items) |byte| {
   3753                         if (byte != 0) break false;
   3754                     } else true;
   3755                     try wasm.parseAtom(atom_index, .{ .data = if (is_zeroes) .uninitialized else .initialized });
   3756                 }
   3757             } else {
   3758                 try wasm.parseAtom(atom_index, .{ .data = .read_only });
   3759             }
   3760 
   3761             // also parse atoms for a decl's locals
   3762             for (atom.locals.items) |local_atom_index| {
   3763                 try wasm.parseAtom(local_atom_index, .{ .data = .read_only });
   3764             }
   3765         }
   3766         // parse anonymous declarations
   3767         for (wasm.anon_decls.keys(), wasm.anon_decls.values()) |decl_val, atom_index| {
   3768             const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val));
   3769             if (ty.zigTypeTag(mod) == .Fn) {
   3770                 try wasm.parseAtom(atom_index, .function);
   3771             } else {
   3772                 try wasm.parseAtom(atom_index, .{ .data = .read_only });
   3773             }
   3774         }
   3775 
   3776         // also parse any backend-generated functions
   3777         for (wasm.synthetic_functions.items) |atom_index| {
   3778             try wasm.parseAtom(atom_index, .function);
   3779         }
   3780 
   3781         if (wasm.dwarf) |*dwarf| {
   3782             try dwarf.flushModule(comp.module.?);
   3783         }
   3784     }
   3785 
   3786     try wasm.mergeSections();
   3787     try wasm.mergeTypes();
   3788     try wasm.allocateAtoms();
   3789     try wasm.setupMemory();
   3790     wasm.allocateVirtualAddresses();
   3791     wasm.mapFunctionTable();
   3792     try wasm.initializeCallCtorsFunction();
   3793     try wasm.setupInitMemoryFunction();
   3794     try wasm.setupTLSRelocationsFunction();
   3795     try wasm.initializeTLSFunction();
   3796     try wasm.setupStartSection();
   3797     try wasm.setupExports();
   3798     try wasm.writeToFile(enabled_features, emit_features_count, arena);
   3799 }
   3800 
   3801 /// Writes the WebAssembly in-memory module to the file
   3802 fn writeToFile(
   3803     wasm: *Wasm,
   3804     enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool,
   3805     feature_count: u32,
   3806     arena: Allocator,
   3807 ) !void {
   3808     const comp = wasm.base.comp;
   3809     const gpa = comp.gpa;
   3810     const use_llvm = comp.config.use_llvm;
   3811     const use_lld = build_options.have_llvm and comp.config.use_lld;
   3812     const shared_memory = comp.config.shared_memory;
   3813     const import_memory = comp.config.import_memory;
   3814     const export_memory = comp.config.export_memory;
   3815 
   3816     // Size of each section header
   3817     const header_size = 5 + 1;
   3818     // The amount of sections that will be written
   3819     var section_count: u32 = 0;
   3820     // Index of the code section. Used to tell relocation table where the section lives.
   3821     var code_section_index: ?u32 = null;
   3822     // Index of the data section. Used to tell relocation table where the section lives.
   3823     var data_section_index: ?u32 = null;
   3824     const is_obj = comp.config.output_mode == .Obj or (!use_llvm and use_lld);
   3825 
   3826     var binary_bytes = std.ArrayList(u8).init(gpa);
   3827     defer binary_bytes.deinit();
   3828     const binary_writer = binary_bytes.writer();
   3829 
   3830     // We write the magic bytes at the end so they will only be written
   3831     // if everything succeeded as expected. So populate with 0's for now.
   3832     try binary_writer.writeAll(&[_]u8{0} ** 8);
   3833     // (Re)set file pointer to 0
   3834     try wasm.base.file.?.setEndPos(0);
   3835     try wasm.base.file.?.seekTo(0);
   3836 
   3837     // Type section
   3838     if (wasm.func_types.items.len != 0) {
   3839         const header_offset = try reserveVecSectionHeader(&binary_bytes);
   3840         log.debug("Writing type section. Count: ({d})", .{wasm.func_types.items.len});
   3841         for (wasm.func_types.items) |func_type| {
   3842             try leb.writeULEB128(binary_writer, std.wasm.function_type);
   3843             try leb.writeULEB128(binary_writer, @as(u32, @intCast(func_type.params.len)));
   3844             for (func_type.params) |param_ty| {
   3845                 try leb.writeULEB128(binary_writer, std.wasm.valtype(param_ty));
   3846             }
   3847             try leb.writeULEB128(binary_writer, @as(u32, @intCast(func_type.returns.len)));
   3848             for (func_type.returns) |ret_ty| {
   3849                 try leb.writeULEB128(binary_writer, std.wasm.valtype(ret_ty));
   3850             }
   3851         }
   3852 
   3853         try writeVecSectionHeader(
   3854             binary_bytes.items,
   3855             header_offset,
   3856             .type,
   3857             @intCast(binary_bytes.items.len - header_offset - header_size),
   3858             @intCast(wasm.func_types.items.len),
   3859         );
   3860         section_count += 1;
   3861     }
   3862 
   3863     // Import section
   3864     if (wasm.imports.count() != 0 or import_memory) {
   3865         const header_offset = try reserveVecSectionHeader(&binary_bytes);
   3866 
   3867         var it = wasm.imports.iterator();
   3868         while (it.next()) |entry| {
   3869             assert(entry.key_ptr.*.getSymbol(wasm).isUndefined());
   3870             const import = entry.value_ptr.*;
   3871             try wasm.emitImport(binary_writer, import);
   3872         }
   3873 
   3874         if (import_memory) {
   3875             const mem_name = if (is_obj) "__linear_memory" else "memory";
   3876             const mem_imp: types.Import = .{
   3877                 .module_name = try wasm.string_table.put(gpa, wasm.host_name),
   3878                 .name = try wasm.string_table.put(gpa, mem_name),
   3879                 .kind = .{ .memory = wasm.memories.limits },
   3880             };
   3881             try wasm.emitImport(binary_writer, mem_imp);
   3882         }
   3883 
   3884         try writeVecSectionHeader(
   3885             binary_bytes.items,
   3886             header_offset,
   3887             .import,
   3888             @intCast(binary_bytes.items.len - header_offset - header_size),
   3889             @intCast(wasm.imports.count() + @intFromBool(import_memory)),
   3890         );
   3891         section_count += 1;
   3892     }
   3893 
   3894     // Function section
   3895     if (wasm.functions.count() != 0) {
   3896         const header_offset = try reserveVecSectionHeader(&binary_bytes);
   3897         for (wasm.functions.values()) |function| {
   3898             try leb.writeULEB128(binary_writer, function.func.type_index);
   3899         }
   3900 
   3901         try writeVecSectionHeader(
   3902             binary_bytes.items,
   3903             header_offset,
   3904             .function,
   3905             @intCast(binary_bytes.items.len - header_offset - header_size),
   3906             @intCast(wasm.functions.count()),
   3907         );
   3908         section_count += 1;
   3909     }
   3910 
   3911     // Table section
   3912     if (wasm.tables.items.len > 0) {
   3913         const header_offset = try reserveVecSectionHeader(&binary_bytes);
   3914 
   3915         for (wasm.tables.items) |table| {
   3916             try leb.writeULEB128(binary_writer, std.wasm.reftype(table.reftype));
   3917             try emitLimits(binary_writer, table.limits);
   3918         }
   3919 
   3920         try writeVecSectionHeader(
   3921             binary_bytes.items,
   3922             header_offset,
   3923             .table,
   3924             @intCast(binary_bytes.items.len - header_offset - header_size),
   3925             @intCast(wasm.tables.items.len),
   3926         );
   3927         section_count += 1;
   3928     }
   3929 
   3930     // Memory section
   3931     if (!import_memory) {
   3932         const header_offset = try reserveVecSectionHeader(&binary_bytes);
   3933 
   3934         try emitLimits(binary_writer, wasm.memories.limits);
   3935         try writeVecSectionHeader(
   3936             binary_bytes.items,
   3937             header_offset,
   3938             .memory,
   3939             @intCast(binary_bytes.items.len - header_offset - header_size),
   3940             1, // wasm currently only supports 1 linear memory segment
   3941         );
   3942         section_count += 1;
   3943     }
   3944 
   3945     // Global section (used to emit stack pointer)
   3946     if (wasm.wasm_globals.items.len > 0) {
   3947         const header_offset = try reserveVecSectionHeader(&binary_bytes);
   3948 
   3949         for (wasm.wasm_globals.items) |global| {
   3950             try binary_writer.writeByte(std.wasm.valtype(global.global_type.valtype));
   3951             try binary_writer.writeByte(@intFromBool(global.global_type.mutable));
   3952             try emitInit(binary_writer, global.init);
   3953         }
   3954 
   3955         try writeVecSectionHeader(
   3956             binary_bytes.items,
   3957             header_offset,
   3958             .global,
   3959             @intCast(binary_bytes.items.len - header_offset - header_size),
   3960             @intCast(wasm.wasm_globals.items.len),
   3961         );
   3962         section_count += 1;
   3963     }
   3964 
   3965     // Export section
   3966     if (wasm.exports.items.len != 0 or export_memory) {
   3967         const header_offset = try reserveVecSectionHeader(&binary_bytes);
   3968 
   3969         for (wasm.exports.items) |exp| {
   3970             const name = wasm.string_table.get(exp.name);
   3971             try leb.writeULEB128(binary_writer, @as(u32, @intCast(name.len)));
   3972             try binary_writer.writeAll(name);
   3973             try leb.writeULEB128(binary_writer, @intFromEnum(exp.kind));
   3974             try leb.writeULEB128(binary_writer, exp.index);
   3975         }
   3976 
   3977         if (export_memory) {
   3978             try leb.writeULEB128(binary_writer, @as(u32, @intCast("memory".len)));
   3979             try binary_writer.writeAll("memory");
   3980             try binary_writer.writeByte(std.wasm.externalKind(.memory));
   3981             try leb.writeULEB128(binary_writer, @as(u32, 0));
   3982         }
   3983 
   3984         try writeVecSectionHeader(
   3985             binary_bytes.items,
   3986             header_offset,
   3987             .@"export",
   3988             @intCast(binary_bytes.items.len - header_offset - header_size),
   3989             @intCast(wasm.exports.items.len + @intFromBool(export_memory)),
   3990         );
   3991         section_count += 1;
   3992     }
   3993 
   3994     if (wasm.entry) |entry_index| {
   3995         const header_offset = try reserveVecSectionHeader(&binary_bytes);
   3996         try writeVecSectionHeader(
   3997             binary_bytes.items,
   3998             header_offset,
   3999             .start,
   4000             @intCast(binary_bytes.items.len - header_offset - header_size),
   4001             entry_index,
   4002         );
   4003     }
   4004 
   4005     // element section (function table)
   4006     if (wasm.function_table.count() > 0) {
   4007         const header_offset = try reserveVecSectionHeader(&binary_bytes);
   4008 
   4009         const table_loc = wasm.findGlobalSymbol("__indirect_function_table").?;
   4010         const table_sym = table_loc.getSymbol(wasm);
   4011 
   4012         const flags: u32 = if (table_sym.index == 0) 0x0 else 0x02; // passive with implicit 0-index table or set table index manually
   4013         try leb.writeULEB128(binary_writer, flags);
   4014         if (flags == 0x02) {
   4015             try leb.writeULEB128(binary_writer, table_sym.index);
   4016         }
   4017         try emitInit(binary_writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid
   4018         if (flags == 0x02) {
   4019             try leb.writeULEB128(binary_writer, @as(u8, 0)); // represents funcref
   4020         }
   4021         try leb.writeULEB128(binary_writer, @as(u32, @intCast(wasm.function_table.count())));
   4022         var symbol_it = wasm.function_table.keyIterator();
   4023         while (symbol_it.next()) |symbol_loc_ptr| {
   4024             const sym = symbol_loc_ptr.*.getSymbol(wasm);
   4025             try leb.writeULEB128(binary_writer, sym.index);
   4026         }
   4027 
   4028         try writeVecSectionHeader(
   4029             binary_bytes.items,
   4030             header_offset,
   4031             .element,
   4032             @intCast(binary_bytes.items.len - header_offset - header_size),
   4033             1,
   4034         );
   4035         section_count += 1;
   4036     }
   4037 
   4038     // When the shared-memory option is enabled, we *must* emit the 'data count' section.
   4039     const data_segments_count = wasm.data_segments.count() - @intFromBool(wasm.data_segments.contains(".bss") and !import_memory);
   4040     if (data_segments_count != 0 and shared_memory) {
   4041         const header_offset = try reserveVecSectionHeader(&binary_bytes);
   4042         try writeVecSectionHeader(
   4043             binary_bytes.items,
   4044             header_offset,
   4045             .data_count,
   4046             @intCast(binary_bytes.items.len - header_offset - header_size),
   4047             @intCast(data_segments_count),
   4048         );
   4049     }
   4050 
   4051     // Code section
   4052     if (wasm.code_section_index != null) {
   4053         const header_offset = try reserveVecSectionHeader(&binary_bytes);
   4054         const start_offset = binary_bytes.items.len - 5; // minus 5 so start offset is 5 to include entry count
   4055 
   4056         var func_it = wasm.functions.iterator();
   4057         while (func_it.next()) |entry| {
   4058             const sym_loc: SymbolLoc = .{ .index = entry.value_ptr.sym_index, .file = entry.key_ptr.file };
   4059             const atom_index = wasm.symbol_atom.get(sym_loc).?;
   4060             const atom = wasm.getAtomPtr(atom_index);
   4061 
   4062             if (!is_obj) {
   4063                 atom.resolveRelocs(wasm);
   4064             }
   4065             atom.offset = @intCast(binary_bytes.items.len - start_offset);
   4066             try leb.writeULEB128(binary_writer, atom.size);
   4067             try binary_writer.writeAll(atom.code.items);
   4068         }
   4069 
   4070         try writeVecSectionHeader(
   4071             binary_bytes.items,
   4072             header_offset,
   4073             .code,
   4074             @intCast(binary_bytes.items.len - header_offset - header_size),
   4075             @intCast(wasm.functions.count()),
   4076         );
   4077         code_section_index = section_count;
   4078         section_count += 1;
   4079     }
   4080 
   4081     // Data section
   4082     if (data_segments_count != 0) {
   4083         const header_offset = try reserveVecSectionHeader(&binary_bytes);
   4084 
   4085         var it = wasm.data_segments.iterator();
   4086         var segment_count: u32 = 0;
   4087         while (it.next()) |entry| {
   4088             // do not output 'bss' section unless we import memory and therefore
   4089             // want to guarantee the data is zero initialized
   4090             if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue;
   4091             const segment_index = entry.value_ptr.*;
   4092             const segment = wasm.segments.items[segment_index];
   4093             if (segment.size == 0) continue; // do not emit empty segments
   4094             segment_count += 1;
   4095             var atom_index = wasm.atoms.get(segment_index).?;
   4096 
   4097             try leb.writeULEB128(binary_writer, segment.flags);
   4098             if (segment.flags & @intFromEnum(Wasm.Segment.Flag.WASM_DATA_SEGMENT_HAS_MEMINDEX) != 0) {
   4099                 try leb.writeULEB128(binary_writer, @as(u32, 0)); // memory is always index 0 as we only have 1 memory entry
   4100             }
   4101             // when a segment is passive, it's initialized during runtime.
   4102             if (!segment.isPassive()) {
   4103                 try emitInit(binary_writer, .{ .i32_const = @as(i32, @bitCast(segment.offset)) });
   4104             }
   4105             // offset into data section
   4106             try leb.writeULEB128(binary_writer, segment.size);
   4107 
   4108             // fill in the offset table and the data segments
   4109             var current_offset: u32 = 0;
   4110             while (true) {
   4111                 const atom = wasm.getAtomPtr(atom_index);
   4112                 if (!is_obj) {
   4113                     atom.resolveRelocs(wasm);
   4114                 }
   4115 
   4116                 // Pad with zeroes to ensure all segments are aligned
   4117                 if (current_offset != atom.offset) {
   4118                     const diff = atom.offset - current_offset;
   4119                     try binary_writer.writeByteNTimes(0, diff);
   4120                     current_offset += diff;
   4121                 }
   4122                 assert(current_offset == atom.offset);
   4123                 assert(atom.code.items.len == atom.size);
   4124                 try binary_writer.writeAll(atom.code.items);
   4125 
   4126                 current_offset += atom.size;
   4127                 if (atom.prev) |prev| {
   4128                     atom_index = prev;
   4129                 } else {
   4130                     // also pad with zeroes when last atom to ensure
   4131                     // segments are aligned.
   4132                     if (current_offset != segment.size) {
   4133                         try binary_writer.writeByteNTimes(0, segment.size - current_offset);
   4134                         current_offset += segment.size - current_offset;
   4135                     }
   4136                     break;
   4137                 }
   4138             }
   4139             assert(current_offset == segment.size);
   4140         }
   4141 
   4142         try writeVecSectionHeader(
   4143             binary_bytes.items,
   4144             header_offset,
   4145             .data,
   4146             @intCast(binary_bytes.items.len - header_offset - header_size),
   4147             @intCast(segment_count),
   4148         );
   4149         data_section_index = section_count;
   4150         section_count += 1;
   4151     }
   4152 
   4153     if (is_obj) {
   4154         // relocations need to point to the index of a symbol in the final symbol table. To save memory,
   4155         // we never store all symbols in a single table, but store a location reference instead.
   4156         // This means that for a relocatable object file, we need to generate one and provide it to the relocation sections.
   4157         var symbol_table = std.AutoArrayHashMap(SymbolLoc, u32).init(arena);
   4158         try wasm.emitLinkSection(&binary_bytes, &symbol_table);
   4159         if (code_section_index) |code_index| {
   4160             try wasm.emitCodeRelocations(&binary_bytes, code_index, symbol_table);
   4161         }
   4162         if (data_section_index) |data_index| {
   4163             try wasm.emitDataRelocations(&binary_bytes, data_index, symbol_table);
   4164         }
   4165     } else if (comp.config.debug_format != .strip) {
   4166         try wasm.emitNameSection(&binary_bytes, arena);
   4167     }
   4168 
   4169     if (comp.config.debug_format != .strip) {
   4170         // The build id must be computed on the main sections only,
   4171         // so we have to do it now, before the debug sections.
   4172         switch (wasm.base.build_id) {
   4173             .none => {},
   4174             .fast => {
   4175                 var id: [16]u8 = undefined;
   4176                 std.crypto.hash.sha3.TurboShake128(null).hash(binary_bytes.items, &id, .{});
   4177                 var uuid: [36]u8 = undefined;
   4178                 _ = try std.fmt.bufPrint(&uuid, "{s}-{s}-{s}-{s}-{s}", .{
   4179                     std.fmt.fmtSliceHexLower(id[0..4]),
   4180                     std.fmt.fmtSliceHexLower(id[4..6]),
   4181                     std.fmt.fmtSliceHexLower(id[6..8]),
   4182                     std.fmt.fmtSliceHexLower(id[8..10]),
   4183                     std.fmt.fmtSliceHexLower(id[10..]),
   4184                 });
   4185                 try emitBuildIdSection(&binary_bytes, &uuid);
   4186             },
   4187             .hexstring => |hs| {
   4188                 var buffer: [32 * 2]u8 = undefined;
   4189                 const str = std.fmt.bufPrint(&buffer, "{s}", .{
   4190                     std.fmt.fmtSliceHexLower(hs.toSlice()),
   4191                 }) catch unreachable;
   4192                 try emitBuildIdSection(&binary_bytes, str);
   4193             },
   4194             else => |mode| log.err("build-id '{s}' is not supported for WASM", .{@tagName(mode)}),
   4195         }
   4196 
   4197         // if (wasm.dwarf) |*dwarf| {
   4198         //     const mod = comp.module.?;
   4199         //     try dwarf.writeDbgAbbrev();
   4200         //     // for debug info and ranges, the address is always 0,
   4201         //     // as locations are always offsets relative to 'code' section.
   4202         //     try dwarf.writeDbgInfoHeader(mod, 0, code_section_size);
   4203         //     try dwarf.writeDbgAranges(0, code_section_size);
   4204         //     try dwarf.writeDbgLineHeader();
   4205         // }
   4206 
   4207         var debug_bytes = std.ArrayList(u8).init(gpa);
   4208         defer debug_bytes.deinit();
   4209 
   4210         const DebugSection = struct {
   4211             name: []const u8,
   4212             index: ?u32,
   4213         };
   4214 
   4215         const debug_sections: []const DebugSection = &.{
   4216             .{ .name = ".debug_info", .index = wasm.debug_info_index },
   4217             .{ .name = ".debug_pubtypes", .index = wasm.debug_pubtypes_index },
   4218             .{ .name = ".debug_abbrev", .index = wasm.debug_abbrev_index },
   4219             .{ .name = ".debug_line", .index = wasm.debug_line_index },
   4220             .{ .name = ".debug_str", .index = wasm.debug_str_index },
   4221             .{ .name = ".debug_pubnames", .index = wasm.debug_pubnames_index },
   4222             .{ .name = ".debug_loc", .index = wasm.debug_loc_index },
   4223             .{ .name = ".debug_ranges", .index = wasm.debug_ranges_index },
   4224         };
   4225 
   4226         for (debug_sections) |item| {
   4227             if (item.index) |index| {
   4228                 var atom = wasm.getAtomPtr(wasm.atoms.get(index).?);
   4229                 while (true) {
   4230                     atom.resolveRelocs(wasm);
   4231                     try debug_bytes.appendSlice(atom.code.items);
   4232                     atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
   4233                 }
   4234                 try emitDebugSection(&binary_bytes, debug_bytes.items, item.name);
   4235                 debug_bytes.clearRetainingCapacity();
   4236             }
   4237         }
   4238 
   4239         try emitProducerSection(&binary_bytes);
   4240         if (feature_count > 0) {
   4241             try emitFeaturesSection(&binary_bytes, &enabled_features, feature_count);
   4242         }
   4243     }
   4244 
   4245     // Only when writing all sections executed properly we write the magic
   4246     // bytes. This allows us to easily detect what went wrong while generating
   4247     // the final binary.
   4248     {
   4249         const src = std.wasm.magic ++ std.wasm.version;
   4250         binary_bytes.items[0..src.len].* = src;
   4251     }
   4252 
   4253     // finally, write the entire binary into the file.
   4254     var iovec = [_]std.os.iovec_const{.{
   4255         .iov_base = binary_bytes.items.ptr,
   4256         .iov_len = binary_bytes.items.len,
   4257     }};
   4258     try wasm.base.file.?.writevAll(&iovec);
   4259 }
   4260 
   4261 fn emitDebugSection(binary_bytes: *std.ArrayList(u8), data: []const u8, name: []const u8) !void {
   4262     if (data.len == 0) return;
   4263     const header_offset = try reserveCustomSectionHeader(binary_bytes);
   4264     const writer = binary_bytes.writer();
   4265     try leb.writeULEB128(writer, @as(u32, @intCast(name.len)));
   4266     try writer.writeAll(name);
   4267 
   4268     const start = binary_bytes.items.len - header_offset;
   4269     log.debug("Emit debug section: '{s}' start=0x{x:0>8} end=0x{x:0>8}", .{ name, start, start + data.len });
   4270     try writer.writeAll(data);
   4271 
   4272     try writeCustomSectionHeader(
   4273         binary_bytes.items,
   4274         header_offset,
   4275         @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
   4276     );
   4277 }
   4278 
   4279 fn emitProducerSection(binary_bytes: *std.ArrayList(u8)) !void {
   4280     const header_offset = try reserveCustomSectionHeader(binary_bytes);
   4281 
   4282     const writer = binary_bytes.writer();
   4283     const producers = "producers";
   4284     try leb.writeULEB128(writer, @as(u32, @intCast(producers.len)));
   4285     try writer.writeAll(producers);
   4286 
   4287     try leb.writeULEB128(writer, @as(u32, 2)); // 2 fields: Language + processed-by
   4288 
   4289     // used for the Zig version
   4290     var version_buf: [100]u8 = undefined;
   4291     const version = try std.fmt.bufPrint(&version_buf, "{}", .{build_options.semver});
   4292 
   4293     // language field
   4294     {
   4295         const language = "language";
   4296         try leb.writeULEB128(writer, @as(u32, @intCast(language.len)));
   4297         try writer.writeAll(language);
   4298 
   4299         // field_value_count (TODO: Parse object files for producer sections to detect their language)
   4300         try leb.writeULEB128(writer, @as(u32, 1));
   4301 
   4302         // versioned name
   4303         {
   4304             try leb.writeULEB128(writer, @as(u32, 3)); // len of "Zig"
   4305             try writer.writeAll("Zig");
   4306 
   4307             try leb.writeULEB128(writer, @as(u32, @intCast(version.len)));
   4308             try writer.writeAll(version);
   4309         }
   4310     }
   4311 
   4312     // processed-by field
   4313     {
   4314         const processed_by = "processed-by";
   4315         try leb.writeULEB128(writer, @as(u32, @intCast(processed_by.len)));
   4316         try writer.writeAll(processed_by);
   4317 
   4318         // field_value_count (TODO: Parse object files for producer sections to detect other used tools)
   4319         try leb.writeULEB128(writer, @as(u32, 1));
   4320 
   4321         // versioned name
   4322         {
   4323             try leb.writeULEB128(writer, @as(u32, 3)); // len of "Zig"
   4324             try writer.writeAll("Zig");
   4325 
   4326             try leb.writeULEB128(writer, @as(u32, @intCast(version.len)));
   4327             try writer.writeAll(version);
   4328         }
   4329     }
   4330 
   4331     try writeCustomSectionHeader(
   4332         binary_bytes.items,
   4333         header_offset,
   4334         @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
   4335     );
   4336 }
   4337 
   4338 fn emitBuildIdSection(binary_bytes: *std.ArrayList(u8), build_id: []const u8) !void {
   4339     const header_offset = try reserveCustomSectionHeader(binary_bytes);
   4340 
   4341     const writer = binary_bytes.writer();
   4342     const hdr_build_id = "build_id";
   4343     try leb.writeULEB128(writer, @as(u32, @intCast(hdr_build_id.len)));
   4344     try writer.writeAll(hdr_build_id);
   4345 
   4346     try leb.writeULEB128(writer, @as(u32, 1));
   4347     try leb.writeULEB128(writer, @as(u32, @intCast(build_id.len)));
   4348     try writer.writeAll(build_id);
   4349 
   4350     try writeCustomSectionHeader(
   4351         binary_bytes.items,
   4352         header_offset,
   4353         @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
   4354     );
   4355 }
   4356 
   4357 fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []const bool, features_count: u32) !void {
   4358     const header_offset = try reserveCustomSectionHeader(binary_bytes);
   4359 
   4360     const writer = binary_bytes.writer();
   4361     const target_features = "target_features";
   4362     try leb.writeULEB128(writer, @as(u32, @intCast(target_features.len)));
   4363     try writer.writeAll(target_features);
   4364 
   4365     try leb.writeULEB128(writer, features_count);
   4366     for (enabled_features, 0..) |enabled, feature_index| {
   4367         if (enabled) {
   4368             const feature: types.Feature = .{ .prefix = .used, .tag = @as(types.Feature.Tag, @enumFromInt(feature_index)) };
   4369             try leb.writeULEB128(writer, @intFromEnum(feature.prefix));
   4370             var buf: [100]u8 = undefined;
   4371             const string = try std.fmt.bufPrint(&buf, "{}", .{feature.tag});
   4372             try leb.writeULEB128(writer, @as(u32, @intCast(string.len)));
   4373             try writer.writeAll(string);
   4374         }
   4375     }
   4376 
   4377     try writeCustomSectionHeader(
   4378         binary_bytes.items,
   4379         header_offset,
   4380         @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
   4381     );
   4382 }
   4383 
   4384 fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem.Allocator) !void {
   4385     const comp = wasm.base.comp;
   4386     const import_memory = comp.config.import_memory;
   4387     const Name = struct {
   4388         index: u32,
   4389         name: []const u8,
   4390 
   4391         fn lessThan(context: void, lhs: @This(), rhs: @This()) bool {
   4392             _ = context;
   4393             return lhs.index < rhs.index;
   4394         }
   4395     };
   4396 
   4397     // we must de-duplicate symbols that point to the same function
   4398     var funcs = std.AutoArrayHashMap(u32, Name).init(arena);
   4399     try funcs.ensureUnusedCapacity(wasm.functions.count() + wasm.imported_functions_count);
   4400     var globals = try std.ArrayList(Name).initCapacity(arena, wasm.wasm_globals.items.len + wasm.imported_globals_count);
   4401     var segments = try std.ArrayList(Name).initCapacity(arena, wasm.data_segments.count());
   4402 
   4403     for (wasm.resolved_symbols.keys()) |sym_loc| {
   4404         const symbol = sym_loc.getSymbol(wasm).*;
   4405         if (symbol.isDead()) {
   4406             continue;
   4407         }
   4408         const name = sym_loc.getName(wasm);
   4409         switch (symbol.tag) {
   4410             .function => {
   4411                 const gop = funcs.getOrPutAssumeCapacity(symbol.index);
   4412                 if (!gop.found_existing) {
   4413                     gop.value_ptr.* = .{ .index = symbol.index, .name = name };
   4414                 }
   4415             },
   4416             .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = name }),
   4417             else => {},
   4418         }
   4419     }
   4420     // data segments are already 'ordered'
   4421     var data_segment_index: u32 = 0;
   4422     for (wasm.data_segments.keys()) |key| {
   4423         // bss section is not emitted when this condition holds true, so we also
   4424         // do not output a name for it.
   4425         if (!import_memory and std.mem.eql(u8, key, ".bss")) continue;
   4426         segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key });
   4427         data_segment_index += 1;
   4428     }
   4429 
   4430     mem.sort(Name, funcs.values(), {}, Name.lessThan);
   4431     mem.sort(Name, globals.items, {}, Name.lessThan);
   4432 
   4433     const header_offset = try reserveCustomSectionHeader(binary_bytes);
   4434     const writer = binary_bytes.writer();
   4435     try leb.writeULEB128(writer, @as(u32, @intCast("name".len)));
   4436     try writer.writeAll("name");
   4437 
   4438     try wasm.emitNameSubsection(.function, funcs.values(), writer);
   4439     try wasm.emitNameSubsection(.global, globals.items, writer);
   4440     try wasm.emitNameSubsection(.data_segment, segments.items, writer);
   4441 
   4442     try writeCustomSectionHeader(
   4443         binary_bytes.items,
   4444         header_offset,
   4445         @as(u32, @intCast(binary_bytes.items.len - header_offset - 6)),
   4446     );
   4447 }
   4448 
   4449 fn emitNameSubsection(wasm: *Wasm, section_id: std.wasm.NameSubsection, names: anytype, writer: anytype) !void {
   4450     const gpa = wasm.base.comp.gpa;
   4451 
   4452     // We must emit subsection size, so first write to a temporary list
   4453     var section_list = std.ArrayList(u8).init(gpa);
   4454     defer section_list.deinit();
   4455     const sub_writer = section_list.writer();
   4456 
   4457     try leb.writeULEB128(sub_writer, @as(u32, @intCast(names.len)));
   4458     for (names) |name| {
   4459         log.debug("Emit symbol '{s}' type({s})", .{ name.name, @tagName(section_id) });
   4460         try leb.writeULEB128(sub_writer, name.index);
   4461         try leb.writeULEB128(sub_writer, @as(u32, @intCast(name.name.len)));
   4462         try sub_writer.writeAll(name.name);
   4463     }
   4464 
   4465     // From now, write to the actual writer
   4466     try leb.writeULEB128(writer, @intFromEnum(section_id));
   4467     try leb.writeULEB128(writer, @as(u32, @intCast(section_list.items.len)));
   4468     try writer.writeAll(section_list.items);
   4469 }
   4470 
   4471 fn emitLimits(writer: anytype, limits: std.wasm.Limits) !void {
   4472     try writer.writeByte(limits.flags);
   4473     try leb.writeULEB128(writer, limits.min);
   4474     if (limits.hasFlag(.WASM_LIMITS_FLAG_HAS_MAX)) {
   4475         try leb.writeULEB128(writer, limits.max);
   4476     }
   4477 }
   4478 
   4479 fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void {
   4480     switch (init_expr) {
   4481         .i32_const => |val| {
   4482             try writer.writeByte(std.wasm.opcode(.i32_const));
   4483             try leb.writeILEB128(writer, val);
   4484         },
   4485         .i64_const => |val| {
   4486             try writer.writeByte(std.wasm.opcode(.i64_const));
   4487             try leb.writeILEB128(writer, val);
   4488         },
   4489         .f32_const => |val| {
   4490             try writer.writeByte(std.wasm.opcode(.f32_const));
   4491             try writer.writeInt(u32, @bitCast(val), .little);
   4492         },
   4493         .f64_const => |val| {
   4494             try writer.writeByte(std.wasm.opcode(.f64_const));
   4495             try writer.writeInt(u64, @bitCast(val), .little);
   4496         },
   4497         .global_get => |val| {
   4498             try writer.writeByte(std.wasm.opcode(.global_get));
   4499             try leb.writeULEB128(writer, val);
   4500         },
   4501     }
   4502     try writer.writeByte(std.wasm.opcode(.end));
   4503 }
   4504 
   4505 fn emitImport(wasm: *Wasm, writer: anytype, import: types.Import) !void {
   4506     const module_name = wasm.string_table.get(import.module_name);
   4507     try leb.writeULEB128(writer, @as(u32, @intCast(module_name.len)));
   4508     try writer.writeAll(module_name);
   4509 
   4510     const name = wasm.string_table.get(import.name);
   4511     try leb.writeULEB128(writer, @as(u32, @intCast(name.len)));
   4512     try writer.writeAll(name);
   4513 
   4514     try writer.writeByte(@intFromEnum(import.kind));
   4515     switch (import.kind) {
   4516         .function => |type_index| try leb.writeULEB128(writer, type_index),
   4517         .global => |global_type| {
   4518             try leb.writeULEB128(writer, std.wasm.valtype(global_type.valtype));
   4519             try writer.writeByte(@intFromBool(global_type.mutable));
   4520         },
   4521         .table => |table| {
   4522             try leb.writeULEB128(writer, std.wasm.reftype(table.reftype));
   4523             try emitLimits(writer, table.limits);
   4524         },
   4525         .memory => |limits| {
   4526             try emitLimits(writer, limits);
   4527         },
   4528     }
   4529 }
   4530 
   4531 fn linkWithLLD(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) !void {
   4532     const tracy = trace(@src());
   4533     defer tracy.end();
   4534 
   4535     const comp = wasm.base.comp;
   4536     const shared_memory = comp.config.shared_memory;
   4537     const export_memory = comp.config.export_memory;
   4538     const import_memory = comp.config.import_memory;
   4539     const target = comp.root_mod.resolved_target.result;
   4540 
   4541     const gpa = comp.gpa;
   4542 
   4543     const directory = wasm.base.emit.directory; // Just an alias to make it shorter to type.
   4544     const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.emit.sub_path});
   4545 
   4546     // If there is no Zig code to compile, then we should skip flushing the output file because it
   4547     // will not be part of the linker line anyway.
   4548     const module_obj_path: ?[]const u8 = if (comp.module != null) blk: {
   4549         try wasm.flushModule(arena, prog_node);
   4550 
   4551         if (fs.path.dirname(full_out_path)) |dirname| {
   4552             break :blk try fs.path.join(arena, &.{ dirname, wasm.base.zcu_object_sub_path.? });
   4553         } else {
   4554             break :blk wasm.base.zcu_object_sub_path.?;
   4555         }
   4556     } else null;
   4557 
   4558     var sub_prog_node = prog_node.start("LLD Link", 0);
   4559     sub_prog_node.activate();
   4560     sub_prog_node.context.refresh();
   4561     defer sub_prog_node.end();
   4562 
   4563     const is_obj = comp.config.output_mode == .Obj;
   4564     const compiler_rt_path: ?[]const u8 = blk: {
   4565         if (comp.compiler_rt_lib) |lib| break :blk lib.full_object_path;
   4566         if (comp.compiler_rt_obj) |obj| break :blk obj.full_object_path;
   4567         break :blk null;
   4568     };
   4569 
   4570     const id_symlink_basename = "lld.id";
   4571 
   4572     var man: Cache.Manifest = undefined;
   4573     defer if (!wasm.base.disable_lld_caching) man.deinit();
   4574 
   4575     var digest: [Cache.hex_digest_len]u8 = undefined;
   4576 
   4577     if (!wasm.base.disable_lld_caching) {
   4578         man = comp.cache_parent.obtain();
   4579 
   4580         // We are about to obtain this lock, so here we give other processes a chance first.
   4581         wasm.base.releaseLock();
   4582 
   4583         comptime assert(Compilation.link_hash_implementation_version == 11);
   4584 
   4585         for (comp.objects) |obj| {
   4586             _ = try man.addFile(obj.path, null);
   4587             man.hash.add(obj.must_link);
   4588         }
   4589         for (comp.c_object_table.keys()) |key| {
   4590             _ = try man.addFile(key.status.success.object_path, null);
   4591         }
   4592         try man.addOptionalFile(module_obj_path);
   4593         try man.addOptionalFile(compiler_rt_path);
   4594         man.hash.addOptionalBytes(wasm.entry_name);
   4595         man.hash.add(wasm.base.stack_size);
   4596         man.hash.add(wasm.base.build_id);
   4597         man.hash.add(import_memory);
   4598         man.hash.add(export_memory);
   4599         man.hash.add(wasm.import_table);
   4600         man.hash.add(wasm.export_table);
   4601         man.hash.addOptional(wasm.initial_memory);
   4602         man.hash.addOptional(wasm.max_memory);
   4603         man.hash.add(shared_memory);
   4604         man.hash.addOptional(wasm.global_base);
   4605         man.hash.add(wasm.export_symbol_names.len);
   4606         // strip does not need to go into the linker hash because it is part of the hash namespace
   4607         for (wasm.export_symbol_names) |symbol_name| {
   4608             man.hash.addBytes(symbol_name);
   4609         }
   4610 
   4611         // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
   4612         _ = try man.hit();
   4613         digest = man.final();
   4614 
   4615         var prev_digest_buf: [digest.len]u8 = undefined;
   4616         const prev_digest: []u8 = Cache.readSmallFile(
   4617             directory.handle,
   4618             id_symlink_basename,
   4619             &prev_digest_buf,
   4620         ) catch |err| blk: {
   4621             log.debug("WASM LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
   4622             // Handle this as a cache miss.
   4623             break :blk prev_digest_buf[0..0];
   4624         };
   4625         if (mem.eql(u8, prev_digest, &digest)) {
   4626             log.debug("WASM LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
   4627             // Hot diggity dog! The output binary is already there.
   4628             wasm.base.lock = man.toOwnedLock();
   4629             return;
   4630         }
   4631         log.debug("WASM LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
   4632 
   4633         // We are about to change the output file to be different, so we invalidate the build hash now.
   4634         directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
   4635             error.FileNotFound => {},
   4636             else => |e| return e,
   4637         };
   4638     }
   4639 
   4640     if (is_obj) {
   4641         // LLD's WASM driver does not support the equivalent of `-r` so we do a simple file copy
   4642         // here. TODO: think carefully about how we can avoid this redundant operation when doing
   4643         // build-obj. See also the corresponding TODO in linkAsArchive.
   4644         const the_object_path = blk: {
   4645             if (comp.objects.len != 0)
   4646                 break :blk comp.objects[0].path;
   4647 
   4648             if (comp.c_object_table.count() != 0)
   4649                 break :blk comp.c_object_table.keys()[0].status.success.object_path;
   4650 
   4651             if (module_obj_path) |p|
   4652                 break :blk p;
   4653 
   4654             // TODO I think this is unreachable. Audit this situation when solving the above TODO
   4655             // regarding eliding redundant object -> object transformations.
   4656             return error.NoObjectsToLink;
   4657         };
   4658         // This can happen when using --enable-cache and using the stage1 backend. In this case
   4659         // we can skip the file copy.
   4660         if (!mem.eql(u8, the_object_path, full_out_path)) {
   4661             try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
   4662         }
   4663     } else {
   4664         // Create an LLD command line and invoke it.
   4665         var argv = std.ArrayList([]const u8).init(gpa);
   4666         defer argv.deinit();
   4667         // We will invoke ourselves as a child process to gain access to LLD.
   4668         // This is necessary because LLD does not behave properly as a library -
   4669         // it calls exit() and does not reset all global data between invocations.
   4670         const linker_command = "wasm-ld";
   4671         try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });
   4672         try argv.append("--error-limit=0");
   4673 
   4674         if (comp.config.lto) {
   4675             switch (comp.root_mod.optimize_mode) {
   4676                 .Debug => {},
   4677                 .ReleaseSmall => try argv.append("-O2"),
   4678                 .ReleaseFast, .ReleaseSafe => try argv.append("-O3"),
   4679             }
   4680         }
   4681 
   4682         if (import_memory) {
   4683             try argv.append("--import-memory");
   4684         }
   4685 
   4686         if (export_memory) {
   4687             try argv.append("--export-memory");
   4688         }
   4689 
   4690         if (wasm.import_table) {
   4691             assert(!wasm.export_table);
   4692             try argv.append("--import-table");
   4693         }
   4694 
   4695         if (wasm.export_table) {
   4696             assert(!wasm.import_table);
   4697             try argv.append("--export-table");
   4698         }
   4699 
   4700         // For wasm-ld we only need to specify '--no-gc-sections' when the user explicitly
   4701         // specified it as garbage collection is enabled by default.
   4702         if (!wasm.base.gc_sections) {
   4703             try argv.append("--no-gc-sections");
   4704         }
   4705 
   4706         if (comp.config.debug_format == .strip) {
   4707             try argv.append("-s");
   4708         }
   4709 
   4710         if (wasm.initial_memory) |initial_memory| {
   4711             const arg = try std.fmt.allocPrint(arena, "--initial-memory={d}", .{initial_memory});
   4712             try argv.append(arg);
   4713         }
   4714 
   4715         if (wasm.max_memory) |max_memory| {
   4716             const arg = try std.fmt.allocPrint(arena, "--max-memory={d}", .{max_memory});
   4717             try argv.append(arg);
   4718         }
   4719 
   4720         if (shared_memory) {
   4721             try argv.append("--shared-memory");
   4722         }
   4723 
   4724         if (wasm.global_base) |global_base| {
   4725             const arg = try std.fmt.allocPrint(arena, "--global-base={d}", .{global_base});
   4726             try argv.append(arg);
   4727         } else {
   4728             // We prepend it by default, so when a stack overflow happens the runtime will trap correctly,
   4729             // rather than silently overwrite all global declarations. See https://github.com/ziglang/zig/issues/4496
   4730             //
   4731             // The user can overwrite this behavior by setting the global-base
   4732             try argv.append("--stack-first");
   4733         }
   4734 
   4735         // Users are allowed to specify which symbols they want to export to the wasm host.
   4736         for (wasm.export_symbol_names) |symbol_name| {
   4737             const arg = try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name});
   4738             try argv.append(arg);
   4739         }
   4740 
   4741         if (comp.config.rdynamic) {
   4742             try argv.append("--export-dynamic");
   4743         }
   4744 
   4745         if (wasm.entry_name) |entry_name| {
   4746             try argv.appendSlice(&.{ "--entry", entry_name });
   4747         } else {
   4748             try argv.append("--no-entry");
   4749         }
   4750 
   4751         try argv.appendSlice(&.{
   4752             "-z",
   4753             try std.fmt.allocPrint(arena, "stack-size={d}", .{wasm.base.stack_size}),
   4754         });
   4755 
   4756         if (wasm.import_symbols) {
   4757             try argv.append("--allow-undefined");
   4758         }
   4759 
   4760         if (comp.config.output_mode == .Lib and comp.config.link_mode == .Dynamic) {
   4761             try argv.append("--shared");
   4762         }
   4763         if (comp.config.pie) {
   4764             try argv.append("--pie");
   4765         }
   4766 
   4767         // XXX - TODO: add when wasm-ld supports --build-id.
   4768         // if (wasm.base.build_id) {
   4769         //     try argv.append("--build-id=tree");
   4770         // }
   4771 
   4772         try argv.appendSlice(&.{ "-o", full_out_path });
   4773 
   4774         if (target.cpu.arch == .wasm64) {
   4775             try argv.append("-mwasm64");
   4776         }
   4777 
   4778         if (target.os.tag == .wasi) {
   4779             const is_exe_or_dyn_lib = comp.config.output_mode == .Exe or
   4780                 (comp.config.output_mode == .Lib and comp.config.link_mode == .Dynamic);
   4781             if (is_exe_or_dyn_lib) {
   4782                 for (comp.wasi_emulated_libs) |crt_file| {
   4783                     try argv.append(try comp.get_libc_crt_file(
   4784                         arena,
   4785                         wasi_libc.emulatedLibCRFileLibName(crt_file),
   4786                     ));
   4787                 }
   4788 
   4789                 if (comp.config.link_libc) {
   4790                     try argv.append(try comp.get_libc_crt_file(
   4791                         arena,
   4792                         wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model),
   4793                     ));
   4794                     try argv.append(try comp.get_libc_crt_file(arena, "libc.a"));
   4795                 }
   4796 
   4797                 if (comp.config.link_libcpp) {
   4798                     try argv.append(comp.libcxx_static_lib.?.full_object_path);
   4799                     try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
   4800                 }
   4801             }
   4802         }
   4803 
   4804         // Positional arguments to the linker such as object files.
   4805         var whole_archive = false;
   4806         for (comp.objects) |obj| {
   4807             if (obj.must_link and !whole_archive) {
   4808                 try argv.append("-whole-archive");
   4809                 whole_archive = true;
   4810             } else if (!obj.must_link and whole_archive) {
   4811                 try argv.append("-no-whole-archive");
   4812                 whole_archive = false;
   4813             }
   4814             try argv.append(obj.path);
   4815         }
   4816         if (whole_archive) {
   4817             try argv.append("-no-whole-archive");
   4818             whole_archive = false;
   4819         }
   4820 
   4821         for (comp.c_object_table.keys()) |key| {
   4822             try argv.append(key.status.success.object_path);
   4823         }
   4824         if (module_obj_path) |p| {
   4825             try argv.append(p);
   4826         }
   4827 
   4828         if (comp.config.output_mode != .Obj and
   4829             !comp.skip_linker_dependencies and
   4830             !comp.config.link_libc)
   4831         {
   4832             try argv.append(comp.libc_static_lib.?.full_object_path);
   4833         }
   4834 
   4835         if (compiler_rt_path) |p| {
   4836             try argv.append(p);
   4837         }
   4838 
   4839         if (comp.verbose_link) {
   4840             // Skip over our own name so that the LLD linker name is the first argv item.
   4841             Compilation.dump_argv(argv.items[1..]);
   4842         }
   4843 
   4844         if (std.process.can_spawn) {
   4845             // If possible, we run LLD as a child process because it does not always
   4846             // behave properly as a library, unfortunately.
   4847             // https://github.com/ziglang/zig/issues/3825
   4848             var child = std.ChildProcess.init(argv.items, arena);
   4849             if (comp.clang_passthrough_mode) {
   4850                 child.stdin_behavior = .Inherit;
   4851                 child.stdout_behavior = .Inherit;
   4852                 child.stderr_behavior = .Inherit;
   4853 
   4854                 const term = child.spawnAndWait() catch |err| {
   4855                     log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
   4856                     return error.UnableToSpawnWasm;
   4857                 };
   4858                 switch (term) {
   4859                     .Exited => |code| {
   4860                         if (code != 0) {
   4861                             std.process.exit(code);
   4862                         }
   4863                     },
   4864                     else => std.process.abort(),
   4865                 }
   4866             } else {
   4867                 child.stdin_behavior = .Ignore;
   4868                 child.stdout_behavior = .Ignore;
   4869                 child.stderr_behavior = .Pipe;
   4870 
   4871                 try child.spawn();
   4872 
   4873                 const stderr = try child.stderr.?.reader().readAllAlloc(arena, std.math.maxInt(usize));
   4874 
   4875                 const term = child.wait() catch |err| {
   4876                     log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
   4877                     return error.UnableToSpawnWasm;
   4878                 };
   4879 
   4880                 switch (term) {
   4881                     .Exited => |code| {
   4882                         if (code != 0) {
   4883                             comp.lockAndParseLldStderr(linker_command, stderr);
   4884                             return error.LLDReportedFailure;
   4885                         }
   4886                     },
   4887                     else => {
   4888                         log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
   4889                         return error.LLDCrashed;
   4890                     },
   4891                 }
   4892 
   4893                 if (stderr.len != 0) {
   4894                     log.warn("unexpected LLD stderr:\n{s}", .{stderr});
   4895                 }
   4896             }
   4897         } else {
   4898             const exit_code = try lldMain(arena, argv.items, false);
   4899             if (exit_code != 0) {
   4900                 if (comp.clang_passthrough_mode) {
   4901                     std.process.exit(exit_code);
   4902                 } else {
   4903                     return error.LLDReportedFailure;
   4904                 }
   4905             }
   4906         }
   4907 
   4908         // Give +x to the .wasm file if it is an executable and the OS is WASI.
   4909         // Some systems may be configured to execute such binaries directly. Even if that
   4910         // is not the case, it means we will get "exec format error" when trying to run
   4911         // it, and then can react to that in the same way as trying to run an ELF file
   4912         // from a foreign CPU architecture.
   4913         if (fs.has_executable_bit and target.os.tag == .wasi and
   4914             comp.config.output_mode == .Exe)
   4915         {
   4916             // TODO: what's our strategy for reporting linker errors from this function?
   4917             // report a nice error here with the file path if it fails instead of
   4918             // just returning the error code.
   4919             // chmod does not interact with umask, so we use a conservative -rwxr--r-- here.
   4920             try std.os.fchmodat(fs.cwd().fd, full_out_path, 0o744, 0);
   4921         }
   4922     }
   4923 
   4924     if (!wasm.base.disable_lld_caching) {
   4925         // Update the file with the digest. If it fails we can continue; it only
   4926         // means that the next invocation will have an unnecessary cache miss.
   4927         Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
   4928             log.warn("failed to save linking hash digest symlink: {s}", .{@errorName(err)});
   4929         };
   4930         // Again failure here only means an unnecessary cache miss.
   4931         man.writeManifest() catch |err| {
   4932             log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
   4933         };
   4934         // We hang on to this lock so that the output file path can be used without
   4935         // other processes clobbering it.
   4936         wasm.base.lock = man.toOwnedLock();
   4937     }
   4938 }
   4939 
   4940 fn reserveVecSectionHeader(bytes: *std.ArrayList(u8)) !u32 {
   4941     // section id + fixed leb contents size + fixed leb vector length
   4942     const header_size = 1 + 5 + 5;
   4943     const offset = @as(u32, @intCast(bytes.items.len));
   4944     try bytes.appendSlice(&[_]u8{0} ** header_size);
   4945     return offset;
   4946 }
   4947 
   4948 fn reserveCustomSectionHeader(bytes: *std.ArrayList(u8)) !u32 {
   4949     // unlike regular section, we don't emit the count
   4950     const header_size = 1 + 5;
   4951     const offset = @as(u32, @intCast(bytes.items.len));
   4952     try bytes.appendSlice(&[_]u8{0} ** header_size);
   4953     return offset;
   4954 }
   4955 
   4956 fn writeVecSectionHeader(buffer: []u8, offset: u32, section: std.wasm.Section, size: u32, items: u32) !void {
   4957     var buf: [1 + 5 + 5]u8 = undefined;
   4958     buf[0] = @intFromEnum(section);
   4959     leb.writeUnsignedFixed(5, buf[1..6], size);
   4960     leb.writeUnsignedFixed(5, buf[6..], items);
   4961     buffer[offset..][0..buf.len].* = buf;
   4962 }
   4963 
   4964 fn writeCustomSectionHeader(buffer: []u8, offset: u32, size: u32) !void {
   4965     var buf: [1 + 5]u8 = undefined;
   4966     buf[0] = 0; // 0 = 'custom' section
   4967     leb.writeUnsignedFixed(5, buf[1..6], size);
   4968     buffer[offset..][0..buf.len].* = buf;
   4969 }
   4970 
   4971 fn emitLinkSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void {
   4972     const offset = try reserveCustomSectionHeader(binary_bytes);
   4973     const writer = binary_bytes.writer();
   4974     // emit "linking" custom section name
   4975     const section_name = "linking";
   4976     try leb.writeULEB128(writer, section_name.len);
   4977     try writer.writeAll(section_name);
   4978 
   4979     // meta data version, which is currently '2'
   4980     try leb.writeULEB128(writer, @as(u32, 2));
   4981 
   4982     // For each subsection type (found in types.Subsection) we can emit a section.
   4983     // Currently, we only support emitting segment info and the symbol table.
   4984     try wasm.emitSymbolTable(binary_bytes, symbol_table);
   4985     try wasm.emitSegmentInfo(binary_bytes);
   4986 
   4987     const size: u32 = @intCast(binary_bytes.items.len - offset - 6);
   4988     try writeCustomSectionHeader(binary_bytes.items, offset, size);
   4989 }
   4990 
   4991 fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void {
   4992     const writer = binary_bytes.writer();
   4993 
   4994     try leb.writeULEB128(writer, @intFromEnum(types.SubsectionType.WASM_SYMBOL_TABLE));
   4995     const table_offset = binary_bytes.items.len;
   4996 
   4997     var symbol_count: u32 = 0;
   4998     for (wasm.resolved_symbols.keys()) |sym_loc| {
   4999         const symbol = sym_loc.getSymbol(wasm).*;
   5000         if (symbol.tag == .dead) continue; // Do not emit dead symbols
   5001         try symbol_table.putNoClobber(sym_loc, symbol_count);
   5002         symbol_count += 1;
   5003         log.debug("Emit symbol: {}", .{symbol});
   5004         try leb.writeULEB128(writer, @intFromEnum(symbol.tag));
   5005         try leb.writeULEB128(writer, symbol.flags);
   5006 
   5007         const sym_name = if (wasm.export_names.get(sym_loc)) |exp_name| wasm.string_table.get(exp_name) else sym_loc.getName(wasm);
   5008         switch (symbol.tag) {
   5009             .data => {
   5010                 try leb.writeULEB128(writer, @as(u32, @intCast(sym_name.len)));
   5011                 try writer.writeAll(sym_name);
   5012 
   5013                 if (symbol.isDefined()) {
   5014                     try leb.writeULEB128(writer, symbol.index);
   5015                     const atom_index = wasm.symbol_atom.get(sym_loc).?;
   5016                     const atom = wasm.getAtom(atom_index);
   5017                     try leb.writeULEB128(writer, @as(u32, atom.offset));
   5018                     try leb.writeULEB128(writer, @as(u32, atom.size));
   5019                 }
   5020             },
   5021             .section => {
   5022                 try leb.writeULEB128(writer, symbol.index);
   5023             },
   5024             else => {
   5025                 try leb.writeULEB128(writer, symbol.index);
   5026                 if (symbol.isDefined()) {
   5027                     try leb.writeULEB128(writer, @as(u32, @intCast(sym_name.len)));
   5028                     try writer.writeAll(sym_name);
   5029                 }
   5030             },
   5031         }
   5032     }
   5033 
   5034     var buf: [10]u8 = undefined;
   5035     leb.writeUnsignedFixed(5, buf[0..5], @intCast(binary_bytes.items.len - table_offset + 5));
   5036     leb.writeUnsignedFixed(5, buf[5..], symbol_count);
   5037     try binary_bytes.insertSlice(table_offset, &buf);
   5038 }
   5039 
   5040 fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void {
   5041     const writer = binary_bytes.writer();
   5042     try leb.writeULEB128(writer, @intFromEnum(types.SubsectionType.WASM_SEGMENT_INFO));
   5043     const segment_offset = binary_bytes.items.len;
   5044 
   5045     try leb.writeULEB128(writer, @as(u32, @intCast(wasm.segment_info.count())));
   5046     for (wasm.segment_info.values()) |segment_info| {
   5047         log.debug("Emit segment: {s} align({d}) flags({b})", .{
   5048             segment_info.name,
   5049             segment_info.alignment,
   5050             segment_info.flags,
   5051         });
   5052         try leb.writeULEB128(writer, @as(u32, @intCast(segment_info.name.len)));
   5053         try writer.writeAll(segment_info.name);
   5054         try leb.writeULEB128(writer, segment_info.alignment.toLog2Units());
   5055         try leb.writeULEB128(writer, segment_info.flags);
   5056     }
   5057 
   5058     var buf: [5]u8 = undefined;
   5059     leb.writeUnsignedFixed(5, &buf, @as(u32, @intCast(binary_bytes.items.len - segment_offset)));
   5060     try binary_bytes.insertSlice(segment_offset, &buf);
   5061 }
   5062 
   5063 pub fn getULEB128Size(uint_value: anytype) u32 {
   5064     const T = @TypeOf(uint_value);
   5065     const U = if (@typeInfo(T).Int.bits < 8) u8 else T;
   5066     var value = @as(U, @intCast(uint_value));
   5067 
   5068     var size: u32 = 0;
   5069     while (value != 0) : (size += 1) {
   5070         value >>= 7;
   5071     }
   5072     return size;
   5073 }
   5074 
   5075 /// For each relocatable section, emits a custom "relocation.<section_name>" section
   5076 fn emitCodeRelocations(
   5077     wasm: *Wasm,
   5078     binary_bytes: *std.ArrayList(u8),
   5079     section_index: u32,
   5080     symbol_table: std.AutoArrayHashMap(SymbolLoc, u32),
   5081 ) !void {
   5082     const code_index = wasm.code_section_index orelse return;
   5083     const writer = binary_bytes.writer();
   5084     const header_offset = try reserveCustomSectionHeader(binary_bytes);
   5085 
   5086     // write custom section information
   5087     const name = "reloc.CODE";
   5088     try leb.writeULEB128(writer, @as(u32, @intCast(name.len)));
   5089     try writer.writeAll(name);
   5090     try leb.writeULEB128(writer, section_index);
   5091     const reloc_start = binary_bytes.items.len;
   5092 
   5093     var count: u32 = 0;
   5094     var atom: *Atom = wasm.getAtomPtr(wasm.atoms.get(code_index).?);
   5095     // for each atom, we calculate the uleb size and append that
   5096     var size_offset: u32 = 5; // account for code section size leb128
   5097     while (true) {
   5098         size_offset += getULEB128Size(atom.size);
   5099         for (atom.relocs.items) |relocation| {
   5100             count += 1;
   5101             const sym_loc: SymbolLoc = .{ .file = atom.file, .index = relocation.index };
   5102             const symbol_index = symbol_table.get(sym_loc).?;
   5103             try leb.writeULEB128(writer, @intFromEnum(relocation.relocation_type));
   5104             const offset = atom.offset + relocation.offset + size_offset;
   5105             try leb.writeULEB128(writer, offset);
   5106             try leb.writeULEB128(writer, symbol_index);
   5107             if (relocation.relocation_type.addendIsPresent()) {
   5108                 try leb.writeILEB128(writer, relocation.addend);
   5109             }
   5110             log.debug("Emit relocation: {}", .{relocation});
   5111         }
   5112         atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
   5113     }
   5114     if (count == 0) return;
   5115     var buf: [5]u8 = undefined;
   5116     leb.writeUnsignedFixed(5, &buf, count);
   5117     try binary_bytes.insertSlice(reloc_start, &buf);
   5118     const size: u32 = @intCast(binary_bytes.items.len - header_offset - 6);
   5119     try writeCustomSectionHeader(binary_bytes.items, header_offset, size);
   5120 }
   5121 
   5122 fn emitDataRelocations(
   5123     wasm: *Wasm,
   5124     binary_bytes: *std.ArrayList(u8),
   5125     section_index: u32,
   5126     symbol_table: std.AutoArrayHashMap(SymbolLoc, u32),
   5127 ) !void {
   5128     if (wasm.data_segments.count() == 0) return;
   5129     const writer = binary_bytes.writer();
   5130     const header_offset = try reserveCustomSectionHeader(binary_bytes);
   5131 
   5132     // write custom section information
   5133     const name = "reloc.DATA";
   5134     try leb.writeULEB128(writer, @as(u32, @intCast(name.len)));
   5135     try writer.writeAll(name);
   5136     try leb.writeULEB128(writer, section_index);
   5137     const reloc_start = binary_bytes.items.len;
   5138 
   5139     var count: u32 = 0;
   5140     // for each atom, we calculate the uleb size and append that
   5141     var size_offset: u32 = 5; // account for code section size leb128
   5142     for (wasm.data_segments.values()) |segment_index| {
   5143         var atom: *Atom = wasm.getAtomPtr(wasm.atoms.get(segment_index).?);
   5144         while (true) {
   5145             size_offset += getULEB128Size(atom.size);
   5146             for (atom.relocs.items) |relocation| {
   5147                 count += 1;
   5148                 const sym_loc: SymbolLoc = .{
   5149                     .file = atom.file,
   5150                     .index = relocation.index,
   5151                 };
   5152                 const symbol_index = symbol_table.get(sym_loc).?;
   5153                 try leb.writeULEB128(writer, @intFromEnum(relocation.relocation_type));
   5154                 const offset = atom.offset + relocation.offset + size_offset;
   5155                 try leb.writeULEB128(writer, offset);
   5156                 try leb.writeULEB128(writer, symbol_index);
   5157                 if (relocation.relocation_type.addendIsPresent()) {
   5158                     try leb.writeILEB128(writer, relocation.addend);
   5159                 }
   5160                 log.debug("Emit relocation: {}", .{relocation});
   5161             }
   5162             atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
   5163         }
   5164     }
   5165     if (count == 0) return;
   5166 
   5167     var buf: [5]u8 = undefined;
   5168     leb.writeUnsignedFixed(5, &buf, count);
   5169     try binary_bytes.insertSlice(reloc_start, &buf);
   5170     const size = @as(u32, @intCast(binary_bytes.items.len - header_offset - 6));
   5171     try writeCustomSectionHeader(binary_bytes.items, header_offset, size);
   5172 }
   5173 
   5174 fn hasPassiveInitializationSegments(wasm: *const Wasm) bool {
   5175     const comp = wasm.base.comp;
   5176     const import_memory = comp.config.import_memory;
   5177 
   5178     var it = wasm.data_segments.iterator();
   5179     while (it.next()) |entry| {
   5180         const segment: Segment = wasm.segments.items[entry.value_ptr.*];
   5181         if (segment.needsPassiveInitialization(import_memory, entry.key_ptr.*)) {
   5182             return true;
   5183         }
   5184     }
   5185     return false;
   5186 }
   5187 
   5188 pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 {
   5189     var index: u32 = 0;
   5190     while (index < wasm.func_types.items.len) : (index += 1) {
   5191         if (wasm.func_types.items[index].eql(func_type)) return index;
   5192     }
   5193     return null;
   5194 }
   5195 
   5196 /// Searches for a matching function signature. When no matching signature is found,
   5197 /// a new entry will be made. The value returned is the index of the type within `wasm.func_types`.
   5198 pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 {
   5199     if (wasm.getTypeIndex(func_type)) |index| {
   5200         return index;
   5201     }
   5202     const gpa = wasm.base.comp.gpa;
   5203 
   5204     // functype does not exist.
   5205     const index: u32 = @intCast(wasm.func_types.items.len);
   5206     const params = try gpa.dupe(std.wasm.Valtype, func_type.params);
   5207     errdefer gpa.free(params);
   5208     const returns = try gpa.dupe(std.wasm.Valtype, func_type.returns);
   5209     errdefer gpa.free(returns);
   5210     try wasm.func_types.append(gpa, .{
   5211         .params = params,
   5212         .returns = returns,
   5213     });
   5214     return index;
   5215 }
   5216 
   5217 /// For the given `decl_index`, stores the corresponding type representing the function signature.
   5218 /// Asserts declaration has an associated `Atom`.
   5219 /// Returns the index into the list of types.
   5220 pub fn storeDeclType(wasm: *Wasm, decl_index: InternPool.DeclIndex, func_type: std.wasm.Type) !u32 {
   5221     const gpa = wasm.base.comp.gpa;
   5222     const atom_index = wasm.decls.get(decl_index).?;
   5223     const index = try wasm.putOrGetFuncType(func_type);
   5224     try wasm.atom_types.put(gpa, atom_index, index);
   5225     return index;
   5226 }
   5227 
   5228 /// Verifies all resolved symbols and checks whether itself needs to be marked alive,
   5229 /// as well as any of its references.
   5230 fn markReferences(wasm: *Wasm) !void {
   5231     const tracy = trace(@src());
   5232     defer tracy.end();
   5233 
   5234     const do_garbage_collect = wasm.base.gc_sections;
   5235     const comp = wasm.base.comp;
   5236 
   5237     for (wasm.resolved_symbols.keys()) |sym_loc| {
   5238         const sym = sym_loc.getSymbol(wasm);
   5239         if (sym.isExported(comp.config.rdynamic) or sym.isNoStrip() or !do_garbage_collect) {
   5240             try wasm.mark(sym_loc);
   5241             continue;
   5242         }
   5243 
   5244         // Debug sections may require to be parsed and marked when it contains
   5245         // relocations to alive symbols.
   5246         if (sym.tag == .section and comp.config.debug_format != .strip) {
   5247             const file = sym_loc.file orelse continue; // Incremental debug info is done independently
   5248             const object = &wasm.objects.items[file];
   5249             const atom_index = try Object.parseSymbolIntoAtom(object, file, sym_loc.index, wasm);
   5250             const atom = wasm.getAtom(atom_index);
   5251             const atom_sym = atom.symbolLoc().getSymbol(wasm);
   5252             atom_sym.mark();
   5253         }
   5254     }
   5255 }
   5256 
   5257 /// Marks a symbol as 'alive' recursively so itself and any references it contains to
   5258 /// other symbols will not be omit from the binary.
   5259 fn mark(wasm: *Wasm, loc: SymbolLoc) !void {
   5260     const symbol = loc.getSymbol(wasm);
   5261     if (symbol.isAlive()) {
   5262         // Symbol is already marked alive, including its references.
   5263         // This means we can skip it so we don't end up marking the same symbols
   5264         // multiple times.
   5265         return;
   5266     }
   5267     symbol.mark();
   5268     if (symbol.isUndefined()) {
   5269         // undefined symbols do not have an associated `Atom` and therefore also
   5270         // do not contain relocations.
   5271         return;
   5272     }
   5273 
   5274     const atom_index = if (loc.file) |file_index| idx: {
   5275         const object = &wasm.objects.items[file_index];
   5276         const atom_index = try object.parseSymbolIntoAtom(file_index, loc.index, wasm);
   5277         break :idx atom_index;
   5278     } else wasm.symbol_atom.get(loc) orelse return;
   5279 
   5280     const atom = wasm.getAtom(atom_index);
   5281     for (atom.relocs.items) |reloc| {
   5282         const target_loc: SymbolLoc = .{ .index = reloc.index, .file = loc.file };
   5283         try wasm.mark(target_loc.finalLoc(wasm));
   5284     }
   5285 }
   5286 
   5287 fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8 {
   5288     return switch (wasi_exec_model) {
   5289         .reactor => "_initialize",
   5290         .command => "_start",
   5291     };
   5292 }