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