wasm linker: fix crashes when parsing compiler_rt

This commit is contained in:
Andrew Kelley 2024-12-23 18:18:54 -08:00
parent 4b9dc2922f
commit 1a4c5837fe
10 changed files with 526 additions and 381 deletions

View file

@ -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;
}
};

View file

@ -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(

View file

@ -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,

View file

@ -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;
};

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
},
}

View file

@ -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.

View file

@ -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;

View file

@ -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,