diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2022-12-20 18:42:14 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-12-20 18:42:14 +0100 |
| commit | dc22c3b9a5ef382ed9c7fb4ff503c7d8be187cee (patch) | |
| tree | e93ff39a7bedae4c8c637bd8493f2545cb900d89 /src | |
| parent | 4aa8462cc936820bc2c81ea1f3cfc613ff07a9f7 (diff) | |
| parent | 6f44e2d1d3dc1ac7942082dc98947f7beaa1f205 (diff) | |
| download | zig-dc22c3b9a5ef382ed9c7fb4ff503c7d8be187cee.tar.gz zig-dc22c3b9a5ef382ed9c7fb4ff503c7d8be187cee.zip | |
Merge pull request #14006 from Luukdegram/wasm-no-lld
wasm-linker: allow usage of the in-house WebAssembly linker
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/Wasm.zig | 309 | ||||
| -rw-r--r-- | src/link/Wasm/Archive.zig | 15 | ||||
| -rw-r--r-- | src/link/Wasm/Atom.zig | 35 | ||||
| -rw-r--r-- | src/link/Wasm/Object.zig | 8 | ||||
| -rw-r--r-- | src/link/Wasm/Symbol.zig | 2 |
5 files changed, 309 insertions, 60 deletions
diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 74a303b8c7..346c92ebbe 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -112,6 +112,8 @@ func_types: std.ArrayListUnmanaged(std.wasm.Type) = .{}, functions: std.AutoArrayHashMapUnmanaged(struct { file: ?u16, index: u32 }, std.wasm.Func) = .{}, /// Output global section wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .{}, +/// Global symbols for exported data symbols +address_globals: std.ArrayListUnmanaged(SymbolLoc) = .{}, /// Memory section memories: std.wasm.Memory = .{ .limits = .{ .min = 0, .max = null } }, /// Output table section @@ -311,13 +313,23 @@ pub const StringTable = struct { pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Wasm { assert(options.target.ofmt == .wasm); - if (build_options.have_llvm and options.use_llvm) { + if (build_options.have_llvm and options.use_llvm and options.use_lld) { return createEmpty(allocator, options); } const wasm_bin = try createEmpty(allocator, options); errdefer wasm_bin.base.destroy(); + // We are not using LLD at this point, so ensure we set the intermediary basename + if (build_options.have_llvm and options.use_llvm and options.module != null) { + // TODO this intermediary_basename isn't enough; in the case of `zig build-exe`, + // we also want to put the intermediary object file in the cache while the + // main emit directory is the cwd. + wasm_bin.base.intermediary_basename = try std.fmt.allocPrint(allocator, "{s}{s}", .{ + options.emit.?.sub_path, options.target.ofmt.fileExt(options.target.cpu.arch), + }); + } + // TODO: read the file and keep valid parts instead of truncating const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true }); wasm_bin.base.file = file; @@ -613,7 +625,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { try wasm.resolved_symbols.put(wasm.base.allocator, location, {}); assert(wasm.resolved_symbols.swapRemove(existing_loc)); if (existing_sym.isUndefined()) { - assert(wasm.undefs.swapRemove(sym_name)); + _ = wasm.undefs.swapRemove(sym_name); } } } @@ -624,8 +636,7 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void { log.debug("Resolving symbols in archives", .{}); var index: u32 = 0; undef_loop: while (index < wasm.undefs.count()) { - const undef_sym_loc = wasm.undefs.values()[index]; - const sym_name = undef_sym_loc.getName(wasm); + const sym_name = wasm.undefs.keys()[index]; for (wasm.archives.items) |archive| { const offset = archive.toc.get(sym_name) orelse { @@ -829,6 +840,7 @@ pub fn deinit(wasm: *Wasm) void { wasm.func_types.deinit(gpa); wasm.functions.deinit(gpa); wasm.wasm_globals.deinit(gpa); + wasm.address_globals.deinit(gpa); wasm.function_table.deinit(gpa); wasm.tables.deinit(gpa); wasm.exports.deinit(gpa); @@ -1556,9 +1568,13 @@ fn allocateAtoms(wasm: *Wasm) !void { var atom: *Atom = entry.value_ptr.*.getFirst(); var offset: u32 = 0; while (true) { + const symbol_loc = atom.symbolLoc(); + if (!wasm.resolved_symbols.contains(symbol_loc)) { + atom = atom.next orelse break; + continue; + } offset = std.mem.alignForwardGeneric(u32, offset, atom.alignment); atom.offset = offset; - const symbol_loc = atom.symbolLoc(); log.debug("Atom '{s}' allocated from 0x{x:0>8} to 0x{x:0>8} size={d}", .{ symbol_loc.getName(wasm), offset, @@ -1566,7 +1582,7 @@ fn allocateAtoms(wasm: *Wasm) !void { atom.size, }); offset += atom.size; - try wasm.symbol_atom.put(wasm.base.allocator, atom.symbolLoc(), atom); // Update atom pointers + try wasm.symbol_atom.put(wasm.base.allocator, symbol_loc, atom); // Update atom pointers atom = atom.next orelse break; } segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment); @@ -1702,7 +1718,7 @@ fn mergeSections(wasm: *Wasm) !void { continue; } - const object = wasm.objects.items[sym_loc.file.?]; + const object = &wasm.objects.items[sym_loc.file.?]; const symbol = &object.symtable[sym_loc.index]; if (symbol.isUndefined() or (symbol.tag != .function and symbol.tag != .global and symbol.tag != .table)) { // Skip undefined symbols as they go in the `import` section @@ -1714,13 +1730,12 @@ fn mergeSections(wasm: *Wasm) !void { const index = symbol.index - offset; switch (symbol.tag) { .function => { - const original_func = object.functions[index]; const gop = try wasm.functions.getOrPut( wasm.base.allocator, .{ .file = sym_loc.file, .index = symbol.index }, ); if (!gop.found_existing) { - gop.value_ptr.* = original_func; + gop.value_ptr.* = object.functions[index]; } symbol.index = @intCast(u32, gop.index) + wasm.imported_functions_count; }, @@ -1768,7 +1783,7 @@ fn mergeTypes(wasm: *Wasm) !void { if (symbol.isUndefined()) { log.debug("Adding type from extern function '{s}'", .{sym_loc.getName(wasm)}); - const import: *types.Import = wasm.imports.getPtr(sym_loc).?; + const import: *types.Import = wasm.imports.getPtr(sym_loc) orelse continue; const original_type = object.func_types[import.kind.function]; import.kind.function = try wasm.putOrGetFuncType(original_type); } else if (!dirty.contains(symbol.index)) { @@ -1794,7 +1809,15 @@ fn setupExports(wasm: *Wasm) !void { if (sym_loc.file == null) break :blk symbol.name; break :blk try wasm.string_table.put(wasm.base.allocator, sym_name); }; - const exp: types.Export = .{ + const exp: types.Export = if (symbol.tag == .data) exp: { + const global_index = @intCast(u32, wasm.wasm_globals.items.len + wasm.address_globals.items.len); + try wasm.address_globals.append(wasm.base.allocator, sym_loc); + break :exp .{ + .name = export_name, + .kind = .global, + .index = global_index, + }; + } else .{ .name = export_name, .kind = symbol.tag.externalType(), .index = symbol.index, @@ -2134,7 +2157,6 @@ pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) ! const new_index = @intCast(u32, wasm.segments.items.len); index.* = new_index; try wasm.appendDummySegment(); - // _ = index; const sym_index = wasm.symbols_free_list.popOrNull() orelse idx: { const tmp_index = @intCast(u32, wasm.symbols.items.len); @@ -2202,13 +2224,213 @@ pub fn flush(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) lin } return; } + if (build_options.have_llvm and wasm.base.options.use_lld) { return wasm.linkWithLLD(comp, prog_node); + } else if (build_options.have_llvm and wasm.base.options.use_llvm and !wasm.base.options.use_lld) { + return wasm.linkWithZld(comp, prog_node); } else { return wasm.flushModule(comp, prog_node); } } +/// Uses the in-house linker to link one or multiple object -and archive files into a WebAssembly binary. +fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { + const tracy = trace(@src()); + defer tracy.end(); + + const gpa = wasm.base.allocator; + const options = wasm.base.options; + + // Used for all temporary memory allocated during flushin + var arena_instance = std.heap.ArenaAllocator.init(gpa); + defer arena_instance.deinit(); + const arena = arena_instance.allocator(); + + const directory = options.emit.?.directory; // Just an alias to make it shorter to type. + const full_out_path = try directory.join(arena, &[_][]const u8{options.emit.?.sub_path}); + + // If there is no Zig code to compile, then we should skip flushing the output file because it + // will not be part of the linker line anyway. + const module_obj_path: ?[]const u8 = if (options.module != null) blk: { + assert(options.use_llvm); // `linkWithZld` should never be called when the Wasm backend is used + try wasm.flushModule(comp, prog_node); + + if (fs.path.dirname(full_out_path)) |dirname| { + break :blk try fs.path.join(arena, &.{ dirname, wasm.base.intermediary_basename.? }); + } else { + break :blk wasm.base.intermediary_basename.?; + } + } else null; + + var sub_prog_node = prog_node.start("Wasm Flush", 0); + sub_prog_node.activate(); + defer sub_prog_node.end(); + + const is_obj = options.output_mode == .Obj; + const compiler_rt_path: ?[]const u8 = if (options.include_compiler_rt and !is_obj) + comp.compiler_rt_lib.?.full_object_path + else + null; + const id_symlink_basename = "zld.id"; + + var man: Cache.Manifest = undefined; + defer if (!options.disable_lld_caching) man.deinit(); + var digest: [Cache.hex_digest_len]u8 = undefined; + + // NOTE: The following section must be maintained to be equal + // as the section defined in `linkWithLLD` + if (!options.disable_lld_caching) { + man = comp.cache_parent.obtain(); + + // We are about to obtain this lock, so here we give other processes a chance first. + wasm.base.releaseLock(); + + comptime assert(Compilation.link_hash_implementation_version == 7); + + for (options.objects) |obj| { + _ = try man.addFile(obj.path, null); + man.hash.add(obj.must_link); + } + for (comp.c_object_table.keys()) |key| { + _ = try man.addFile(key.status.success.object_path, null); + } + try man.addOptionalFile(module_obj_path); + try man.addOptionalFile(compiler_rt_path); + man.hash.addOptionalBytes(options.entry); + man.hash.addOptional(options.stack_size_override); + man.hash.add(options.import_memory); + man.hash.add(options.import_table); + man.hash.add(options.export_table); + man.hash.addOptional(options.initial_memory); + man.hash.addOptional(options.max_memory); + man.hash.add(options.shared_memory); + man.hash.addOptional(options.global_base); + man.hash.add(options.export_symbol_names.len); + // strip does not need to go into the linker hash because it is part of the hash namespace + for (options.export_symbol_names) |symbol_name| { + man.hash.addBytes(symbol_name); + } + + // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. + _ = try man.hit(); + digest = man.final(); + + var prev_digest_buf: [digest.len]u8 = undefined; + const prev_digest: []u8 = Cache.readSmallFile( + directory.handle, + id_symlink_basename, + &prev_digest_buf, + ) catch |err| blk: { + log.debug("WASM LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); + // Handle this as a cache miss. + break :blk prev_digest_buf[0..0]; + }; + if (mem.eql(u8, prev_digest, &digest)) { + log.debug("WASM LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); + // Hot diggity dog! The output binary is already there. + wasm.base.lock = man.toOwnedLock(); + return; + } + log.debug("WASM LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); + + // We are about to change the output file to be different, so we invalidate the build hash now. + directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + }; + } + + // Positional arguments to the linker such as object files and static archives. + var positionals = std.ArrayList([]const u8).init(arena); + try positionals.ensureUnusedCapacity(options.objects.len); + + // When the target os is WASI, we allow linking with WASI-LIBC + if (options.target.os.tag == .wasi) { + const is_exe_or_dyn_lib = wasm.base.options.output_mode == .Exe or + (wasm.base.options.output_mode == .Lib and wasm.base.options.link_mode == .Dynamic); + if (is_exe_or_dyn_lib) { + const wasi_emulated_libs = wasm.base.options.wasi_emulated_libs; + for (wasi_emulated_libs) |crt_file| { + try positionals.append(try comp.get_libc_crt_file( + arena, + wasi_libc.emulatedLibCRFileLibName(crt_file), + )); + } + + if (wasm.base.options.link_libc) { + try positionals.append(try comp.get_libc_crt_file( + arena, + wasi_libc.execModelCrtFileFullName(wasm.base.options.wasi_exec_model), + )); + try positionals.append(try comp.get_libc_crt_file(arena, "libc.a")); + } + + if (wasm.base.options.link_libcpp) { + try positionals.append(comp.libcxx_static_lib.?.full_object_path); + try positionals.append(comp.libcxxabi_static_lib.?.full_object_path); + } + } + } + + if (module_obj_path) |path| { + try positionals.append(path); + } + + for (options.objects) |object| { + try positionals.append(object.path); + } + + for (comp.c_object_table.keys()) |c_object| { + try positionals.append(c_object.status.success.object_path); + } + + if (comp.compiler_rt_lib) |lib| { + try positionals.append(lib.full_object_path); + } + + try wasm.parseInputFiles(positionals.items); + + for (wasm.objects.items) |_, object_index| { + try wasm.resolveSymbolsInObject(@intCast(u16, object_index)); + } + + var emit_features_count: u32 = 0; + var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined; + try wasm.validateFeatures(&enabled_features, &emit_features_count); + try wasm.resolveSymbolsInArchives(); + + try wasm.setupStart(); + try wasm.setupImports(); + + for (wasm.objects.items) |*object, object_index| { + try object.parseIntoAtoms(gpa, @intCast(u16, object_index), wasm); + } + + try wasm.allocateAtoms(); + try wasm.setupMemory(); + wasm.mapFunctionTable(); + try wasm.mergeSections(); + try wasm.mergeTypes(); + try wasm.setupExports(); + try wasm.writeToFile(enabled_features, emit_features_count, arena); + + if (!wasm.base.options.disable_lld_caching) { + // Update the file with the digest. If it fails we can continue; it only + // means that the next invocation will have an unnecessary cache miss. + Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { + log.warn("failed to save linking hash digest symlink: {s}", .{@errorName(err)}); + }; + // Again failure here only means an unnecessary cache miss. + man.writeManifest() catch |err| { + log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); + }; + // We hang on to this lock so that the output file path can be used without + // other processes clobbering it. + wasm.base.lock = man.toOwnedLock(); + } +} + pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { const tracy = trace(@src()); defer tracy.end(); @@ -2219,20 +2441,13 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } } - var sub_prog_node = prog_node.start("WASM Flush", 0); + var sub_prog_node = prog_node.start("Wasm Flush", 0); sub_prog_node.activate(); defer sub_prog_node.end(); // ensure the error names table is populated when an error name is referenced try wasm.populateErrorNameTable(); - // The amount of sections that will be written - var section_count: u32 = 0; - // Index of the code section. Used to tell relocation table where the section lives. - var code_section_index: ?u32 = null; - // Index of the data section. Used to tell relocation table where the section lives. - var data_section_index: ?u32 = null; - // Used for all temporary memory allocated during flushin var arena_instance = std.heap.ArenaAllocator.init(wasm.base.allocator); defer arena_instance.deinit(); @@ -2312,9 +2527,25 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try wasm.mergeSections(); try wasm.mergeTypes(); try wasm.setupExports(); + try wasm.writeToFile(enabled_features, emit_features_count, arena); +} +/// Writes the WebAssembly in-memory module to the file +fn writeToFile( + wasm: *Wasm, + enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool, + feature_count: u32, + arena: Allocator, +) !void { + // Size of each section header const header_size = 5 + 1; - const is_obj = wasm.base.options.output_mode == .Obj; + // The amount of sections that will be written + var section_count: u32 = 0; + // Index of the code section. Used to tell relocation table where the section lives. + var code_section_index: ?u32 = null; + // Index of the data section. Used to tell relocation table where the section lives. + var data_section_index: ?u32 = null; + const is_obj = wasm.base.options.output_mode == .Obj or (!wasm.base.options.use_llvm and wasm.base.options.use_lld); var binary_bytes = std.ArrayList(u8).init(wasm.base.allocator); defer binary_bytes.deinit(); @@ -2461,10 +2692,22 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod if (wasm.wasm_globals.items.len > 0) { const header_offset = try reserveVecSectionHeader(&binary_bytes); + var global_count: u32 = 0; for (wasm.wasm_globals.items) |global| { try binary_writer.writeByte(std.wasm.valtype(global.global_type.valtype)); try binary_writer.writeByte(@boolToInt(global.global_type.mutable)); try emitInit(binary_writer, global.init); + global_count += 1; + } + + for (wasm.address_globals.items) |sym_loc| { + const atom = wasm.symbol_atom.get(sym_loc).?; + try binary_writer.writeByte(std.wasm.valtype(.i32)); + try binary_writer.writeByte(0); // immutable + try emitInit(binary_writer, .{ + .i32_const = @bitCast(i32, atom.offset), + }); + global_count += 1; } try writeVecSectionHeader( @@ -2472,7 +2715,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod header_offset, .global, @intCast(u32, binary_bytes.items.len - header_offset - header_size), - @intCast(u32, wasm.wasm_globals.items.len), + @intCast(u32, global_count), ); section_count += 1; } @@ -2708,8 +2951,8 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } try emitProducerSection(&binary_bytes); - if (emit_features_count > 0) { - try emitFeaturesSection(&binary_bytes, &enabled_features, emit_features_count); + if (feature_count > 0) { + try emitFeaturesSection(&binary_bytes, &enabled_features, feature_count); } } @@ -2849,7 +3092,9 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem for (wasm.resolved_symbols.keys()) |sym_loc| { const symbol = sym_loc.getSymbol(wasm).*; const name = if (symbol.isUndefined()) blk: { - break :blk wasm.string_table.get(wasm.imports.get(sym_loc).?.name); + if (symbol.tag == .data) continue; + const imp = wasm.imports.get(sym_loc) orelse continue; + break :blk wasm.string_table.get(imp.name); } else sym_loc.getName(wasm); switch (symbol.tag) { .function => { @@ -3607,15 +3852,23 @@ fn emitDataRelocations( try writeCustomSectionHeader(binary_bytes.items, header_offset, size); } -/// Searches for an a matching function signature, when not found -/// a new entry will be made. The index of the existing/new signature will be returned. -pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 { +pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 { var index: u32 = 0; while (index < wasm.func_types.items.len) : (index += 1) { if (wasm.func_types.items[index].eql(func_type)) return index; } + return null; +} + +/// Searches for an a matching function signature, when not found +/// a new entry will be made. The index of the existing/new signature will be returned. +pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 { + if (wasm.getTypeIndex(func_type)) |index| { + return index; + } // functype does not exist. + const index = @intCast(u32, wasm.func_types.items.len); const params = try wasm.base.allocator.dupe(std.wasm.Valtype, func_type.params); errdefer wasm.base.allocator.free(params); const returns = try wasm.base.allocator.dupe(std.wasm.Valtype, func_type.returns); diff --git a/src/link/Wasm/Archive.zig b/src/link/Wasm/Archive.zig index 2fa1e07915..c4fb9b8291 100644 --- a/src/link/Wasm/Archive.zig +++ b/src/link/Wasm/Archive.zig @@ -157,13 +157,12 @@ fn parseTableOfContents(archive: *Archive, allocator: Allocator, reader: anytype }; var i: usize = 0; - while (i < sym_tab.len) { - const string = mem.sliceTo(sym_tab[i..], 0); - if (string.len == 0) { - i += 1; - continue; - } - i += string.len; + var pos: usize = 0; + while (i < num_symbols) : (i += 1) { + const string = mem.sliceTo(sym_tab[pos..], 0); + pos += string.len + 1; + if (string.len == 0) continue; + const name = try allocator.dupe(u8, string); errdefer allocator.free(name); const gop = try archive.toc.getOrPut(allocator, name); @@ -172,7 +171,7 @@ fn parseTableOfContents(archive: *Archive, allocator: Allocator, reader: anytype } else { gop.value_ptr.* = .{}; } - try gop.value_ptr.append(allocator, symbol_positions[gop.index]); + try gop.value_ptr.append(allocator, symbol_positions[i]); } } diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index 4c1da180bd..eb3a31b8a0 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -168,12 +168,13 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa .R_WASM_TABLE_INDEX_SLEB, .R_WASM_TABLE_INDEX_SLEB64, => return wasm_bin.function_table.get(target_loc) orelse 0, - .R_WASM_TYPE_INDEX_LEB => return blk: { - if (symbol.isUndefined()) { - const imp = wasm_bin.imports.get(target_loc).?; - break :blk imp.kind.function; - } - break :blk wasm_bin.functions.values()[symbol.index - wasm_bin.imported_functions_count].type_index; + .R_WASM_TYPE_INDEX_LEB => { + const file_index = atom.file orelse { + return relocation.index; + }; + + const original_type = wasm_bin.objects.items[file_index].func_types[relocation.index]; + return wasm_bin.getTypeIndex(original_type).?; }, .R_WASM_GLOBAL_INDEX_I32, .R_WASM_GLOBAL_INDEX_LEB, @@ -185,7 +186,11 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa .R_WASM_MEMORY_ADDR_SLEB, .R_WASM_MEMORY_ADDR_SLEB64, => { - std.debug.assert(symbol.tag == .data and !symbol.isUndefined()); + std.debug.assert(symbol.tag == .data); + if (symbol.isUndefined()) { + return 0; + } + const merge_segment = wasm_bin.base.options.output_mode != .Obj; const target_atom = wasm_bin.symbol_atom.get(target_loc).?; const segment_info = if (target_atom.file) |object_index| blk: { @@ -204,18 +209,10 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa return @intCast(u32, rel_value); }, .R_WASM_FUNCTION_OFFSET_I32 => { - const target_atom = wasm_bin.symbol_atom.get(target_loc).?; - var current_atom = target_atom.getFirst(); - var offset: u32 = 0; - // TODO: Calculate this during atom allocation, rather than - // this linear calculation. For now it's done here as atoms - // are being sorted after atom allocation, as functions aren't - // merged until later. - while (true) { - offset += 5; // each atom uses 5 bytes to store its body's size - if (current_atom == target_atom) break; - current_atom = current_atom.next.?; - } + const target_atom = wasm_bin.symbol_atom.get(target_loc) orelse { + return @bitCast(u32, @as(i32, -1)); + }; + const offset: u32 = 11 + Wasm.getULEB128Size(target_atom.size); // Header (11 bytes fixed-size) + body size (leb-encoded) const rel_value = @intCast(i32, target_atom.offset + offset) + relocation.addend; return @intCast(u32, rel_value); }, diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index e5947228a5..8f49d68712 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -923,7 +923,7 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b try atom.relocs.append(gpa, reloc); if (relocation.isTableIndex()) { - try wasm_bin.function_table.putNoClobber(gpa, .{ + try wasm_bin.function_table.put(gpa, .{ .file = object_index, .index = relocation.index, }, 0); @@ -938,17 +938,17 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b .index = relocatable_data.getIndex(), })) |symbols| { atom.sym_index = symbols.pop(); + try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom); // symbols referencing the same atom will be added as alias // or as 'parent' when they are global. while (symbols.popOrNull()) |idx| { + try wasm_bin.symbol_atom.putNoClobber(gpa, .{ .file = atom.file, .index = idx }, atom); const alias_symbol = object.symtable[idx]; - const symbol = object.symtable[atom.sym_index]; - if (alias_symbol.isGlobal() and symbol.isLocal()) { + if (alias_symbol.isGlobal()) { atom.sym_index = idx; } } - try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom); } const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index]; diff --git a/src/link/Wasm/Symbol.zig b/src/link/Wasm/Symbol.zig index d857e6de44..365f4a2c0b 100644 --- a/src/link/Wasm/Symbol.zig +++ b/src/link/Wasm/Symbol.zig @@ -38,7 +38,7 @@ pub const Tag = enum { return switch (tag) { .function => .function, .global => .global, - .data => .memory, + .data => unreachable, // Data symbols will generate a global .section => unreachable, // Not an external type .event => unreachable, // Not an external type .dead => unreachable, // Dead symbols should not be referenced |
