diff --git a/src/arch/wasm/Emit.zig b/src/arch/wasm/Emit.zig index 3f64f3a2ef..dff38da272 100644 --- a/src/arch/wasm/Emit.zig +++ b/src/arch/wasm/Emit.zig @@ -108,7 +108,7 @@ pub fn lowerToCode(emit: *Emit) Error!void { try wasm.out_relocs.append(gpa, .{ .offset = @intCast(code.items.len), .pointee = .{ .symbol_index = try wasm.errorNameTableSymbolIndex() }, - .tag = if (is_wasm32) .MEMORY_ADDR_LEB else .MEMORY_ADDR_LEB64, + .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64, .addend = 0, }); code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10); @@ -162,7 +162,7 @@ pub fn lowerToCode(emit: *Emit) Error!void { try wasm.out_relocs.append(gpa, .{ .offset = @intCast(code.items.len), .pointee = .{ .symbol_index = try wasm.navSymbolIndex(datas[inst].nav_index) }, - .tag = .FUNCTION_INDEX_LEB, + .tag = .function_index_leb, .addend = 0, }); code.appendNTimesAssumeCapacity(0, 5); @@ -182,7 +182,7 @@ pub fn lowerToCode(emit: *Emit) Error!void { try wasm.out_relocs.append(gpa, .{ .offset = @intCast(code.items.len), .pointee = .{ .type_index = func_ty_index }, - .tag = .TYPE_INDEX_LEB, + .tag = .type_index_leb, .addend = 0, }); code.appendNTimesAssumeCapacity(0, 5); @@ -202,7 +202,7 @@ pub fn lowerToCode(emit: *Emit) Error!void { try wasm.out_relocs.append(gpa, .{ .offset = @intCast(code.items.len), .pointee = .{ .symbol_index = try wasm.tagNameSymbolIndex(datas[inst].ip_index) }, - .tag = .FUNCTION_INDEX_LEB, + .tag = .function_index_leb, .addend = 0, }); code.appendNTimesAssumeCapacity(0, 5); @@ -226,7 +226,7 @@ pub fn lowerToCode(emit: *Emit) Error!void { try wasm.out_relocs.append(gpa, .{ .offset = @intCast(code.items.len), .pointee = .{ .symbol_index = try wasm.symbolNameIndex(symbol_name) }, - .tag = .FUNCTION_INDEX_LEB, + .tag = .function_index_leb, .addend = 0, }); code.appendNTimesAssumeCapacity(0, 5); @@ -245,7 +245,7 @@ pub fn lowerToCode(emit: *Emit) Error!void { try wasm.out_relocs.append(gpa, .{ .offset = @intCast(code.items.len), .pointee = .{ .symbol_index = try wasm.stackPointerSymbolIndex() }, - .tag = .GLOBAL_INDEX_LEB, + .tag = .global_index_leb, .addend = 0, }); code.appendNTimesAssumeCapacity(0, 5); @@ -922,7 +922,7 @@ fn uavRefOffObj(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRef try wasm.out_relocs.append(gpa, .{ .offset = @intCast(code.items.len), .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(data.uav_obj.key(wasm).*) }, - .tag = if (is_wasm32) .MEMORY_ADDR_LEB else .MEMORY_ADDR_LEB64, + .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64, .addend = data.offset, }); code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10); @@ -957,7 +957,7 @@ fn navRefOff(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.NavRefOff try wasm.out_relocs.append(gpa, .{ .offset = @intCast(code.items.len), .pointee = .{ .symbol_index = try wasm.navSymbolIndex(data.nav_index) }, - .tag = if (is_wasm32) .MEMORY_ADDR_LEB else .MEMORY_ADDR_LEB64, + .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64, .addend = data.offset, }); code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10); diff --git a/src/codegen.zig b/src/codegen.zig index a649438bba..86d6c4930a 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -670,7 +670,7 @@ fn lowerUavRef( try wasm.out_relocs.append(gpa, .{ .offset = @intCast(code.items.len), .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(uav.val) }, - .tag = if (ptr_width_bytes == 4) .MEMORY_ADDR_I32 else .MEMORY_ADDR_I64, + .tag = if (ptr_width_bytes == 4) .memory_addr_i32 else .memory_addr_i64, .addend = @intCast(offset), }); } else { @@ -742,7 +742,7 @@ fn lowerNavRef( try wasm.out_relocs.append(gpa, .{ .offset = @intCast(code.items.len), .pointee = .{ .symbol_index = try wasm.navSymbolIndex(nav_index) }, - .tag = if (ptr_width_bytes == 4) .MEMORY_ADDR_I32 else .MEMORY_ADDR_I64, + .tag = if (ptr_width_bytes == 4) .memory_addr_i32 else .memory_addr_i64, .addend = @intCast(offset), }); } else { diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 4dd52aac8d..49df77d6a8 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -93,13 +93,13 @@ func_types: std.AutoArrayHashMapUnmanaged(FunctionType, void) = .empty, /// Local functions may be unnamed. object_function_imports: std.AutoArrayHashMapUnmanaged(String, FunctionImport) = .empty, /// All functions for all objects. -object_functions: std.ArrayListUnmanaged(Function) = .empty, +object_functions: std.ArrayListUnmanaged(ObjectFunction) = .empty, /// Provides a mapping of both imports and provided globals to symbol name. /// Local globals may be unnamed. object_global_imports: std.AutoArrayHashMapUnmanaged(String, GlobalImport) = .empty, /// All globals for all objects. -object_globals: std.ArrayListUnmanaged(Global) = .empty, +object_globals: std.ArrayListUnmanaged(ObjectGlobal) = .empty, /// All table imports for all objects. object_table_imports: std.AutoArrayHashMapUnmanaged(String, TableImport) = .empty, @@ -386,7 +386,7 @@ pub const GlobalIndex = enum(u32) { pub const stack_pointer: GlobalIndex = @enumFromInt(0); /// Same as `stack_pointer` but with a safety assertion. - pub fn stackPointer(wasm: *const Wasm) Global.Index { + pub fn stackPointer(wasm: *const Wasm) ObjectGlobal.Index { const comp = wasm.base.comp; assert(comp.config.output_mode != .Obj); assert(comp.zcu != null); @@ -513,26 +513,19 @@ pub const SymbolFlags = packed struct(u32) { // Above here matches the tooling conventions ABI. - padding1: u5 = 0, + padding1: u13 = 0, /// Zig-specific. Dead things are allowed to be garbage collected. alive: bool = false, - /// Zig-specific. Segments only. Signals that the segment contains only - /// null terminated strings allowing the linker to perform merging. - strings: bool = false, /// Zig-specific. This symbol comes from an object that must be included in /// the final link. must_link: bool = false, - /// Zig-specific. Data segments only. - is_passive: bool = false, - /// Zig-specific. Data segments only. - alignment: Alignment = .none, - /// Zig-specific. Globals only. + /// Zig-specific. global_type: GlobalType4 = .zero, - /// Zig-specific. Tables only. + /// Zig-specific. limits_has_max: bool = false, - /// Zig-specific. Tables only. + /// Zig-specific. limits_is_shared: bool = false, - /// Zig-specific. Tables only. + /// Zig-specific. ref_type: RefType1 = .funcref, pub const Binding = enum(u2) { @@ -554,10 +547,7 @@ pub const SymbolFlags = packed struct(u32) { pub fn initZigSpecific(flags: *SymbolFlags, must_link: bool, no_strip: bool) void { flags.no_strip = no_strip; flags.alive = false; - flags.strings = false; flags.must_link = must_link; - flags.is_passive = false; - flags.alignment = .none; flags.global_type = .zero; flags.limits_has_max = false; flags.limits_is_shared = false; @@ -603,7 +593,7 @@ pub const GlobalType4 = packed struct(u4) { pub const zero: GlobalType4 = @bitCast(@as(u4, 0)); - pub fn to(gt: GlobalType4) Global.Type { + pub fn to(gt: GlobalType4) ObjectGlobal.Type { return .{ .valtype = gt.valtype.to(), .mutable = gt.mutable, @@ -1001,18 +991,24 @@ pub const FunctionImport = extern struct { }; }; -pub const Function = extern struct { +pub const ObjectFunction = extern struct { flags: SymbolFlags, /// `none` if this function has no symbol describing it. name: OptionalString, type_index: FunctionType.Index, code: Code, - /// The offset within the section where the data starts. + /// The offset within the code section where the data starts. offset: u32, - section_index: ObjectSectionIndex, - source_location: SourceLocation, + /// The object file whose code section contains this function. + object_index: ObjectIndex, pub const Code = DataPayload; + + fn relocations(of: *const ObjectFunction, wasm: *const Wasm) ObjectRelocation.IterableSlice { + const code_section_index = of.object_index.ptr(wasm).code_section_index.?; + const relocs = wasm.object_relocations_table.get(code_section_index) orelse return .empty; + return .init(relocs, of.offset, of.code.len, wasm); + } }; pub const GlobalImport = extern struct { @@ -1101,6 +1097,10 @@ pub const GlobalImport = extern struct { }); } + fn fromObjectGlobal(wasm: *const Wasm, object_global: ObjectGlobalIndex) Resolution { + return pack(wasm, .{ .object_global = object_global }); + } + pub fn name(r: Resolution, wasm: *const Wasm) ?[]const u8 { return switch (unpack(r, wasm)) { .unresolved => unreachable, @@ -1137,22 +1137,32 @@ pub const GlobalImport = extern struct { return index.value(wasm).module_name; } - pub fn globalType(index: Index, wasm: *const Wasm) Global.Type { + pub fn globalType(index: Index, wasm: *const Wasm) ObjectGlobal.Type { return value(index, wasm).flags.global_type.to(); } }; }; -pub const Global = extern struct { +pub const ObjectGlobal = extern struct { /// `none` if this function has no symbol describing it. name: OptionalString, flags: SymbolFlags, expr: Expr, + /// The object file whose global section contains this global. + object_index: ObjectIndex, + offset: u32, + size: u32, pub const Type = struct { valtype: std.wasm.Valtype, mutable: bool, }; + + fn relocations(og: *const ObjectGlobal, wasm: *const Wasm) ObjectRelocation.IterableSlice { + const global_section_index = og.object_index.ptr(wasm).global_section_index.?; + const relocs = wasm.object_relocations_table.get(global_section_index) orelse return .empty; + return .init(relocs, og.offset, og.size, wasm); + } }; pub const RefType1 = enum(u1) { @@ -1205,6 +1215,18 @@ pub const TableImport = extern struct { }; } + fn pack(unpacked: Unpacked) Resolution { + return switch (unpacked) { + .unresolved => .unresolved, + .__indirect_function_table => .__indirect_function_table, + .object_table => |i| @enumFromInt(first_object_table + @intFromEnum(i)), + }; + } + + fn fromObjectTable(object_table: ObjectTableIndex) Resolution { + return pack(.{ .object_table = object_table }); + } + pub fn refType(r: Resolution, wasm: *const Wasm) std.wasm.RefType { return switch (unpack(r)) { .unresolved => unreachable, @@ -1298,7 +1320,7 @@ pub const ObjectTableIndex = enum(u32) { pub const ObjectGlobalIndex = enum(u32) { _, - pub fn ptr(index: ObjectGlobalIndex, wasm: *const Wasm) *Global { + pub fn ptr(index: ObjectGlobalIndex, wasm: *const Wasm) *ObjectGlobal { return &wasm.object_globals.items[@intFromEnum(index)]; } @@ -1338,7 +1360,7 @@ pub const ObjectMemory = extern struct { pub const ObjectFunctionIndex = enum(u32) { _, - pub fn ptr(index: ObjectFunctionIndex, wasm: *const Wasm) *Function { + pub fn ptr(index: ObjectFunctionIndex, wasm: *const Wasm) *ObjectFunction { return &wasm.object_functions.items[@intFromEnum(index)]; } @@ -1363,8 +1385,28 @@ pub const OptionalObjectFunctionIndex = enum(u32) { pub const ObjectDataSegment = extern struct { /// `none` if segment info custom subsection is missing. name: OptionalString, - flags: SymbolFlags, + flags: Flags, payload: DataPayload, + offset: u32, + object_index: ObjectIndex, + + pub const Flags = packed struct(u32) { + alive: bool = false, + is_passive: bool = false, + alignment: Alignment = .none, + /// Signals that the segment contains only null terminated strings allowing + /// the linker to perform merging. + strings: bool = false, + /// The segment contains thread-local data. This means that a unique copy + /// of this segment will be created for each thread. + tls: bool = false, + /// If the object file is included in the final link, the segment should be + /// retained in the final output regardless of whether it is used by the + /// program. + retain: bool = false, + + _: u21 = 0, + }; /// Index into `Wasm.object_data_segments`. pub const Index = enum(u32) { @@ -1374,6 +1416,12 @@ pub const ObjectDataSegment = extern struct { return &wasm.object_data_segments.items[@intFromEnum(i)]; } }; + + fn relocations(ods: *const ObjectDataSegment, wasm: *const Wasm) ObjectRelocation.IterableSlice { + const data_section_index = ods.object_index.ptr(wasm).data_section_index.?; + const relocs = wasm.object_relocations_table.get(data_section_index) orelse return .empty; + return .init(relocs, ods.offset, ods.payload.len, wasm); + } }; /// A local or exported global const from an object file. @@ -1383,8 +1431,7 @@ pub const ObjectData = extern struct { offset: u32, /// May be zero. `offset + size` must be <= the segment's size. size: u32, - /// `none` if no symbol describes it. - name: OptionalString, + name: String, flags: SymbolFlags, /// Index into `Wasm.object_datas`. @@ -1845,7 +1892,7 @@ pub const ZcuImportIndex = enum(u32) { return getExistingFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target).?; } - pub fn globalType(index: ZcuImportIndex, wasm: *const Wasm) Global.Type { + pub fn globalType(index: ZcuImportIndex, wasm: *const Wasm) ObjectGlobal.Type { _ = index; _ = wasm; unreachable; // Zig has no way to create Wasm globals yet. @@ -1997,7 +2044,7 @@ pub const GlobalImportId = enum(u32) { }; } - pub fn globalType(id: GlobalImportId, wasm: *Wasm) Global.Type { + pub fn globalType(id: GlobalImportId, wasm: *Wasm) ObjectGlobal.Type { return switch (unpack(id, wasm)) { inline .object_global_import, .zcu_import => |i| i.globalType(wasm), }; @@ -2014,7 +2061,7 @@ pub const SymbolTableIndex = enum(u32) { }; pub const OutReloc = struct { - tag: ObjectRelocation.Tag, + tag: Object.RelocationType, offset: u32, pointee: Pointee, addend: i32, @@ -2041,15 +2088,144 @@ pub const ObjectRelocation = struct { /// When `offset` is zero, its position is immediately after the id and size of the section. offset: u32, pointee: Pointee, - /// Populated only for `MEMORY_ADDR_*`, `FUNCTION_OFFSET_I32` and `SECTION_OFFSET_I32`. + /// Populated only for `memory_addr_*`, `function_offset_i32` and `section_offset_i32`. addend: i32, + pub const Tag = enum(u8) { + // These use `Pointee.function`. + function_index_i32, + function_index_leb, + function_offset_i32, + function_offset_i64, + table_index_i32, + table_index_i64, + table_index_rel_sleb, + table_index_rel_sleb64, + table_index_sleb, + table_index_sleb64, + // These use `Pointee.symbol_name`. + function_import_index_i32, + function_import_index_leb, + function_import_offset_i32, + function_import_offset_i64, + table_import_index_i32, + table_import_index_i64, + table_import_index_rel_sleb, + table_import_index_rel_sleb64, + table_import_index_sleb, + table_import_index_sleb64, + // These use `Pointee.global`. + global_index_i32, + global_index_leb, + // These use `Pointee.symbol_name`. + global_import_index_i32, + global_import_index_leb, + // These use `Pointee.data`. + memory_addr_i32, + memory_addr_i64, + memory_addr_leb, + memory_addr_leb64, + memory_addr_locrel_i32, + memory_addr_rel_sleb, + memory_addr_rel_sleb64, + memory_addr_sleb, + memory_addr_sleb64, + memory_addr_tls_sleb, + memory_addr_tls_sleb64, + // These use `Pointee.symbol_name`. + memory_addr_import_i32, + memory_addr_import_i64, + memory_addr_import_leb, + memory_addr_import_leb64, + memory_addr_import_locrel_i32, + memory_addr_import_rel_sleb, + memory_addr_import_rel_sleb64, + memory_addr_import_sleb, + memory_addr_import_sleb64, + memory_addr_import_tls_sleb, + memory_addr_import_tls_sleb64, + /// Uses `Pointee.section`. + section_offset_i32, + /// Uses `Pointee.table`. + table_number_leb, + /// Uses `Pointee.symbol_name`. + table_import_number_leb, + /// Uses `Pointee.type_index`. + type_index_leb, + + pub fn fromType(t: Object.RelocationType) Tag { + return switch (t) { + .event_index_leb => unreachable, + .function_index_i32 => .function_index_i32, + .function_index_leb => .function_index_leb, + .function_offset_i32 => .function_offset_i32, + .function_offset_i64 => .function_offset_i64, + .global_index_i32 => .global_index_i32, + .global_index_leb => .global_index_leb, + .memory_addr_i32 => .memory_addr_i32, + .memory_addr_i64 => .memory_addr_i64, + .memory_addr_leb => .memory_addr_leb, + .memory_addr_leb64 => .memory_addr_leb64, + .memory_addr_locrel_i32 => .memory_addr_locrel_i32, + .memory_addr_rel_sleb => .memory_addr_rel_sleb, + .memory_addr_rel_sleb64 => .memory_addr_rel_sleb64, + .memory_addr_sleb => .memory_addr_sleb, + .memory_addr_sleb64 => .memory_addr_sleb64, + .memory_addr_tls_sleb => .memory_addr_tls_sleb, + .memory_addr_tls_sleb64 => .memory_addr_tls_sleb64, + .section_offset_i32 => .section_offset_i32, + .table_index_i32 => .table_index_i32, + .table_index_i64 => .table_index_i64, + .table_index_rel_sleb => .table_index_rel_sleb, + .table_index_rel_sleb64 => .table_index_rel_sleb64, + .table_index_sleb => .table_index_sleb, + .table_index_sleb64 => .table_index_sleb64, + .table_number_leb => .table_number_leb, + .type_index_leb => .type_index_leb, + }; + } + + pub fn fromTypeImport(t: Object.RelocationType) Tag { + return switch (t) { + .event_index_leb => unreachable, + .function_index_i32 => .function_import_index_i32, + .function_index_leb => .function_import_index_leb, + .function_offset_i32 => .function_import_offset_i32, + .function_offset_i64 => .function_import_offset_i64, + .global_index_i32 => .global_import_index_i32, + .global_index_leb => .global_import_index_leb, + .memory_addr_i32 => .memory_addr_import_i32, + .memory_addr_i64 => .memory_addr_import_i64, + .memory_addr_leb => .memory_addr_import_leb, + .memory_addr_leb64 => .memory_addr_import_leb64, + .memory_addr_locrel_i32 => .memory_addr_import_locrel_i32, + .memory_addr_rel_sleb => .memory_addr_import_rel_sleb, + .memory_addr_rel_sleb64 => .memory_addr_import_rel_sleb64, + .memory_addr_sleb => .memory_addr_import_sleb, + .memory_addr_sleb64 => .memory_addr_import_sleb64, + .memory_addr_tls_sleb => .memory_addr_import_tls_sleb, + .memory_addr_tls_sleb64 => .memory_addr_import_tls_sleb64, + .section_offset_i32 => unreachable, + .table_index_i32 => .table_import_index_i32, + .table_index_i64 => .table_import_index_i64, + .table_index_rel_sleb => .table_import_index_rel_sleb, + .table_index_rel_sleb64 => .table_import_index_rel_sleb64, + .table_index_sleb => .table_import_index_sleb, + .table_index_sleb64 => .table_import_index_sleb64, + .table_number_leb => .table_import_number_leb, + .type_index_leb => unreachable, + }; + } + }; + pub const Pointee = union { symbol_name: String, + data: ObjectData.Index, type_index: FunctionType.Index, section: ObjectSectionIndex, - data: ObjectData.Index, - function: Wasm.ObjectFunctionIndex, + function: ObjectFunctionIndex, + global: ObjectGlobalIndex, + table: ObjectTableIndex, }; pub const Slice = extern struct { @@ -2057,63 +2233,47 @@ pub const ObjectRelocation = struct { off: u32, len: u32, - pub fn slice(s: Slice, wasm: *const Wasm) []ObjectRelocation { - return wasm.relocations.items[s.off..][0..s.len]; + const empty: Slice = .{ .off = 0, .len = 0 }; + + fn tags(s: Slice, wasm: *const Wasm) []const ObjectRelocation.Tag { + return wasm.object_relocations.items(.tag)[s.off..][0..s.len]; + } + + fn offsets(s: Slice, wasm: *const Wasm) []const u32 { + return wasm.object_relocations.items(.offset)[s.off..][0..s.len]; + } + + fn pointees(s: Slice, wasm: *const Wasm) []const Pointee { + return wasm.object_relocations.items(.pointee)[s.off..][0..s.len]; + } + + fn addends(s: Slice, wasm: *const Wasm) []const i32 { + return wasm.object_relocations.items(.addend)[s.off..][0..s.len]; } }; - pub const Tag = enum(u8) { - /// Uses `function`. - FUNCTION_INDEX_LEB = 0, - /// Uses `table_index`. - TABLE_INDEX_SLEB = 1, - /// Uses `table_index`. - TABLE_INDEX_I32 = 2, - /// Uses `data_segment`. - MEMORY_ADDR_LEB = 3, - /// Uses `data_segment`. - MEMORY_ADDR_SLEB = 4, - /// Uses `data_segment`. - MEMORY_ADDR_I32 = 5, - /// Uses `type_index`. - TYPE_INDEX_LEB = 6, - /// Uses `symbol_name`. - GLOBAL_INDEX_LEB = 7, - FUNCTION_OFFSET_I32 = 8, - SECTION_OFFSET_I32 = 9, - TAG_INDEX_LEB = 10, - /// Uses `data_segment`. - MEMORY_ADDR_REL_SLEB = 11, - TABLE_INDEX_REL_SLEB = 12, - /// Uses `symbol_name`. - GLOBAL_INDEX_I32 = 13, - /// Uses `data_segment`. - MEMORY_ADDR_LEB64 = 14, - /// Uses `data_segment`. - MEMORY_ADDR_SLEB64 = 15, - /// Uses `data_segment`. - MEMORY_ADDR_I64 = 16, - /// Uses `data_segment`. - MEMORY_ADDR_REL_SLEB64 = 17, - /// Uses `table_index`. - TABLE_INDEX_SLEB64 = 18, - /// Uses `table_index`. - TABLE_INDEX_I64 = 19, - TABLE_NUMBER_LEB = 20, - /// Uses `data_segment`. - MEMORY_ADDR_TLS_SLEB = 21, - FUNCTION_OFFSET_I64 = 22, - /// Uses `data_segment`. - MEMORY_ADDR_LOCREL_I32 = 23, - TABLE_INDEX_REL_SLEB64 = 24, - /// Uses `data_segment`. - MEMORY_ADDR_TLS_SLEB64 = 25, - /// Uses `symbol_name`. - FUNCTION_INDEX_I32 = 26, + pub const IterableSlice = struct { + slice: Slice, + /// Offset at which point to stop iterating. + end: u32, - // Above here, the tags correspond to symbol table ABI described in - // https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md - // Below, the tags are compiler-internal. + const empty: IterableSlice = .{ .slice = .empty, .end = 0 }; + + fn init(relocs: Slice, offset: u32, size: u32, wasm: *const Wasm) IterableSlice { + const offsets = relocs.offsets(wasm); + const start = std.sort.lowerBound(u32, offsets, offset, order); + return .{ + .slice = .{ + .off = @intCast(relocs.off + start), + .len = @intCast(relocs.len - start), + }, + .end = offset + size, + }; + } + + fn order(lhs: u32, rhs: u32) std.math.Order { + return std.math.order(lhs, rhs); + } }; }; @@ -2781,7 +2941,7 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v // At the end, output functions and globals will be populated. for (wasm.object_function_imports.keys(), wasm.object_function_imports.values(), 0..) |name, *import, i| { if (import.flags.isIncluded(rdynamic)) { - try markFunction(wasm, name, import, @enumFromInt(i)); + try markFunctionImport(wasm, name, import, @enumFromInt(i)); } } wasm.functions_end_prelink = @intCast(wasm.functions.entries.len); @@ -2789,7 +2949,7 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v for (wasm.object_global_imports.keys(), wasm.object_global_imports.values(), 0..) |name, *import, i| { if (import.flags.isIncluded(rdynamic)) { - try markGlobal(wasm, name, import, @enumFromInt(i)); + try markGlobalImport(wasm, name, import, @enumFromInt(i)); } } wasm.globals_end_prelink = @intCast(wasm.globals.entries.len); @@ -2797,13 +2957,12 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v for (wasm.object_table_imports.keys(), wasm.object_table_imports.values(), 0..) |name, *import, i| { if (import.flags.isIncluded(rdynamic)) { - try markTable(wasm, name, import, @enumFromInt(i)); + try markTableImport(wasm, name, import, @enumFromInt(i)); } } } -/// Recursively mark alive everything referenced by the function. -fn markFunction( +fn markFunctionImport( wasm: *Wasm, name: String, import: *FunctionImport, @@ -2814,8 +2973,6 @@ fn markFunction( const comp = wasm.base.comp; const gpa = comp.gpa; - const rdynamic = comp.config.rdynamic; - const is_obj = comp.config.output_mode == .Obj; try wasm.functions.ensureUnusedCapacity(gpa, 1); @@ -2836,20 +2993,31 @@ fn markFunction( try wasm.function_imports.put(gpa, name, .fromObject(func_index, wasm)); } } else { - const gop = wasm.functions.getOrPutAssumeCapacity(import.resolution); - - if (!is_obj and import.flags.isExported(rdynamic)) try wasm.function_exports.append(gpa, .{ - .name = name, - .function_index = @enumFromInt(gop.index), - }); - - for (try wasm.functionResolutionRelocSlice(import.resolution)) |reloc| - try wasm.markReloc(reloc); + try markFunction(wasm, import.resolution.unpack(wasm).object_function); } } +/// Recursively mark alive everything referenced by the function. +fn markFunction(wasm: *Wasm, i: ObjectFunctionIndex) Allocator.Error!void { + const comp = wasm.base.comp; + const gpa = comp.gpa; + const gop = try wasm.functions.getOrPut(gpa, .fromObjectFunction(wasm, i)); + if (gop.found_existing) return; + + const rdynamic = comp.config.rdynamic; + const is_obj = comp.config.output_mode == .Obj; + const function = i.ptr(wasm); + + if (!is_obj and function.flags.isExported(rdynamic)) try wasm.function_exports.append(gpa, .{ + .name = function.name.unwrap().?, + .function_index = @enumFromInt(gop.index), + }); + + try wasm.markRelocations(function.relocations(wasm)); +} + /// Recursively mark alive everything referenced by the global. -fn markGlobal( +fn markGlobalImport( wasm: *Wasm, name: String, import: *GlobalImport, @@ -2860,8 +3028,6 @@ fn markGlobal( const comp = wasm.base.comp; const gpa = comp.gpa; - const rdynamic = comp.config.rdynamic; - const is_obj = comp.config.output_mode == .Obj; try wasm.globals.ensureUnusedCapacity(gpa, 1); @@ -2888,19 +3054,29 @@ fn markGlobal( try wasm.global_imports.put(gpa, name, .fromObject(global_index, wasm)); } } else { - const gop = wasm.globals.getOrPutAssumeCapacity(import.resolution); - - if (!is_obj and import.flags.isExported(rdynamic)) try wasm.global_exports.append(gpa, .{ - .name = name, - .global_index = @enumFromInt(gop.index), - }); - - for (try wasm.globalResolutionRelocSlice(import.resolution)) |reloc| - try wasm.markReloc(reloc); + try markGlobal(wasm, import.resolution.unpack(wasm).object_global); } } -fn markTable( +fn markGlobal(wasm: *Wasm, i: ObjectGlobalIndex) Allocator.Error!void { + const comp = wasm.base.comp; + const gpa = comp.gpa; + const gop = try wasm.globals.getOrPut(gpa, .fromObjectGlobal(wasm, i)); + if (gop.found_existing) return; + + const rdynamic = comp.config.rdynamic; + const is_obj = comp.config.output_mode == .Obj; + const global = i.ptr(wasm); + + if (!is_obj and global.flags.isExported(rdynamic)) try wasm.global_exports.append(gpa, .{ + .name = global.name.unwrap().?, + .global_index = @enumFromInt(gop.index), + }); + + try wasm.markRelocations(global.relocations(wasm)); +} + +fn markTableImport( wasm: *Wasm, name: String, import: *TableImport, @@ -2927,22 +3103,101 @@ fn markTable( } } -fn globalResolutionRelocSlice(wasm: *Wasm, resolution: GlobalImport.Resolution) ![]const ObjectRelocation { - assert(resolution != .unresolved); - _ = wasm; - @panic("TODO"); +fn markDataSegment(wasm: *Wasm, segment_index: ObjectDataSegment.Index) Allocator.Error!void { + const segment = segment_index.ptr(wasm); + if (segment.flags.alive) return; + segment.flags.alive = true; + + try wasm.markRelocations(segment.relocations(wasm)); } -fn functionResolutionRelocSlice(wasm: *Wasm, resolution: FunctionImport.Resolution) ![]const ObjectRelocation { - assert(resolution != .unresolved); - _ = wasm; - @panic("TODO"); -} +fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) Allocator.Error!void { + for (relocs.slice.tags(wasm), relocs.slice.pointees(wasm), relocs.slice.offsets(wasm)) |tag, *pointee, offset| { + if (offset >= relocs.end) break; + switch (tag) { + .function_import_index_leb, + .function_import_index_i32, + .function_import_offset_i32, + .function_import_offset_i64, + .table_import_index_sleb, + .table_import_index_i32, + .table_import_index_sleb64, + .table_import_index_i64, + .table_import_index_rel_sleb, + .table_import_index_rel_sleb64, + => { + const name = pointee.symbol_name; + const i: FunctionImport.Index = @enumFromInt(wasm.object_function_imports.getIndex(name).?); + try markFunctionImport(wasm, name, i.value(wasm), i); + }, + .global_import_index_leb, .global_import_index_i32 => { + const name = pointee.symbol_name; + const i: GlobalImport.Index = @enumFromInt(wasm.object_global_imports.getIndex(name).?); + try markGlobalImport(wasm, name, i.value(wasm), i); + }, + .table_import_number_leb => { + const name = pointee.symbol_name; + const i: TableImport.Index = @enumFromInt(wasm.object_table_imports.getIndex(name).?); + try markTableImport(wasm, name, i.value(wasm), i); + }, -fn markReloc(wasm: *Wasm, reloc: ObjectRelocation) !void { - _ = wasm; - _ = reloc; - @panic("TODO"); + .function_index_leb, + .function_index_i32, + .function_offset_i32, + .function_offset_i64, + .table_index_sleb, + .table_index_i32, + .table_index_sleb64, + .table_index_i64, + .table_index_rel_sleb, + .table_index_rel_sleb64, + => try markFunction(wasm, pointee.function), + .global_index_leb, + .global_index_i32, + => try markGlobal(wasm, pointee.global), + .table_number_leb, + => try wasm.tables.put(wasm.base.comp.gpa, .fromObjectTable(pointee.table), {}), + + .section_offset_i32 => { + log.warn("TODO: ensure section {d} is included in output", .{pointee.section}); + }, + .memory_addr_import_leb, + .memory_addr_import_sleb, + .memory_addr_import_i32, + .memory_addr_import_rel_sleb, + .memory_addr_import_leb64, + .memory_addr_import_sleb64, + .memory_addr_import_i64, + .memory_addr_import_rel_sleb64, + .memory_addr_import_tls_sleb, + .memory_addr_import_locrel_i32, + .memory_addr_import_tls_sleb64, + => { + const name = pointee.symbol_name; + if (name == wasm.preloaded_strings.__heap_end or + name == wasm.preloaded_strings.__heap_base) + { + continue; + } + log.warn("TODO: ensure data symbol {s} is included in output", .{name.slice(wasm)}); + }, + + .memory_addr_leb, + .memory_addr_sleb, + .memory_addr_i32, + .memory_addr_rel_sleb, + .memory_addr_leb64, + .memory_addr_sleb64, + .memory_addr_i64, + .memory_addr_rel_sleb64, + .memory_addr_tls_sleb, + .memory_addr_locrel_i32, + .memory_addr_tls_sleb64, + => try markDataSegment(wasm, pointee.data.ptr(wasm).segment), + + .type_index_leb => continue, + } + } } pub fn flushModule( diff --git a/src/link/Wasm/Flush.zig b/src/link/Wasm/Flush.zig index 21a5919010..a2fec91c34 100644 --- a/src/link/Wasm/Flush.zig +++ b/src/link/Wasm/Flush.zig @@ -1398,148 +1398,6 @@ fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void { // try binary_bytes.insertSlice(table_offset, &buf); //} -///// Resolves the relocations within the atom, writing the new value -///// at the calculated offset. -//fn resolveAtomRelocs(wasm: *const Wasm, atom: *Atom) void { -// const symbol_name = wasm.symbolLocName(atom.symbolLoc()); -// log.debug("resolving {d} relocs in atom '{s}'", .{ atom.relocs.len, symbol_name }); -// -// for (atom.relocSlice(wasm)) |reloc| { -// const value = atomRelocationValue(wasm, atom, reloc); -// log.debug("relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{ -// wasm.symbolLocName(.{ -// .file = atom.file, -// .index = @enumFromInt(reloc.index), -// }), -// symbol_name, -// reloc.offset, -// value, -// }); -// -// switch (reloc.tag) { -// .TABLE_INDEX_I32, -// .FUNCTION_OFFSET_I32, -// .GLOBAL_INDEX_I32, -// .MEMORY_ADDR_I32, -// .SECTION_OFFSET_I32, -// => mem.writeInt(u32, atom.code.slice(wasm)[reloc.offset - atom.original_offset ..][0..4], @as(u32, @truncate(value)), .little), -// -// .TABLE_INDEX_I64, -// .MEMORY_ADDR_I64, -// => mem.writeInt(u64, atom.code.slice(wasm)[reloc.offset - atom.original_offset ..][0..8], value, .little), -// -// .GLOBAL_INDEX_LEB, -// .EVENT_INDEX_LEB, -// .FUNCTION_INDEX_LEB, -// .MEMORY_ADDR_LEB, -// .MEMORY_ADDR_SLEB, -// .TABLE_INDEX_SLEB, -// .TABLE_NUMBER_LEB, -// .TYPE_INDEX_LEB, -// .MEMORY_ADDR_TLS_SLEB, -// => leb.writeUnsignedFixed(5, atom.code.slice(wasm)[reloc.offset - atom.original_offset ..][0..5], @as(u32, @truncate(value))), -// -// .MEMORY_ADDR_LEB64, -// .MEMORY_ADDR_SLEB64, -// .TABLE_INDEX_SLEB64, -// .MEMORY_ADDR_TLS_SLEB64, -// => leb.writeUnsignedFixed(10, atom.code.slice(wasm)[reloc.offset - atom.original_offset ..][0..10], value), -// } -// } -//} - -///// From a given `relocation` will return the new value to be written. -///// All values will be represented as a `u64` as all values can fit within it. -///// The final value must be casted to the correct size. -//fn atomRelocationValue(wasm: *const Wasm, atom: *const Atom, relocation: *const Relocation) u64 { -// if (relocation.tag == .TYPE_INDEX_LEB) { -// // Eagerly resolved when parsing the object file. -// if (true) @panic("TODO the eager resolve when parsing"); -// return relocation.index; -// } -// const target_loc = wasm.symbolLocFinalLoc(.{ -// .file = atom.file, -// .index = @enumFromInt(relocation.index), -// }); -// const symbol = wasm.finalSymbolByLoc(target_loc); -// if (symbol.tag != .section and !symbol.flags.alive) { -// const val = atom.tombstone(wasm) orelse relocation.addend; -// return @bitCast(val); -// } -// return switch (relocation.tag) { -// .FUNCTION_INDEX_LEB => if (symbol.flags.undefined) -// @intFromEnum(symbol.pointee.function_import) -// else -// @intFromEnum(symbol.pointee.function) + f.function_imports.items.len, -// .TABLE_NUMBER_LEB => if (symbol.flags.undefined) -// @intFromEnum(symbol.pointee.table_import) -// else -// @intFromEnum(symbol.pointee.table) + wasm.table_imports.items.len, -// .TABLE_INDEX_I32, -// .TABLE_INDEX_I64, -// .TABLE_INDEX_SLEB, -// .TABLE_INDEX_SLEB64, -// => wasm.indirect_function_table.get(.{ .file = atom.file, .index = @enumFromInt(relocation.index) }) orelse 0, -// -// .TYPE_INDEX_LEB => unreachable, // handled above -// .GLOBAL_INDEX_I32, .GLOBAL_INDEX_LEB => if (symbol.flags.undefined) -// @intFromEnum(symbol.pointee.global_import) -// else -// @intFromEnum(symbol.pointee.global) + f.global_imports.items.len, -// -// .MEMORY_ADDR_I32, -// .MEMORY_ADDR_I64, -// .MEMORY_ADDR_LEB, -// .MEMORY_ADDR_LEB64, -// .MEMORY_ADDR_SLEB, -// .MEMORY_ADDR_SLEB64, -// => { -// assert(symbol.tag == .data); -// if (symbol.flags.undefined) return 0; -// const va: i33 = symbol.virtual_address; -// return @intCast(va + relocation.addend); -// }, -// .EVENT_INDEX_LEB => @panic("TODO: expose this as an error, events are unsupported"), -// .SECTION_OFFSET_I32 => { -// const target_atom_index = wasm.symbol_atom.get(target_loc).?; -// const target_atom = wasm.getAtom(target_atom_index); -// const rel_value: i33 = target_atom.offset; -// return @intCast(rel_value + relocation.addend); -// }, -// .FUNCTION_OFFSET_I32 => { -// if (symbol.flags.undefined) { -// const val = atom.tombstone(wasm) orelse relocation.addend; -// return @bitCast(val); -// } -// const target_atom_index = wasm.symbol_atom.get(target_loc).?; -// const target_atom = wasm.getAtom(target_atom_index); -// const rel_value: i33 = target_atom.offset; -// return @intCast(rel_value + relocation.addend); -// }, -// .MEMORY_ADDR_TLS_SLEB, -// .MEMORY_ADDR_TLS_SLEB64, -// => { -// const va: i33 = symbol.virtual_address; -// return @intCast(va + relocation.addend); -// }, -// }; -//} - -///// For a given `Atom` returns whether it has a tombstone value or not. -///// This defines whether we want a specific value when a section is dead. -//fn tombstone(atom: Atom, wasm: *const Wasm) ?i64 { -// const atom_name = wasm.finalSymbolByLoc(atom.symbolLoc()).name; -// if (atom_name == wasm.custom_sections.@".debug_ranges".name or -// atom_name == wasm.custom_sections.@".debug_loc".name) -// { -// return -2; -// } else if (mem.startsWith(u8, atom_name.slice(wasm), ".debug_")) { -// return -1; -// } else { -// return null; -// } -//} - fn uleb128size(x: u32) u32 { var value = x; var size: u32 = 0; diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index 69a467b0de..b42b9c8967 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -36,12 +36,16 @@ global_imports: RelativeSlice, table_imports: RelativeSlice, /// Points into Wasm object_custom_segments custom_segments: RelativeSlice, -/// For calculating local section index from `Wasm.ObjectSectionIndex`. -local_section_index_base: u32, /// Points into Wasm object_init_funcs init_funcs: RelativeSlice, /// Points into Wasm object_comdats comdats: RelativeSlice, +/// Guaranteed to be non-null when functions has nonzero length. +code_section_index: ?Wasm.ObjectSectionIndex, +/// Guaranteed to be non-null when globals has nonzero length. +global_section_index: ?Wasm.ObjectSectionIndex, +/// Guaranteed to be non-null when data segments has nonzero length. +data_section_index: ?Wasm.ObjectSectionIndex, pub const RelativeSlice = struct { off: u32, @@ -52,7 +56,8 @@ pub const SegmentInfo = struct { name: Wasm.String, flags: Flags, - const Flags = packed struct(u32) { + /// Matches the ABI. + pub const Flags = packed struct(u32) { /// Signals that the segment contains only null terminated strings allowing /// the linker to perform merging. strings: bool, @@ -101,6 +106,37 @@ pub const SubsectionType = enum(u8) { symbol_table = 8, }; +/// Specified by https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md +pub const RelocationType = enum(u8) { + function_index_leb = 0, + table_index_sleb = 1, + table_index_i32 = 2, + memory_addr_leb = 3, + memory_addr_sleb = 4, + memory_addr_i32 = 5, + type_index_leb = 6, + global_index_leb = 7, + function_offset_i32 = 8, + section_offset_i32 = 9, + event_index_leb = 10, + memory_addr_rel_sleb = 11, + table_index_rel_sleb = 12, + global_index_i32 = 13, + memory_addr_leb64 = 14, + memory_addr_sleb64 = 15, + memory_addr_i64 = 16, + memory_addr_rel_sleb64 = 17, + table_index_sleb64 = 18, + table_index_i64 = 19, + table_number_leb = 20, + memory_addr_tls_sleb = 21, + function_offset_i64 = 22, + memory_addr_locrel_i32 = 23, + table_index_rel_sleb64 = 24, + memory_addr_tls_sleb64 = 25, + function_index_i32 = 26, +}; + pub const Symbol = struct { flags: Wasm.SymbolFlags, name: Wasm.OptionalString, @@ -245,7 +281,8 @@ pub fn parse( const global_imports_start: u32 = @intCast(wasm.object_global_imports.entries.len); const table_imports_start: u32 = @intCast(wasm.object_table_imports.entries.len); const local_section_index_base = wasm.object_total_sections; - const source_location: Wasm.SourceLocation = .fromObject(@enumFromInt(wasm.objects.items.len), wasm); + const object_index: Wasm.ObjectIndex = @enumFromInt(wasm.objects.items.len); + const source_location: Wasm.SourceLocation = .fromObject(object_index, wasm); ss.clear(); @@ -254,6 +291,9 @@ pub fn parse( var saw_linking_section = false; var has_tls = false; var table_import_symbol_count: usize = 0; + var code_section_index: ?Wasm.ObjectSectionIndex = null; + var global_section_index: ?Wasm.ObjectSectionIndex = null; + var data_section_index: ?Wasm.ObjectSectionIndex = null; while (pos < bytes.len) : (wasm.object_total_sections += 1) { const section_index: Wasm.ObjectSectionIndex = @enumFromInt(wasm.object_total_sections); @@ -365,7 +405,8 @@ pub fn parse( switch (tag) { .data => { const name, pos = readBytes(bytes, pos); - symbol.name = (try wasm.internString(name)).toOptional(); + const interned_name = try wasm.internString(name); + symbol.name = interned_name.toOptional(); if (symbol.flags.undefined) { symbol.pointee = .data_import; } else { @@ -376,7 +417,7 @@ pub fn parse( .segment = @enumFromInt(data_segment_start + segment_index), .offset = segment_offset, .size = size, - .name = symbol.name, + .name = interned_name, .flags = symbol.flags, }); symbol.pointee = .{ @@ -468,7 +509,7 @@ pub fn parse( var prev_offset: u32 = 0; try wasm.object_relocations.ensureUnusedCapacity(gpa, count); for (0..count) |_| { - const tag: Wasm.ObjectRelocation.Tag = @enumFromInt(bytes[pos]); + const tag: RelocationType = @enumFromInt(bytes[pos]); pos += 1; const offset, pos = readLeb(u32, bytes, pos); const index, pos = readLeb(u32, bytes, pos); @@ -477,75 +518,134 @@ pub fn parse( return diags.failParse(path, "relocation entries not sorted by offset", .{}); prev_offset = offset; + const sym = &ss.symbol_table.items[index]; + switch (tag) { - .MEMORY_ADDR_LEB, - .MEMORY_ADDR_SLEB, - .MEMORY_ADDR_I32, - .MEMORY_ADDR_REL_SLEB, - .MEMORY_ADDR_LEB64, - .MEMORY_ADDR_SLEB64, - .MEMORY_ADDR_I64, - .MEMORY_ADDR_REL_SLEB64, - .MEMORY_ADDR_TLS_SLEB, - .MEMORY_ADDR_LOCREL_I32, - .MEMORY_ADDR_TLS_SLEB64, + .memory_addr_leb, + .memory_addr_sleb, + .memory_addr_i32, + .memory_addr_rel_sleb, + .memory_addr_leb64, + .memory_addr_sleb64, + .memory_addr_i64, + .memory_addr_rel_sleb64, + .memory_addr_tls_sleb, + .memory_addr_locrel_i32, + .memory_addr_tls_sleb64, => { const addend: i32, pos = readLeb(i32, bytes, pos); - wasm.object_relocations.appendAssumeCapacity(.{ - .tag = tag, - .offset = offset, - .pointee = .{ .data = ss.symbol_table.items[index].pointee.data }, - .addend = addend, + wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) { + .data => |data| .{ + .tag = .fromType(tag), + .offset = offset, + .pointee = .{ .data = data }, + .addend = addend, + }, + .data_import => .{ + .tag = .fromTypeImport(tag), + .offset = offset, + .pointee = .{ .symbol_name = sym.name.unwrap().? }, + .addend = addend, + }, + else => unreachable, }); }, - .FUNCTION_OFFSET_I32, - .FUNCTION_OFFSET_I64, - => { + .function_offset_i32, .function_offset_i64 => { + const addend: i32, pos = readLeb(i32, bytes, pos); + wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) { + .function => .{ + .tag = .fromType(tag), + .offset = offset, + .pointee = .{ .function = sym.pointee.function }, + .addend = addend, + }, + .function_import => .{ + .tag = .fromTypeImport(tag), + .offset = offset, + .pointee = .{ .symbol_name = sym.name.unwrap().? }, + .addend = addend, + }, + else => unreachable, + }); + }, + .section_offset_i32 => { const addend: i32, pos = readLeb(i32, bytes, pos); wasm.object_relocations.appendAssumeCapacity(.{ - .tag = tag, + .tag = .section_offset_i32, .offset = offset, - .pointee = .{ .function = ss.symbol_table.items[index].pointee.function }, + .pointee = .{ .section = sym.pointee.section }, .addend = addend, }); }, - .SECTION_OFFSET_I32 => { - const addend: i32, pos = readLeb(i32, bytes, pos); + .type_index_leb => { wasm.object_relocations.appendAssumeCapacity(.{ - .tag = tag, - .offset = offset, - .pointee = .{ .section = ss.symbol_table.items[index].pointee.section }, - .addend = addend, - }); - }, - .TYPE_INDEX_LEB => { - wasm.object_relocations.appendAssumeCapacity(.{ - .tag = tag, + .tag = .type_index_leb, .offset = offset, .pointee = .{ .type_index = ss.func_types.items[index] }, .addend = undefined, }); }, - .FUNCTION_INDEX_LEB, - .FUNCTION_INDEX_I32, - .GLOBAL_INDEX_LEB, - .GLOBAL_INDEX_I32, - .TABLE_INDEX_SLEB, - .TABLE_INDEX_I32, - .TABLE_INDEX_SLEB64, - .TABLE_INDEX_I64, - .TABLE_NUMBER_LEB, - .TABLE_INDEX_REL_SLEB, - .TABLE_INDEX_REL_SLEB64, - .TAG_INDEX_LEB, + .function_index_leb, + .function_index_i32, + .table_index_sleb, + .table_index_i32, + .table_index_sleb64, + .table_index_i64, + .table_index_rel_sleb, + .table_index_rel_sleb64, => { - wasm.object_relocations.appendAssumeCapacity(.{ - .tag = tag, - .offset = offset, - .pointee = .{ .symbol_name = ss.symbol_table.items[index].name.unwrap().? }, - .addend = undefined, + wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) { + .function => .{ + .tag = .fromType(tag), + .offset = offset, + .pointee = .{ .function = sym.pointee.function }, + .addend = undefined, + }, + .function_import => .{ + .tag = .fromTypeImport(tag), + .offset = offset, + .pointee = .{ .symbol_name = sym.name.unwrap().? }, + .addend = undefined, + }, + else => unreachable, }); }, + .global_index_leb, .global_index_i32 => { + wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) { + .global => .{ + .tag = .fromType(tag), + .offset = offset, + .pointee = .{ .global = sym.pointee.global }, + .addend = undefined, + }, + .global_import => .{ + .tag = .fromTypeImport(tag), + .offset = offset, + .pointee = .{ .symbol_name = sym.name.unwrap().? }, + .addend = undefined, + }, + else => unreachable, + }); + }, + + .table_number_leb => { + wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) { + .table => .{ + .tag = .fromType(tag), + .offset = offset, + .pointee = .{ .table = sym.pointee.table }, + .addend = undefined, + }, + .table_import => .{ + .tag = .fromTypeImport(tag), + .offset = offset, + .pointee = .{ .symbol_name = sym.name.unwrap().? }, + .addend = undefined, + }, + else => unreachable, + }); + }, + .event_index_leb => return diags.failParse(path, "unsupported relocation: R_WASM_EVENT_INDEX_LEB", .{}), } } @@ -684,11 +784,17 @@ pub fn parse( } }, .global => { + if (global_section_index != null) + return diags.failParse(path, "object has more than one global section", .{}); + global_section_index = section_index; + + const section_start = pos; const globals_len, pos = readLeb(u32, bytes, pos); for (try wasm.object_globals.addManyAsSlice(gpa, globals_len)) |*global| { const valtype, pos = readEnum(std.wasm.Valtype, bytes, pos); const mutable = bytes[pos] == 0x01; pos += 1; + const init_start = pos; const expr, pos = try readInit(wasm, bytes, pos); global.* = .{ .name = .none, @@ -699,6 +805,9 @@ pub fn parse( }, }, .expr = expr, + .object_index = object_index, + .offset = @intCast(init_start - section_start), + .size = @intCast(pos - init_start), }; } }, @@ -728,10 +837,14 @@ pub fn parse( start_function = @enumFromInt(functions_start + index); }, .element => { - log.warn("unimplemented: element section in {}", .{path}); + log.warn("unimplemented: element section in {} {s}", .{ path, archive_member_name.? }); pos = section_end; }, .code => { + if (code_section_index != null) + return diags.failParse(path, "object has more than one code section", .{}); + code_section_index = section_index; + const start = pos; const count, pos = readLeb(u32, bytes, pos); for (try wasm.object_functions.addManyAsSlice(gpa, count)) |*elem| { @@ -745,12 +858,16 @@ pub fn parse( .type_index = undefined, // populated from func_types .code = payload, .offset = offset, - .section_index = section_index, - .source_location = source_location, + .object_index = object_index, }; } }, .data => { + if (data_section_index != null) + return diags.failParse(path, "object has more than one data section", .{}); + data_section_index = section_index; + + const section_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); @@ -761,12 +878,17 @@ 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_start = pos; const payload = try wasm.addRelocatableDataPayload(bytes[pos..][0..data_len]); pos += data_len; elem.* = .{ .payload = payload, - .name = .none, // Populated from symbol table - .flags = .{}, // Populated from symbol table and segment_info + .name = .none, // Populated from segment_info + .flags = .{ + .is_passive = flags == .passive, + }, // Remainder populated from segment_info + .offset = @intCast(segment_start - section_start), + .object_index = object_index, }; } }, @@ -1033,8 +1155,10 @@ pub fn parse( } }, .data_import => { - const name = symbol.name.unwrap().?; - log.warn("TODO data import '{s}'", .{name.slice(wasm)}); + if (symbol.flags.undefined and symbol.flags.binding == .local) { + const name = symbol.name.slice(wasm).?; + diags.addParseError(path, "local symbol '{s}' references import", .{name}); + } }, .data => continue, // `wasm.object_datas` has already been populated. }; @@ -1055,16 +1179,21 @@ pub fn parse( } // Apply segment_info. - for (wasm.object_data_segments.items[data_segment_start..], ss.segment_info.items) |*data, info| { + const data_segments = wasm.object_data_segments.items[data_segment_start..]; + if (data_segments.len != ss.segment_info.items.len) { + return diags.failParse(path, "expected {d} segment_info entries; found {d}", .{ + data_segments.len, ss.segment_info.items.len, + }); + } + for (data_segments, ss.segment_info.items) |*data, info| { data.name = info.name.toOptional(); - data.flags.strings = info.flags.strings; - data.flags.tls = data.flags.tls or info.flags.tls; - data.flags.no_strip = info.flags.retain; - data.flags.alignment = info.flags.alignment; - if (data.flags.undefined and data.flags.binding == .local) { - const name = info.name.slice(wasm); - diags.addParseError(path, "local symbol '{s}' references import", .{name}); - } + data.flags = .{ + .is_passive = data.flags.is_passive, + .strings = info.flags.strings, + .tls = info.flags.tls, + .retain = info.flags.retain, + .alignment = info.flags.alignment, + }; } // Check for indirect function table in case of an MVP object file. @@ -1106,6 +1235,10 @@ pub fn parse( }); } + const functions_len: u32 = @intCast(wasm.object_functions.items.len - functions_start); + if (functions_len > 0 and code_section_index == null) + return diags.failParse(path, "code section missing ({d} functions)", .{functions_len}); + return .{ .version = version, .path = path, @@ -1114,7 +1247,7 @@ pub fn parse( .features = features, .functions = .{ .off = functions_start, - .len = @intCast(wasm.object_functions.items.len - functions_start), + .len = functions_len, }, .function_imports = .{ .off = function_imports_start, @@ -1140,7 +1273,9 @@ pub fn parse( .off = custom_segment_start, .len = @intCast(wasm.object_custom_segments.entries.len - custom_segment_start), }, - .local_section_index_base = local_section_index_base, + .code_section_index = code_section_index, + .global_section_index = global_section_index, + .data_section_index = data_section_index, }; }