diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 469850b665..a6d1ade61d 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -123,9 +123,10 @@ object_init_funcs: std.ArrayListUnmanaged(InitFunc) = .empty, /// logically to an object file's .data section, or .rodata section. In /// the case of `-fdata-sections` there will be one segment per data symbol. object_data_segments: std.ArrayListUnmanaged(ObjectDataSegment) = .empty, -/// Each segment has many data symbols. These correspond logically to global +/// Each segment has many data symbols, which correspond logically to global /// constants. object_datas: std.ArrayListUnmanaged(ObjectData) = .empty, +object_data_imports: std.AutoArrayHashMapUnmanaged(String, ObjectDataImport) = .empty, /// Non-synthetic section that can essentially be mem-cpy'd into place after performing relocations. object_custom_segments: std.AutoArrayHashMapUnmanaged(ObjectSectionIndex, CustomSegment) = .empty, @@ -227,6 +228,22 @@ functions_end_prelink: u32 = 0, /// symbol errors, or import section entries depending on the output mode. function_imports: std.AutoArrayHashMapUnmanaged(String, FunctionImportId) = .empty, +/// At the end of prelink, this is populated with data symbols needed by +/// objects. +/// +/// During the Zcu phase, entries are not deleted from this table +/// because doing so would be irreversible when a `deleteExport` call is +/// handled. However, entries are added during the Zcu phase when extern +/// functions are passed to `updateNav`. +/// +/// `flush` gets a copy of this table, and then Zcu exports are applied to +/// remove elements from the table, and the remainder are either undefined +/// symbol errors, or symbol table entries depending on the output mode. +data_imports: std.AutoArrayHashMapUnmanaged(String, DataImportId) = .empty, +/// Set of data symbols that will appear in the final binary. Used to populate +/// `Flush.data_segments` before sorting. +data_segments: std.AutoArrayHashMapUnmanaged(DataId, void) = .empty, + /// Ordered list of non-import globals that will appear in the final binary. /// Empty until prelink. globals: std.AutoArrayHashMapUnmanaged(GlobalImport.Resolution, void) = .empty, @@ -251,6 +268,7 @@ error_name_table_ref_count: u32 = 0, /// value must be this OR'd with the same logic for zig functions /// (set to true if any threadlocal global is used). any_tls_relocs: bool = false, +any_passive_inits: bool = false, /// All MIR instructions for all Zcu functions. mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, @@ -1499,6 +1517,50 @@ pub const ObjectData = extern struct { }; }; +pub const ObjectDataImport = extern struct { + resolution: Resolution, + flags: SymbolFlags, + source_location: SourceLocation, + + pub const Resolution = enum(u32) { + __zig_error_names, + __zig_error_name_table, + __heap_base, + __heap_end, + unresolved = std.math.maxInt(u32), + _, + + comptime { + assert(@intFromEnum(Resolution.__zig_error_names) == @intFromEnum(DataId.__zig_error_names)); + assert(@intFromEnum(Resolution.__zig_error_name_table) == @intFromEnum(DataId.__zig_error_name_table)); + assert(@intFromEnum(Resolution.__heap_base) == @intFromEnum(DataId.__heap_base)); + assert(@intFromEnum(Resolution.__heap_end) == @intFromEnum(DataId.__heap_end)); + } + + pub fn toDataId(r: Resolution) ?DataId { + if (r == .unresolved) return null; + return @enumFromInt(@intFromEnum(r)); + } + + pub fn fromObjectDataIndex(wasm: *const Wasm, object_data_index: ObjectData.Index) Resolution { + return @enumFromInt(@intFromEnum(DataId.pack(wasm, .{ .object = object_data_index.ptr(wasm).segment }))); + } + }; + + /// Points into `Wasm.object_data_imports`. + pub const Index = enum(u32) { + _, + + pub fn value(i: @This(), wasm: *const Wasm) *ObjectDataImport { + return &wasm.object_data_imports.values()[@intFromEnum(i)]; + } + + pub fn fromSymbolName(wasm: *const Wasm, name: String) ?Index { + return @enumFromInt(wasm.object_data_imports.getIndex(name) orelse return null); + } + }; +}; + pub const DataPayload = extern struct { off: Off, /// The size in bytes of the data representing the segment within the section. @@ -1524,12 +1586,17 @@ pub const DataPayload = extern struct { pub const DataId = enum(u32) { __zig_error_names, __zig_error_name_table, + /// This and `__heap_end` are better retrieved via a global, but there is + /// some suboptimal code out there (wasi libc) that additionally needs them + /// as data symbols. + __heap_base, + __heap_end, /// First, an `ObjectDataSegment.Index`. /// Next, index into `uavs_obj` or `uavs_exe` depending on whether emitting an object. /// Next, index into `navs_obj` or `navs_exe` depending on whether emitting an object. _, - const first_object = @intFromEnum(DataId.__zig_error_name_table) + 1; + const first_object = @intFromEnum(DataId.__heap_end) + 1; pub const Category = enum { /// Thread-local variables. @@ -1544,6 +1611,8 @@ pub const DataId = enum(u32) { pub const Unpacked = union(enum) { __zig_error_names, __zig_error_name_table, + __heap_base, + __heap_end, object: ObjectDataSegment.Index, uav_exe: UavsExeIndex, uav_obj: UavsObjIndex, @@ -1555,6 +1624,8 @@ pub const DataId = enum(u32) { return switch (unpacked) { .__zig_error_names => .__zig_error_names, .__zig_error_name_table => .__zig_error_name_table, + .__heap_base => .__heap_base, + .__heap_end => .__heap_end, .object => |i| @enumFromInt(first_object + @intFromEnum(i)), inline .uav_exe, .uav_obj => |i| @enumFromInt(first_object + wasm.object_data_segments.items.len + @intFromEnum(i)), .nav_exe => |i| @enumFromInt(first_object + wasm.object_data_segments.items.len + wasm.uavs_exe.entries.len + @intFromEnum(i)), @@ -1566,6 +1637,8 @@ pub const DataId = enum(u32) { return switch (id) { .__zig_error_names => .__zig_error_names, .__zig_error_name_table => .__zig_error_name_table, + .__heap_base => .__heap_base, + .__heap_end => .__heap_end, _ => { const object_index = @intFromEnum(id) - first_object; @@ -1601,7 +1674,7 @@ pub const DataId = enum(u32) { pub fn category(id: DataId, wasm: *const Wasm) Category { return switch (unpack(id, wasm)) { - .__zig_error_names, .__zig_error_name_table => .data, + .__zig_error_names, .__zig_error_name_table, .__heap_base, .__heap_end => .data, .object => |i| { const ptr = i.ptr(wasm); if (ptr.flags.tls) return .tls; @@ -1622,7 +1695,7 @@ pub const DataId = enum(u32) { pub fn isTls(id: DataId, wasm: *const Wasm) bool { return switch (unpack(id, wasm)) { - .__zig_error_names, .__zig_error_name_table => false, + .__zig_error_names, .__zig_error_name_table, .__heap_base, .__heap_end => false, .object => |i| i.ptr(wasm).flags.tls, .uav_exe, .uav_obj => false, inline .nav_exe, .nav_obj => |i| { @@ -1640,7 +1713,7 @@ pub const DataId = enum(u32) { pub fn name(id: DataId, wasm: *const Wasm) []const u8 { return switch (unpack(id, wasm)) { - .__zig_error_names, .__zig_error_name_table, .uav_exe, .uav_obj => ".data", + .__zig_error_names, .__zig_error_name_table, .uav_exe, .uav_obj, .__heap_base, .__heap_end => ".data", .object => |i| i.ptr(wasm).name.unwrap().?.slice(wasm), inline .nav_exe, .nav_obj => |i| { const zcu = wasm.base.comp.zcu.?; @@ -1654,7 +1727,7 @@ pub const DataId = enum(u32) { pub fn alignment(id: DataId, wasm: *const Wasm) Alignment { return switch (unpack(id, wasm)) { .__zig_error_names => .@"1", - .__zig_error_name_table => wasm.pointerAlignment(), + .__zig_error_name_table, .__heap_base, .__heap_end => wasm.pointerAlignment(), .object => |i| i.ptr(wasm).flags.alignment, inline .uav_exe, .uav_obj => |i| { const zcu = wasm.base.comp.zcu.?; @@ -1683,7 +1756,7 @@ pub const DataId = enum(u32) { return switch (unpack(id, wasm)) { .__zig_error_names => @intCast(wasm.error_name_offs.items.len), .__zig_error_name_table => wasm.error_name_table_ref_count, - .object, .uav_obj, .nav_obj => 0, + .object, .uav_obj, .nav_obj, .__heap_base, .__heap_end => 0, inline .uav_exe, .nav_exe => |i| i.value(wasm).count, }; } @@ -1692,7 +1765,7 @@ pub const DataId = enum(u32) { const comp = wasm.base.comp; if (comp.config.import_memory and !id.isBss(wasm)) return true; return switch (unpack(id, wasm)) { - .__zig_error_names, .__zig_error_name_table => false, + .__zig_error_names, .__zig_error_name_table, .__heap_base, .__heap_end => false, .object => |i| i.ptr(wasm).flags.is_passive, .uav_exe, .uav_obj, .nav_exe, .nav_obj => false, }; @@ -1700,7 +1773,7 @@ pub const DataId = enum(u32) { pub fn isEmpty(id: DataId, wasm: *const Wasm) bool { return switch (unpack(id, wasm)) { - .__zig_error_names, .__zig_error_name_table => false, + .__zig_error_names, .__zig_error_name_table, .__heap_base, .__heap_end => false, .object => |i| i.ptr(wasm).payload.off == .none, inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.off == .none, }; @@ -1716,6 +1789,7 @@ pub const DataId = enum(u32) { const elem_size = ZcuType.slice_const_u8_sentinel_0.abiSize(zcu); return @intCast(errors_len * elem_size); }, + .__heap_base, .__heap_end => wasm.pointerSize(), .object => |i| i.ptr(wasm).payload.len, inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.len, }; @@ -2110,6 +2184,55 @@ pub const GlobalImportId = enum(u32) { } }; +/// 0. Index into `Wasm.object_data_imports`. +/// 1. Index into `Wasm.imports`. +pub const DataImportId = enum(u32) { + _, + + pub const Unpacked = union(enum) { + object_data_import: ObjectDataImport.Index, + zcu_import: ZcuImportIndex, + }; + + pub fn pack(unpacked: Unpacked, wasm: *const Wasm) DataImportId { + return switch (unpacked) { + .object_data_import => |i| @enumFromInt(@intFromEnum(i)), + .zcu_import => |i| @enumFromInt(@intFromEnum(i) - wasm.object_data_imports.entries.len), + }; + } + + pub fn unpack(id: DataImportId, wasm: *const Wasm) Unpacked { + const i = @intFromEnum(id); + if (i < wasm.object_data_imports.entries.len) return .{ .object_data_import = @enumFromInt(i) }; + const zcu_import_i = i - wasm.object_data_imports.entries.len; + return .{ .zcu_import = @enumFromInt(zcu_import_i) }; + } + + pub fn fromZcuImport(zcu_import: ZcuImportIndex, wasm: *const Wasm) DataImportId { + return pack(.{ .zcu_import = zcu_import }, wasm); + } + + pub fn fromObject(object_data_import: ObjectDataImport.Index, wasm: *const Wasm) DataImportId { + return pack(.{ .object_data_import = object_data_import }, wasm); + } + + pub fn sourceLocation(id: DataImportId, wasm: *const Wasm) SourceLocation { + switch (id.unpack(wasm)) { + .object_data_import => |obj_data_index| { + // TODO binary search + for (wasm.objects.items, 0..) |o, i| { + if (o.data_imports.off <= @intFromEnum(obj_data_index) and + o.data_imports.off + o.data_imports.len > @intFromEnum(obj_data_index)) + { + return .pack(.{ .object_index = @enumFromInt(i) }, wasm); + } + } else unreachable; + }, + .zcu_import => return .zig_object_nofile, // TODO give a better source location + } + } +}; + /// Index into `Wasm.symbol_table`. pub const SymbolTableIndex = enum(u32) { _, @@ -2716,6 +2839,7 @@ pub fn deinit(wasm: *Wasm) void { wasm.object_memory_imports.deinit(gpa); wasm.object_memories.deinit(gpa); wasm.object_relocations.deinit(gpa); + wasm.object_data_imports.deinit(gpa); wasm.object_data_segments.deinit(gpa); wasm.object_datas.deinit(gpa); wasm.object_custom_segments.deinit(gpa); @@ -2734,6 +2858,8 @@ pub fn deinit(wasm: *Wasm) void { wasm.global_imports.deinit(gpa); wasm.table_imports.deinit(gpa); wasm.tables.deinit(gpa); + wasm.data_imports.deinit(gpa); + wasm.data_segments.deinit(gpa); wasm.symbol_table.deinit(gpa); wasm.out_relocs.deinit(gpa); wasm.uav_fixups.deinit(gpa); @@ -2805,15 +2931,16 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index const name = try wasm.internString(ext.name.toSlice(ip)); if (ext.lib_name.toSlice(ip)) |ext_name| _ = try wasm.internString(ext_name); try wasm.imports.ensureUnusedCapacity(gpa, 1); + try wasm.function_imports.ensureUnusedCapacity(gpa, 1); + try wasm.data_imports.ensureUnusedCapacity(gpa, 1); + const zcu_import = wasm.addZcuImportReserved(ext.owner_nav); if (ip.isFunctionType(nav.typeOf(ip))) { - try wasm.function_imports.ensureUnusedCapacity(gpa, 1); - const zcu_import = wasm.addZcuImportReserved(ext.owner_nav); wasm.function_imports.putAssumeCapacity(name, .fromZcuImport(zcu_import, wasm)); // Ensure there is a corresponding function type table entry. const fn_info = zcu.typeToFunc(.fromInterned(ext.ty)).?; _ = try internFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target); } else { - @panic("TODO extern data"); + wasm.data_imports.putAssumeCapacity(name, .fromZcuImport(zcu_import, wasm)); } return; }, @@ -3023,6 +3150,12 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v try markTableImport(wasm, name, import, @enumFromInt(i)); } } + + for (wasm.object_data_imports.keys(), wasm.object_data_imports.values(), 0..) |name, *import, i| { + if (import.flags.isIncluded(rdynamic)) { + try markDataImport(wasm, name, import, @enumFromInt(i)); + } + } } fn markFunctionImport( @@ -3163,13 +3296,45 @@ fn markTableImport( } fn markDataSegment(wasm: *Wasm, segment_index: ObjectDataSegment.Index) link.File.FlushError!void { + const comp = wasm.base.comp; const segment = segment_index.ptr(wasm); if (segment.flags.alive) return; segment.flags.alive = true; + wasm.any_passive_inits = wasm.any_passive_inits or segment.flags.is_passive or + (comp.config.import_memory and !wasm.isBss(segment.name)); + + try wasm.data_segments.put(comp.gpa, .pack(wasm, .{ .object = segment_index }), {}); try wasm.markRelocations(segment.relocations(wasm)); } +fn markDataImport( + wasm: *Wasm, + name: String, + import: *ObjectDataImport, + data_index: ObjectDataImport.Index, +) link.File.FlushError!void { + if (import.flags.alive) return; + import.flags.alive = true; + + const comp = wasm.base.comp; + const gpa = comp.gpa; + + if (import.resolution == .unresolved) { + if (name == wasm.preloaded_strings.__heap_base) { + import.resolution = .__heap_base; + wasm.data_segments.putAssumeCapacity(.__heap_base, {}); + } else if (name == wasm.preloaded_strings.__heap_end) { + import.resolution = .__heap_end; + wasm.data_segments.putAssumeCapacity(.__heap_end, {}); + } else { + try wasm.data_imports.put(gpa, name, .fromObject(data_index, wasm)); + } + } else { + try markDataSegment(wasm, import.resolution.toDataId().?.unpack(wasm).object); + } +} + fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.File.FlushError!void { for (relocs.slice.tags(wasm), relocs.slice.pointees(wasm), relocs.slice.offsets(wasm)) |tag, pointee, offset| { if (offset >= relocs.end) break; @@ -3199,6 +3364,22 @@ fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.Fil const i: TableImport.Index = @enumFromInt(wasm.object_table_imports.getIndex(name).?); try markTableImport(wasm, name, i.value(wasm), i); }, + .memory_addr_import_leb, + .memory_addr_import_sleb, + .memory_addr_import_i32, + .memory_addr_import_rel_sleb, + .memory_addr_import_leb64, + .memory_addr_import_sleb64, + .memory_addr_import_i64, + .memory_addr_import_rel_sleb64, + .memory_addr_import_tls_sleb, + .memory_addr_import_locrel_i32, + .memory_addr_import_tls_sleb64, + => { + const name = pointee.symbol_name; + const i = ObjectDataImport.Index.fromSymbolName(wasm, name).?; + try markDataImport(wasm, name, i.value(wasm), i); + }, .function_index_leb, .function_index_i32, @@ -3220,26 +3401,6 @@ fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.Fil .section_offset_i32 => { log.warn("TODO: ensure section {d} is included in output", .{pointee.section}); }, - .memory_addr_import_leb, - .memory_addr_import_sleb, - .memory_addr_import_i32, - .memory_addr_import_rel_sleb, - .memory_addr_import_leb64, - .memory_addr_import_sleb64, - .memory_addr_import_i64, - .memory_addr_import_rel_sleb64, - .memory_addr_import_tls_sleb, - .memory_addr_import_locrel_i32, - .memory_addr_import_tls_sleb64, - => { - const name = pointee.symbol_name; - if (name == wasm.preloaded_strings.__heap_end or - name == wasm.preloaded_strings.__heap_base) - { - continue; - } - log.warn("TODO: ensure data symbol {s} is included in output", .{name.slice(wasm)}); - }, .memory_addr_leb, .memory_addr_sleb, @@ -3309,6 +3470,7 @@ pub fn flushModule( try wasm.flush_buffer.missing_exports.reinit(gpa, wasm.missing_exports.keys(), &.{}); try wasm.flush_buffer.function_imports.reinit(gpa, wasm.function_imports.keys(), wasm.function_imports.values()); try wasm.flush_buffer.global_imports.reinit(gpa, wasm.global_imports.keys(), wasm.global_imports.values()); + try wasm.flush_buffer.data_imports.reinit(gpa, wasm.data_imports.keys(), wasm.data_imports.values()); return wasm.flush_buffer.finish(wasm) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, @@ -4117,6 +4279,15 @@ fn pointerAlignment(wasm: *const Wasm) Alignment { }; } +fn pointerSize(wasm: *const Wasm) u32 { + const target = &wasm.base.comp.root_mod.resolved_target.result; + return switch (target.cpu.arch) { + .wasm32 => 4, + .wasm64 => 8, + else => unreachable, + }; +} + fn addZcuImportReserved(wasm: *Wasm, nav_index: InternPool.Nav.Index) ZcuImportIndex { const gop = wasm.imports.getOrPutAssumeCapacity(nav_index); gop.value_ptr.* = {}; diff --git a/src/link/Wasm/Flush.zig b/src/link/Wasm/Flush.zig index d2ef4eb81e..68a3c3ac69 100644 --- a/src/link/Wasm/Flush.zig +++ b/src/link/Wasm/Flush.zig @@ -32,6 +32,7 @@ binary_bytes: std.ArrayListUnmanaged(u8) = .empty, missing_exports: std.AutoArrayHashMapUnmanaged(String, void) = .empty, function_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.FunctionImportId) = .empty, global_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.GlobalImportId) = .empty, +data_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.DataImportId) = .empty, /// For debug purposes only. memory_layout_finished: bool = false, @@ -50,6 +51,7 @@ pub fn deinit(f: *Flush, gpa: Allocator) void { f.missing_exports.deinit(gpa); f.function_imports.deinit(gpa); f.global_imports.deinit(gpa); + f.data_imports.deinit(gpa); f.* = undefined; } @@ -108,7 +110,9 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { .global_index = Wasm.GlobalIndex.fromIpNav(wasm, nav_export.nav_index).?, }); _ = f.missing_exports.swapRemove(nav_export.name); - _ = f.global_imports.swapRemove(nav_export.name); + _ = f.data_imports.swapRemove(nav_export.name); + // `f.global_imports` is ignored because Zcu has no way to + // export wasm globals. } } @@ -139,6 +143,10 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { const src_loc = table_import_id.value(wasm).source_location; src_loc.addError(wasm, "undefined table: {s}", .{name.slice(wasm)}); } + for (f.data_imports.keys(), f.data_imports.values()) |name, data_import_id| { + const src_loc = data_import_id.sourceLocation(wasm); + src_loc.addError(wasm, "undefined data: {s}", .{name.slice(wasm)}); + } } if (diags.hasErrors()) return error.LinkFailure; @@ -151,11 +159,9 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { try wasm.functions.put(gpa, .__wasm_call_ctors, {}); } - var any_passive_inits = false; - // Merge and order the data segments. Depends on garbage collection so that // unused segments can be omitted. - try f.data_segments.ensureUnusedCapacity(gpa, wasm.object_data_segments.items.len + + try f.data_segments.ensureUnusedCapacity(gpa, wasm.data_segments.entries.len + wasm.uavs_obj.entries.len + wasm.navs_obj.entries.len + wasm.uavs_exe.entries.len + wasm.navs_exe.entries.len + 2); if (is_obj) assert(wasm.uavs_exe.entries.len == 0); @@ -174,18 +180,11 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { for (0..wasm.navs_exe.entries.len) |navs_index| f.data_segments.putAssumeCapacityNoClobber(.pack(wasm, .{ .nav_exe = @enumFromInt(navs_index), }), @as(u32, undefined)); - for (wasm.object_data_segments.items, 0..) |*ds, i| { - if (!ds.flags.alive) continue; - const obj_seg_index: Wasm.ObjectDataSegment.Index = @enumFromInt(i); - any_passive_inits = any_passive_inits or ds.flags.is_passive or (import_memory and !wasm.isBss(ds.name)); - _ = f.data_segments.putAssumeCapacityNoClobber(.pack(wasm, .{ - .object = obj_seg_index, - }), @as(u32, undefined)); - } if (wasm.error_name_table_ref_count > 0) { f.data_segments.putAssumeCapacity(.__zig_error_names, @as(u32, undefined)); f.data_segments.putAssumeCapacity(.__zig_error_name_table, @as(u32, undefined)); } + for (wasm.data_segments.keys()) |data_id| f.data_segments.putAssumeCapacity(data_id, @as(u32, undefined)); try wasm.functions.ensureUnusedCapacity(gpa, 3); @@ -194,7 +193,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { // dropped in __wasm_init_memory, which is registered as the start function // We also initialize bss segments (using memory.fill) as part of this // function. - if (any_passive_inits) { + if (wasm.any_passive_inits) { wasm.functions.putAssumeCapacity(.__wasm_init_memory, {}); } @@ -349,7 +348,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { if (category != .zero) try f.data_segment_groups.append(gpa, @intCast(memory_ptr)); } - if (shared_memory and any_passive_inits) { + if (shared_memory and wasm.any_passive_inits) { memory_ptr = pointer_alignment.forward(memory_ptr); virtual_addrs.init_memory_flag = @intCast(memory_ptr); memory_ptr += 4; @@ -774,6 +773,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { const code_start = binary_bytes.items.len; append: { const code = switch (segment_id.unpack(wasm)) { + .__heap_base => @panic("TODO"), + .__heap_end => @panic("TODO"), .__zig_error_names => { try binary_bytes.appendSlice(gpa, wasm.error_name_bytes.items); break :append; diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index ad04a8dcc5..12c51be88e 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -26,14 +26,16 @@ start_function: Wasm.OptionalObjectFunctionIndex, /// (or therefore missing) and must generate an error when another object uses /// features that are not supported by the other. features: Wasm.Feature.Set, -/// Points into Wasm object_functions +/// Points into `Wasm.object_functions` functions: RelativeSlice, -/// Points into Wasm object_function_imports +/// Points into `Wasm.object_function_imports` function_imports: RelativeSlice, -/// Points into Wasm object_global_imports +/// Points into `Wasm.object_global_imports` global_imports: RelativeSlice, -/// Points into Wasm object_table_imports +/// Points into `Wasm.object_table_imports` table_imports: RelativeSlice, +// Points into `Wasm.object_data_imports` +data_imports: RelativeSlice, /// Points into Wasm object_custom_segments custom_segments: RelativeSlice, /// Points into Wasm object_init_funcs @@ -280,6 +282,7 @@ pub fn parse( const function_imports_start: u32 = @intCast(wasm.object_function_imports.entries.len); const global_imports_start: u32 = @intCast(wasm.object_global_imports.entries.len); const table_imports_start: u32 = @intCast(wasm.object_table_imports.entries.len); + const data_imports_start: u32 = @intCast(wasm.object_data_imports.entries.len); const local_section_index_base = wasm.object_total_sections; const object_index: Wasm.ObjectIndex = @enumFromInt(wasm.objects.items.len); const source_location: Wasm.SourceLocation = .fromObject(object_index, wasm); @@ -1087,6 +1090,19 @@ pub fn parse( gop.value_ptr.flags.ref_type = .from(ptr.ref_type); } }, + .data_import => { + const name = symbol.name.unwrap().?; + if (symbol.flags.binding == .local) { + diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)}); + continue; + } + const gop = try wasm.object_data_imports.getOrPut(gpa, name); + if (!gop.found_existing) gop.value_ptr.* = .{ + .flags = symbol.flags, + .source_location = source_location, + .resolution = .unresolved, + }; + }, .function => |index| { assert(!symbol.flags.undefined); const ptr = index.ptr(wasm); @@ -1134,12 +1150,13 @@ pub fn parse( } }, .global => |index| { + assert(!symbol.flags.undefined); const ptr = index.ptr(wasm); ptr.name = symbol.name; ptr.flags = symbol.flags; if (symbol.flags.binding == .local) continue; // No participation in symbol resolution. - const new_ty = ptr.type(); const name = symbol.name.unwrap().?; + const new_ty = ptr.type(); const gop = try wasm.object_global_imports.getOrPut(gpa, name); if (gop.found_existing) { const existing_ty = gop.value_ptr.type(); @@ -1192,12 +1209,42 @@ pub fn parse( } }, .table => |i| { + assert(!symbol.flags.undefined); const ptr = i.ptr(wasm); ptr.name = symbol.name; ptr.flags = symbol.flags; - if (symbol.flags.undefined and symbol.flags.binding == .local) { - const name = ptr.name.slice(wasm).?; - diags.addParseError(path, "local symbol '{s}' references import", .{name}); + }, + .data => |index| { + assert(!symbol.flags.undefined); + const ptr = index.ptr(wasm); + const name = ptr.name; + assert(name.toOptional() == symbol.name); + ptr.flags = symbol.flags; + if (symbol.flags.binding == .local) continue; // No participation in symbol resolution. + const gop = try wasm.object_data_imports.getOrPut(gpa, name); + if (gop.found_existing) { + if (gop.value_ptr.resolution == .unresolved or gop.value_ptr.flags.binding == .weak) { + // Intentional: if they're both weak, take the last one. + gop.value_ptr.source_location = source_location; + gop.value_ptr.resolution = .fromObjectDataIndex(wasm, index); + gop.value_ptr.flags = symbol.flags; + continue; + } + if (ptr.flags.binding == .weak) { + // Keep the existing one. + continue; + } + var err = try diags.addErrorWithNotes(2); + try err.addMsg("symbol collision: {s}", .{name.slice(wasm)}); + gop.value_ptr.source_location.addNote(&err, "exported here", .{}); + source_location.addNote(&err, "exported here", .{}); + continue; + } else { + gop.value_ptr.* = .{ + .flags = symbol.flags, + .source_location = source_location, + .resolution = .unresolved, + }; } }, .section => |i| { @@ -1210,13 +1257,6 @@ pub fn parse( diags.addParseError(path, "local symbol '{s}' references import", .{name}); } }, - .data_import => { - if (symbol.flags.undefined and symbol.flags.binding == .local) { - const name = symbol.name.slice(wasm).?; - diags.addParseError(path, "local symbol '{s}' references import", .{name}); - } - }, - .data => continue, // `wasm.object_datas` has already been populated. }; // Apply export section info. This is done after the symbol table above so @@ -1317,6 +1357,10 @@ pub fn parse( .off = table_imports_start, .len = @intCast(wasm.object_table_imports.entries.len - table_imports_start), }, + .data_imports = .{ + .off = data_imports_start, + .len = @intCast(wasm.object_data_imports.entries.len - data_imports_start), + }, .init_funcs = .{ .off = init_funcs_start, .len = @intCast(wasm.object_init_funcs.items.len - init_funcs_start),