zig

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

blob 4770da76 (176442B) - Raw


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