diff --git a/src/link.zig b/src/link.zig index e828e24b9964dced00951ec70c88aa03b8aafc6c..b9bcd843fb02fe713a676018387c67ef939ef1b5 100644 --- a/src/link.zig +++ b/src/link.zig @@ -97,15 +97,12 @@ pub const Diags = struct { err_msg.msg = try std.fmt.allocPrint(gpa, format, args); } - pub fn addNote( - err: *ErrorWithNotes, - comptime format: []const u8, - args: anytype, - ) error{OutOfMemory}!void { + pub fn addNote(err: *ErrorWithNotes, comptime format: []const u8, args: anytype) void { const gpa = err.diags.gpa; + const msg = std.fmt.allocPrint(gpa, format, args) catch return err.diags.setAllocFailure(); const err_msg = &err.diags.msgs.items[err.index]; assert(err.note_slot < err_msg.notes.len); - err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) }; + err_msg.notes[err.note_slot] = .{ .msg = msg }; err.note_slot += 1; } }; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index cfd6bcd6c752e65859848c8a0898f8d413a6f4bd..ea2fa56a5d9a75e04933844ce0e4f7c4cb9437ae 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -3394,7 +3394,7 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void { // TODO verify `getMaxNumberOfPhdrs()` is accurate and convert this into no-op var err = try diags.addErrorWithNotes(1); try err.addMsg("fatal linker error: not enough space reserved for EHDR and PHDR table", .{}); - try err.addNote("required 0x{x}, available 0x{x}", .{ needed_size, available_space }); + err.addNote("required 0x{x}, available 0x{x}", .{ needed_size, available_space }); } phdr_table_load.p_filesz = needed_size + ehsize; @@ -4545,12 +4545,12 @@ fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void { for (refs.items[0..nrefs]) |ref| { const atom_ptr = self.atom(ref).?; const file_ptr = atom_ptr.file(self).?; - try err.addNote("referenced by {s}:{s}", .{ file_ptr.fmtPath(), atom_ptr.name(self) }); + err.addNote("referenced by {s}:{s}", .{ file_ptr.fmtPath(), atom_ptr.name(self) }); } if (refs.items.len > max_notes) { const remaining = refs.items.len - max_notes; - try err.addNote("referenced {d} more times", .{remaining}); + err.addNote("referenced {d} more times", .{remaining}); } } } @@ -4567,17 +4567,17 @@ fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemor var err = try diags.addErrorWithNotes(nnotes + 1); try err.addMsg("duplicate symbol definition: {s}", .{sym.name(self)}); - try err.addNote("defined by {}", .{sym.file(self).?.fmtPath()}); + err.addNote("defined by {}", .{sym.file(self).?.fmtPath()}); var inote: usize = 0; while (inote < @min(notes.items.len, max_notes)) : (inote += 1) { const file_ptr = self.file(notes.items[inote]).?; - try err.addNote("defined by {}", .{file_ptr.fmtPath()}); + err.addNote("defined by {}", .{file_ptr.fmtPath()}); } if (notes.items.len > max_notes) { const remaining = notes.items.len - max_notes; - try err.addNote("defined {d} more times", .{remaining}); + err.addNote("defined {d} more times", .{remaining}); } } @@ -4601,7 +4601,7 @@ pub fn addFileError( const diags = &self.base.comp.link_diags; var err = try diags.addErrorWithNotes(1); try err.addMsg(format, args); - try err.addNote("while parsing {}", .{self.file(file_index).?.fmtPath()}); + err.addNote("while parsing {}", .{self.file(file_index).?.fmtPath()}); } pub fn failFile( diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index d34dd6bb63e47f52137b25f4537a9011edf1dd87..f0eb0dce3f225c5342b7f79ced1c06bce0afbcd5 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -523,7 +523,7 @@ fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) Re relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch), rel.r_offset, }); - try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) }); + err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) }); return error.RelocFailure; } @@ -539,7 +539,7 @@ fn reportTextRelocError( rel.r_offset, symbol.name(elf_file), }); - try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) }); + err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) }); return error.RelocFailure; } @@ -555,8 +555,8 @@ fn reportPicError( rel.r_offset, symbol.name(elf_file), }); - try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) }); - try err.addNote("recompile with -fPIC", .{}); + err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) }); + err.addNote("recompile with -fPIC", .{}); return error.RelocFailure; } @@ -572,8 +572,8 @@ fn reportNoPicError( rel.r_offset, symbol.name(elf_file), }); - try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) }); - try err.addNote("recompile with -fno-PIC", .{}); + err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) }); + err.addNote("recompile with -fno-PIC", .{}); return error.RelocFailure; } @@ -1187,7 +1187,7 @@ const x86_64 = struct { x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]) catch { var err = try diags.addErrorWithNotes(1); try err.addMsg("could not relax {s}", .{@tagName(r_type)}); - try err.addNote("in {}:{s} at offset 0x{x}", .{ + err.addNote("in {}:{s} at offset 0x{x}", .{ atom.file(elf_file).?.fmtPath(), atom.name(elf_file), rel.r_offset, @@ -1332,7 +1332,7 @@ const x86_64 = struct { relocation.fmtRelocType(rels[0].r_type(), .x86_64), relocation.fmtRelocType(rels[1].r_type(), .x86_64), }); - try err.addNote("in {}:{s} at offset 0x{x}", .{ + err.addNote("in {}:{s} at offset 0x{x}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file), rels[0].r_offset, @@ -1388,7 +1388,7 @@ const x86_64 = struct { relocation.fmtRelocType(rels[0].r_type(), .x86_64), relocation.fmtRelocType(rels[1].r_type(), .x86_64), }); - try err.addNote("in {}:{s} at offset 0x{x}", .{ + err.addNote("in {}:{s} at offset 0x{x}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file), rels[0].r_offset, @@ -1485,7 +1485,7 @@ const x86_64 = struct { relocation.fmtRelocType(rels[0].r_type(), .x86_64), relocation.fmtRelocType(rels[1].r_type(), .x86_64), }); - try err.addNote("in {}:{s} at offset 0x{x}", .{ + err.addNote("in {}:{s} at offset 0x{x}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file), rels[0].r_offset, @@ -1672,7 +1672,7 @@ const aarch64 = struct { // TODO: relax var err = try diags.addErrorWithNotes(1); try err.addMsg("TODO: relax ADR_GOT_PAGE", .{}); - try err.addNote("in {}:{s} at offset 0x{x}", .{ + err.addNote("in {}:{s} at offset 0x{x}", .{ atom.file(elf_file).?.fmtPath(), atom.name(elf_file), r_offset, @@ -1959,7 +1959,7 @@ const riscv = struct { // TODO: implement searching forward var err = try diags.addErrorWithNotes(1); try err.addMsg("TODO: find HI20 paired reloc scanning forward", .{}); - try err.addNote("in {}:{s} at offset 0x{x}", .{ + err.addNote("in {}:{s} at offset 0x{x}", .{ atom.file(elf_file).?.fmtPath(), atom.name(elf_file), rel.r_offset, diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 65a62ff1a6b5a42e87edb70b81b6c3bdd31eb565..d6076a5558d7a4c08a5c39b44f024c3757a2b3f9 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -797,7 +797,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void { if (!isNull(data[end .. end + sh_entsize])) { var err = try diags.addErrorWithNotes(1); try err.addMsg("string not null terminated", .{}); - try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); + err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); return error.LinkFailure; } end += sh_entsize; @@ -812,7 +812,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void { if (shdr.sh_size % sh_entsize != 0) { var err = try diags.addErrorWithNotes(1); try err.addMsg("size not a multiple of sh_entsize", .{}); - try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); + err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); return error.LinkFailure; } @@ -889,8 +889,8 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{ const res = imsec.findSubsection(@intCast(esym.st_value)) orelse { var err = try diags.addErrorWithNotes(2); try err.addMsg("invalid symbol value: {x}", .{esym.st_value}); - try err.addNote("for symbol {s}", .{sym.name(elf_file)}); - try err.addNote("in {}", .{self.fmtPath()}); + err.addNote("for symbol {s}", .{sym.name(elf_file)}); + err.addNote("in {}", .{self.fmtPath()}); return error.LinkFailure; }; @@ -915,7 +915,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{ const res = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse { var err = try diags.addErrorWithNotes(1); try err.addMsg("invalid relocation at offset 0x{x}", .{rel.r_offset}); - try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); + err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); return error.LinkFailure; }; diff --git a/src/link/Elf/eh_frame.zig b/src/link/Elf/eh_frame.zig index 4df05d49d3d52dd804bf45664a3f2c1985dea73f..bf46fb02621db698367742b21f1d36207da67114 100644 --- a/src/link/Elf/eh_frame.zig +++ b/src/link/Elf/eh_frame.zig @@ -611,7 +611,7 @@ fn reportInvalidReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela) !void { relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch), rel.r_offset, }); - try err.addNote("in {}:.eh_frame", .{elf_file.file(rec.file_index).?.fmtPath()}); + err.addNote("in {}:.eh_frame", .{elf_file.file(rec.file_index).?.fmtPath()}); return error.RelocFailure; } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index e92bd58f2976de83c102e8ad9a1b73dd81e0261e..e5601a13180df7f51e63cae4dfca33c41ef9985d 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1572,21 +1572,21 @@ fn reportUndefs(self: *MachO) !void { try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)}); switch (notes) { - .force_undefined => try err.addNote("referenced with linker flag -u", .{}), - .entry => try err.addNote("referenced with linker flag -e", .{}), - .dyld_stub_binder, .objc_msgsend => try err.addNote("referenced implicitly", .{}), + .force_undefined => err.addNote("referenced with linker flag -u", .{}), + .entry => err.addNote("referenced with linker flag -e", .{}), + .dyld_stub_binder, .objc_msgsend => err.addNote("referenced implicitly", .{}), .refs => |refs| { var inote: usize = 0; while (inote < @min(refs.items.len, max_notes)) : (inote += 1) { const ref = refs.items[inote]; const file = self.getFile(ref.file).?; const atom = ref.getAtom(self).?; - try err.addNote("referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) }); + err.addNote("referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) }); } if (refs.items.len > max_notes) { const remaining = refs.items.len - max_notes; - try err.addNote("referenced {d} more times", .{remaining}); + err.addNote("referenced {d} more times", .{remaining}); } }, } @@ -3473,8 +3473,8 @@ fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !vo seg_id, seg.segName(), }); - try err.addNote("TODO: emit relocations to memory locations in self-hosted backends", .{}); - try err.addNote("as a workaround, try increasing pre-allocated virtual memory of each segment", .{}); + err.addNote("TODO: emit relocations to memory locations in self-hosted backends", .{}); + err.addNote("as a workaround, try increasing pre-allocated virtual memory of each segment", .{}); } seg.vmsize = needed_size; @@ -3776,7 +3776,7 @@ pub fn reportParseError2( const diags = &self.base.comp.link_diags; var err = try diags.addErrorWithNotes(1); try err.addMsg(format, args); - try err.addNote("while parsing {}", .{self.getFile(file_index).?.fmtPath()}); + err.addNote("while parsing {}", .{self.getFile(file_index).?.fmtPath()}); } fn reportMissingDependencyError( @@ -3790,10 +3790,10 @@ fn reportMissingDependencyError( const diags = &self.base.comp.link_diags; var err = try diags.addErrorWithNotes(2 + checked_paths.len); try err.addMsg(format, args); - try err.addNote("while resolving {s}", .{path}); - try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()}); + err.addNote("while resolving {s}", .{path}); + err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()}); for (checked_paths) |p| { - try err.addNote("tried {s}", .{p}); + err.addNote("tried {s}", .{p}); } } @@ -3807,8 +3807,8 @@ fn reportDependencyError( const diags = &self.base.comp.link_diags; var err = try diags.addErrorWithNotes(2); try err.addMsg(format, args); - try err.addNote("while parsing {s}", .{path}); - try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()}); + err.addNote("while parsing {s}", .{path}); + err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()}); } fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void { @@ -3838,17 +3838,17 @@ fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void { var err = try diags.addErrorWithNotes(nnotes + 1); try err.addMsg("duplicate symbol definition: {s}", .{sym.getName(self)}); - try err.addNote("defined by {}", .{sym.getFile(self).?.fmtPath()}); + err.addNote("defined by {}", .{sym.getFile(self).?.fmtPath()}); var inote: usize = 0; while (inote < @min(notes.items.len, max_notes)) : (inote += 1) { const file = self.getFile(notes.items[inote]).?; - try err.addNote("defined by {}", .{file.fmtPath()}); + err.addNote("defined by {}", .{file.fmtPath()}); } if (notes.items.len > max_notes) { const remaining = notes.items.len - max_notes; - try err.addNote("defined {d} more times", .{remaining}); + err.addNote("defined {d} more times", .{remaining}); } } return error.HasDuplicates; diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index 9b613abb9298289092d2bbaa13a624100a4594bf..ed554ffb35ee216720f3b0f366cef04e33e58b44 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -909,8 +909,8 @@ const x86_64 = struct { rel.offset, rel.fmtPretty(.x86_64), }); - try err.addNote("expected .mov instruction but found .{s}", .{@tagName(x)}); - try err.addNote("while parsing {}", .{self.getFile(macho_file).fmtPath()}); + err.addNote("expected .mov instruction but found .{s}", .{@tagName(x)}); + err.addNote("while parsing {}", .{self.getFile(macho_file).fmtPath()}); return error.RelaxFailUnexpectedInstruction; }, } diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index c705bfbbab0e8dbad2c38e81031c6f12b85960b3..bc7ae10cd20847d4d1983c86829ffca2518368a5 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -109,7 +109,7 @@ object_tables: std.ArrayListUnmanaged(Table) = .empty, /// All memory imports for all objects. object_memory_imports: std.ArrayListUnmanaged(MemoryImport) = .empty, /// All parsed memory sections for all objects. -object_memories: std.ArrayListUnmanaged(std.wasm.Memory) = .empty, +object_memories: std.ArrayListUnmanaged(ObjectMemory) = .empty, /// All relocations from all objects concatenated. `relocs_start` marks the end /// point of object relocations and start point of Zcu relocations. @@ -119,8 +119,13 @@ object_relocations: std.MultiArrayList(ObjectRelocation) = .empty, /// by the (synthetic) __wasm_call_ctors function. object_init_funcs: std.ArrayListUnmanaged(InitFunc) = .empty, -/// Non-synthetic section that can essentially be mem-cpy'd into place after performing relocations. -object_data_segments: std.ArrayListUnmanaged(DataSegment) = .empty, +/// The data section of an object has many segments. Each segment corresponds +/// 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 +/// constants. +object_datas: std.ArrayListUnmanaged(ObjectData) = .empty, /// Non-synthetic section that can essentially be mem-cpy'd into place after performing relocations. object_custom_segments: std.AutoArrayHashMapUnmanaged(ObjectSectionIndex, CustomSegment) = .empty, @@ -457,6 +462,21 @@ pub const SourceLocation = enum(u32) { .source_location_index => @panic("TODO"), } } + + pub fn addNote( + sl: SourceLocation, + wasm: *Wasm, + err: *link.Diags.ErrorWithNotes, + comptime f: []const u8, + args: anytype, + ) void { + switch (sl.unpack(wasm)) { + .none => err.addNote(f, args), + .zig_object_nofile => err.addNote("zig compilation unit: " ++ f, args), + .object_index => |i| err.addNote("{}: " ++ f, .{i.ptr(wasm).path} ++ args), + .source_location_index => @panic("TODO"), + } + } }; /// The lower bits of this ABI-match the flags here: @@ -687,13 +707,13 @@ pub const UavsExeIndex = enum(u32) { /// Used when emitting a relocatable object. pub const ZcuDataObj = extern struct { - code: DataSegment.Payload, + code: DataPayload, relocs: OutReloc.Slice, }; /// Used when not emitting a relocatable object. pub const ZcuDataExe = extern struct { - code: DataSegment.Payload, + code: DataPayload, /// Tracks how many references there are for the purposes of sorting data segments. count: u32, }; @@ -917,6 +937,10 @@ pub const FunctionImport = extern struct { return pack(wasm, .{ .zcu_func = @enumFromInt(wasm.zcu_funcs.getIndex(ip_index).?) }); } + pub fn fromObjectFunction(wasm: *const Wasm, object_function: ObjectFunctionIndex) Resolution { + return pack(wasm, .{ .object_function = object_function }); + } + pub fn isNavOrUnresolved(r: Resolution, wasm: *const Wasm) bool { return switch (r.unpack(wasm)) { .unresolved, .zcu_func => true, @@ -988,7 +1012,7 @@ pub const Function = extern struct { section_index: ObjectSectionIndex, source_location: SourceLocation, - pub const Code = DataSegment.Payload; + pub const Code = DataPayload; }; pub const GlobalImport = extern struct { @@ -1283,12 +1307,30 @@ pub const ObjectGlobalIndex = enum(u32) { } }; -/// Index into `Wasm.object_memories`. -pub const ObjectMemoryIndex = enum(u32) { - _, +pub const ObjectMemory = extern struct { + flags: SymbolFlags, + name: OptionalString, + limits_min: u32, + limits_max: u32, - pub fn ptr(index: ObjectMemoryIndex, wasm: *const Wasm) *std.wasm.Memory { - return &wasm.object_memories.items[@intFromEnum(index)]; + /// Index into `Wasm.object_memories`. + pub const Index = enum(u32) { + _, + + pub fn ptr(index: Index, wasm: *const Wasm) *ObjectMemory { + return &wasm.object_memories.items[@intFromEnum(index)]; + } + }; + + pub fn limits(om: *const ObjectMemory) std.wasm.Limits { + return .{ + .flags = .{ + .has_max = om.limits_has_max, + .is_shared = om.limits_is_shared, + }, + .min = om.limits_min, + .max = om.limits_max, + }; } }; @@ -1318,14 +1360,74 @@ pub const OptionalObjectFunctionIndex = enum(u32) { } }; -pub const DataSegment = extern struct { +pub const ObjectDataSegment = extern struct { + /// `none` if segment info custom subsection is missing. + name: OptionalString, + flags: SymbolFlags, + payload: DataPayload, + + /// Index into `Wasm.object_data_segments`. + pub const Index = enum(u32) { + _, + + pub fn ptr(i: Index, wasm: *const Wasm) *ObjectDataSegment { + return &wasm.object_data_segments.items[@intFromEnum(i)]; + } + }; +}; + +/// A local or exported global const from an object file. +pub const ObjectData = extern struct { + segment: ObjectDataSegment.Index, + /// Index into the object segment payload. Must be <= the segment's size. + offset: u32, + /// May be zero. `offset + size` must be <= the segment's size. + size: u32, /// `none` if no symbol describes it. name: OptionalString, flags: SymbolFlags, - payload: Payload, - /// From the data segment start to the first byte of payload. - segment_offset: u32, - section_index: ObjectSectionIndex, + + /// Index into `Wasm.object_datas`. + pub const Index = enum(u32) { + _, + + pub fn ptr(i: Index, wasm: *const Wasm) *ObjectData { + return &wasm.object_datas.items[@intFromEnum(i)]; + } + }; +}; + +pub const DataPayload = extern struct { + off: Off, + /// The size in bytes of the data representing the segment within the section. + len: u32, + + pub const Off = enum(u32) { + /// The payload is all zeroes (bss section). + none = std.math.maxInt(u32), + /// Points into string_bytes. No corresponding string_table entry. + _, + + pub fn unwrap(off: Off) ?u32 { + return if (off == .none) null else @intFromEnum(off); + } + }; + + pub fn slice(p: DataPayload, wasm: *const Wasm) []const u8 { + return wasm.string_bytes.items[p.off.unwrap().?..][0..p.len]; + } +}; + +/// A reference to a local or exported global const. +pub const DataId = enum(u32) { + __zig_error_names, + __zig_error_name_table, + /// 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; pub const Category = enum { /// Thread-local variables. @@ -1337,221 +1439,180 @@ pub const DataSegment = extern struct { zero, }; - pub const Payload = extern struct { - off: Off, - /// The size in bytes of the data representing the segment within the section. - len: u32, - - pub const Off = enum(u32) { - /// The payload is all zeroes (bss section). - none = std.math.maxInt(u32), - /// Points into string_bytes. No corresponding string_table entry. - _, - - pub fn unwrap(off: Off) ?u32 { - return if (off == .none) null else @intFromEnum(off); - } - }; - - pub fn slice(p: DataSegment.Payload, wasm: *const Wasm) []const u8 { - return wasm.string_bytes.items[p.off.unwrap().?..][0..p.len]; - } - }; - - pub const Id = enum(u32) { + pub const Unpacked = union(enum) { __zig_error_names, __zig_error_name_table, - /// First, an `ObjectDataSegmentIndex`. - /// 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(Id.__zig_error_name_table) + 1; - - pub const Unpacked = union(enum) { - __zig_error_names, - __zig_error_name_table, - object: ObjectDataSegmentIndex, - uav_exe: UavsExeIndex, - uav_obj: UavsObjIndex, - nav_exe: NavsExeIndex, - nav_obj: NavsObjIndex, - }; - - pub fn pack(wasm: *const Wasm, unpacked: Unpacked) Id { - return switch (unpacked) { - .__zig_error_names => .__zig_error_names, - .__zig_error_name_table => .__zig_error_name_table, - .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)), - .nav_obj => |i| @enumFromInt(first_object + wasm.object_data_segments.items.len + wasm.uavs_obj.entries.len + @intFromEnum(i)), - }; - } - - pub fn unpack(id: Id, wasm: *const Wasm) Unpacked { - return switch (id) { - .__zig_error_names => .__zig_error_names, - .__zig_error_name_table => .__zig_error_name_table, - _ => { - const object_index = @intFromEnum(id) - first_object; - - const uav_index = if (object_index < wasm.object_data_segments.items.len) - return .{ .object = @enumFromInt(object_index) } - else - object_index - wasm.object_data_segments.items.len; - - const comp = wasm.base.comp; - const is_obj = comp.config.output_mode == .Obj; - if (is_obj) { - const nav_index = if (uav_index < wasm.uavs_obj.entries.len) - return .{ .uav_obj = @enumFromInt(uav_index) } - else - uav_index - wasm.uavs_obj.entries.len; - - return .{ .nav_obj = @enumFromInt(nav_index) }; - } else { - const nav_index = if (uav_index < wasm.uavs_exe.entries.len) - return .{ .uav_exe = @enumFromInt(uav_index) } - else - uav_index - wasm.uavs_exe.entries.len; - - return .{ .nav_exe = @enumFromInt(nav_index) }; - } - }, - }; - } - - pub fn category(id: Id, wasm: *const Wasm) Category { - return switch (unpack(id, wasm)) { - .__zig_error_names, .__zig_error_name_table => .data, - .object => |i| { - const ptr = i.ptr(wasm); - if (ptr.flags.tls) return .tls; - if (wasm.isBss(ptr.name)) return .zero; - return .data; - }, - inline .uav_exe, .uav_obj => |i| if (i.value(wasm).code.off == .none) .zero else .data, - inline .nav_exe, .nav_obj => |i| { - const zcu = wasm.base.comp.zcu.?; - const ip = &zcu.intern_pool; - const nav = ip.getNav(i.key(wasm).*); - if (nav.isThreadLocal(ip)) return .tls; - const code = i.value(wasm).code; - return if (code.off == .none) .zero else .data; - }, - }; - } - - pub fn isTls(id: Id, wasm: *const Wasm) bool { - return switch (unpack(id, wasm)) { - .__zig_error_names, .__zig_error_name_table => false, - .object => |i| i.ptr(wasm).flags.tls, - .uav_exe, .uav_obj => false, - inline .nav_exe, .nav_obj => |i| { - const zcu = wasm.base.comp.zcu.?; - const ip = &zcu.intern_pool; - const nav = ip.getNav(i.key(wasm).*); - return nav.isThreadLocal(ip); - }, - }; - } - - pub fn isBss(id: Id, wasm: *const Wasm) bool { - return id.category(wasm) == .zero; - } - - pub fn name(id: Id, wasm: *const Wasm) []const u8 { - return switch (unpack(id, wasm)) { - .__zig_error_names, .__zig_error_name_table, .uav_exe, .uav_obj => ".data", - .object => |i| i.ptr(wasm).name.unwrap().?.slice(wasm), - inline .nav_exe, .nav_obj => |i| { - const zcu = wasm.base.comp.zcu.?; - const ip = &zcu.intern_pool; - const nav = ip.getNav(i.key(wasm).*); - return nav.status.resolved.@"linksection".toSlice(ip) orelse ".data"; - }, - }; - } - - pub fn alignment(id: Id, wasm: *const Wasm) Alignment { - return switch (unpack(id, wasm)) { - .__zig_error_names => .@"1", - .__zig_error_name_table => wasm.pointerAlignment(), - .object => |i| i.ptr(wasm).flags.alignment, - inline .uav_exe, .uav_obj => |i| { - const zcu = wasm.base.comp.zcu.?; - const ip = &zcu.intern_pool; - const ip_index = i.key(wasm).*; - const ty: ZcuType = .fromInterned(ip.typeOf(ip_index)); - const result = ty.abiAlignment(zcu); - assert(result != .none); - return result; - }, - inline .nav_exe, .nav_obj => |i| { - const zcu = wasm.base.comp.zcu.?; - const ip = &zcu.intern_pool; - const nav = ip.getNav(i.key(wasm).*); - const explicit = nav.status.resolved.alignment; - if (explicit != .none) return explicit; - const ty: ZcuType = .fromInterned(nav.typeOf(ip)); - const result = ty.abiAlignment(zcu); - assert(result != .none); - return result; - }, - }; - } - - pub fn refCount(id: Id, wasm: *const Wasm) 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, - inline .uav_exe, .nav_exe => |i| i.value(wasm).count, - }; - } - - pub fn isPassive(id: Id, wasm: *const Wasm) bool { - 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, - .object => |i| i.ptr(wasm).flags.is_passive, - .uav_exe, .uav_obj, .nav_exe, .nav_obj => false, - }; - } - - pub fn isEmpty(id: Id, wasm: *const Wasm) bool { - return switch (unpack(id, wasm)) { - .__zig_error_names, .__zig_error_name_table => 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, - }; - } - - pub fn size(id: Id, wasm: *const Wasm) u32 { - return switch (unpack(id, wasm)) { - .__zig_error_names => @intCast(wasm.error_name_bytes.items.len), - .__zig_error_name_table => { - const comp = wasm.base.comp; - const zcu = comp.zcu.?; - const errors_len = wasm.error_name_offs.items.len; - const elem_size = ZcuType.slice_const_u8_sentinel_0.abiSize(zcu); - return @intCast(errors_len * elem_size); - }, - .object => |i| i.ptr(wasm).payload.len, - inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.len, - }; - } + object: ObjectDataSegment.Index, + uav_exe: UavsExeIndex, + uav_obj: UavsObjIndex, + nav_exe: NavsExeIndex, + nav_obj: NavsObjIndex, }; -}; -/// Index into `Wasm.object_data_segments`. -pub const ObjectDataSegmentIndex = enum(u32) { - _, + pub fn pack(wasm: *const Wasm, unpacked: Unpacked) DataId { + return switch (unpacked) { + .__zig_error_names => .__zig_error_names, + .__zig_error_name_table => .__zig_error_name_table, + .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)), + .nav_obj => |i| @enumFromInt(first_object + wasm.object_data_segments.items.len + wasm.uavs_obj.entries.len + @intFromEnum(i)), + }; + } - pub fn ptr(i: ObjectDataSegmentIndex, wasm: *const Wasm) *DataSegment { - return &wasm.object_data_segments.items[@intFromEnum(i)]; + pub fn unpack(id: DataId, wasm: *const Wasm) Unpacked { + return switch (id) { + .__zig_error_names => .__zig_error_names, + .__zig_error_name_table => .__zig_error_name_table, + _ => { + const object_index = @intFromEnum(id) - first_object; + + const uav_index = if (object_index < wasm.object_data_segments.items.len) + return .{ .object = @enumFromInt(object_index) } + else + object_index - wasm.object_data_segments.items.len; + + const comp = wasm.base.comp; + const is_obj = comp.config.output_mode == .Obj; + if (is_obj) { + const nav_index = if (uav_index < wasm.uavs_obj.entries.len) + return .{ .uav_obj = @enumFromInt(uav_index) } + else + uav_index - wasm.uavs_obj.entries.len; + + return .{ .nav_obj = @enumFromInt(nav_index) }; + } else { + const nav_index = if (uav_index < wasm.uavs_exe.entries.len) + return .{ .uav_exe = @enumFromInt(uav_index) } + else + uav_index - wasm.uavs_exe.entries.len; + + return .{ .nav_exe = @enumFromInt(nav_index) }; + } + }, + }; + } + + pub fn category(id: DataId, wasm: *const Wasm) Category { + return switch (unpack(id, wasm)) { + .__zig_error_names, .__zig_error_name_table => .data, + .object => |i| { + const ptr = i.ptr(wasm); + if (ptr.flags.tls) return .tls; + if (wasm.isBss(ptr.name)) return .zero; + return .data; + }, + inline .uav_exe, .uav_obj => |i| if (i.value(wasm).code.off == .none) .zero else .data, + inline .nav_exe, .nav_obj => |i| { + const zcu = wasm.base.comp.zcu.?; + const ip = &zcu.intern_pool; + const nav = ip.getNav(i.key(wasm).*); + if (nav.isThreadLocal(ip)) return .tls; + const code = i.value(wasm).code; + return if (code.off == .none) .zero else .data; + }, + }; + } + + pub fn isTls(id: DataId, wasm: *const Wasm) bool { + return switch (unpack(id, wasm)) { + .__zig_error_names, .__zig_error_name_table => false, + .object => |i| i.ptr(wasm).flags.tls, + .uav_exe, .uav_obj => false, + inline .nav_exe, .nav_obj => |i| { + const zcu = wasm.base.comp.zcu.?; + const ip = &zcu.intern_pool; + const nav = ip.getNav(i.key(wasm).*); + return nav.isThreadLocal(ip); + }, + }; + } + + pub fn isBss(id: DataId, wasm: *const Wasm) bool { + return id.category(wasm) == .zero; + } + + 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", + .object => |i| i.ptr(wasm).name.unwrap().?.slice(wasm), + inline .nav_exe, .nav_obj => |i| { + const zcu = wasm.base.comp.zcu.?; + const ip = &zcu.intern_pool; + const nav = ip.getNav(i.key(wasm).*); + return nav.status.resolved.@"linksection".toSlice(ip) orelse ".data"; + }, + }; + } + + pub fn alignment(id: DataId, wasm: *const Wasm) Alignment { + return switch (unpack(id, wasm)) { + .__zig_error_names => .@"1", + .__zig_error_name_table => wasm.pointerAlignment(), + .object => |i| i.ptr(wasm).flags.alignment, + inline .uav_exe, .uav_obj => |i| { + const zcu = wasm.base.comp.zcu.?; + const ip = &zcu.intern_pool; + const ip_index = i.key(wasm).*; + const ty: ZcuType = .fromInterned(ip.typeOf(ip_index)); + const result = ty.abiAlignment(zcu); + assert(result != .none); + return result; + }, + inline .nav_exe, .nav_obj => |i| { + const zcu = wasm.base.comp.zcu.?; + const ip = &zcu.intern_pool; + const nav = ip.getNav(i.key(wasm).*); + const explicit = nav.status.resolved.alignment; + if (explicit != .none) return explicit; + const ty: ZcuType = .fromInterned(nav.typeOf(ip)); + const result = ty.abiAlignment(zcu); + assert(result != .none); + return result; + }, + }; + } + + pub fn refCount(id: DataId, wasm: *const Wasm) 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, + inline .uav_exe, .nav_exe => |i| i.value(wasm).count, + }; + } + + pub fn isPassive(id: DataId, wasm: *const Wasm) bool { + 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, + .object => |i| i.ptr(wasm).flags.is_passive, + .uav_exe, .uav_obj, .nav_exe, .nav_obj => false, + }; + } + + pub fn isEmpty(id: DataId, wasm: *const Wasm) bool { + return switch (unpack(id, wasm)) { + .__zig_error_names, .__zig_error_name_table => 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, + }; + } + + pub fn size(id: DataId, wasm: *const Wasm) u32 { + return switch (unpack(id, wasm)) { + .__zig_error_names => @intCast(wasm.error_name_bytes.items.len), + .__zig_error_name_table => { + const comp = wasm.base.comp; + const zcu = comp.zcu.?; + const errors_len = wasm.error_name_offs.items.len; + const elem_size = ZcuType.slice_const_u8_sentinel_0.abiSize(zcu); + return @intCast(errors_len * elem_size); + }, + .object => |i| i.ptr(wasm).payload.len, + inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.len, + }; } }; @@ -1565,7 +1626,7 @@ pub const CustomSegment = extern struct { flags: SymbolFlags, section_name: String, - pub const Payload = DataSegment.Payload; + pub const Payload = DataPayload; }; /// An index into string_bytes where a wasm expression is found. @@ -1591,9 +1652,13 @@ pub const FunctionType = extern struct { pub const Index = enum(u32) { _, - pub fn ptr(i: FunctionType.Index, wasm: *const Wasm) *FunctionType { + pub fn ptr(i: Index, wasm: *const Wasm) *FunctionType { return &wasm.func_types.keys()[@intFromEnum(i)]; } + + pub fn fmt(i: Index, wasm: *const Wasm) Formatter { + return i.ptr(wasm).fmt(wasm); + } }; pub const format = @compileError("can't format without *Wasm reference"); @@ -1601,6 +1666,46 @@ pub const FunctionType = extern struct { pub fn eql(a: FunctionType, b: FunctionType) bool { return a.params == b.params and a.returns == b.returns; } + + pub fn fmt(ft: FunctionType, wasm: *const Wasm) Formatter { + return .{ .wasm = wasm, .ft = ft }; + } + + const Formatter = struct { + wasm: *const Wasm, + ft: FunctionType, + + pub fn format( + self: Formatter, + comptime format_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + if (format_string.len != 0) std.fmt.invalidFmtError(format_string, self); + _ = options; + const params = self.ft.params.slice(self.wasm); + const returns = self.ft.returns.slice(self.wasm); + + try writer.writeByte('('); + for (params, 0..) |param, i| { + try writer.print("{s}", .{@tagName(param)}); + if (i + 1 != params.len) { + try writer.writeAll(", "); + } + } + try writer.writeAll(") -> "); + if (returns.len == 0) { + try writer.writeAll("nil"); + } else { + for (returns, 0..) |return_ty, i| { + try writer.print("{s}", .{@tagName(return_ty)}); + if (i + 1 != returns.len) { + try writer.writeAll(", "); + } + } + } + } + }; }; /// Represents a function entry, holding the index to its type @@ -1955,7 +2060,7 @@ pub const ObjectRelocation = struct { symbol_name: String, type_index: FunctionType.Index, section: ObjectSectionIndex, - data_segment: ObjectDataSegmentIndex, + data: ObjectData.Index, function: Wasm.ObjectFunctionIndex, }; @@ -2096,6 +2201,8 @@ pub const Feature = packed struct(u8) { /// Type of the feature, must be unique in the sequence of features. tag: Tag, + pub const sentinel: Feature = @bitCast(@as(u8, 0)); + /// Stored identically to `String`. The bytes are reinterpreted as `Feature` /// elements. Elements must be sorted before string-interning. pub const Set = enum(u32) { @@ -2104,6 +2211,14 @@ pub const Feature = packed struct(u8) { pub fn fromString(s: String) Set { return @enumFromInt(@intFromEnum(s)); } + + pub fn string(s: Set) String { + return @enumFromInt(@intFromEnum(s)); + } + + pub fn slice(s: Set, wasm: *const Wasm) [:sentinel]const Feature { + return @ptrCast(string(s).slice(wasm)); + } }; /// Unlike `std.Target.wasm.Feature` this also contains linker-features such as shared-mem. @@ -2129,6 +2244,13 @@ pub const Feature = packed struct(u8) { return @enumFromInt(@intFromEnum(feature)); } + pub fn toCpuFeature(tag: Tag) ?std.Target.wasm.Feature { + return if (@intFromEnum(tag) < @typeInfo(std.Target.wasm.Feature).@"enum".fields.len) + @enumFromInt(@intFromEnum(tag)) + else + null; + } + pub const format = @compileError("use @tagName instead"); }; @@ -2136,15 +2258,14 @@ pub const Feature = packed struct(u8) { pub const Prefix = enum(u2) { /// Reserved so that a 0-byte Feature is invalid and therefore can be a sentinel. invalid, - /// '0x2b': Object uses this feature, and the link fails if feature is - /// not in the allowed set. + /// Object uses this feature, and the link fails if feature is not in + /// the allowed set. @"+", - /// '0x2d': Object does not use this feature, and the link fails if - /// this feature is in the allowed set. + /// Object does not use this feature, and the link fails if this + /// feature is in the allowed set. @"-", - /// '0x3d': Object uses this feature, and the link fails if this - /// feature is not in the allowed set, or if any object does not use - /// this feature. + /// Object uses this feature, and the link fails if this feature is not + /// in the allowed set, or if any object does not use this feature. @"=", }; @@ -2390,6 +2511,7 @@ pub fn deinit(wasm: *Wasm) void { wasm.object_memories.deinit(gpa); wasm.object_relocations.deinit(gpa); wasm.object_data_segments.deinit(gpa); + wasm.object_datas.deinit(gpa); wasm.object_custom_segments.deinit(gpa); wasm.object_init_funcs.deinit(gpa); wasm.object_comdats.deinit(gpa); @@ -3412,7 +3534,7 @@ pub fn addExpr(wasm: *Wasm, bytes: []const u8) Allocator.Error!Expr { return @enumFromInt(wasm.string_bytes.items.len - bytes.len); } -pub fn addRelocatableDataPayload(wasm: *Wasm, bytes: []const u8) Allocator.Error!DataSegment.Payload { +pub fn addRelocatableDataPayload(wasm: *Wasm, bytes: []const u8) Allocator.Error!DataPayload { const gpa = wasm.base.comp.gpa; try wasm.string_bytes.appendSlice(gpa, bytes); return .{ @@ -3546,7 +3668,7 @@ pub fn uavAddr(wasm: *Wasm, uav_index: UavsExeIndex) u32 { assert(wasm.flush_buffer.memory_layout_finished); const comp = wasm.base.comp; assert(comp.config.output_mode != .Obj); - const ds_id: DataSegment.Id = .pack(wasm, .{ .uav_exe = uav_index }); + const ds_id: DataId = .pack(wasm, .{ .uav_exe = uav_index }); return wasm.flush_buffer.data_segments.get(ds_id).?; } @@ -3557,7 +3679,7 @@ pub fn navAddr(wasm: *Wasm, nav_index: InternPool.Nav.Index) u32 { assert(comp.config.output_mode != .Obj); const navs_exe_index: NavsExeIndex = @enumFromInt(wasm.navs_exe.getIndex(nav_index).?); log.debug("navAddr {s} {}", .{ navs_exe_index.name(wasm), nav_index }); - const ds_id: DataSegment.Id = .pack(wasm, .{ .nav_exe = navs_exe_index }); + const ds_id: DataId = .pack(wasm, .{ .nav_exe = navs_exe_index }); return wasm.flush_buffer.data_segments.get(ds_id).?; } @@ -3646,14 +3768,14 @@ fn lowerZcuData(wasm: *Wasm, pt: Zcu.PerThread, ip_index: InternPool.Index) !Zcu const relocs_len: u32 = @intCast(wasm.out_relocs.len - relocs_start); wasm.string_bytes_lock.unlock(); - const naive_code: DataSegment.Payload = .{ + const naive_code: DataPayload = .{ .off = @enumFromInt(code_start), .len = code_len, }; // Only nonzero init values need to take up space in the output. const all_zeroes = std.mem.allEqual(u8, naive_code.slice(wasm), 0); - const code: DataSegment.Payload = if (!all_zeroes) naive_code else c: { + const code: DataPayload = if (!all_zeroes) naive_code else c: { wasm.string_bytes.shrinkRetainingCapacity(code_start); // Indicate empty by making off and len the same value, however, still // transmit the data size by using the size as that value. diff --git a/src/link/Wasm/Flush.zig b/src/link/Wasm/Flush.zig index 939174b6cde3dea3fed0ff0443ebb0764cc1bab7..f4a9ffeb738b4ad8578f93af246e4d44b23f5d4d 100644 --- a/src/link/Wasm/Flush.zig +++ b/src/link/Wasm/Flush.zig @@ -22,7 +22,7 @@ const assert = std.debug.assert; /// Ordered list of data segments that will appear in the final binary. /// When sorted, to-be-merged segments will be made adjacent. /// Values are virtual address. -data_segments: std.AutoArrayHashMapUnmanaged(Wasm.DataSegment.Id, u32) = .empty, +data_segments: std.AutoArrayHashMapUnmanaged(Wasm.DataId, u32) = .empty, /// Each time a `data_segment` offset equals zero it indicates a new group, and /// the next element in this array will contain the total merged segment size. /// Value is the virtual memory address of the end of the segment. @@ -120,7 +120,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { if (wasm.entry_resolution == .unresolved) { var err = try diags.addErrorWithNotes(1); try err.addMsg("entry symbol '{s}' missing", .{name.slice(wasm)}); - try err.addNote("'-fno-entry' suppresses this error", .{}); + err.addNote("'-fno-entry' suppresses this error", .{}); } } } @@ -176,10 +176,10 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { }), @as(u32, undefined)); for (wasm.object_data_segments.items, 0..) |*ds, i| { if (!ds.flags.alive) continue; - const data_segment_index: Wasm.ObjectDataSegmentIndex = @enumFromInt(i); + 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 = data_segment_index, + .object = obj_seg_index, }), @as(u32, undefined)); } if (wasm.error_name_table_ref_count > 0) { @@ -229,7 +229,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { // For the purposes of sorting, they are implicitly all named ".data". const Sort = struct { wasm: *const Wasm, - segments: []const Wasm.DataSegment.Id, + segments: []const Wasm.DataId, pub fn lessThan(ctx: @This(), lhs: usize, rhs: usize) bool { const lhs_segment = ctx.segments[lhs]; const rhs_segment = ctx.segments[rhs]; @@ -312,7 +312,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { const data_vaddr: u32 = @intCast(memory_ptr); { var seen_tls: enum { before, during, after } = .before; - var category: Wasm.DataSegment.Category = undefined; + var category: Wasm.DataId.Category = undefined; for (segment_ids, segment_vaddrs, 0..) |segment_id, *segment_vaddr, i| { const alignment = segment_id.alignment(wasm); category = segment_id.category(wasm); @@ -707,7 +707,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { if (!is_obj) { for (wasm.uav_fixups.items) |uav_fixup| { - const ds_id: Wasm.DataSegment.Id = .pack(wasm, .{ .uav_exe = uav_fixup.uavs_exe_index }); + const ds_id: Wasm.DataId = .pack(wasm, .{ .uav_exe = uav_fixup.uavs_exe_index }); const vaddr = f.data_segments.get(ds_id).?; if (!is64) { mem.writeInt(u32, wasm.string_bytes.items[uav_fixup.offset..][0..4], vaddr, .little); @@ -716,7 +716,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { } } for (wasm.nav_fixups.items) |nav_fixup| { - const ds_id: Wasm.DataSegment.Id = .pack(wasm, .{ .nav_exe = nav_fixup.navs_exe_index }); + const ds_id: Wasm.DataId = .pack(wasm, .{ .nav_exe = nav_fixup.navs_exe_index }); const vaddr = f.data_segments.get(ds_id).?; if (!is64) { mem.writeInt(u32, wasm.string_bytes.items[nav_fixup.offset..][0..4], vaddr, .little); @@ -862,7 +862,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { fn emitNameSection( wasm: *Wasm, - data_segments: *const std.AutoArrayHashMapUnmanaged(Wasm.DataSegment.Id, u32), + data_segments: *const std.AutoArrayHashMapUnmanaged(Wasm.DataId, u32), binary_bytes: *std.ArrayListUnmanaged(u8), ) !void { const f = &wasm.flush_buffer; @@ -1137,9 +1137,9 @@ fn splitSegmentName(name: []const u8) struct { []const u8, []const u8 } { fn wantSegmentMerge( wasm: *const Wasm, - a_id: Wasm.DataSegment.Id, - b_id: Wasm.DataSegment.Id, - b_category: Wasm.DataSegment.Category, + a_id: Wasm.DataId, + b_id: Wasm.DataId, + b_category: Wasm.DataId.Category, ) bool { const a_category = a_id.category(wasm); if (a_category != b_category) return false; diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index ac96956bb487df92bf1c684be36892ba205881bc..75ec5cc3e0c2149264d4dbff75144babbc8087ff 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -102,11 +102,7 @@ pub const Symbol = struct { const Pointee = union(enum) { function: Wasm.ObjectFunctionIndex, function_import: ScratchSpace.FuncImportIndex, - data: struct { - segment_index: Wasm.ObjectDataSegmentIndex, - segment_offset: u32, - size: u32, - }, + data: Wasm.ObjectData.Index, data_import: void, global: Wasm.ObjectGlobalIndex, global_import: Wasm.GlobalImport.Index, @@ -131,7 +127,7 @@ pub const ScratchSpace = struct { const Pointee = union(std.wasm.ExternalKind) { function: Wasm.ObjectFunctionIndex, table: Wasm.ObjectTableIndex, - memory: Wasm.ObjectMemoryIndex, + memory: Wasm.ObjectMemory.Index, global: Wasm.ObjectGlobalIndex, }; }; @@ -184,8 +180,9 @@ pub fn parse( must_link: bool, gc_sections: bool, ) anyerror!Object { - const gpa = wasm.base.comp.gpa; - const diags = &wasm.base.comp.link_diags; + const comp = wasm.base.comp; + const gpa = comp.gpa; + const diags = &comp.link_diags; var pos: usize = 0; @@ -334,12 +331,16 @@ pub fn parse( const segment_index, pos = readLeb(u32, bytes, pos); const segment_offset, pos = readLeb(u32, bytes, pos); const size, pos = readLeb(u32, bytes, pos); - - symbol.pointee = .{ .data = .{ - .segment_index = @enumFromInt(data_segment_start + segment_index), - .segment_offset = segment_offset, + try wasm.object_datas.append(gpa, .{ + .segment = @enumFromInt(data_segment_start + segment_index), + .offset = segment_offset, .size = size, - } }; + .name = symbol.name, + .flags = symbol.flags, + }); + symbol.pointee = .{ + .data = @enumFromInt(wasm.object_datas.items.len - 1), + }; } }, .section => { @@ -405,7 +406,6 @@ pub fn parse( return error.UnrecognizedSymbolType; }, } - log.debug("found symbol: {}", .{symbol}); } }, } @@ -450,22 +450,10 @@ pub fn parse( .MEMORY_ADDR_TLS_SLEB64, => { const addend: i32, pos = readLeb(i32, bytes, pos); - const sym_section = ss.symbol_table.items[index].pointee.data; - if (sym_section.segment_offset != 0) { - return diags.failParse(path, "data symbol {d} has nonzero offset {d}", .{ - index, sym_section.segment_offset, - }); - } - const seg_size = sym_section.segment_index.ptr(wasm).payload.len; - if (sym_section.size != seg_size) { - return diags.failParse(path, "data symbol {d} has size {d}, inequal to corresponding data segment {d} size {d}", .{ - index, sym_section.size, @intFromEnum(sym_section.segment_index), seg_size, - }); - } wasm.object_relocations.appendAssumeCapacity(.{ .tag = tag, .offset = offset, - .pointee = .{ .data_segment = sym_section.segment_index }, + .pointee = .{ .data = ss.symbol_table.items[index].pointee.data }, .addend = addend, }); }, @@ -651,7 +639,15 @@ pub fn parse( const memories_len, pos = readLeb(u32, bytes, pos); for (try wasm.object_memories.addManyAsSlice(gpa, memories_len)) |*memory| { const limits, pos = readLimits(bytes, pos); - memory.* = .{ .limits = limits }; + memory.* = .{ + .name = .none, + .flags = .{ + .limits_has_max = limits.flags.has_max, + .limits_is_shared = limits.flags.is_shared, + }, + .limits_min = limits.min, + .limits_max = limits.max, + }; } }, .global => { @@ -722,7 +718,6 @@ pub fn parse( } }, .data => { - const start = pos; const count, pos = readLeb(u32, bytes, pos); for (try wasm.object_data_segments.addManyAsSlice(gpa, count)) |*elem| { const flags, pos = readEnum(DataSegmentFlags, bytes, pos); @@ -733,13 +728,10 @@ pub fn parse( //const expr, pos = if (flags != .passive) try readInit(wasm, bytes, pos) else .{ .none, pos }; if (flags != .passive) pos = try skipInit(bytes, pos); const data_len, pos = readLeb(u32, bytes, pos); - const segment_offset: u32 = @intCast(pos - start); const payload = try wasm.addRelocatableDataPayload(bytes[pos..][0..data_len]); pos += data_len; elem.* = .{ .payload = payload, - .segment_offset = segment_offset, - .section_index = section_index, .name = .none, // Populated from symbol table .flags = .{}, // Populated from symbol table and segment_info }; @@ -751,20 +743,56 @@ pub fn parse( } if (!saw_linking_section) return error.MissingLinkingSection; + const target_features = comp.root_mod.resolved_target.result.cpu.features; + if (has_tls) { - const cpu_features = wasm.base.comp.root_mod.resolved_target.result.cpu.features; - if (!std.Target.wasm.featureSetHas(cpu_features, .atomics)) + if (!std.Target.wasm.featureSetHas(target_features, .atomics)) return diags.failParse(path, "object has TLS segment but target CPU feature atomics is disabled", .{}); - if (!std.Target.wasm.featureSetHas(cpu_features, .bulk_memory)) + if (!std.Target.wasm.featureSetHas(target_features, .bulk_memory)) return diags.failParse(path, "object has TLS segment but target CPU feature bulk_memory is disabled", .{}); } const features = opt_features orelse return error.MissingFeatures; - if (true) @panic("iterate features, match against target features"); + for (features.slice(wasm)) |feat| { + log.debug("feature: {s}{s}", .{ @tagName(feat.prefix), @tagName(feat.tag) }); + switch (feat.prefix) { + .invalid => unreachable, + .@"-" => switch (feat.tag) { + .@"shared-mem" => if (comp.config.shared_memory) { + return diags.failParse(path, "object forbids shared-mem but compilation enables it", .{}); + }, + else => { + const f = feat.tag.toCpuFeature().?; + if (std.Target.wasm.featureSetHas(target_features, f)) { + return diags.failParse( + path, + "object forbids {s} but specified target features include {s}", + .{ @tagName(feat.tag), @tagName(f) }, + ); + } + }, + }, + .@"+", .@"=" => switch (feat.tag) { + .@"shared-mem" => if (!comp.config.shared_memory) { + return diags.failParse(path, "object requires shared-mem but compilation disables it", .{}); + }, + else => { + const f = feat.tag.toCpuFeature().?; + if (!std.Target.wasm.featureSetHas(target_features, f)) { + return diags.failParse( + path, + "object requires {s} but specified target features exclude {s}", + .{ @tagName(feat.tag), @tagName(f) }, + ); + } + }, + }, + } + } // Apply function type information. - for (ss.func_types.items, wasm.object_functions.items[functions_start..]) |func_type, *func| { - func.type_index = func_type; + for (ss.func_type_indexes.items, wasm.object_functions.items[functions_start..]) |func_type, *func| { + func.type_index = func_type.ptr(ss).*; } // Apply symbol table information. @@ -782,15 +810,21 @@ pub fn parse( if (gop.value_ptr.type != fn_ty_index) { var err = try diags.addErrorWithNotes(2); try err.addMsg("symbol '{s}' mismatching function signatures", .{name.slice(wasm)}); - try err.addSrcNote(gop.value_ptr.source_location, "imported as {} here", .{gop.value_ptr.type.fmt(wasm)}); - try err.addSrcNote(source_location, "imported as {} here", .{fn_ty_index.fmt(wasm)}); + gop.value_ptr.source_location.addNote(wasm, &err, "imported as {} here", .{ + gop.value_ptr.type.fmt(wasm), + }); + source_location.addNote(wasm, &err, "imported as {} here", .{fn_ty_index.fmt(wasm)}); continue; } - if (gop.value_ptr.module_name != ptr.module_name) { + if (gop.value_ptr.module_name != ptr.module_name.toOptional()) { var err = try diags.addErrorWithNotes(2); try err.addMsg("symbol '{s}' mismatching module names", .{name.slice(wasm)}); - try err.addSrcNote(gop.value_ptr.source_location, "module '{s}' here", .{gop.value_ptr.module_name.slice(wasm)}); - try err.addSrcNote(source_location, "module '{s}' here", .{ptr.module_name.slice(wasm)}); + if (gop.value_ptr.module_name.slice(wasm)) |module_name| { + gop.value_ptr.source_location.addNote(wasm, &err, "module '{s}' here", .{module_name}); + } else { + gop.value_ptr.source_location.addNote(wasm, &err, "no module here", .{}); + } + source_location.addNote(wasm, &err, "module '{s}' here", .{ptr.module_name.slice(wasm)}); continue; } if (symbol.flags.binding == .strong) gop.value_ptr.flags.binding = .strong; @@ -799,7 +833,7 @@ pub fn parse( } else { gop.value_ptr.* = .{ .flags = symbol.flags, - .module_name = ptr.module_name, + .module_name = ptr.module_name.toOptional(), .source_location = source_location, .resolution = .unresolved, .type = fn_ty_index, @@ -808,7 +842,7 @@ pub fn parse( }, .function => |index| { assert(!symbol.flags.undefined); - const ptr = index.ptr(); + const ptr = index.ptr(wasm); ptr.name = symbol.name; ptr.flags = symbol.flags; if (symbol.flags.binding == .local) continue; // No participation in symbol resolution. @@ -818,35 +852,46 @@ pub fn parse( if (gop.value_ptr.type != ptr.type_index) { var err = try diags.addErrorWithNotes(2); try err.addMsg("function signature mismatch: {s}", .{name.slice(wasm)}); - try err.addSrcNote(gop.value_ptr.source_location, "exported as {} here", .{ptr.type_index.fmt(wasm)}); - const word = if (gop.value_ptr.resolution == .none) "imported" else "exported"; - try err.addSrcNote(source_location, "{s} as {} here", .{ word, gop.value_ptr.type.fmt(wasm) }); + gop.value_ptr.source_location.addNote(wasm, &err, "exported as {} here", .{ + ptr.type_index.fmt(wasm), + }); + const word = if (gop.value_ptr.resolution == .unresolved) "imported" else "exported"; + source_location.addNote(wasm, &err, "{s} as {} here", .{ word, gop.value_ptr.type.fmt(wasm) }); continue; } - if (gop.value_ptr.resolution == .none or gop.value_ptr.flags.binding == .weak) { + 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.module_name = host_name; - gop.value_ptr.resolution = .fromObjectFunction(index); + gop.value_ptr.resolution = .fromObjectFunction(wasm, index); continue; } var err = try diags.addErrorWithNotes(2); try err.addMsg("symbol collision: {s}", .{name.slice(wasm)}); - try err.addSrcNote(gop.value_ptr.source_location, "exported as {} here", .{ptr.type_index.fmt(wasm)}); - try err.addSrcNote(source_location, "exported as {} here", .{gop.value_ptr.type.fmt(wasm)}); + gop.value_ptr.source_location.addNote(wasm, &err, "exported as {} here", .{ptr.type_index.fmt(wasm)}); + source_location.addNote(wasm, &err, "exported as {} here", .{gop.value_ptr.type.fmt(wasm)}); continue; } else { gop.value_ptr.* = .{ .flags = symbol.flags, .module_name = host_name, .source_location = source_location, - .resolution = .fromObjectFunction(index), + .resolution = .fromObjectFunction(wasm, index), .type = ptr.type_index, }; } }, - inline .global, .global_import, .table, .table_import => |i| { + inline .global_import, .table_import => |i| { + const ptr = i.value(wasm); + assert(i.key(wasm).toOptional() == symbol.name); // TODO + ptr.flags = symbol.flags; + if (symbol.flags.undefined and symbol.flags.binding == .local) { + const name = i.key(wasm).slice(wasm); + diags.addParseError(path, "local symbol '{s}' references import", .{name}); + } + }, + inline .global, .table => |i| { const ptr = i.ptr(wasm); ptr.name = symbol.name; ptr.flags = symbol.flags; @@ -857,10 +902,11 @@ pub fn parse( }, .section => |i| { // Name is provided by the section directly; symbol table does not have it. - const ptr = i.ptr(wasm); - ptr.flags = symbol.flags; + //const ptr = i.ptr(wasm); + //ptr.flags = symbol.flags; + _ = i; if (symbol.flags.undefined and symbol.flags.binding == .local) { - const name = ptr.name.slice(wasm); + const name = symbol.name.slice(wasm).?; diags.addParseError(path, "local symbol '{s}' references import", .{name}); } }, @@ -868,15 +914,7 @@ pub fn parse( const name = symbol.name.unwrap().?; log.warn("TODO data import '{s}'", .{name.slice(wasm)}); }, - .data => |data| { - const ptr = data.ptr(wasm); - const is_passive = ptr.flags.is_passive; - ptr.name = symbol.name; - ptr.flags = symbol.flags; - ptr.flags.is_passive = is_passive; - ptr.offset = data.segment_offset; - ptr.size = data.size; - }, + .data => continue, // `wasm.object_datas` has already been populated. }; // Apply export section info. This is done after the symbol table above so @@ -957,18 +995,6 @@ pub fn parse( .off = functions_start, .len = @intCast(wasm.object_functions.items.len - functions_start), }, - .globals = .{ - .off = globals_start, - .len = @intCast(wasm.object_globals.items.len - globals_start), - }, - .tables = .{ - .off = tables_start, - .len = @intCast(wasm.object_tables.items.len - tables_start), - }, - .memories = .{ - .off = memories_start, - .len = @intCast(wasm.object_memories.items.len - memories_start), - }, .function_imports = .{ .off = function_imports_start, .len = @intCast(wasm.object_function_imports.entries.len - function_imports_start), @@ -979,7 +1005,7 @@ pub fn parse( }, .table_imports = .{ .off = table_imports_start, - .len = @intCast(wasm.object_table_imports.items.len - table_imports_start), + .len = @intCast(wasm.object_table_imports.entries.len - table_imports_start), }, .init_funcs = .{ .off = init_funcs_start,