diff options
| -rw-r--r-- | CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/arch/wasm/CodeGen.zig | 10 | ||||
| -rw-r--r-- | src/arch/wasm/Emit.zig | 10 | ||||
| -rw-r--r-- | src/link/Wasm.zig | 1279 | ||||
| -rw-r--r-- | src/link/Wasm/Atom.zig | 204 | ||||
| -rw-r--r-- | src/link/Wasm/Object.zig | 144 | ||||
| -rw-r--r-- | src/link/Wasm/Symbol.zig | 1 | ||||
| -rw-r--r-- | src/link/Wasm/ZigObject.zig | 256 | ||||
| -rw-r--r-- | src/link/Wasm/file.zig | 132 | ||||
| -rw-r--r-- | src/link/Wasm/types.zig | 267 |
10 files changed, 1146 insertions, 1160 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 23e5dea07f..be92fb9f9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -648,12 +648,9 @@ set(ZIG_STAGE2_SOURCES src/link/StringTable.zig src/link/Wasm.zig src/link/Wasm/Archive.zig - src/link/Wasm/Atom.zig src/link/Wasm/Object.zig src/link/Wasm/Symbol.zig src/link/Wasm/ZigObject.zig - src/link/Wasm/file.zig - src/link/Wasm/types.zig src/link/aarch64.zig src/link/riscv.zig src/link/table_section.zig diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 012210223f..1bc11980bb 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1291,7 +1291,7 @@ fn genFunc(func: *CodeGen) InnerError!void { var prologue = std.ArrayList(Mir.Inst).init(func.gpa); defer prologue.deinit(); - const sp = @intFromEnum(func.bin_file.zigObjectPtr().?.stack_pointer_sym); + const sp = @intFromEnum(func.bin_file.zig_object.?.stack_pointer_sym); // load stack pointer try prologue.append(.{ .tag = .global_get, .data = .{ .label = sp } }); // store stack pointer so we can restore it when we return from the function @@ -1511,7 +1511,7 @@ fn restoreStackPointer(func: *CodeGen) !void { try func.emitWValue(func.initial_stack_value); // save its value in the global stack pointer - try func.addLabel(.global_set, @intFromEnum(func.bin_file.zigObjectPtr().?.stack_pointer_sym)); + try func.addLabel(.global_set, @intFromEnum(func.bin_file.zig_object.?.stack_pointer_sym)); } /// From a given type, will create space on the virtual stack to store the value of such type. @@ -2262,7 +2262,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif } if (callee) |direct| { - const atom_index = func.bin_file.zigObjectPtr().?.navs.get(direct).?.atom; + const atom_index = func.bin_file.zig_object.?.navs.get(direct).?.atom; try func.addLabel(.call, @intFromEnum(func.bin_file.getAtom(atom_index).sym_index)); } else { // in this case we call a function pointer @@ -2274,7 +2274,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif var fn_type = try genFunctype(func.gpa, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), pt, func.target.*); defer fn_type.deinit(func.gpa); - const fn_type_index = try func.bin_file.zigObjectPtr().?.putOrGetFuncType(func.gpa, fn_type); + const fn_type_index = try func.bin_file.zig_object.?.putOrGetFuncType(func.gpa, fn_type); try func.addLabel(.call_indirect, fn_type_index); } @@ -7178,7 +7178,7 @@ fn callIntrinsic( const zcu = pt.zcu; var func_type = try genFunctype(func.gpa, .{ .wasm_watc = .{} }, param_types, return_type, pt, func.target.*); defer func_type.deinit(func.gpa); - const func_type_index = try func.bin_file.zigObjectPtr().?.putOrGetFuncType(func.gpa, func_type); + const func_type_index = try func.bin_file.zig_object.?.putOrGetFuncType(func.gpa, func_type); try func.bin_file.addOrUpdateImport(name, symbol_index, null, func_type_index); const want_sret_param = firstParamSRet(.{ .wasm_watc = .{} }, return_type, pt, func.target.*); diff --git a/src/arch/wasm/Emit.zig b/src/arch/wasm/Emit.zig index 648af47153..cd744cd53e 100644 --- a/src/arch/wasm/Emit.zig +++ b/src/arch/wasm/Emit.zig @@ -310,7 +310,7 @@ fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void { const global_offset = emit.offset(); try emit.code.appendSlice(&buf); - const atom_index = emit.bin_file.zigObjectPtr().?.navs.get(emit.owner_nav).?.atom; + const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom; const atom = emit.bin_file.getAtomPtr(atom_index); try atom.relocs.append(gpa, .{ .index = label, @@ -370,7 +370,7 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void { try emit.code.appendSlice(&buf); if (label != 0) { - const atom_index = emit.bin_file.zigObjectPtr().?.navs.get(emit.owner_nav).?.atom; + const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom; const atom = emit.bin_file.getAtomPtr(atom_index); try atom.relocs.append(gpa, .{ .offset = call_offset, @@ -390,7 +390,7 @@ fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void { leb128.writeUnsignedFixed(5, &buf, type_index); try emit.code.appendSlice(&buf); if (type_index != 0) { - const atom_index = emit.bin_file.zigObjectPtr().?.navs.get(emit.owner_nav).?.atom; + const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom; const atom = emit.bin_file.getAtomPtr(atom_index); try atom.relocs.append(emit.bin_file.base.comp.gpa, .{ .offset = call_offset, @@ -412,7 +412,7 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void { try emit.code.appendSlice(&buf); if (symbol_index != 0) { - const atom_index = emit.bin_file.zigObjectPtr().?.navs.get(emit.owner_nav).?.atom; + const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom; const atom = emit.bin_file.getAtomPtr(atom_index); try atom.relocs.append(gpa, .{ .offset = index_offset, @@ -443,7 +443,7 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void { } if (mem.pointer != 0) { - const atom_index = emit.bin_file.zigObjectPtr().?.navs.get(emit.owner_nav).?.atom; + const atom_index = emit.bin_file.zig_object.?.navs.get(emit.owner_nav).?.atom; const atom = emit.bin_file.getAtomPtr(atom_index); try atom.relocs.append(gpa, .{ .offset = mem_offset, diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index f291d6c2d6..358436ff9c 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -15,7 +15,6 @@ const log = std.log.scoped(.link); const gc_log = std.log.scoped(.gc); const mem = std.mem; const trace = @import("../tracy.zig").trace; -const types = @import("Wasm/types.zig"); const wasi_libc = @import("../wasi_libc.zig"); const Air = @import("../Air.zig"); @@ -26,7 +25,6 @@ const Path = Cache.Path; const CodeGen = @import("../arch/wasm/CodeGen.zig"); const Compilation = @import("../Compilation.zig"); const Dwarf = @import("Dwarf.zig"); -const File = @import("Wasm/file.zig").File; const InternPool = @import("../InternPool.zig"); const Liveness = @import("../Liveness.zig"); const LlvmObject = @import("../codegen/llvm.zig").Object; @@ -37,9 +35,6 @@ const Type = @import("../Type.zig"); const Value = @import("../Value.zig"); const ZigObject = @import("Wasm/ZigObject.zig"); -pub const Atom = @import("Wasm/Atom.zig"); -pub const Relocation = types.Relocation; - base: link.File, /// Symbol name of the entry function to export entry_name: ?[]const u8, @@ -61,11 +56,9 @@ export_table: bool, name: []const u8, /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. llvm_object: ?LlvmObject.Ptr = null, -/// The file index of a `ZigObject`. This will only contain a valid index when a zcu exists, -/// and the chosen backend is the Wasm backend. -zig_object_index: File.Index = .null, +zig_object: ?*ZigObject, /// List of relocatable files to be linked into the final binary. -files: std.MultiArrayList(File.Entry) = .{}, +objects: std.ArrayListUnmanaged(Object) = .{}, /// When importing objects from the host environment, a name must be supplied. /// LLVM uses "env" by default when none is given. This would be a good default for Zig /// to support existing code. @@ -105,17 +98,17 @@ imported_globals_count: u32 = 0, /// The count of imported tables. This number will be appended /// to the table indexes when sections are merged. imported_tables_count: u32 = 0, -/// Map of symbol locations, represented by its `types.Import` -imports: std.AutoHashMapUnmanaged(SymbolLoc, types.Import) = .empty, +/// Map of symbol locations, represented by its `Import` +imports: std.AutoHashMapUnmanaged(SymbolLoc, Import) = .empty, /// Represents non-synthetic section entries. /// Used for code, data and custom sections. segments: std.ArrayListUnmanaged(Segment) = .empty, /// Maps a data segment key (such as .rodata) to the index into `segments`. data_segments: std.StringArrayHashMapUnmanaged(u32) = .empty, -/// A table of `types.Segment` which provide meta data +/// A table of `NamedSegment` which provide meta data /// about a data symbol such as its name where the key is /// the segment index, which can be found from `data_segments` -segment_info: std.AutoArrayHashMapUnmanaged(u32, types.Segment) = .empty, +segment_info: std.AutoArrayHashMapUnmanaged(u32, NamedSegment) = .empty, /// Deduplicated string table for strings used by symbols, imports and exports. string_table: StringTable = .{}, @@ -126,8 +119,15 @@ func_types: std.ArrayListUnmanaged(std.wasm.Type) = .empty, /// function index and the value is function. /// This allows us to map multiple symbols to the same function. functions: std.AutoArrayHashMapUnmanaged( - struct { file: File.Index, index: u32 }, - struct { func: std.wasm.Func, sym_index: Symbol.Index }, + struct { + /// `none` in the case of synthetic sections. + file: OptionalObjectId, + index: u32, + }, + struct { + func: std.wasm.Func, + sym_index: Symbol.Index, + }, ) = .{}, /// Output global section wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .empty, @@ -140,7 +140,7 @@ memories: std.wasm.Memory = .{ .limits = .{ /// Output table section tables: std.ArrayListUnmanaged(std.wasm.Table) = .empty, /// Output export section -exports: std.ArrayListUnmanaged(types.Export) = .empty, +exports: std.ArrayListUnmanaged(Export) = .empty, /// List of initialization functions. These must be called in order of priority /// by the (synthetic) __wasm_call_ctors function. init_funcs: std.ArrayListUnmanaged(InitFuncLoc) = .empty, @@ -154,8 +154,6 @@ entry: ?u32 = null, /// Note: Key is symbol location, value represents the index into the table function_table: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .empty, -/// All object files and their data which are linked into the final binary -objects: std.ArrayListUnmanaged(File.Index) = .empty, /// All archive files that are lazy loaded. /// e.g. when an undefined symbol references a symbol from the archive. archives: std.ArrayListUnmanaged(Archive) = .empty, @@ -178,7 +176,29 @@ undefs: std.AutoArrayHashMapUnmanaged(u32, SymbolLoc) = .empty, /// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped. symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, Atom.Index) = .empty, -pub const Alignment = types.Alignment; +/// Index into objects array or the zig object. +pub const ObjectId = enum(u16) { + zig_object = std.math.maxInt(u16) - 1, + _, + + pub fn toOptional(i: ObjectId) OptionalObjectId { + const result: OptionalObjectId = @enumFromInt(@intFromEnum(i)); + assert(result != .none); + return result; + } +}; + +/// Optional index into objects array or the zig object. +pub const OptionalObjectId = enum(u16) { + zig_object = std.math.maxInt(u16) - 1, + none = std.math.maxInt(u16), + _, + + pub fn unwrap(i: OptionalObjectId) ?ObjectId { + if (i == .none) return null; + return @enumFromInt(@intFromEnum(i)); + } +}; pub const Segment = struct { alignment: Alignment, @@ -208,43 +228,55 @@ pub const SymbolLoc = struct { /// The index of the symbol within the specified file index: Symbol.Index, /// The index of the object file where the symbol resides. - file: File.Index, + file: OptionalObjectId, +}; - /// From a given location, returns the corresponding symbol in the wasm binary - pub fn getSymbol(loc: SymbolLoc, wasm_file: *const Wasm) *Symbol { - if (wasm_file.discarded.get(loc)) |new_loc| { - return new_loc.getSymbol(wasm_file); - } - if (wasm_file.file(loc.file)) |obj_file| { - return obj_file.symbol(loc.index); - } - return &wasm_file.synthetic_symbols.items[@intFromEnum(loc.index)]; +/// From a given location, returns the corresponding symbol in the wasm binary +pub fn symbolLocSymbol(wasm: *const Wasm, loc: SymbolLoc) *Symbol { + if (wasm.discarded.get(loc)) |new_loc| { + return symbolLocSymbol(wasm, new_loc); } + return switch (loc.file) { + .none => &wasm.synthetic_symbols.items[@intFromEnum(loc.index)], + .zig_object => wasm.zig_object.?.symbol(loc.index), + _ => &wasm.objects.items[@intFromEnum(loc.file)].symtable[@intFromEnum(loc.index)], + }; +} - /// From a given location, returns the name of the symbol. - pub fn getName(loc: SymbolLoc, wasm_file: *const Wasm) []const u8 { - if (wasm_file.discarded.get(loc)) |new_loc| { - return new_loc.getName(wasm_file); - } - if (wasm_file.file(loc.file)) |obj_file| { - return obj_file.symbolName(loc.index); - } - const sym = wasm_file.synthetic_symbols.items[@intFromEnum(loc.index)]; - return wasm_file.string_table.get(sym.name); +/// From a given location, returns the name of the symbol. +pub fn symbolLocName(wasm: *const Wasm, loc: SymbolLoc) []const u8 { + if (wasm.discarded.get(loc)) |new_loc| { + return wasm.symbolLocName(new_loc); + } + switch (loc.file) { + .none => { + const sym = wasm.synthetic_symbols.items[@intFromEnum(loc.index)]; + return wasm.string_table.get(sym.name); + }, + .zig_object => { + const zo = wasm.zig_object.?; + const sym = zo.symbols.items[@intFromEnum(loc.index)]; + return zo.string_table.get(sym.name).?; + }, + _ => { + const obj = &wasm.objects.items[@intFromEnum(loc.file)]; + const sym = obj.symtable[@intFromEnum(loc.index)]; + return obj.string_table.get(sym.name); + }, } +} - /// From a given symbol location, returns the final location. - /// e.g. when a symbol was resolved and replaced by the symbol - /// in a different file, this will return said location. - /// If the symbol wasn't replaced by another, this will return - /// the given location itwasm. - pub fn finalLoc(loc: SymbolLoc, wasm_file: *const Wasm) SymbolLoc { - if (wasm_file.discarded.get(loc)) |new_loc| { - return new_loc.finalLoc(wasm_file); - } - return loc; +/// From a given symbol location, returns the final location. +/// e.g. when a symbol was resolved and replaced by the symbol +/// in a different file, this will return said location. +/// If the symbol wasn't replaced by another, this will return +/// the given location itwasm. +pub fn symbolLocFinalLoc(wasm: *const Wasm, loc: SymbolLoc) SymbolLoc { + if (wasm.discarded.get(loc)) |new_loc| { + return symbolLocFinalLoc(wasm, new_loc); } -}; + return loc; +} // Contains the location of the function symbol, as well as /// the priority itself of the initialization function. @@ -252,7 +284,7 @@ pub const InitFuncLoc = struct { /// object file index in the list of objects. /// Unlike `SymbolLoc` this cannot be `null` as we never define /// our own ctors. - file: File.Index, + file: ObjectId, /// Symbol index within the corresponding object file. index: Symbol.Index, /// The priority in which the constructor must be called. @@ -260,12 +292,15 @@ pub const InitFuncLoc = struct { /// From a given `InitFuncLoc` returns the corresponding function symbol fn getSymbol(loc: InitFuncLoc, wasm: *const Wasm) *Symbol { - return getSymbolLoc(loc).getSymbol(wasm); + return wasm.symbolLocSymbol(getSymbolLoc(loc)); } /// Turns the given `InitFuncLoc` into a `SymbolLoc` fn getSymbolLoc(loc: InitFuncLoc) SymbolLoc { - return .{ .file = loc.file, .index = loc.index }; + return .{ + .file = loc.file.toOptional(), + .index = loc.index, + }; } /// Returns true when `lhs` has a higher priority (e.i. value closer to 0) than `rhs`. @@ -307,7 +342,7 @@ pub const StringTable = struct { } try table.string_data.ensureUnusedCapacity(allocator, string.len + 1); - const offset = @as(u32, @intCast(table.string_data.items.len)); + const offset: u32 = @intCast(table.string_data.items.len); log.debug("writing new string '{s}' at offset 0x{x}", .{ string, offset }); @@ -414,6 +449,7 @@ pub fn createEmpty( .enabled => defaultEntrySymbolName(wasi_exec_model), .named => |name| name, }, + .zig_object = null, }; if (use_llvm and comp.config.have_zcu) { wasm.llvm_object = try LlvmObject.create(arena, comp); @@ -446,7 +482,7 @@ pub fn createEmpty( // create stack pointer symbol { const loc = try wasm.createSyntheticSymbol("__stack_pointer", .global); - const symbol = loc.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(loc); // For object files we will import the stack pointer symbol if (output_mode == .Obj) { symbol.setUndefined(true); @@ -478,7 +514,7 @@ pub fn createEmpty( // create indirect function pointer symbol { const loc = try wasm.createSyntheticSymbol("__indirect_function_table", .table); - const symbol = loc.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(loc); const table: std.wasm.Table = .{ .limits = .{ .flags = 0, .min = 0, .max = undefined }, // will be overwritten during `mapFunctionTable` .reftype = .funcref, @@ -506,7 +542,7 @@ pub fn createEmpty( // create __wasm_call_ctors { const loc = try wasm.createSyntheticSymbol("__wasm_call_ctors", .function); - const symbol = loc.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(loc); symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); // we do not know the function index until after we merged all sections. // Therefore we set `symbol.index` and create its corresponding references @@ -517,7 +553,7 @@ pub fn createEmpty( if (shared_memory) { { const loc = try wasm.createSyntheticSymbol("__tls_base", .global); - const symbol = loc.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(loc); symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); symbol.mark(); @@ -528,7 +564,7 @@ pub fn createEmpty( } { const loc = try wasm.createSyntheticSymbol("__tls_size", .global); - const symbol = loc.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(loc); symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); symbol.mark(); @@ -539,7 +575,7 @@ pub fn createEmpty( } { const loc = try wasm.createSyntheticSymbol("__tls_align", .global); - const symbol = loc.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(loc); symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); symbol.mark(); @@ -550,42 +586,26 @@ pub fn createEmpty( } { const loc = try wasm.createSyntheticSymbol("__wasm_init_tls", .function); - const symbol = loc.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(loc); symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); } } if (comp.zcu) |zcu| { if (!use_llvm) { - const index: File.Index = @enumFromInt(wasm.files.len); - var zig_object: ZigObject = .{ - .index = index, + const zig_object = try arena.create(ZigObject); + wasm.zig_object = zig_object; + zig_object.* = .{ .path = try std.fmt.allocPrint(gpa, "{s}.o", .{std.fs.path.stem(zcu.main_mod.root_src_path)}), .stack_pointer_sym = .null, }; try zig_object.init(wasm); - try wasm.files.append(gpa, .{ .zig_object = zig_object }); - wasm.zig_object_index = index; } } return wasm; } -pub fn file(wasm: *const Wasm, index: File.Index) ?File { - if (index == .null) return null; - const tag = wasm.files.items(.tags)[@intFromEnum(index)]; - return switch (tag) { - .zig_object => .{ .zig_object = &wasm.files.items(.data)[@intFromEnum(index)].zig_object }, - .object => .{ .object = &wasm.files.items(.data)[@intFromEnum(index)].object }, - }; -} - -pub fn zigObjectPtr(wasm: *Wasm) ?*ZigObject { - if (wasm.zig_object_index == .null) return null; - return &wasm.files.items(.data)[@intFromEnum(wasm.zig_object_index)].zig_object; -} - pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 { var index: u32 = 0; while (index < wasm.func_types.items.len) : (index += 1) { @@ -610,7 +630,7 @@ pub fn addOrUpdateImport( /// is asserted instead. type_index: ?u32, ) !void { - return wasm.zigObjectPtr().?.addOrUpdateImport(wasm, name, symbol_index, lib_name, type_index); + return wasm.zig_object.?.addOrUpdateImport(wasm, name, symbol_index, lib_name, type_index); } /// For a given name, creates a new global synthetic symbol. @@ -623,7 +643,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) !SymbolLoc { const sym_index: Symbol.Index = @enumFromInt(wasm.synthetic_symbols.items.len); - const loc: SymbolLoc = .{ .index = sym_index, .file = .null }; + const loc: SymbolLoc = .{ .index = sym_index, .file = .none }; const gpa = wasm.base.comp.gpa; try wasm.synthetic_symbols.append(gpa, .{ .name = name_offset, @@ -657,28 +677,29 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool { }, }; errdefer object.deinit(gpa); - object.index = @enumFromInt(wasm.files.len); - try wasm.files.append(gpa, .{ .object = object }); - try wasm.objects.append(gpa, object.index); + try wasm.objects.append(gpa, object); return true; } /// Creates a new empty `Atom` and returns its `Atom.Index` -pub fn createAtom(wasm: *Wasm, sym_index: Symbol.Index, file_index: File.Index) !Atom.Index { +pub fn createAtom(wasm: *Wasm, sym_index: Symbol.Index, object_index: OptionalObjectId) !Atom.Index { const gpa = wasm.base.comp.gpa; const index: Atom.Index = @enumFromInt(wasm.managed_atoms.items.len); const atom = try wasm.managed_atoms.addOne(gpa); - atom.* = .{ .file = file_index, .sym_index = sym_index }; + atom.* = .{ + .file = object_index, + .sym_index = sym_index, + }; try wasm.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), index); return index; } -pub inline fn getAtom(wasm: *const Wasm, index: Atom.Index) Atom { +pub fn getAtom(wasm: *const Wasm, index: Atom.Index) Atom { return wasm.managed_atoms.items[@intFromEnum(index)]; } -pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom { +pub fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom { return &wasm.managed_atoms.items[@intFromEnum(index)]; } @@ -733,15 +754,13 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool { } for (offsets.keys()) |file_offset| { - var object = archive.parseObject(wasm, file_offset) catch |e| { + const object = archive.parseObject(wasm, file_offset) catch |e| { var err_note = try diags.addErrorWithNotes(1); try err_note.addMsg("Failed parsing object: {s}", .{@errorName(e)}); try err_note.addNote("while parsing object in archive {s}", .{path}); return error.FlushFailure; }; - object.index = @enumFromInt(wasm.files.len); - try wasm.files.append(gpa, .{ .object = object }); - try wasm.objects.append(gpa, object.index); + try wasm.objects.append(gpa, object); } return true; @@ -749,23 +768,102 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool { fn requiresTLSReloc(wasm: *const Wasm) bool { for (wasm.got_symbols.items) |loc| { - if (loc.getSymbol(wasm).isTLS()) { + if (wasm.symbolLocSymbol(loc).isTLS()) { return true; } } return false; } -fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void { +fn objectPath(wasm: *const Wasm, object_id: ObjectId) []const u8 { + const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.path; + return obj.path; +} + +fn objectSymbols(wasm: *const Wasm, object_id: ObjectId) []const Symbol { + const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.symbols.items; + return obj.symtable; +} + +fn objectSymbol(wasm: *const Wasm, object_id: ObjectId, index: Symbol.Index) *Symbol { + const obj = wasm.objectById(object_id) orelse return &wasm.zig_object.?.symbols.items[@intFromEnum(index)]; + return &obj.symtable[@intFromEnum(index)]; +} + +fn objectSymbolName(wasm: *const Wasm, object_id: ObjectId, index: Symbol.Index) []const u8 { + const obj = wasm.objectById(object_id) orelse { + const zo = wasm.zig_object.?; + const sym = zo.symbols.items[@intFromEnum(index)]; + return zo.string_table.get(sym.name).?; + }; + const sym = obj.symtable[@intFromEnum(index)]; + return obj.string_table.get(sym.name); +} + +fn objectFunction(wasm: *const Wasm, object_id: ObjectId, sym_index: Symbol.Index) std.wasm.Func { + const obj = wasm.objectById(object_id) orelse { + const zo = wasm.zig_object.?; + const sym = zo.symbols.items[@intFromEnum(sym_index)]; + return zo.functions.items[sym.index]; + }; + const sym = obj.symtable[@intFromEnum(sym_index)]; + return obj.functions[sym.index - obj.imported_functions_count]; +} + +fn objectImportedFunctions(wasm: *const Wasm, object_id: ObjectId) u32 { + const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.imported_functions_count; + return obj.imported_functions_count; +} + +fn objectGlobals(wasm: *const Wasm, object_id: ObjectId) []const std.wasm.Global { + const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.globals.items; + return obj.globals; +} + +fn objectFuncTypes(wasm: *const Wasm, object_id: ObjectId) []const std.wasm.Type { + const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.func_types.items; + return obj.func_types; +} + +fn objectSegmentInfo(wasm: *const Wasm, object_id: ObjectId) []const NamedSegment { + const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.segment_info.items; + return obj.segment_info; +} + +/// For a given symbol index, find its corresponding import. +/// Asserts import exists. +fn objectImport(wasm: *const Wasm, object_id: ObjectId, symbol_index: Symbol.Index) Import { + const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.imports.get(symbol_index).?; + return obj.findImport(obj.symtable[@intFromEnum(symbol_index)]); +} + +/// For a given offset, returns its string value. +/// Asserts string exists in the object string table. +fn objectString(wasm: *const Wasm, object_id: ObjectId, offset: u32) []const u8 { + const obj = wasm.objectById(object_id) orelse return wasm.zig_object.?.string_table.get(offset).?; + return obj.string_table.get(offset); +} + +/// Returns the object element pointer, or null if it is the ZigObject. +fn objectById(wasm: *const Wasm, object_id: ObjectId) ?*Object { + if (object_id == .zig_object) return null; + return &wasm.objects.items[@intFromEnum(object_id)]; +} + +fn resolveSymbolsInObject(wasm: *Wasm, object_id: ObjectId) !void { const gpa = wasm.base.comp.gpa; const diags = &wasm.base.comp.link_diags; - const obj_file = wasm.file(file_index).?; - log.debug("Resolving symbols in object: '{s}'", .{obj_file.path()}); + const obj_path = objectPath(wasm, object_id); + log.debug("Resolving symbols in object: '{s}'", .{obj_path}); + const symbols = objectSymbols(wasm, object_id); - for (obj_file.symbols(), 0..) |symbol, i| { + for (symbols, 0..) |symbol, i| { const sym_index: Symbol.Index = @enumFromInt(i); - const location: SymbolLoc = .{ .file = file_index, .index = sym_index }; - const sym_name = obj_file.string(symbol.name); + const location: SymbolLoc = .{ + .file = object_id.toOptional(), + .index = sym_index, + }; + const sym_name = objectString(wasm, object_id, symbol.name); if (mem.eql(u8, sym_name, "__indirect_function_table")) { continue; } @@ -775,7 +873,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void { if (symbol.isUndefined()) { var err = try diags.addErrorWithNotes(1); try err.addMsg("Local symbols are not allowed to reference imports", .{}); - try err.addNote("symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() }); + try err.addNote("symbol '{s}' defined in '{s}'", .{ sym_name, obj_path }); } try wasm.resolved_symbols.putNoClobber(gpa, location, {}); continue; @@ -793,13 +891,8 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void { } const existing_loc = maybe_existing.value_ptr.*; - const existing_sym: *Symbol = existing_loc.getSymbol(wasm); - const existing_file = wasm.file(existing_loc.file); - - const existing_file_path = if (existing_file) |existing_obj_file| - existing_obj_file.path() - else - wasm.name; + const existing_sym: *Symbol = wasm.symbolLocSymbol(existing_loc); + const existing_file_path = if (existing_loc.file.unwrap()) |id| objectPath(wasm, id) else wasm.name; if (!existing_sym.isUndefined()) outer: { if (!symbol.isUndefined()) inner: { @@ -813,7 +906,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void { var err = try diags.addErrorWithNotes(2); try err.addMsg("symbol '{s}' defined multiple times", .{sym_name}); try err.addNote("first definition in '{s}'", .{existing_file_path}); - try err.addNote("next definition in '{s}'", .{obj_file.path()}); + try err.addNote("next definition in '{s}'", .{obj_path}); } try wasm.discarded.put(gpa, location, existing_loc); @@ -824,22 +917,22 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void { var err = try diags.addErrorWithNotes(2); try err.addMsg("symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) }); try err.addNote("first definition in '{s}'", .{existing_file_path}); - try err.addNote("next definition in '{s}'", .{obj_file.path()}); + try err.addNote("next definition in '{s}'", .{obj_path}); } if (existing_sym.isUndefined() and symbol.isUndefined()) { // only verify module/import name for function symbols if (symbol.tag == .function) { - const existing_name = if (existing_file) |existing_obj| blk: { - const imp = existing_obj.import(existing_loc.index); - break :blk existing_obj.string(imp.module_name); + const existing_name = if (existing_loc.file.unwrap()) |existing_obj_id| blk: { + const imp = objectImport(wasm, existing_obj_id, existing_loc.index); + break :blk objectString(wasm, existing_obj_id, imp.module_name); } else blk: { const name_index = wasm.imports.get(existing_loc).?.module_name; break :blk wasm.string_table.get(name_index); }; - const imp = obj_file.import(sym_index); - const module_name = obj_file.string(imp.module_name); + const imp = objectImport(wasm, object_id, sym_index); + const module_name = objectString(wasm, object_id, imp.module_name); if (!mem.eql(u8, existing_name, module_name)) { var err = try diags.addErrorWithNotes(2); try err.addMsg("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{ @@ -848,7 +941,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void { module_name, }); try err.addNote("first definition in '{s}'", .{existing_file_path}); - try err.addNote("next definition in '{s}'", .{obj_file.path()}); + try err.addNote("next definition in '{s}'", .{obj_path}); } } @@ -864,7 +957,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void { var err = try diags.addErrorWithNotes(2); try err.addMsg("symbol '{s}' mismatching global types", .{sym_name}); try err.addNote("first definition in '{s}'", .{existing_file_path}); - try err.addNote("next definition in '{s}'", .{obj_file.path()}); + try err.addNote("next definition in '{s}'", .{obj_path}); } } @@ -876,7 +969,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void { try err.addMsg("symbol '{s}' mismatching function signatures.", .{sym_name}); try err.addNote("expected signature {}, but found signature {}", .{ existing_ty, new_ty }); try err.addNote("first definition in '{s}'", .{existing_file_path}); - try err.addNote("next definition in '{s}'", .{obj_file.path()}); + try err.addNote("next definition in '{s}'", .{obj_path}); } } @@ -891,7 +984,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void { // simply overwrite with the new symbol log.debug("Overwriting symbol '{s}'", .{sym_name}); log.debug(" old definition in '{s}'", .{existing_file_path}); - log.debug(" new definition in '{s}'", .{obj_file.path()}); + log.debug(" new definition in '{s}'", .{obj_path}); try wasm.discarded.putNoClobber(gpa, existing_loc, location); maybe_existing.value_ptr.* = location; try wasm.globals.put(gpa, sym_name_index, location); @@ -924,16 +1017,14 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void { // Symbol is found in unparsed object file within current archive. // Parse object and and resolve symbols again before we check remaining // undefined symbols. - var object = archive.parseObject(wasm, offset.items[0]) catch |e| { + const object = archive.parseObject(wasm, offset.items[0]) catch |e| { var err_note = try diags.addErrorWithNotes(1); try err_note.addMsg("Failed parsing object: {s}", .{@errorName(e)}); try err_note.addNote("while parsing object in archive {s}", .{archive.name}); return error.FlushFailure; }; - object.index = @enumFromInt(wasm.files.len); - try wasm.files.append(gpa, .{ .object = object }); - try wasm.objects.append(gpa, object.index); - try wasm.resolveSymbolsInObject(object.index); + try wasm.objects.append(gpa, object); + try wasm.resolveSymbolsInObject(@enumFromInt(wasm.objects.items.len - 1)); // continue loop for any remaining undefined symbols that still exist // after resolving last object file @@ -964,13 +1055,13 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { return; } const sym_loc = try wasm.createSyntheticSymbol("__wasm_init_memory", .function); - sym_loc.getSymbol(wasm).mark(); + wasm.symbolLocSymbol(sym_loc).mark(); const flag_address: u32 = if (shared_memory) address: { // when we have passive initialization segments and shared memory // `setupMemory` will create this symbol and set its virtual address. const loc = wasm.findGlobalSymbol("__wasm_init_memory_flag").?; - break :address loc.getSymbol(wasm).virtual_address; + break :address wasm.symbolLocSymbol(loc).virtual_address; } else 0; var function_body = std.ArrayList(u8).init(gpa); @@ -1028,7 +1119,7 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { try writeI32Const(writer, segment.offset); try writer.writeByte(std.wasm.opcode(.global_set)); const loc = wasm.findGlobalSymbol("__tls_base").?; - try leb.writeUleb128(writer, loc.getSymbol(wasm).index); + try leb.writeUleb128(writer, wasm.symbolLocSymbol(loc).index); } try writeI32Const(writer, 0); @@ -1128,7 +1219,7 @@ fn setupTLSRelocationsFunction(wasm: *Wasm) !void { } const loc = try wasm.createSyntheticSymbol("__wasm_apply_global_tls_relocs", .function); - loc.getSymbol(wasm).mark(); + wasm.symbolLocSymbol(loc).mark(); var function_body = std.ArrayList(u8).init(gpa); defer function_body.deinit(); const writer = function_body.writer(); @@ -1136,12 +1227,12 @@ fn setupTLSRelocationsFunction(wasm: *Wasm) !void { // locals (we have none) try writer.writeByte(0); for (wasm.got_symbols.items, 0..) |got_loc, got_index| { - const sym: *Symbol = got_loc.getSymbol(wasm); + const sym: *Symbol = wasm.symbolLocSymbol(got_loc); if (!sym.isTLS()) continue; // only relocate TLS symbols if (sym.tag == .data and sym.isDefined()) { // get __tls_base try writer.writeByte(std.wasm.opcode(.global_get)); - try leb.writeUleb128(writer, wasm.findGlobalSymbol("__tls_base").?.getSymbol(wasm).index); + try leb.writeUleb128(writer, wasm.symbolLocSymbol(wasm.findGlobalSymbol("__tls_base").?).index); // add the virtual address of the symbol try writer.writeByte(std.wasm.opcode(.i32_const)); @@ -1165,7 +1256,7 @@ fn setupTLSRelocationsFunction(wasm: *Wasm) !void { fn validateFeatures( wasm: *const Wasm, - to_emit: *[@typeInfo(types.Feature.Tag).@"enum".fields.len]bool, + to_emit: *[@typeInfo(Feature.Tag).@"enum".fields.len]bool, emit_features_count: *u32, ) !void { const comp = wasm.base.comp; @@ -1174,7 +1265,7 @@ fn validateFeatures( const shared_memory = comp.config.shared_memory; const cpu_features = target.cpu.features; const infer = cpu_features.isEmpty(); // when the user did not define any features, we infer them from linked objects. - const known_features_count = @typeInfo(types.Feature.Tag).@"enum".fields.len; + const known_features_count = @typeInfo(Feature.Tag).@"enum".fields.len; var allowed = [_]bool{false} ** known_features_count; var used = [_]u17{0} ** known_features_count; @@ -1199,10 +1290,9 @@ fn validateFeatures( // extract all the used, disallowed and required features from each // linked object file so we can test them. - for (wasm.objects.items) |file_index| { - const object: Object = wasm.files.items(.data)[@intFromEnum(file_index)].object; + for (wasm.objects.items, 0..) |*object, file_index| { for (object.features) |feature| { - const value = @as(u16, @intFromEnum(file_index)) << 1 | @as(u1, 1); + const value = (@as(u16, @intCast(file_index)) << 1) | 1; switch (feature.prefix) { .used => { used[@intFromEnum(feature.tag)] = value; @@ -1234,8 +1324,8 @@ fn validateFeatures( emit_features_count.* += @intFromBool(is_enabled); } else if (is_enabled and !allowed[used_index]) { var err = try diags.addErrorWithNotes(1); - try err.addMsg("feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))}); - try err.addNote("defined in '{s}'", .{wasm.files.items(.data)[used_set >> 1].object.path}); + try err.addMsg("feature '{}' not allowed, but used by linked object", .{@as(Feature.Tag, @enumFromInt(used_index))}); + try err.addNote("defined in '{s}'", .{wasm.objects.items[used_set >> 1].path}); valid_feature_set = false; } } @@ -1245,17 +1335,17 @@ fn validateFeatures( } if (shared_memory) { - const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)]; + const disallowed_feature = disallowed[@intFromEnum(Feature.Tag.shared_mem)]; if (@as(u1, @truncate(disallowed_feature)) != 0) { var err = try diags.addErrorWithNotes(0); try err.addMsg( "shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled", - .{wasm.files.items(.data)[disallowed_feature >> 1].object.path}, + .{wasm.objects.items[disallowed_feature >> 1].path}, ); valid_feature_set = false; } - for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| { + for ([_]Feature.Tag{ .atomics, .bulk_memory }) |feature| { if (!allowed[@intFromEnum(feature)]) { var err = try diags.addErrorWithNotes(0); try err.addMsg("feature '{}' is not used but is required for shared-memory", .{feature}); @@ -1264,7 +1354,7 @@ fn validateFeatures( } if (has_tls) { - for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| { + for ([_]Feature.Tag{ .atomics, .bulk_memory }) |feature| { if (!allowed[@intFromEnum(feature)]) { var err = try diags.addErrorWithNotes(0); try err.addMsg("feature '{}' is not used but is required for thread-local storage", .{feature}); @@ -1272,9 +1362,8 @@ fn validateFeatures( } } // For each linked object, validate the required and disallowed features - for (wasm.objects.items) |file_index| { + for (wasm.objects.items) |*object| { var object_used_features = [_]bool{false} ** known_features_count; - const object = wasm.files.items(.data)[@intFromEnum(file_index)].object; for (object.features) |feature| { if (feature.prefix == .disallowed) continue; // already defined in 'disallowed' set. // from here a feature is always used @@ -1282,7 +1371,7 @@ fn validateFeatures( if (@as(u1, @truncate(disallowed_feature)) != 0) { var err = try diags.addErrorWithNotes(2); try err.addMsg("feature '{}' is disallowed, but used by linked object", .{feature.tag}); - try err.addNote("disallowed by '{s}'", .{wasm.files.items(.data)[disallowed_feature >> 1].object.path}); + try err.addNote("disallowed by '{s}'", .{wasm.objects.items[disallowed_feature >> 1].path}); try err.addNote("used in '{s}'", .{object.path}); valid_feature_set = false; } @@ -1295,8 +1384,8 @@ fn validateFeatures( const is_required = @as(u1, @truncate(required_feature)) != 0; if (is_required and !object_used_features[feature_index]) { var err = try diags.addErrorWithNotes(2); - try err.addMsg("feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))}); - try err.addNote("required by '{s}'", .{wasm.files.items(.data)[required_feature >> 1].object.path}); + try err.addMsg("feature '{}' is required but not used in linked object", .{@as(Feature.Tag, @enumFromInt(feature_index))}); + try err.addNote("required by '{s}'", .{wasm.objects.items[required_feature >> 1].path}); try err.addNote("missing in '{s}'", .{object.path}); valid_feature_set = false; } @@ -1341,7 +1430,7 @@ fn resolveLazySymbols(wasm: *Wasm) !void { const loc = try wasm.createSyntheticSymbolOffset(name_offset, .global); try wasm.discarded.putNoClobber(gpa, kv.value, loc); _ = wasm.resolved_symbols.swapRemove(kv.value); - const symbol = loc.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(loc); symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); try wasm.wasm_globals.append(gpa, .{ @@ -1368,14 +1457,15 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void { var found_undefined_symbols = false; for (wasm.undefs.values()) |undef| { - const symbol = undef.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(undef); if (symbol.tag == .data) { found_undefined_symbols = true; - const file_name = if (wasm.file(undef.file)) |obj_file| - obj_file.path() - else - wasm.name; - const symbol_name = undef.getName(wasm); + const file_name = switch (undef.file) { + .zig_object => wasm.zig_object.?.path, + .none => wasm.name, + _ => wasm.objects.items[@intFromEnum(undef.file)].path, + }; + const symbol_name = wasm.symbolLocName(undef); var err = try diags.addErrorWithNotes(1); try err.addMsg("could not resolve undefined symbol '{s}'", .{symbol_name}); try err.addNote("defined in '{s}'", .{file_name}); @@ -1396,11 +1486,11 @@ pub fn deinit(wasm: *Wasm) void { for (wasm.segment_info.values()) |segment_info| { gpa.free(segment_info.name); } - if (wasm.zigObjectPtr()) |zig_obj| { + if (wasm.zig_object) |zig_obj| { zig_obj.deinit(wasm); } - for (wasm.objects.items) |obj_index| { - wasm.file(obj_index).?.object.deinit(gpa); + for (wasm.objects.items) |*object| { + object.deinit(gpa); } for (wasm.archives.items) |*archive| { @@ -1437,7 +1527,6 @@ pub fn deinit(wasm: *Wasm) void { wasm.exports.deinit(gpa); wasm.string_table.deinit(gpa); - wasm.files.deinit(gpa); } pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void { @@ -1445,7 +1534,7 @@ pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index, @panic("Attempted to compile for object format that was disabled by build configuration"); } if (wasm.llvm_object) |llvm_object| return llvm_object.updateFunc(pt, func_index, air, liveness); - try wasm.zigObjectPtr().?.updateFunc(wasm, pt, func_index, air, liveness); + try wasm.zig_object.?.updateFunc(wasm, pt, func_index, air, liveness); } // Generate code for the "Nav", storing it in memory to be later written to @@ -1455,51 +1544,84 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !voi @panic("Attempted to compile for object format that was disabled by build configuration"); } if (wasm.llvm_object) |llvm_object| return llvm_object.updateNav(pt, nav); - try wasm.zigObjectPtr().?.updateNav(wasm, pt, nav); + try wasm.zig_object.?.updateNav(wasm, pt, nav); } pub fn updateNavLineNumber(wasm: *Wasm, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !void { if (wasm.llvm_object) |_| return; - try wasm.zigObjectPtr().?.updateNavLineNumber(pt, nav); + try wasm.zig_object.?.updateNavLineNumber(pt, nav); } /// From a given symbol location, returns its `wasm.GlobalType`. /// Asserts the Symbol represents a global. fn getGlobalType(wasm: *const Wasm, loc: SymbolLoc) std.wasm.GlobalType { - const symbol = loc.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(loc); assert(symbol.tag == .global); const is_undefined = symbol.isUndefined(); - if (wasm.file(loc.file)) |obj_file| { - if (is_undefined) { - return obj_file.import(loc.index).kind.global; - } - return obj_file.globals()[symbol.index - obj_file.importedGlobals()].global_type; - } - if (is_undefined) { - return wasm.imports.get(loc).?.kind.global; + switch (loc.file) { + .zig_object => { + const zo = wasm.zig_object.?; + return if (is_undefined) + zo.imports.get(loc.index).?.kind.global + else + zo.globals.items[symbol.index - zo.imported_globals_count].global_type; + }, + .none => { + return if (is_undefined) + wasm.imports.get(loc).?.kind.global + else + wasm.wasm_globals.items[symbol.index].global_type; + }, + _ => { + const obj = &wasm.objects.items[@intFromEnum(loc.file)]; + return if (is_undefined) + obj.findImport(obj.symtable[@intFromEnum(loc.index)]).kind.global + else + obj.globals[symbol.index - obj.imported_globals_count].global_type; + }, } - return wasm.wasm_globals.items[symbol.index].global_type; } /// From a given symbol location, returns its `wasm.Type`. /// Asserts the Symbol represents a function. fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type { - const symbol = loc.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(loc); assert(symbol.tag == .function); const is_undefined = symbol.isUndefined(); - if (wasm.file(loc.file)) |obj_file| { - if (is_undefined) { - const ty_index = obj_file.import(loc.index).kind.function; - return obj_file.funcTypes()[ty_index]; - } - const type_index = obj_file.function(loc.index).type_index; - return obj_file.funcTypes()[type_index]; - } - if (is_undefined) { - const ty_index = wasm.imports.get(loc).?.kind.function; - return wasm.func_types.items[ty_index]; + switch (loc.file) { + .zig_object => { + const zo = wasm.zig_object.?; + if (is_undefined) { + const type_index = zo.imports.get(loc.index).?.kind.function; + return zo.func_types.items[type_index]; + } + const sym = zo.symbols.items[@intFromEnum(loc.index)]; + const type_index = zo.functions.items[sym.index].type_index; + return zo.func_types.items[type_index]; + }, + .none => { + if (is_undefined) { + const type_index = wasm.imports.get(loc).?.kind.function; + return wasm.func_types.items[type_index]; + } + return wasm.func_types.items[ + wasm.functions.get(.{ + .file = .none, + .index = symbol.index, + }).?.func.type_index + ]; + }, + _ => { + const obj = &wasm.objects.items[@intFromEnum(loc.file)]; + if (is_undefined) { + const type_index = obj.findImport(obj.symtable[@intFromEnum(loc.index)]).kind.function; + return obj.func_types[type_index]; + } + const sym = obj.symtable[@intFromEnum(loc.index)]; + const type_index = obj.functions[sym.index - obj.imported_functions_count].type_index; + return obj.func_types[type_index]; + }, } - return wasm.func_types.items[wasm.functions.get(.{ .file = loc.file, .index = symbol.index }).?.func.type_index]; } /// Returns the symbol index from a symbol of which its flag is set global, @@ -1508,7 +1630,7 @@ fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type { /// and then returns the index to it. pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8, lib_name: ?[]const u8) !Symbol.Index { _ = lib_name; - return wasm.zigObjectPtr().?.getGlobalSymbol(wasm.base.comp.gpa, name); + return wasm.zig_object.?.getGlobalSymbol(wasm.base.comp.gpa, name); } /// For a given `Nav`, find the given symbol index's atom, and create a relocation for the type. @@ -1519,7 +1641,7 @@ pub fn getNavVAddr( nav: InternPool.Nav.Index, reloc_info: link.File.RelocInfo, ) !u64 { - return wasm.zigObjectPtr().?.getNavVAddr(wasm, pt, nav, reloc_info); + return wasm.zig_object.?.getNavVAddr(wasm, pt, nav, reloc_info); } pub fn lowerUav( @@ -1529,11 +1651,11 @@ pub fn lowerUav( explicit_alignment: Alignment, src_loc: Zcu.LazySrcLoc, ) !codegen.GenResult { - return wasm.zigObjectPtr().?.lowerUav(wasm, pt, uav, explicit_alignment, src_loc); + return wasm.zig_object.?.lowerUav(wasm, pt, uav, explicit_alignment, src_loc); } pub fn getUavVAddr(wasm: *Wasm, uav: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 { - return wasm.zigObjectPtr().?.getUavVAddr(wasm, uav, reloc_info); + return wasm.zig_object.?.getUavVAddr(wasm, uav, reloc_info); } pub fn deleteExport( @@ -1542,7 +1664,7 @@ pub fn deleteExport( name: InternPool.NullTerminatedString, ) void { if (wasm.llvm_object) |_| return; - return wasm.zigObjectPtr().?.deleteExport(wasm, exported, name); + return wasm.zig_object.?.deleteExport(wasm, exported, name); } pub fn updateExports( @@ -1555,12 +1677,12 @@ pub fn updateExports( @panic("Attempted to compile for object format that was disabled by build configuration"); } if (wasm.llvm_object) |llvm_object| return llvm_object.updateExports(pt, exported, export_indices); - return wasm.zigObjectPtr().?.updateExports(wasm, pt, exported, export_indices); + return wasm.zig_object.?.updateExports(wasm, pt, exported, export_indices); } pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void { if (wasm.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); - return wasm.zigObjectPtr().?.freeDecl(wasm, decl_index); + return wasm.zig_object.?.freeDecl(wasm, decl_index); } /// Assigns indexes to all indirect functions. @@ -1570,7 +1692,7 @@ fn mapFunctionTable(wasm: *Wasm) void { var it = wasm.function_table.iterator(); var index: u32 = 1; while (it.next()) |entry| { - const symbol = entry.key_ptr.*.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(entry.key_ptr.*); if (symbol.isAlive()) { entry.value_ptr.* = index; index += 1; @@ -1586,7 +1708,7 @@ fn mapFunctionTable(wasm: *Wasm) void { } else if (index > 1) { log.debug("Appending indirect function table", .{}); const sym_loc = wasm.findGlobalSymbol("__indirect_function_table").?; - const symbol = sym_loc.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(sym_loc); const table = &wasm.tables.items[symbol.index - wasm.imported_tables_count]; table.limits = .{ .min = index, .max = index, .flags = 0x1 }; } @@ -1625,10 +1747,11 @@ fn allocateAtoms(wasm: *Wasm) !void { // Ensure we get the original symbol, so we verify the correct symbol on whether // it is dead or not and ensure an atom is removed when dead. // This is required as we may have parsed aliases into atoms. - const sym = if (wasm.file(symbol_loc.file)) |obj_file| - obj_file.symbol(symbol_loc.index).* - else - wasm.synthetic_symbols.items[@intFromEnum(symbol_loc.index)]; + const sym = switch (symbol_loc.file) { + .zig_object => wasm.zig_object.?.symbols.items[@intFromEnum(symbol_loc.index)], + .none => wasm.synthetic_symbols.items[@intFromEnum(symbol_loc.index)], + _ => wasm.objects.items[@intFromEnum(symbol_loc.file)].symtable[@intFromEnum(symbol_loc.index)], + }; // Dead symbols must be unlinked from the linked-list to prevent them // from being emit into the binary. @@ -1647,7 +1770,7 @@ fn allocateAtoms(wasm: *Wasm) !void { offset = @intCast(atom.alignment.forward(offset)); atom.offset = offset; log.debug("Atom '{s}' allocated from 0x{x:0>8} to 0x{x:0>8} size={d}", .{ - symbol_loc.getName(wasm), + wasm.symbolLocName(symbol_loc), offset, offset + atom.size, atom.size, @@ -1663,7 +1786,7 @@ fn allocateAtoms(wasm: *Wasm) !void { /// For each data symbol, sets the virtual address. fn allocateVirtualAddresses(wasm: *Wasm) void { for (wasm.resolved_symbols.keys()) |loc| { - const symbol = loc.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(loc); if (symbol.tag != .data or symbol.isDead()) { // Only data symbols have virtual addresses. // Dead symbols do not get allocated, so we don't need to set their virtual address either. @@ -1676,10 +1799,11 @@ fn allocateVirtualAddresses(wasm: *Wasm) void { const atom = wasm.getAtom(atom_index); const merge_segment = wasm.base.comp.config.output_mode != .Obj; - const segment_info = if (atom.file != .null) - wasm.file(atom.file).?.segmentInfo() - else - wasm.segment_info.values(); + const segment_info = switch (atom.file) { + .zig_object => wasm.zig_object.?.segment_info.items, + .none => wasm.segment_info.values(), + _ => wasm.objects.items[@intFromEnum(atom.file)].segment_info, + }; const segment_name = segment_info[symbol.index].outputName(merge_segment); const segment_index = wasm.data_segments.get(segment_name).?; const segment = wasm.segments.items[segment_index]; @@ -1737,13 +1861,12 @@ fn setupInitFunctions(wasm: *Wasm) !void { const gpa = wasm.base.comp.gpa; const diags = &wasm.base.comp.link_diags; // There's no constructors for Zig so we can simply search through linked object files only. - for (wasm.objects.items) |file_index| { - const object: Object = wasm.files.items(.data)[@intFromEnum(file_index)].object; + for (wasm.objects.items, 0..) |*object, object_index| { try wasm.init_funcs.ensureUnusedCapacity(gpa, object.init_funcs.len); for (object.init_funcs) |init_func| { const symbol = object.symtable[init_func.symbol_index]; const ty: std.wasm.Type = if (symbol.isUndefined()) ty: { - const imp: types.Import = object.findImport(symbol); + const imp: Import = object.findImport(symbol); break :ty object.func_types[imp.kind.function]; } else ty: { const func_index = symbol.index - object.imported_functions_count; @@ -1757,10 +1880,13 @@ fn setupInitFunctions(wasm: *Wasm) !void { log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)}); wasm.init_funcs.appendAssumeCapacity(.{ .index = @enumFromInt(init_func.symbol_index), - .file = file_index, + .file = @enumFromInt(object_index), .priority = init_func.priority, }); - try wasm.mark(.{ .index = @enumFromInt(init_func.symbol_index), .file = file_index }); + try wasm.mark(.{ + .index = @enumFromInt(init_func.symbol_index), + .file = @enumFromInt(object_index), + }); } } @@ -1834,7 +1960,7 @@ fn createSyntheticFunction( ) !void { const gpa = wasm.base.comp.gpa; const loc = wasm.findGlobalSymbol(symbol_name).?; // forgot to create symbol? - const symbol = loc.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(loc); if (symbol.isDead()) { return; } @@ -1843,13 +1969,13 @@ fn createSyntheticFunction( const func_index = wasm.imported_functions_count + @as(u32, @intCast(wasm.functions.count())); try wasm.functions.putNoClobber( gpa, - .{ .file = .null, .index = func_index }, + .{ .file = .none, .index = func_index }, .{ .func = .{ .type_index = ty_index }, .sym_index = loc.index }, ); symbol.index = func_index; // create the atom that will be output into the final binary - const atom_index = try wasm.createAtom(loc.index, .null); + const atom_index = try wasm.createAtom(loc.index, .none); const atom = wasm.getAtomPtr(atom_index); atom.size = @intCast(function_body.items.len); atom.code = function_body.moveToUnmanaged(); @@ -1866,13 +1992,13 @@ pub fn createFunction( function_body: *std.ArrayList(u8), relocations: *std.ArrayList(Relocation), ) !Symbol.Index { - return wasm.zigObjectPtr().?.createFunction(wasm, symbol_name, func_ty, function_body, relocations); + return wasm.zig_object.?.createFunction(wasm, symbol_name, func_ty, function_body, relocations); } /// If required, sets the function index in the `start` section. fn setupStartSection(wasm: *Wasm) !void { if (wasm.findGlobalSymbol("__wasm_init_memory")) |loc| { - wasm.entry = loc.getSymbol(wasm).index; + wasm.entry = wasm.symbolLocSymbol(loc).index; } } @@ -1884,7 +2010,7 @@ fn initializeTLSFunction(wasm: *Wasm) !void { if (!shared_memory) return; // ensure function is marked as we must emit it - wasm.findGlobalSymbol("__wasm_init_tls").?.getSymbol(wasm).mark(); + wasm.symbolLocSymbol(wasm.findGlobalSymbol("__wasm_init_tls").?).mark(); var function_body = std.ArrayList(u8).init(gpa); defer function_body.deinit(); @@ -1905,7 +2031,7 @@ fn initializeTLSFunction(wasm: *Wasm) !void { const tls_base_loc = wasm.findGlobalSymbol("__tls_base").?; try writer.writeByte(std.wasm.opcode(.global_set)); - try leb.writeUleb128(writer, tls_base_loc.getSymbol(wasm).index); + try leb.writeUleb128(writer, wasm.symbolLocSymbol(tls_base_loc).index); // load stack values for the bulk-memory operation { @@ -1933,8 +2059,8 @@ fn initializeTLSFunction(wasm: *Wasm) !void { // generated by the linker. if (wasm.findGlobalSymbol("__wasm_apply_global_tls_relocs")) |loc| { try writer.writeByte(std.wasm.opcode(.call)); - try leb.writeUleb128(writer, loc.getSymbol(wasm).index); - loc.getSymbol(wasm).mark(); + try leb.writeUleb128(writer, wasm.symbolLocSymbol(loc).index); + wasm.symbolLocSymbol(loc).mark(); } try writer.writeByte(std.wasm.opcode(.end)); @@ -1950,27 +2076,27 @@ fn setupImports(wasm: *Wasm) !void { const gpa = wasm.base.comp.gpa; log.debug("Merging imports", .{}); for (wasm.resolved_symbols.keys()) |symbol_loc| { - const obj_file = wasm.file(symbol_loc.file) orelse { + const object_id = symbol_loc.file.unwrap() orelse { // Synthetic symbols will already exist in the `import` section continue; }; - const symbol = symbol_loc.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(symbol_loc); if (symbol.isDead() or !symbol.requiresImport() or - std.mem.eql(u8, symbol_loc.getName(wasm), "__indirect_function_table")) + std.mem.eql(u8, wasm.symbolLocName(symbol_loc), "__indirect_function_table")) { continue; } - log.debug("Symbol '{s}' will be imported from the host", .{symbol_loc.getName(wasm)}); - const import = obj_file.import(symbol_loc.index); + log.debug("Symbol '{s}' will be imported from the host", .{wasm.symbolLocName(symbol_loc)}); + const import = objectImport(wasm, object_id, symbol_loc.index); // We copy the import to a new import to ensure the names contain references // to the internal string table, rather than of the object file. - const new_imp: types.Import = .{ - .module_name = try wasm.string_table.put(gpa, obj_file.string(import.module_name)), - .name = try wasm.string_table.put(gpa, obj_file.string(import.name)), + const new_imp: Import = .{ + .module_name = try wasm.string_table.put(gpa, objectString(wasm, object_id, import.module_name)), + .name = try wasm.string_table.put(gpa, objectString(wasm, object_id, import.name)), .kind = import.kind, }; // TODO: De-duplicate imports when they contain the same names and type @@ -1983,8 +2109,8 @@ fn setupImports(wasm: *Wasm) !void { var table_index: u32 = 0; var it = wasm.imports.iterator(); while (it.next()) |entry| { - const symbol = entry.key_ptr.*.getSymbol(wasm); - const import: types.Import = entry.value_ptr.*; + const symbol = wasm.symbolLocSymbol(entry.key_ptr.*); + const import: Import = entry.value_ptr.*; switch (import.kind) { .function => { symbol.index = function_index; @@ -2021,12 +2147,12 @@ fn mergeSections(wasm: *Wasm) !void { defer removed_duplicates.deinit(); for (wasm.resolved_symbols.keys()) |sym_loc| { - const obj_file = wasm.file(sym_loc.file) orelse { + const object_id = sym_loc.file.unwrap() orelse { // Synthetic symbols already live in the corresponding sections. continue; }; - const symbol = obj_file.symbol(sym_loc.index); + const symbol = objectSymbol(wasm, object_id, sym_loc.index); if (symbol.isDead() or symbol.isUndefined()) { // Skip undefined symbols as they go in the `import` section continue; @@ -2044,7 +2170,7 @@ fn mergeSections(wasm: *Wasm) !void { // we only emit a single function, instead of duplicates. // we favor keeping the global over a local. const original_loc: SymbolLoc = .{ .file = gop.key_ptr.file, .index = gop.value_ptr.sym_index }; - const original_sym = original_loc.getSymbol(wasm); + const original_sym = wasm.symbolLocSymbol(original_loc); if (original_sym.isLocal() and symbol.isGlobal()) { original_sym.unmark(); try wasm.discarded.put(gpa, original_loc, sym_loc); @@ -2056,20 +2182,23 @@ fn mergeSections(wasm: *Wasm) !void { continue; } } - gop.value_ptr.* = .{ .func = obj_file.function(sym_loc.index), .sym_index = sym_loc.index }; + gop.value_ptr.* = .{ + .func = objectFunction(wasm, object_id, sym_loc.index), + .sym_index = sym_loc.index, + }; symbol.index = @as(u32, @intCast(gop.index)) + wasm.imported_functions_count; }, .global => { - const index = symbol.index - obj_file.importedFunctions(); - const original_global = obj_file.globals()[index]; + const index = symbol.index - objectImportedFunctions(wasm, object_id); + const original_global = objectGlobals(wasm, object_id)[index]; symbol.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count; try wasm.wasm_globals.append(gpa, original_global); }, .table => { - const index = symbol.index - obj_file.importedFunctions(); + const index = symbol.index - objectImportedFunctions(wasm, object_id); // assert it's a regular relocatable object file as `ZigObject` will never // contain a table. - const original_table = obj_file.object.tables[index]; + const original_table = wasm.objectById(object_id).?.tables[index]; symbol.index = @as(u32, @intCast(wasm.tables.items.len)) + wasm.imported_tables_count; try wasm.tables.append(gpa, original_table); }, @@ -2081,7 +2210,7 @@ fn mergeSections(wasm: *Wasm) !void { // For any removed duplicates, remove them from the resolved symbols list for (removed_duplicates.items) |sym_loc| { assert(wasm.resolved_symbols.swapRemove(sym_loc)); - gc_log.debug("Removed duplicate for function '{s}'", .{sym_loc.getName(wasm)}); + gc_log.debug("Removed duplicate for function '{s}'", .{wasm.symbolLocName(sym_loc)}); } log.debug("Merged ({d}) functions", .{wasm.functions.count()}); @@ -2102,26 +2231,26 @@ fn mergeTypes(wasm: *Wasm) !void { defer dirty.deinit(); for (wasm.resolved_symbols.keys()) |sym_loc| { - const obj_file = wasm.file(sym_loc.file) orelse { + const object_id = sym_loc.file.unwrap() orelse { // zig code-generated symbols are already present in final type section continue; }; - const symbol = obj_file.symbol(sym_loc.index); + const symbol = objectSymbol(wasm, object_id, sym_loc.index); if (symbol.tag != .function or symbol.isDead()) { // Only functions have types. Only retrieve the type of referenced functions. continue; } if (symbol.isUndefined()) { - log.debug("Adding type from extern function '{s}'", .{sym_loc.getName(wasm)}); - const import: *types.Import = wasm.imports.getPtr(sym_loc) orelse continue; - const original_type = obj_file.funcTypes()[import.kind.function]; + log.debug("Adding type from extern function '{s}'", .{wasm.symbolLocName(sym_loc)}); + const import: *Import = wasm.imports.getPtr(sym_loc) orelse continue; + const original_type = objectFuncTypes(wasm, object_id)[import.kind.function]; import.kind.function = try wasm.putOrGetFuncType(original_type); } else if (!dirty.contains(symbol.index)) { - log.debug("Adding type from function '{s}'", .{sym_loc.getName(wasm)}); + log.debug("Adding type from function '{s}'", .{wasm.symbolLocName(sym_loc)}); const func = &wasm.functions.values()[symbol.index - wasm.imported_functions_count].func; - func.type_index = try wasm.putOrGetFuncType(obj_file.funcTypes()[func.type_index]); + func.type_index = try wasm.putOrGetFuncType(objectFuncTypes(wasm, object_id)[func.type_index]); dirty.putAssumeCapacityNoClobber(symbol.index, {}); } } @@ -2142,7 +2271,7 @@ fn checkExportNames(wasm: *Wasm) !void { continue; }; - const symbol = loc.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(loc); symbol.setFlag(.WASM_SYM_EXPORTED); } @@ -2159,15 +2288,15 @@ fn setupExports(wasm: *Wasm) !void { log.debug("Building exports from symbols", .{}); for (wasm.resolved_symbols.keys()) |sym_loc| { - const symbol = sym_loc.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(sym_loc); if (!symbol.isExported(comp.config.rdynamic)) continue; - const sym_name = sym_loc.getName(wasm); - const export_name = if (sym_loc.file == .null) + const sym_name = wasm.symbolLocName(sym_loc); + const export_name = if (sym_loc.file == .none) symbol.name else try wasm.string_table.put(gpa, sym_name); - const exp: types.Export = if (symbol.tag == .data) exp: { + const exp: Export = if (symbol.tag == .data) exp: { const global_index = @as(u32, @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len)); try wasm.wasm_globals.append(gpa, .{ .global_type = .{ .valtype = .i32, .mutable = false }, @@ -2206,7 +2335,7 @@ fn setupStart(wasm: *Wasm) !void { return error.FlushFailure; }; - const symbol = symbol_loc.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(symbol_loc); if (symbol.tag != .function) { var err = try diags.addErrorWithNotes(0); try err.addMsg("Entry symbol '{s}' is not a function", .{entry_name}); @@ -2240,7 +2369,7 @@ fn setupMemory(wasm: *Wasm) !void { const is_obj = comp.config.output_mode == .Obj; const stack_ptr = if (wasm.findGlobalSymbol("__stack_pointer")) |loc| index: { - const sym = loc.getSymbol(wasm); + const sym = wasm.symbolLocSymbol(loc); break :index sym.index - wasm.imported_globals_count; } else null; @@ -2262,15 +2391,15 @@ fn setupMemory(wasm: *Wasm) !void { // set TLS-related symbols if (mem.eql(u8, entry.key_ptr.*, ".tdata")) { if (wasm.findGlobalSymbol("__tls_size")) |loc| { - const sym = loc.getSymbol(wasm); + const sym = wasm.symbolLocSymbol(loc); wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.size); } if (wasm.findGlobalSymbol("__tls_align")) |loc| { - const sym = loc.getSymbol(wasm); + const sym = wasm.symbolLocSymbol(loc); wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.alignment.toByteUnits().?); } if (wasm.findGlobalSymbol("__tls_base")) |loc| { - const sym = loc.getSymbol(wasm); + const sym = wasm.symbolLocSymbol(loc); wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = if (shared_memory) @as(i32, 0) else @@ -2288,7 +2417,7 @@ fn setupMemory(wasm: *Wasm) !void { // align to pointer size memory_ptr = mem.alignForward(u64, memory_ptr, 4); const loc = try wasm.createSyntheticSymbol("__wasm_init_memory_flag", .data); - const sym = loc.getSymbol(wasm); + const sym = wasm.symbolLocSymbol(loc); sym.mark(); sym.virtual_address = @as(u32, @intCast(memory_ptr)); memory_ptr += 4; @@ -2305,7 +2434,7 @@ fn setupMemory(wasm: *Wasm) !void { // One of the linked object files has a reference to the __heap_base symbol. // We must set its virtual address so it can be used in relocations. if (wasm.findGlobalSymbol("__heap_base")) |loc| { - const symbol = loc.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(loc); symbol.virtual_address = @intCast(heap_alignment.forward(memory_ptr)); } @@ -2335,7 +2464,7 @@ fn setupMemory(wasm: *Wasm) !void { log.debug("Total memory pages: {d}", .{wasm.memories.limits.min}); if (wasm.findGlobalSymbol("__heap_end")) |loc| { - const symbol = loc.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(loc); symbol.virtual_address = @as(u32, @intCast(memory_ptr)); } @@ -2364,18 +2493,17 @@ fn setupMemory(wasm: *Wasm) !void { /// From a given object's index and the index of the segment, returns the corresponding /// index of the segment within the final data section. When the segment does not yet /// exist, a new one will be initialized and appended. The new index will be returned in that case. -pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: Symbol.Index) !u32 { +pub fn getMatchingSegment(wasm: *Wasm, object_id: ObjectId, symbol_index: Symbol.Index) !u32 { const comp = wasm.base.comp; const gpa = comp.gpa; const diags = &wasm.base.comp.link_diags; - const obj_file = wasm.file(file_index).?; - const symbol = obj_file.symbols()[@intFromEnum(symbol_index)]; + const symbol = objectSymbols(wasm, object_id)[@intFromEnum(symbol_index)]; const index: u32 = @intCast(wasm.segments.items.len); const shared_memory = comp.config.shared_memory; switch (symbol.tag) { .data => { - const segment_info = obj_file.segmentInfo()[symbol.index]; + const segment_info = objectSegmentInfo(wasm, object_id)[symbol.index]; const merge_segment = comp.config.output_mode != .Obj; const result = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(merge_segment)); if (!result.found_existing) { @@ -2404,7 +2532,7 @@ pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: Sym break :blk index; }, .section => { - const section_name = obj_file.symbolName(symbol_index); + const section_name = objectSymbolName(wasm, object_id, symbol_index); if (mem.eql(u8, section_name, ".debug_info")) { return wasm.debug_info_index orelse blk: { wasm.debug_info_index = index; @@ -2456,7 +2584,7 @@ pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: Sym } else { var err = try diags.addErrorWithNotes(1); try err.addMsg("found unknown section '{s}'", .{section_name}); - try err.addNote("defined in '{s}'", .{obj_file.path()}); + try err.addNote("defined in '{s}'", .{objectPath(wasm, object_id)}); return error.UnexpectedValue; } }, @@ -2523,7 +2651,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no const link_libcpp = comp.config.link_libcpp; const wasi_exec_model = comp.config.wasi_exec_model; - if (wasm.zigObjectPtr()) |zig_object| { + if (wasm.zig_object) |zig_object| { try zig_object.flushModule(wasm, tid); } @@ -2578,17 +2706,17 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no log.warn("Unexpected file format at path: '{s}'", .{path}); } - if (wasm.zig_object_index != .null) { - try wasm.resolveSymbolsInObject(wasm.zig_object_index); + if (wasm.zig_object != null) { + try wasm.resolveSymbolsInObject(.zig_object); } if (diags.hasErrors()) return error.FlushFailure; - for (wasm.objects.items) |object_index| { - try wasm.resolveSymbolsInObject(object_index); + for (0..wasm.objects.items.len) |object_index| { + try wasm.resolveSymbolsInObject(@enumFromInt(object_index)); } if (diags.hasErrors()) return error.FlushFailure; var emit_features_count: u32 = 0; - var enabled_features: [@typeInfo(types.Feature.Tag).@"enum".fields.len]bool = undefined; + var enabled_features: [@typeInfo(Feature.Tag).@"enum".fields.len]bool = undefined; try wasm.validateFeatures(&enabled_features, &emit_features_count); try wasm.resolveSymbolsInArchives(); if (diags.hasErrors()) return error.FlushFailure; @@ -2622,7 +2750,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no /// Writes the WebAssembly in-memory module to the file fn writeToFile( wasm: *Wasm, - enabled_features: [@typeInfo(types.Feature.Tag).@"enum".fields.len]bool, + enabled_features: [@typeInfo(Feature.Tag).@"enum".fields.len]bool, feature_count: u32, arena: Allocator, ) !void { @@ -2688,14 +2816,14 @@ fn writeToFile( var it = wasm.imports.iterator(); while (it.next()) |entry| { - assert(entry.key_ptr.*.getSymbol(wasm).isUndefined()); + assert(wasm.symbolLocSymbol(entry.key_ptr.*).isUndefined()); const import = entry.value_ptr.*; try wasm.emitImport(binary_writer, import); } if (import_memory) { const mem_name = if (is_obj) "__linear_memory" else "memory"; - const mem_imp: types.Import = .{ + const mem_imp: Import = .{ .module_name = try wasm.string_table.put(gpa, wasm.host_name), .name = try wasm.string_table.put(gpa, mem_name), .kind = .{ .memory = wasm.memories.limits }, @@ -2829,7 +2957,7 @@ fn writeToFile( const header_offset = try reserveVecSectionHeader(&binary_bytes); const table_loc = wasm.findGlobalSymbol("__indirect_function_table").?; - const table_sym = table_loc.getSymbol(wasm); + const table_sym = wasm.symbolLocSymbol(table_loc); const flags: u32 = if (table_sym.index == 0) 0x0 else 0x02; // passive with implicit 0-index table or set table index manually try leb.writeUleb128(binary_writer, flags); @@ -2843,7 +2971,7 @@ fn writeToFile( try leb.writeUleb128(binary_writer, @as(u32, @intCast(wasm.function_table.count()))); var symbol_it = wasm.function_table.keyIterator(); while (symbol_it.next()) |symbol_loc_ptr| { - const sym = symbol_loc_ptr.getSymbol(wasm); + const sym = wasm.symbolLocSymbol(symbol_loc_ptr.*); std.debug.assert(sym.isAlive()); std.debug.assert(sym.index < wasm.functions.count() + wasm.imported_functions_count); try leb.writeUleb128(binary_writer, sym.index); @@ -3183,7 +3311,7 @@ fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []con try leb.writeUleb128(writer, features_count); for (enabled_features, 0..) |enabled, feature_index| { if (enabled) { - const feature: types.Feature = .{ .prefix = .used, .tag = @as(types.Feature.Tag, @enumFromInt(feature_index)) }; + const feature: Feature = .{ .prefix = .used, .tag = @as(Feature.Tag, @enumFromInt(feature_index)) }; try leb.writeUleb128(writer, @intFromEnum(feature.prefix)); var buf: [100]u8 = undefined; const string = try std.fmt.bufPrint(&buf, "{}", .{feature.tag}); @@ -3219,11 +3347,11 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem var segments = try std.ArrayList(Name).initCapacity(arena, wasm.data_segments.count()); for (wasm.resolved_symbols.keys()) |sym_loc| { - const symbol = sym_loc.getSymbol(wasm).*; + const symbol = wasm.symbolLocSymbol(sym_loc).*; if (symbol.isDead()) { continue; } - const name = sym_loc.getName(wasm); + const name = wasm.symbolLocName(sym_loc); switch (symbol.tag) { .function => { const gop = funcs.getOrPutAssumeCapacity(symbol.index); @@ -3320,7 +3448,7 @@ fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void { try writer.writeByte(std.wasm.opcode(.end)); } -fn emitImport(wasm: *Wasm, writer: anytype, import: types.Import) !void { +fn emitImport(wasm: *Wasm, writer: anytype, import: Import) !void { const module_name = wasm.string_table.get(import.module_name); try leb.writeUleb128(writer, @as(u32, @intCast(module_name.len))); try writer.writeAll(module_name); @@ -3800,7 +3928,7 @@ fn emitLinkSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: // meta data version, which is currently '2' try leb.writeUleb128(writer, @as(u32, 2)); - // For each subsection type (found in types.Subsection) we can emit a section. + // For each subsection type (found in Subsection) we can emit a section. // Currently, we only support emitting segment info and the symbol table. try wasm.emitSymbolTable(binary_bytes, symbol_table); try wasm.emitSegmentInfo(binary_bytes); @@ -3812,12 +3940,12 @@ fn emitLinkSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { const writer = binary_bytes.writer(); - try leb.writeUleb128(writer, @intFromEnum(types.SubsectionType.WASM_SYMBOL_TABLE)); + try leb.writeUleb128(writer, @intFromEnum(SubsectionType.WASM_SYMBOL_TABLE)); const table_offset = binary_bytes.items.len; var symbol_count: u32 = 0; for (wasm.resolved_symbols.keys()) |sym_loc| { - const symbol = sym_loc.getSymbol(wasm).*; + const symbol = wasm.symbolLocSymbol(sym_loc).*; if (symbol.tag == .dead) continue; // Do not emit dead symbols try symbol_table.putNoClobber(sym_loc, symbol_count); symbol_count += 1; @@ -3825,7 +3953,7 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: try leb.writeUleb128(writer, @intFromEnum(symbol.tag)); try leb.writeUleb128(writer, symbol.flags); - const sym_name = sym_loc.getName(wasm); + const sym_name = wasm.symbolLocName(sym_loc); switch (symbol.tag) { .data => { try leb.writeUleb128(writer, @as(u32, @intCast(sym_name.len))); @@ -3860,7 +3988,7 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void { const writer = binary_bytes.writer(); - try leb.writeUleb128(writer, @intFromEnum(types.SubsectionType.WASM_SEGMENT_INFO)); + try leb.writeUleb128(writer, @intFromEnum(SubsectionType.WASM_SEGMENT_INFO)); const segment_offset = binary_bytes.items.len; try leb.writeUleb128(writer, @as(u32, @intCast(wasm.segment_info.count()))); @@ -4030,22 +4158,22 @@ pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 { /// Asserts declaration has an associated `Atom`. /// Returns the index into the list of types. pub fn storeNavType(wasm: *Wasm, nav: InternPool.Nav.Index, func_type: std.wasm.Type) !u32 { - return wasm.zigObjectPtr().?.storeDeclType(wasm.base.comp.gpa, nav, func_type); + return wasm.zig_object.?.storeDeclType(wasm.base.comp.gpa, nav, func_type); } /// Returns the symbol index of the error name table. /// /// When the symbol does not yet exist, it will create a new one instead. -pub fn getErrorTableSymbol(wasm_file: *Wasm, pt: Zcu.PerThread) !u32 { - const sym_index = try wasm_file.zigObjectPtr().?.getErrorTableSymbol(wasm_file, pt); +pub fn getErrorTableSymbol(wasm: *Wasm, pt: Zcu.PerThread) !u32 { + const sym_index = try wasm.zig_object.?.getErrorTableSymbol(wasm, pt); return @intFromEnum(sym_index); } /// For a given `InternPool.DeclIndex` returns its corresponding `Atom.Index`. /// When the index was not found, a new `Atom` will be created, and its index will be returned. /// The newly created Atom is empty with default fields as specified by `Atom.empty`. -pub fn getOrCreateAtomForNav(wasm_file: *Wasm, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !Atom.Index { - return wasm_file.zigObjectPtr().?.getOrCreateAtomForNav(wasm_file, pt, nav); +pub fn getOrCreateAtomForNav(wasm: *Wasm, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !Atom.Index { + return wasm.zig_object.?.getOrCreateAtomForNav(wasm, pt, nav); } /// Verifies all resolved symbols and checks whether itself needs to be marked alive, @@ -4058,7 +4186,7 @@ fn markReferences(wasm: *Wasm) !void { const comp = wasm.base.comp; for (wasm.resolved_symbols.keys()) |sym_loc| { - const sym = sym_loc.getSymbol(wasm); + const sym = wasm.symbolLocSymbol(sym_loc); if (sym.isExported(comp.config.rdynamic) or sym.isNoStrip() or !do_garbage_collect) { try wasm.mark(sym_loc); continue; @@ -4067,8 +4195,8 @@ fn markReferences(wasm: *Wasm) !void { // Debug sections may require to be parsed and marked when it contains // relocations to alive symbols. if (sym.tag == .section and comp.config.debug_format != .strip) { - const obj_file = wasm.file(sym_loc.file) orelse continue; // Incremental debug info is done independently - _ = try obj_file.parseSymbolIntoAtom(wasm, sym_loc.index); + const object_id = sym_loc.file.unwrap() orelse continue; // Incremental debug info is done independently + _ = try wasm.parseSymbolIntoAtom(object_id, sym_loc.index); sym.mark(); } } @@ -4077,7 +4205,7 @@ fn markReferences(wasm: *Wasm) !void { /// Marks a symbol as 'alive' recursively so itself and any references it contains to /// other symbols will not be omit from the binary. fn mark(wasm: *Wasm, loc: SymbolLoc) !void { - const symbol = loc.getSymbol(wasm); + const symbol = wasm.symbolLocSymbol(loc); if (symbol.isAlive()) { // Symbol is already marked alive, including its references. // This means we can skip it so we don't end up marking the same symbols @@ -4085,22 +4213,22 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) !void { return; } symbol.mark(); - gc_log.debug("Marked symbol '{s}'", .{loc.getName(wasm)}); + gc_log.debug("Marked symbol '{s}'", .{wasm.symbolLocName(loc)}); if (symbol.isUndefined()) { // undefined symbols do not have an associated `Atom` and therefore also // do not contain relocations. return; } - const atom_index = if (wasm.file(loc.file)) |obj_file| - try obj_file.parseSymbolIntoAtom(wasm, loc.index) + const atom_index = if (loc.file.unwrap()) |object_id| + try wasm.parseSymbolIntoAtom(object_id, loc.index) else wasm.symbol_atom.get(loc) orelse return; const atom = wasm.getAtom(atom_index); for (atom.relocs.items) |reloc| { const target_loc: SymbolLoc = .{ .index = @enumFromInt(reloc.index), .file = loc.file }; - try wasm.mark(target_loc.finalLoc(wasm)); + try wasm.mark(wasm.symbolLocFinalLoc(target_loc)); } } @@ -4110,3 +4238,566 @@ fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8 .command => "_start", }; } + +pub const Atom = struct { + /// Represents the index of the file this atom was generated from. + /// This is `none` when the atom was generated by a synthetic linker symbol. + file: OptionalObjectId, + /// symbol index of the symbol representing this atom + sym_index: Symbol.Index, + /// Size of the atom, used to calculate section sizes in the final binary + size: u32 = 0, + /// List of relocations belonging to this atom + relocs: std.ArrayListUnmanaged(Relocation) = .empty, + /// Contains the binary data of an atom, which can be non-relocated + code: std.ArrayListUnmanaged(u8) = .empty, + /// For code this is 1, for data this is set to the highest value of all segments + alignment: Wasm.Alignment = .@"1", + /// Offset into the section where the atom lives, this already accounts + /// for alignment. + offset: u32 = 0, + /// The original offset within the object file. This value is subtracted from + /// relocation offsets to determine where in the `data` to rewrite the value + original_offset: u32 = 0, + /// Previous atom in relation to this atom. + /// is null when this atom is the first in its order + prev: Atom.Index = .null, + /// Contains atoms local to a decl, all managed by this `Atom`. + /// When the parent atom is being freed, it will also do so for all local atoms. + locals: std.ArrayListUnmanaged(Atom.Index) = .empty, + + /// Represents the index of an Atom where `null` is considered + /// an invalid atom. + pub const Index = enum(u32) { + null = std.math.maxInt(u32), + _, + }; + + /// Frees all resources owned by this `Atom`. + pub fn deinit(atom: *Atom, gpa: std.mem.Allocator) void { + atom.relocs.deinit(gpa); + atom.code.deinit(gpa); + atom.locals.deinit(gpa); + atom.* = undefined; + } + + /// Sets the length of relocations and code to '0', + /// effectively resetting them and allowing them to be re-populated. + pub fn clear(atom: *Atom) void { + atom.relocs.clearRetainingCapacity(); + atom.code.clearRetainingCapacity(); + } + + pub fn format(atom: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { + _ = fmt; + _ = options; + try writer.print("Atom{{ .sym_index = {d}, .alignment = {d}, .size = {d}, .offset = 0x{x:0>8} }}", .{ + @intFromEnum(atom.sym_index), + atom.alignment, + atom.size, + atom.offset, + }); + } + + /// Returns the location of the symbol that represents this `Atom` + pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc { + return .{ + .file = atom.file, + .index = atom.sym_index, + }; + } + + /// Resolves the relocations within the atom, writing the new value + /// at the calculated offset. + pub fn resolveRelocs(atom: *Atom, wasm: *const Wasm) void { + if (atom.relocs.items.len == 0) return; + const symbol_name = wasm.symbolLocName(atom.symbolLoc()); + log.debug("Resolving relocs in atom '{s}' count({d})", .{ + symbol_name, + atom.relocs.items.len, + }); + + for (atom.relocs.items) |reloc| { + const value = atom.relocationValue(reloc, wasm); + 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.relocation_type) { + .R_WASM_TABLE_INDEX_I32, + .R_WASM_FUNCTION_OFFSET_I32, + .R_WASM_GLOBAL_INDEX_I32, + .R_WASM_MEMORY_ADDR_I32, + .R_WASM_SECTION_OFFSET_I32, + => std.mem.writeInt(u32, atom.code.items[reloc.offset - atom.original_offset ..][0..4], @as(u32, @truncate(value)), .little), + .R_WASM_TABLE_INDEX_I64, + .R_WASM_MEMORY_ADDR_I64, + => std.mem.writeInt(u64, atom.code.items[reloc.offset - atom.original_offset ..][0..8], value, .little), + .R_WASM_GLOBAL_INDEX_LEB, + .R_WASM_EVENT_INDEX_LEB, + .R_WASM_FUNCTION_INDEX_LEB, + .R_WASM_MEMORY_ADDR_LEB, + .R_WASM_MEMORY_ADDR_SLEB, + .R_WASM_TABLE_INDEX_SLEB, + .R_WASM_TABLE_NUMBER_LEB, + .R_WASM_TYPE_INDEX_LEB, + .R_WASM_MEMORY_ADDR_TLS_SLEB, + => leb.writeUnsignedFixed(5, atom.code.items[reloc.offset - atom.original_offset ..][0..5], @as(u32, @truncate(value))), + .R_WASM_MEMORY_ADDR_LEB64, + .R_WASM_MEMORY_ADDR_SLEB64, + .R_WASM_TABLE_INDEX_SLEB64, + .R_WASM_MEMORY_ADDR_TLS_SLEB64, + => leb.writeUnsignedFixed(10, atom.code.items[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 relocationValue(atom: Atom, relocation: Relocation, wasm: *const Wasm) u64 { + const target_loc = wasm.symbolLocFinalLoc(.{ + .file = atom.file, + .index = @enumFromInt(relocation.index), + }); + const symbol = wasm.symbolLocSymbol(target_loc); + if (relocation.relocation_type != .R_WASM_TYPE_INDEX_LEB and + symbol.tag != .section and + symbol.isDead()) + { + const val = atom.thombstone(wasm) orelse relocation.addend; + return @bitCast(val); + } + switch (relocation.relocation_type) { + .R_WASM_FUNCTION_INDEX_LEB => return symbol.index, + .R_WASM_TABLE_NUMBER_LEB => return symbol.index, + .R_WASM_TABLE_INDEX_I32, + .R_WASM_TABLE_INDEX_I64, + .R_WASM_TABLE_INDEX_SLEB, + .R_WASM_TABLE_INDEX_SLEB64, + => return wasm.function_table.get(.{ .file = atom.file, .index = @enumFromInt(relocation.index) }) orelse 0, + .R_WASM_TYPE_INDEX_LEB => { + const object_id = atom.file.unwrap() orelse return relocation.index; + const original_type = objectFuncTypes(wasm, object_id)[relocation.index]; + return wasm.getTypeIndex(original_type).?; + }, + .R_WASM_GLOBAL_INDEX_I32, + .R_WASM_GLOBAL_INDEX_LEB, + => return symbol.index, + .R_WASM_MEMORY_ADDR_I32, + .R_WASM_MEMORY_ADDR_I64, + .R_WASM_MEMORY_ADDR_LEB, + .R_WASM_MEMORY_ADDR_LEB64, + .R_WASM_MEMORY_ADDR_SLEB, + .R_WASM_MEMORY_ADDR_SLEB64, + => { + std.debug.assert(symbol.tag == .data); + if (symbol.isUndefined()) { + return 0; + } + const va: i33 = @intCast(symbol.virtual_address); + return @intCast(va + relocation.addend); + }, + .R_WASM_EVENT_INDEX_LEB => return symbol.index, + .R_WASM_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 = @intCast(target_atom.offset); + return @intCast(rel_value + relocation.addend); + }, + .R_WASM_FUNCTION_OFFSET_I32 => { + if (symbol.isUndefined()) { + const val = atom.thombstone(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 = @intCast(target_atom.offset); + return @intCast(rel_value + relocation.addend); + }, + .R_WASM_MEMORY_ADDR_TLS_SLEB, + .R_WASM_MEMORY_ADDR_TLS_SLEB64, + => { + const va: i33 = @intCast(symbol.virtual_address); + return @intCast(va + relocation.addend); + }, + } + } + + // For a given `Atom` returns whether it has a thombstone value or not. + /// This defines whether we want a specific value when a section is dead. + fn thombstone(atom: Atom, wasm: *const Wasm) ?i64 { + const atom_name = wasm.symbolLocName(atom.symbolLoc()); + if (std.mem.eql(u8, atom_name, ".debug_ranges") or std.mem.eql(u8, atom_name, ".debug_loc")) { + return -2; + } else if (std.mem.startsWith(u8, atom_name, ".debug_")) { + return -1; + } + return null; + } +}; + +pub const Relocation = struct { + /// Represents the type of the `Relocation` + relocation_type: RelocationType, + /// Offset of the value to rewrite relative to the relevant section's contents. + /// When `offset` is zero, its position is immediately after the id and size of the section. + offset: u32, + /// The index of the symbol used. + /// When the type is `R_WASM_TYPE_INDEX_LEB`, it represents the index of the type. + index: u32, + /// Addend to add to the address. + /// This field is only non-zero for `R_WASM_MEMORY_ADDR_*`, `R_WASM_FUNCTION_OFFSET_I32` and `R_WASM_SECTION_OFFSET_I32`. + addend: i32 = 0, + + /// All possible relocation types currently existing. + /// This enum is exhaustive as the spec is WIP and new types + /// can be added which means that a generated binary will be invalid, + /// so instead we will show an error in such cases. + pub const RelocationType = enum(u8) { + R_WASM_FUNCTION_INDEX_LEB = 0, + R_WASM_TABLE_INDEX_SLEB = 1, + R_WASM_TABLE_INDEX_I32 = 2, + R_WASM_MEMORY_ADDR_LEB = 3, + R_WASM_MEMORY_ADDR_SLEB = 4, + R_WASM_MEMORY_ADDR_I32 = 5, + R_WASM_TYPE_INDEX_LEB = 6, + R_WASM_GLOBAL_INDEX_LEB = 7, + R_WASM_FUNCTION_OFFSET_I32 = 8, + R_WASM_SECTION_OFFSET_I32 = 9, + R_WASM_EVENT_INDEX_LEB = 10, + R_WASM_GLOBAL_INDEX_I32 = 13, + R_WASM_MEMORY_ADDR_LEB64 = 14, + R_WASM_MEMORY_ADDR_SLEB64 = 15, + R_WASM_MEMORY_ADDR_I64 = 16, + R_WASM_TABLE_INDEX_SLEB64 = 18, + R_WASM_TABLE_INDEX_I64 = 19, + R_WASM_TABLE_NUMBER_LEB = 20, + R_WASM_MEMORY_ADDR_TLS_SLEB = 21, + R_WASM_MEMORY_ADDR_TLS_SLEB64 = 25, + + /// Returns true for relocation types where the `addend` field is present. + pub fn addendIsPresent(self: RelocationType) bool { + return switch (self) { + .R_WASM_MEMORY_ADDR_LEB, + .R_WASM_MEMORY_ADDR_SLEB, + .R_WASM_MEMORY_ADDR_I32, + .R_WASM_MEMORY_ADDR_LEB64, + .R_WASM_MEMORY_ADDR_SLEB64, + .R_WASM_MEMORY_ADDR_I64, + .R_WASM_MEMORY_ADDR_TLS_SLEB, + .R_WASM_MEMORY_ADDR_TLS_SLEB64, + .R_WASM_FUNCTION_OFFSET_I32, + .R_WASM_SECTION_OFFSET_I32, + => true, + else => false, + }; + } + }; + + /// Verifies the relocation type of a given `Relocation` and returns + /// true when the relocation references a function call or address to a function. + pub fn isFunction(self: Relocation) bool { + return switch (self.relocation_type) { + .R_WASM_FUNCTION_INDEX_LEB, + .R_WASM_TABLE_INDEX_SLEB, + => true, + else => false, + }; + } + + pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { + _ = fmt; + _ = options; + try writer.print("{s} offset=0x{x:0>6} symbol={d}", .{ + @tagName(self.relocation_type), + self.offset, + self.index, + }); + } +}; + +/// Unlike the `Import` object defined by the wasm spec, and existing +/// in the std.wasm namespace, this construct saves the 'module name' and 'name' +/// of the import using offsets into a string table, rather than the slices itself. +/// This saves us (potentially) 24 bytes per import on 64bit machines. +pub const Import = struct { + module_name: u32, + name: u32, + kind: std.wasm.Import.Kind, +}; + +/// Unlike the `Export` object defined by the wasm spec, and existing +/// in the std.wasm namespace, this construct saves the 'name' +/// of the export using offsets into a string table, rather than the slice itself. +/// This saves us (potentially) 12 bytes per export on 64bit machines. +pub const Export = struct { + name: u32, + index: u32, + kind: std.wasm.ExternalKind, +}; + +pub const SubsectionType = enum(u8) { + WASM_SEGMENT_INFO = 5, + WASM_INIT_FUNCS = 6, + WASM_COMDAT_INFO = 7, + WASM_SYMBOL_TABLE = 8, +}; + +pub const Alignment = @import("../InternPool.zig").Alignment; + +pub const NamedSegment = struct { + /// Segment's name, encoded as UTF-8 bytes. + name: []const u8, + /// The required alignment of the segment, encoded as a power of 2 + alignment: Alignment, + /// Bitfield containing flags for a segment + flags: u32, + + pub fn isTLS(segment: NamedSegment) bool { + return segment.flags & @intFromEnum(Flags.WASM_SEG_FLAG_TLS) != 0; + } + + /// Returns the name as how it will be output into the final object + /// file or binary. When `merge_segments` is true, this will return the + /// short name. i.e. ".rodata". When false, it returns the entire name instead. + pub fn outputName(segment: NamedSegment, merge_segments: bool) []const u8 { + if (segment.isTLS()) { + return ".tdata"; + } else if (!merge_segments) { + return segment.name; + } else if (std.mem.startsWith(u8, segment.name, ".rodata.")) { + return ".rodata"; + } else if (std.mem.startsWith(u8, segment.name, ".text.")) { + return ".text"; + } else if (std.mem.startsWith(u8, segment.name, ".data.")) { + return ".data"; + } else if (std.mem.startsWith(u8, segment.name, ".bss.")) { + return ".bss"; + } + return segment.name; + } + + pub const Flags = enum(u32) { + WASM_SEG_FLAG_STRINGS = 0x1, + WASM_SEG_FLAG_TLS = 0x2, + }; +}; + +pub const InitFunc = struct { + /// Priority of the init function + priority: u32, + /// The symbol index of init function (not the function index). + symbol_index: u32, +}; + +pub const Comdat = struct { + name: []const u8, + /// Must be zero, no flags are currently defined by the tool-convention. + flags: u32, + symbols: []const ComdatSym, +}; + +pub const ComdatSym = struct { + kind: @This().Type, + /// Index of the data segment/function/global/event/table within a WASM module. + /// The object must not be an import. + index: u32, + + pub const Type = enum(u8) { + WASM_COMDAT_DATA = 0, + WASM_COMDAT_FUNCTION = 1, + WASM_COMDAT_GLOBAL = 2, + WASM_COMDAT_EVENT = 3, + WASM_COMDAT_TABLE = 4, + WASM_COMDAT_SECTION = 5, + }; +}; + +pub const Feature = struct { + /// Provides information about the usage of the feature. + /// - '0x2b' (+): 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. + /// - '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. + prefix: Prefix, + /// Type of the feature, must be unique in the sequence of features. + tag: Tag, + + /// Unlike `std.Target.wasm.Feature` this also contains linker-features such as shared-mem + pub const Tag = enum { + atomics, + bulk_memory, + exception_handling, + extended_const, + half_precision, + multimemory, + multivalue, + mutable_globals, + nontrapping_fptoint, + reference_types, + relaxed_simd, + sign_ext, + simd128, + tail_call, + shared_mem, + + /// From a given cpu feature, returns its linker feature + pub fn fromCpuFeature(feature: std.Target.wasm.Feature) Tag { + return @as(Tag, @enumFromInt(@intFromEnum(feature))); + } + + pub fn format(tag: Tag, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void { + _ = fmt; + _ = opt; + try writer.writeAll(switch (tag) { + .atomics => "atomics", + .bulk_memory => "bulk-memory", + .exception_handling => "exception-handling", + .extended_const => "extended-const", + .half_precision => "half-precision", + .multimemory => "multimemory", + .multivalue => "multivalue", + .mutable_globals => "mutable-globals", + .nontrapping_fptoint => "nontrapping-fptoint", + .reference_types => "reference-types", + .relaxed_simd => "relaxed-simd", + .sign_ext => "sign-ext", + .simd128 => "simd128", + .tail_call => "tail-call", + .shared_mem => "shared-mem", + }); + } + }; + + pub const Prefix = enum(u8) { + used = '+', + disallowed = '-', + required = '=', + }; + + pub fn format(feature: Feature, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void { + _ = opt; + _ = fmt; + try writer.print("{c} {}", .{ feature.prefix, feature.tag }); + } +}; + +pub const known_features = std.StaticStringMap(Feature.Tag).initComptime(.{ + .{ "atomics", .atomics }, + .{ "bulk-memory", .bulk_memory }, + .{ "exception-handling", .exception_handling }, + .{ "extended-const", .extended_const }, + .{ "half-precision", .half_precision }, + .{ "multimemory", .multimemory }, + .{ "multivalue", .multivalue }, + .{ "mutable-globals", .mutable_globals }, + .{ "nontrapping-fptoint", .nontrapping_fptoint }, + .{ "reference-types", .reference_types }, + .{ "relaxed-simd", .relaxed_simd }, + .{ "sign-ext", .sign_ext }, + .{ "simd128", .simd128 }, + .{ "tail-call", .tail_call }, + .{ "shared-mem", .shared_mem }, +}); + +/// Parses an object file into atoms, for code and data sections +fn parseSymbolIntoAtom(wasm: *Wasm, object_id: ObjectId, symbol_index: Symbol.Index) !Atom.Index { + const object = wasm.objectById(object_id) orelse + return wasm.zig_object.?.parseSymbolIntoAtom(wasm, symbol_index); + const comp = wasm.base.comp; + const gpa = comp.gpa; + const symbol = &object.symtable[@intFromEnum(symbol_index)]; + const relocatable_data: Object.RelocatableData = switch (symbol.tag) { + .function => object.relocatable_data.get(.code).?[symbol.index - object.imported_functions_count], + .data => object.relocatable_data.get(.data).?[symbol.index], + .section => blk: { + const data = object.relocatable_data.get(.custom).?; + for (data) |dat| { + if (dat.section_index == symbol.index) { + break :blk dat; + } + } + unreachable; + }, + else => unreachable, + }; + const final_index = try wasm.getMatchingSegment(object_id, symbol_index); + const atom_index = try wasm.createAtom(symbol_index, object_id.toOptional()); + try wasm.appendAtomAtIndex(final_index, atom_index); + + const atom = wasm.getAtomPtr(atom_index); + atom.size = relocatable_data.size; + atom.alignment = relocatable_data.getAlignment(object); + atom.code = std.ArrayListUnmanaged(u8).fromOwnedSlice(relocatable_data.data[0..relocatable_data.size]); + atom.original_offset = relocatable_data.offset; + + const segment: *Wasm.Segment = &wasm.segments.items[final_index]; + if (relocatable_data.type == .data) { //code section and custom sections are 1-byte aligned + segment.alignment = segment.alignment.max(atom.alignment); + } + + if (object.relocations.get(relocatable_data.section_index)) |relocations| { + const start = searchRelocStart(relocations, relocatable_data.offset); + const len = searchRelocEnd(relocations[start..], relocatable_data.offset + atom.size); + atom.relocs = std.ArrayListUnmanaged(Wasm.Relocation).fromOwnedSlice(relocations[start..][0..len]); + for (atom.relocs.items) |reloc| { + switch (reloc.relocation_type) { + .R_WASM_TABLE_INDEX_I32, + .R_WASM_TABLE_INDEX_I64, + .R_WASM_TABLE_INDEX_SLEB, + .R_WASM_TABLE_INDEX_SLEB64, + => { + try wasm.function_table.put(gpa, .{ + .file = object_id.toOptional(), + .index = @enumFromInt(reloc.index), + }, 0); + }, + .R_WASM_GLOBAL_INDEX_I32, + .R_WASM_GLOBAL_INDEX_LEB, + => { + const sym = object.symtable[reloc.index]; + if (sym.tag != .global) { + try wasm.got_symbols.append(gpa, .{ + .file = object_id.toOptional(), + .index = @enumFromInt(reloc.index), + }); + } + }, + else => {}, + } + } + } + + return atom_index; +} + +fn searchRelocStart(relocs: []const Wasm.Relocation, address: u32) usize { + var min: usize = 0; + var max: usize = relocs.len; + while (min < max) { + const index = (min + max) / 2; + const curr = relocs[index]; + if (curr.offset < address) { + min = index + 1; + } else { + max = index; + } + } + return min; +} + +fn searchRelocEnd(relocs: []const Wasm.Relocation, address: u32) usize { + for (relocs, 0..relocs.len) |reloc, index| { + if (reloc.offset > address) { + return index; + } + } + return relocs.len; +} diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig deleted file mode 100644 index dd373552d5..0000000000 --- a/src/link/Wasm/Atom.zig +++ /dev/null @@ -1,204 +0,0 @@ -/// Represents the index of the file this atom was generated from. -/// This is 'null' when the atom was generated by a synthetic linker symbol. -file: FileIndex, -/// symbol index of the symbol representing this atom -sym_index: Symbol.Index, -/// Size of the atom, used to calculate section sizes in the final binary -size: u32 = 0, -/// List of relocations belonging to this atom -relocs: std.ArrayListUnmanaged(types.Relocation) = .empty, -/// Contains the binary data of an atom, which can be non-relocated -code: std.ArrayListUnmanaged(u8) = .empty, -/// For code this is 1, for data this is set to the highest value of all segments -alignment: Wasm.Alignment = .@"1", -/// Offset into the section where the atom lives, this already accounts -/// for alignment. -offset: u32 = 0, -/// The original offset within the object file. This value is subtracted from -/// relocation offsets to determine where in the `data` to rewrite the value -original_offset: u32 = 0, -/// Previous atom in relation to this atom. -/// is null when this atom is the first in its order -prev: Atom.Index = .null, -/// Contains atoms local to a decl, all managed by this `Atom`. -/// When the parent atom is being freed, it will also do so for all local atoms. -locals: std.ArrayListUnmanaged(Atom.Index) = .empty, - -/// Represents the index of an Atom where `null` is considered -/// an invalid atom. -pub const Index = enum(u32) { - null = std.math.maxInt(u32), - _, -}; - -/// Frees all resources owned by this `Atom`. -pub fn deinit(atom: *Atom, gpa: std.mem.Allocator) void { - atom.relocs.deinit(gpa); - atom.code.deinit(gpa); - atom.locals.deinit(gpa); - atom.* = undefined; -} - -/// Sets the length of relocations and code to '0', -/// effectively resetting them and allowing them to be re-populated. -pub fn clear(atom: *Atom) void { - atom.relocs.clearRetainingCapacity(); - atom.code.clearRetainingCapacity(); -} - -pub fn format(atom: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { - _ = fmt; - _ = options; - try writer.print("Atom{{ .sym_index = {d}, .alignment = {d}, .size = {d}, .offset = 0x{x:0>8} }}", .{ - @intFromEnum(atom.sym_index), - atom.alignment, - atom.size, - atom.offset, - }); -} - -/// Returns the location of the symbol that represents this `Atom` -pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc { - return .{ .file = atom.file, .index = atom.sym_index }; -} - -/// Resolves the relocations within the atom, writing the new value -/// at the calculated offset. -pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void { - if (atom.relocs.items.len == 0) return; - const symbol_name = atom.symbolLoc().getName(wasm_bin); - log.debug("Resolving relocs in atom '{s}' count({d})", .{ - symbol_name, - atom.relocs.items.len, - }); - - for (atom.relocs.items) |reloc| { - const value = atom.relocationValue(reloc, wasm_bin); - log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{ - (Wasm.SymbolLoc{ .file = atom.file, .index = @enumFromInt(reloc.index) }).getName(wasm_bin), - symbol_name, - reloc.offset, - value, - }); - - switch (reloc.relocation_type) { - .R_WASM_TABLE_INDEX_I32, - .R_WASM_FUNCTION_OFFSET_I32, - .R_WASM_GLOBAL_INDEX_I32, - .R_WASM_MEMORY_ADDR_I32, - .R_WASM_SECTION_OFFSET_I32, - => std.mem.writeInt(u32, atom.code.items[reloc.offset - atom.original_offset ..][0..4], @as(u32, @truncate(value)), .little), - .R_WASM_TABLE_INDEX_I64, - .R_WASM_MEMORY_ADDR_I64, - => std.mem.writeInt(u64, atom.code.items[reloc.offset - atom.original_offset ..][0..8], value, .little), - .R_WASM_GLOBAL_INDEX_LEB, - .R_WASM_EVENT_INDEX_LEB, - .R_WASM_FUNCTION_INDEX_LEB, - .R_WASM_MEMORY_ADDR_LEB, - .R_WASM_MEMORY_ADDR_SLEB, - .R_WASM_TABLE_INDEX_SLEB, - .R_WASM_TABLE_NUMBER_LEB, - .R_WASM_TYPE_INDEX_LEB, - .R_WASM_MEMORY_ADDR_TLS_SLEB, - => leb.writeUnsignedFixed(5, atom.code.items[reloc.offset - atom.original_offset ..][0..5], @as(u32, @truncate(value))), - .R_WASM_MEMORY_ADDR_LEB64, - .R_WASM_MEMORY_ADDR_SLEB64, - .R_WASM_TABLE_INDEX_SLEB64, - .R_WASM_MEMORY_ADDR_TLS_SLEB64, - => leb.writeUnsignedFixed(10, atom.code.items[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 relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) u64 { - const target_loc = (Wasm.SymbolLoc{ .file = atom.file, .index = @enumFromInt(relocation.index) }).finalLoc(wasm_bin); - const symbol = target_loc.getSymbol(wasm_bin); - if (relocation.relocation_type != .R_WASM_TYPE_INDEX_LEB and - symbol.tag != .section and - symbol.isDead()) - { - const val = atom.thombstone(wasm_bin) orelse relocation.addend; - return @bitCast(val); - } - switch (relocation.relocation_type) { - .R_WASM_FUNCTION_INDEX_LEB => return symbol.index, - .R_WASM_TABLE_NUMBER_LEB => return symbol.index, - .R_WASM_TABLE_INDEX_I32, - .R_WASM_TABLE_INDEX_I64, - .R_WASM_TABLE_INDEX_SLEB, - .R_WASM_TABLE_INDEX_SLEB64, - => return wasm_bin.function_table.get(.{ .file = atom.file, .index = @enumFromInt(relocation.index) }) orelse 0, - .R_WASM_TYPE_INDEX_LEB => { - const obj_file = wasm_bin.file(atom.file) orelse return relocation.index; - const original_type = obj_file.funcTypes()[relocation.index]; - return wasm_bin.getTypeIndex(original_type).?; - }, - .R_WASM_GLOBAL_INDEX_I32, - .R_WASM_GLOBAL_INDEX_LEB, - => return symbol.index, - .R_WASM_MEMORY_ADDR_I32, - .R_WASM_MEMORY_ADDR_I64, - .R_WASM_MEMORY_ADDR_LEB, - .R_WASM_MEMORY_ADDR_LEB64, - .R_WASM_MEMORY_ADDR_SLEB, - .R_WASM_MEMORY_ADDR_SLEB64, - => { - std.debug.assert(symbol.tag == .data); - if (symbol.isUndefined()) { - return 0; - } - const va: i33 = @intCast(symbol.virtual_address); - return @intCast(va + relocation.addend); - }, - .R_WASM_EVENT_INDEX_LEB => return symbol.index, - .R_WASM_SECTION_OFFSET_I32 => { - const target_atom_index = wasm_bin.symbol_atom.get(target_loc).?; - const target_atom = wasm_bin.getAtom(target_atom_index); - const rel_value: i33 = @intCast(target_atom.offset); - return @intCast(rel_value + relocation.addend); - }, - .R_WASM_FUNCTION_OFFSET_I32 => { - if (symbol.isUndefined()) { - const val = atom.thombstone(wasm_bin) orelse relocation.addend; - return @bitCast(val); - } - const target_atom_index = wasm_bin.symbol_atom.get(target_loc).?; - const target_atom = wasm_bin.getAtom(target_atom_index); - const rel_value: i33 = @intCast(target_atom.offset); - return @intCast(rel_value + relocation.addend); - }, - .R_WASM_MEMORY_ADDR_TLS_SLEB, - .R_WASM_MEMORY_ADDR_TLS_SLEB64, - => { - const va: i33 = @intCast(symbol.virtual_address); - return @intCast(va + relocation.addend); - }, - } -} - -// For a given `Atom` returns whether it has a thombstone value or not. -/// This defines whether we want a specific value when a section is dead. -fn thombstone(atom: Atom, wasm: *const Wasm) ?i64 { - const atom_name = atom.symbolLoc().getName(wasm); - if (std.mem.eql(u8, atom_name, ".debug_ranges") or std.mem.eql(u8, atom_name, ".debug_loc")) { - return -2; - } else if (std.mem.startsWith(u8, atom_name, ".debug_")) { - return -1; - } - return null; -} - -const leb = std.leb; -const log = std.log.scoped(.link); -const mem = std.mem; -const std = @import("std"); -const types = @import("types.zig"); - -const Allocator = mem.Allocator; -const Atom = @This(); -const FileIndex = @import("file.zig").File.Index; -const Symbol = @import("Symbol.zig"); -const Wasm = @import("../Wasm.zig"); diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index b4fc552025..d6a86c3c62 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -3,22 +3,18 @@ //! the data on correctness. The result can then be used by the linker. const Object = @This(); -const Atom = @import("Atom.zig"); -const types = @import("types.zig"); -const std = @import("std"); const Wasm = @import("../Wasm.zig"); +const Atom = Wasm.Atom; +const Alignment = Wasm.Alignment; const Symbol = @import("Symbol.zig"); -const Alignment = types.Alignment; -const File = @import("file.zig").File; +const std = @import("std"); const Allocator = std.mem.Allocator; const leb = std.leb; const meta = std.meta; const log = std.log.scoped(.object); -/// Index into the list of relocatable object files within the linker driver. -index: File.Index = .null, /// Wasm spec version used for this `Object` version: u32 = 0, /// The file descriptor that represents the wasm object file. @@ -28,7 +24,7 @@ path: []const u8, /// Parsed type section func_types: []const std.wasm.Type = &.{}, /// A list of all imports for this module -imports: []const types.Import = &.{}, +imports: []const Wasm.Import = &.{}, /// Parsed function section functions: []const std.wasm.Func = &.{}, /// Parsed table section @@ -38,7 +34,7 @@ memories: []const std.wasm.Memory = &.{}, /// Parsed global section globals: []const std.wasm.Global = &.{}, /// Parsed export section -exports: []const types.Export = &.{}, +exports: []const Wasm.Export = &.{}, /// Parsed element section elements: []const std.wasm.Element = &.{}, /// Represents the function ID that must be called on startup. @@ -48,18 +44,18 @@ start: ?u32 = null, /// A slice of features that tell the linker what features are mandatory, /// used (or therefore missing) and must generate an error when another /// object uses features that are not supported by the other. -features: []const types.Feature = &.{}, +features: []const Wasm.Feature = &.{}, /// A table that maps the relocations we must perform where the key represents /// the section that the list of relocations applies to. -relocations: std.AutoArrayHashMapUnmanaged(u32, []types.Relocation) = .empty, +relocations: std.AutoArrayHashMapUnmanaged(u32, []Wasm.Relocation) = .empty, /// Table of symbols belonging to this Object file symtable: []Symbol = &.{}, /// Extra metadata about the linking section, such as alignment of segments and their name -segment_info: []const types.Segment = &.{}, +segment_info: []const Wasm.NamedSegment = &.{}, /// A sequence of function initializers that must be called on startup -init_funcs: []const types.InitFunc = &.{}, +init_funcs: []const Wasm.InitFunc = &.{}, /// Comdat information -comdat_info: []const types.Comdat = &.{}, +comdat_info: []const Wasm.Comdat = &.{}, /// Represents non-synthetic sections that can essentially be mem-cpy'd into place /// after performing relocations. relocatable_data: std.AutoHashMapUnmanaged(RelocatableData.Tag, []RelocatableData) = .empty, @@ -75,7 +71,7 @@ imported_globals_count: u32 = 0, imported_tables_count: u32 = 0, /// Represents a single item within a section (depending on its `type`) -const RelocatableData = struct { +pub const RelocatableData = struct { /// The type of the relocatable data type: Tag, /// Pointer to the data of the segment, where its length is written to `size` @@ -209,7 +205,7 @@ pub fn deinit(object: *Object, gpa: Allocator) void { /// Finds the import within the list of imports from a given kind and index of that kind. /// Asserts the import exists -pub fn findImport(object: *const Object, sym: Symbol) types.Import { +pub fn findImport(object: *const Object, sym: Symbol) Wasm.Import { var i: u32 = 0; return for (object.imports) |import| { if (std.meta.activeTag(import.kind) == sym.tag.externalType()) { @@ -261,7 +257,7 @@ fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?S return error.MissingTableSymbols; } - const table_import: types.Import = for (object.imports) |imp| { + const table_import: Wasm.Import = for (object.imports) |imp| { if (imp.kind == .table) { break imp; } @@ -592,13 +588,13 @@ fn Parser(comptime ReaderType: type) type { const diags = &parser.wasm_file.base.comp.link_diags; const reader = parser.reader.reader(); for (try readVec(&parser.object.features, reader, gpa)) |*feature| { - const prefix = try readEnum(types.Feature.Prefix, reader); + const prefix = try readEnum(Wasm.Feature.Prefix, reader); const name_len = try leb.readUleb128(u32, reader); const name = try gpa.alloc(u8, name_len); defer gpa.free(name); try reader.readNoEof(name); - const tag = types.known_features.get(name) orelse { + const tag = Wasm.known_features.get(name) orelse { var err = try diags.addErrorWithNotes(1); try err.addMsg("Object file contains unknown feature: {s}", .{name}); try err.addNote("defined in '{s}'", .{parser.object.path}); @@ -618,7 +614,7 @@ fn Parser(comptime ReaderType: type) type { const reader = parser.reader.reader(); const section = try leb.readUleb128(u32, reader); const count = try leb.readUleb128(u32, reader); - const relocations = try gpa.alloc(types.Relocation, count); + const relocations = try gpa.alloc(Wasm.Relocation, count); errdefer gpa.free(relocations); log.debug("Found {d} relocations for section ({d})", .{ @@ -628,7 +624,7 @@ fn Parser(comptime ReaderType: type) type { for (relocations) |*relocation| { const rel_type = try reader.readByte(); - const rel_type_enum = std.meta.intToEnum(types.Relocation.RelocationType, rel_type) catch return error.MalformedSection; + const rel_type_enum = std.meta.intToEnum(Wasm.Relocation.RelocationType, rel_type) catch return error.MalformedSection; relocation.* = .{ .relocation_type = rel_type_enum, .offset = try leb.readUleb128(u32, reader), @@ -671,7 +667,7 @@ fn Parser(comptime ReaderType: type) type { /// such as access to the `import` section to find the name of a symbol. fn parseSubsection(parser: *ObjectParser, gpa: Allocator, reader: anytype) !void { const sub_type = try leb.readUleb128(u8, reader); - log.debug("Found subsection: {s}", .{@tagName(@as(types.SubsectionType, @enumFromInt(sub_type)))}); + log.debug("Found subsection: {s}", .{@tagName(@as(Wasm.SubsectionType, @enumFromInt(sub_type)))}); const payload_len = try leb.readUleb128(u32, reader); if (payload_len == 0) return; @@ -681,9 +677,9 @@ fn Parser(comptime ReaderType: type) type { // every subsection contains a 'count' field const count = try leb.readUleb128(u32, limited_reader); - switch (@as(types.SubsectionType, @enumFromInt(sub_type))) { + switch (@as(Wasm.SubsectionType, @enumFromInt(sub_type))) { .WASM_SEGMENT_INFO => { - const segments = try gpa.alloc(types.Segment, count); + const segments = try gpa.alloc(Wasm.NamedSegment, count); errdefer gpa.free(segments); for (segments) |*segment| { const name_len = try leb.readUleb128(u32, reader); @@ -704,13 +700,13 @@ fn Parser(comptime ReaderType: type) type { // support legacy object files that specified being TLS by the name instead of the TLS flag. if (!segment.isTLS() and (std.mem.startsWith(u8, segment.name, ".tdata") or std.mem.startsWith(u8, segment.name, ".tbss"))) { // set the flag so we can simply check for the flag in the rest of the linker. - segment.flags |= @intFromEnum(types.Segment.Flags.WASM_SEG_FLAG_TLS); + segment.flags |= @intFromEnum(Wasm.NamedSegment.Flags.WASM_SEG_FLAG_TLS); } } parser.object.segment_info = segments; }, .WASM_INIT_FUNCS => { - const funcs = try gpa.alloc(types.InitFunc, count); + const funcs = try gpa.alloc(Wasm.InitFunc, count); errdefer gpa.free(funcs); for (funcs) |*func| { func.* = .{ @@ -722,7 +718,7 @@ fn Parser(comptime ReaderType: type) type { parser.object.init_funcs = funcs; }, .WASM_COMDAT_INFO => { - const comdats = try gpa.alloc(types.Comdat, count); + const comdats = try gpa.alloc(Wasm.Comdat, count); errdefer gpa.free(comdats); for (comdats) |*comdat| { const name_len = try leb.readUleb128(u32, reader); @@ -736,11 +732,11 @@ fn Parser(comptime ReaderType: type) type { } const symbol_count = try leb.readUleb128(u32, reader); - const symbols = try gpa.alloc(types.ComdatSym, symbol_count); + const symbols = try gpa.alloc(Wasm.ComdatSym, symbol_count); errdefer gpa.free(symbols); for (symbols) |*symbol| { symbol.* = .{ - .kind = @as(types.ComdatSym.Type, @enumFromInt(try leb.readUleb128(u8, reader))), + .kind = @as(Wasm.ComdatSym.Type, @enumFromInt(try leb.readUleb128(u8, reader))), .index = try leb.readUleb128(u32, reader), }; } @@ -921,93 +917,3 @@ fn assertEnd(reader: anytype) !void { if (len != 0) return error.MalformedSection; if (reader.context.bytes_left != 0) return error.MalformedSection; } - -/// Parses an object file into atoms, for code and data sections -pub fn parseSymbolIntoAtom(object: *Object, wasm: *Wasm, symbol_index: Symbol.Index) !Atom.Index { - const comp = wasm.base.comp; - const gpa = comp.gpa; - const symbol = &object.symtable[@intFromEnum(symbol_index)]; - const relocatable_data: RelocatableData = switch (symbol.tag) { - .function => object.relocatable_data.get(.code).?[symbol.index - object.imported_functions_count], - .data => object.relocatable_data.get(.data).?[symbol.index], - .section => blk: { - const data = object.relocatable_data.get(.custom).?; - for (data) |dat| { - if (dat.section_index == symbol.index) { - break :blk dat; - } - } - unreachable; - }, - else => unreachable, - }; - const final_index = try wasm.getMatchingSegment(object.index, symbol_index); - const atom_index = try wasm.createAtom(symbol_index, object.index); - try wasm.appendAtomAtIndex(final_index, atom_index); - - const atom = wasm.getAtomPtr(atom_index); - atom.size = relocatable_data.size; - atom.alignment = relocatable_data.getAlignment(object); - atom.code = std.ArrayListUnmanaged(u8).fromOwnedSlice(relocatable_data.data[0..relocatable_data.size]); - atom.original_offset = relocatable_data.offset; - - const segment: *Wasm.Segment = &wasm.segments.items[final_index]; - if (relocatable_data.type == .data) { //code section and custom sections are 1-byte aligned - segment.alignment = segment.alignment.max(atom.alignment); - } - - if (object.relocations.get(relocatable_data.section_index)) |relocations| { - const start = searchRelocStart(relocations, relocatable_data.offset); - const len = searchRelocEnd(relocations[start..], relocatable_data.offset + atom.size); - atom.relocs = std.ArrayListUnmanaged(types.Relocation).fromOwnedSlice(relocations[start..][0..len]); - for (atom.relocs.items) |reloc| { - switch (reloc.relocation_type) { - .R_WASM_TABLE_INDEX_I32, - .R_WASM_TABLE_INDEX_I64, - .R_WASM_TABLE_INDEX_SLEB, - .R_WASM_TABLE_INDEX_SLEB64, - => { - try wasm.function_table.put(gpa, .{ - .file = object.index, - .index = @enumFromInt(reloc.index), - }, 0); - }, - .R_WASM_GLOBAL_INDEX_I32, - .R_WASM_GLOBAL_INDEX_LEB, - => { - const sym = object.symtable[reloc.index]; - if (sym.tag != .global) { - try wasm.got_symbols.append(gpa, .{ .file = object.index, .index = @enumFromInt(reloc.index) }); - } - }, - else => {}, - } - } - } - - return atom_index; -} - -fn searchRelocStart(relocs: []const types.Relocation, address: u32) usize { - var min: usize = 0; - var max: usize = relocs.len; - while (min < max) { - const index = (min + max) / 2; - const curr = relocs[index]; - if (curr.offset < address) { - min = index + 1; - } else { - max = index; - } - } - return min; -} - -fn searchRelocEnd(relocs: []const types.Relocation, address: u32) usize { - for (relocs, 0..relocs.len) |reloc, index| { - if (reloc.offset > address) { - return index; - } - } - return relocs.len; -} diff --git a/src/link/Wasm/Symbol.zig b/src/link/Wasm/Symbol.zig index b0d5644b0a..1383234782 100644 --- a/src/link/Wasm/Symbol.zig +++ b/src/link/Wasm/Symbol.zig @@ -206,5 +206,4 @@ pub fn format(symbol: Symbol, comptime fmt: []const u8, options: std.fmt.FormatO } const std = @import("std"); -const types = @import("types.zig"); const Symbol = @This(); diff --git a/src/link/Wasm/ZigObject.zig b/src/link/Wasm/ZigObject.zig index 962024eeef..56e389efd3 100644 --- a/src/link/Wasm/ZigObject.zig +++ b/src/link/Wasm/ZigObject.zig @@ -4,8 +4,6 @@ //! Think about this as fake in-memory Object file for the Zig module. path: []const u8, -/// Index within the list of relocatable objects of the linker driver. -index: File.Index, /// Map of all `Nav` that are currently alive. /// Each index maps to the corresponding `NavInfo`. navs: std.AutoHashMapUnmanaged(InternPool.Nav.Index, NavInfo) = .empty, @@ -16,8 +14,8 @@ func_types: std.ArrayListUnmanaged(std.wasm.Type) = .empty, functions: std.ArrayListUnmanaged(std.wasm.Func) = .empty, /// List of indexes pointing to an entry within the `functions` list which has been removed. functions_free_list: std.ArrayListUnmanaged(u32) = .empty, -/// Map of symbol locations, represented by its `types.Import`. -imports: std.AutoHashMapUnmanaged(Symbol.Index, types.Import) = .empty, +/// Map of symbol locations, represented by its `Wasm.Import`. +imports: std.AutoHashMapUnmanaged(Symbol.Index, Wasm.Import) = .empty, /// List of WebAssembly globals. globals: std.ArrayListUnmanaged(std.wasm.Global) = .empty, /// Mapping between an `Atom` and its type index representing the Wasm @@ -30,7 +28,7 @@ global_syms: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .empty, /// List of symbol indexes which are free to be used. symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .empty, /// Extra metadata about the linking section, such as alignment of segments and their name. -segment_info: std.ArrayListUnmanaged(types.Segment) = .empty, +segment_info: std.ArrayListUnmanaged(Wasm.NamedSegment) = .empty, /// List of indexes which contain a free slot in the `segment_info` list. segment_free_list: std.ArrayListUnmanaged(u32) = .empty, /// File encapsulated string table, used to deduplicate strings within the generated file. @@ -117,39 +115,39 @@ const NavInfo = struct { }; /// Initializes the `ZigObject` with initial symbols. -pub fn init(zig_object: *ZigObject, wasm_file: *Wasm) !void { +pub fn init(zig_object: *ZigObject, wasm: *Wasm) !void { // Initialize an undefined global with the name __stack_pointer. Codegen will use // this to generate relocations when moving the stack pointer. This symbol will be // resolved automatically by the final linking stage. - try zig_object.createStackPointer(wasm_file); + try zig_object.createStackPointer(wasm); // TODO: Initialize debug information when we reimplement Dwarf support. } -fn createStackPointer(zig_object: *ZigObject, wasm_file: *Wasm) !void { - const gpa = wasm_file.base.comp.gpa; +fn createStackPointer(zig_object: *ZigObject, wasm: *Wasm) !void { + const gpa = wasm.base.comp.gpa; const sym_index = try zig_object.getGlobalSymbol(gpa, "__stack_pointer"); const sym = zig_object.symbol(sym_index); sym.index = zig_object.imported_globals_count; sym.tag = .global; - const is_wasm32 = wasm_file.base.comp.root_mod.resolved_target.result.cpu.arch == .wasm32; + const is_wasm32 = wasm.base.comp.root_mod.resolved_target.result.cpu.arch == .wasm32; try zig_object.imports.putNoClobber(gpa, sym_index, .{ .name = sym.name, - .module_name = try zig_object.string_table.insert(gpa, wasm_file.host_name), + .module_name = try zig_object.string_table.insert(gpa, wasm.host_name), .kind = .{ .global = .{ .valtype = if (is_wasm32) .i32 else .i64, .mutable = true } }, }); zig_object.imported_globals_count += 1; zig_object.stack_pointer_sym = sym_index; } -fn symbol(zig_object: *const ZigObject, index: Symbol.Index) *Symbol { +pub fn symbol(zig_object: *const ZigObject, index: Symbol.Index) *Symbol { return &zig_object.symbols.items[@intFromEnum(index)]; } /// Frees and invalidates all memory of the incrementally compiled Zig module. /// It is illegal behavior to access the `ZigObject` after calling `deinit`. -pub fn deinit(zig_object: *ZigObject, wasm_file: *Wasm) void { - const gpa = wasm_file.base.comp.gpa; +pub fn deinit(zig_object: *ZigObject, wasm: *Wasm) void { + const gpa = wasm.base.comp.gpa; for (zig_object.segment_info.items) |segment_info| { gpa.free(segment_info.name); } @@ -157,9 +155,9 @@ pub fn deinit(zig_object: *ZigObject, wasm_file: *Wasm) void { { var it = zig_object.navs.valueIterator(); while (it.next()) |nav_info| { - const atom = wasm_file.getAtomPtr(nav_info.atom); + const atom = wasm.getAtomPtr(nav_info.atom); for (atom.locals.items) |local_index| { - const local_atom = wasm_file.getAtomPtr(local_index); + const local_atom = wasm.getAtomPtr(local_index); local_atom.deinit(gpa); } atom.deinit(gpa); @@ -168,24 +166,24 @@ pub fn deinit(zig_object: *ZigObject, wasm_file: *Wasm) void { } { for (zig_object.uavs.values()) |atom_index| { - const atom = wasm_file.getAtomPtr(atom_index); + const atom = wasm.getAtomPtr(atom_index); for (atom.locals.items) |local_index| { - const local_atom = wasm_file.getAtomPtr(local_index); + const local_atom = wasm.getAtomPtr(local_index); local_atom.deinit(gpa); } atom.deinit(gpa); } } if (zig_object.findGlobalSymbol("__zig_errors_len")) |sym_index| { - const atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = sym_index }).?; - wasm_file.getAtomPtr(atom_index).deinit(gpa); + const atom_index = wasm.symbol_atom.get(.{ .file = .zig_object, .index = sym_index }).?; + wasm.getAtomPtr(atom_index).deinit(gpa); } - if (wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = zig_object.error_table_symbol })) |atom_index| { - const atom = wasm_file.getAtomPtr(atom_index); + if (wasm.symbol_atom.get(.{ .file = .zig_object, .index = zig_object.error_table_symbol })) |atom_index| { + const atom = wasm.getAtomPtr(atom_index); atom.deinit(gpa); } for (zig_object.synthetic_functions.items) |atom_index| { - const atom = wasm_file.getAtomPtr(atom_index); + const atom = wasm.getAtomPtr(atom_index); atom.deinit(gpa); } zig_object.synthetic_functions.deinit(gpa); @@ -193,7 +191,7 @@ pub fn deinit(zig_object: *ZigObject, wasm_file: *Wasm) void { ty.deinit(gpa); } if (zig_object.error_names_atom != .null) { - const atom = wasm_file.getAtomPtr(zig_object.error_names_atom); + const atom = wasm.getAtomPtr(zig_object.error_names_atom); atom.deinit(gpa); } zig_object.global_syms.deinit(gpa); @@ -240,7 +238,7 @@ pub fn allocateSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator) !Symbol.In // the file on flush(). pub fn updateNav( zig_object: *ZigObject, - wasm_file: *Wasm, + wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, ) !void { @@ -260,19 +258,19 @@ pub fn updateNav( }; if (nav_init.typeOf(zcu).hasRuntimeBits(zcu)) { - const gpa = wasm_file.base.comp.gpa; - const atom_index = try zig_object.getOrCreateAtomForNav(wasm_file, pt, nav_index); - const atom = wasm_file.getAtomPtr(atom_index); + const gpa = wasm.base.comp.gpa; + const atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, nav_index); + const atom = wasm.getAtomPtr(atom_index); atom.clear(); if (is_extern) - return zig_object.addOrUpdateImport(wasm_file, nav.name.toSlice(ip), atom.sym_index, lib_name.toSlice(ip), null); + return zig_object.addOrUpdateImport(wasm, nav.name.toSlice(ip), atom.sym_index, lib_name.toSlice(ip), null); var code_writer = std.ArrayList(u8).init(gpa); defer code_writer.deinit(); const res = try codegen.generateSymbol( - &wasm_file.base, + &wasm.base, pt, zcu.navSrcLoc(nav_index), nav_init, @@ -288,13 +286,13 @@ pub fn updateNav( }, }; - try zig_object.finishUpdateNav(wasm_file, pt, nav_index, code); + try zig_object.finishUpdateNav(wasm, pt, nav_index, code); } } pub fn updateFunc( zig_object: *ZigObject, - wasm_file: *Wasm, + wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, @@ -303,14 +301,14 @@ pub fn updateFunc( const zcu = pt.zcu; const gpa = zcu.gpa; const func = pt.zcu.funcInfo(func_index); - const atom_index = try zig_object.getOrCreateAtomForNav(wasm_file, pt, func.owner_nav); - const atom = wasm_file.getAtomPtr(atom_index); + const atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, func.owner_nav); + const atom = wasm.getAtomPtr(atom_index); atom.clear(); var code_writer = std.ArrayList(u8).init(gpa); defer code_writer.deinit(); const result = try codegen.generateFunction( - &wasm_file.base, + &wasm.base, pt, zcu.navSrcLoc(func.owner_nav), func_index, @@ -328,12 +326,12 @@ pub fn updateFunc( }, }; - return zig_object.finishUpdateNav(wasm_file, pt, func.owner_nav, code); + return zig_object.finishUpdateNav(wasm, pt, func.owner_nav, code); } fn finishUpdateNav( zig_object: *ZigObject, - wasm_file: *Wasm, + wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, code: []const u8, @@ -345,7 +343,7 @@ fn finishUpdateNav( const nav_val = zcu.navValue(nav_index); const nav_info = zig_object.navs.get(nav_index).?; const atom_index = nav_info.atom; - const atom = wasm_file.getAtomPtr(atom_index); + const atom = wasm.getAtomPtr(atom_index); const sym = zig_object.symbol(atom.sym_index); sym.name = try zig_object.string_table.insert(gpa, nav.fqn.toSlice(ip)); try atom.code.appendSlice(gpa, code); @@ -376,7 +374,7 @@ fn finishUpdateNav( } break :name ".bss."; }; - if ((wasm_file.base.isObject() or wasm_file.base.comp.config.import_memory) and + if ((wasm.base.isObject() or wasm.base.comp.config.import_memory) and std.mem.startsWith(u8, segment_name, ".bss")) { @memset(atom.code.items, 0); @@ -422,7 +420,7 @@ fn createDataSegment( /// The newly created Atom is empty with default fields as specified by `Atom.empty`. pub fn getOrCreateAtomForNav( zig_object: *ZigObject, - wasm_file: *Wasm, + wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, ) !Atom.Index { @@ -431,7 +429,7 @@ pub fn getOrCreateAtomForNav( const gop = try zig_object.navs.getOrPut(gpa, nav_index); if (!gop.found_existing) { const sym_index = try zig_object.allocateSymbol(gpa); - gop.value_ptr.* = .{ .atom = try wasm_file.createAtom(sym_index, zig_object.index) }; + gop.value_ptr.* = .{ .atom = try wasm.createAtom(sym_index, .zig_object) }; const nav = ip.getNav(nav_index); const sym = zig_object.symbol(sym_index); sym.name = try zig_object.string_table.insert(gpa, nav.fqn.toSlice(ip)); @@ -441,13 +439,13 @@ pub fn getOrCreateAtomForNav( pub fn lowerUav( zig_object: *ZigObject, - wasm_file: *Wasm, + wasm: *Wasm, pt: Zcu.PerThread, uav: InternPool.Index, explicit_alignment: InternPool.Alignment, src_loc: Zcu.LazySrcLoc, ) !codegen.GenResult { - const gpa = wasm_file.base.comp.gpa; + const gpa = wasm.base.comp.gpa; const gop = try zig_object.uavs.getOrPut(gpa, uav); if (!gop.found_existing) { var name_buf: [32]u8 = undefined; @@ -455,13 +453,13 @@ pub fn lowerUav( @intFromEnum(uav), }) catch unreachable; - switch (try zig_object.lowerConst(wasm_file, pt, name, Value.fromInterned(uav), src_loc)) { + switch (try zig_object.lowerConst(wasm, pt, name, Value.fromInterned(uav), src_loc)) { .ok => |atom_index| zig_object.uavs.values()[gop.index] = atom_index, .fail => |em| return .{ .fail = em }, } } - const atom = wasm_file.getAtomPtr(zig_object.uavs.values()[gop.index]); + const atom = wasm.getAtomPtr(zig_object.uavs.values()[gop.index]); atom.alignment = switch (atom.alignment) { .none => explicit_alignment, else => switch (explicit_alignment) { @@ -479,25 +477,25 @@ const LowerConstResult = union(enum) { fn lowerConst( zig_object: *ZigObject, - wasm_file: *Wasm, + wasm: *Wasm, pt: Zcu.PerThread, name: []const u8, val: Value, src_loc: Zcu.LazySrcLoc, ) !LowerConstResult { - const gpa = wasm_file.base.comp.gpa; - const zcu = wasm_file.base.comp.zcu.?; + const gpa = wasm.base.comp.gpa; + const zcu = wasm.base.comp.zcu.?; const ty = val.typeOf(zcu); // Create and initialize a new local symbol and atom const sym_index = try zig_object.allocateSymbol(gpa); - const atom_index = try wasm_file.createAtom(sym_index, zig_object.index); + const atom_index = try wasm.createAtom(sym_index, .zig_object); var value_bytes = std.ArrayList(u8).init(gpa); defer value_bytes.deinit(); const code = code: { - const atom = wasm_file.getAtomPtr(atom_index); + const atom = wasm.getAtomPtr(atom_index); atom.alignment = ty.abiAlignment(zcu); const segment_name = try std.mem.concat(gpa, u8, &.{ ".rodata.", name }); errdefer gpa.free(segment_name); @@ -514,7 +512,7 @@ fn lowerConst( }; const result = try codegen.generateSymbol( - &wasm_file.base, + &wasm.base, pt, src_loc, val, @@ -529,7 +527,7 @@ fn lowerConst( }; }; - const atom = wasm_file.getAtomPtr(atom_index); + const atom = wasm.getAtomPtr(atom_index); atom.size = @intCast(code.len); try atom.code.appendSlice(gpa, code); return .{ .ok = atom_index }; @@ -538,7 +536,7 @@ fn lowerConst( /// Returns the symbol index of the error name table. /// /// When the symbol does not yet exist, it will create a new one instead. -pub fn getErrorTableSymbol(zig_object: *ZigObject, wasm_file: *Wasm, pt: Zcu.PerThread) !Symbol.Index { +pub fn getErrorTableSymbol(zig_object: *ZigObject, wasm: *Wasm, pt: Zcu.PerThread) !Symbol.Index { if (zig_object.error_table_symbol != .null) { return zig_object.error_table_symbol; } @@ -546,10 +544,10 @@ pub fn getErrorTableSymbol(zig_object: *ZigObject, wasm_file: *Wasm, pt: Zcu.Per // no error was referenced yet, so create a new symbol and atom for it // and then return said symbol's index. The final table will be populated // during `flush` when we know all possible error names. - const gpa = wasm_file.base.comp.gpa; + const gpa = wasm.base.comp.gpa; const sym_index = try zig_object.allocateSymbol(gpa); - const atom_index = try wasm_file.createAtom(sym_index, zig_object.index); - const atom = wasm_file.getAtomPtr(atom_index); + const atom_index = try wasm.createAtom(sym_index, .zig_object); + const atom = wasm.getAtomPtr(atom_index); const slice_ty = Type.slice_const_u8_sentinel_0; atom.alignment = slice_ty.abiAlignment(pt.zcu); @@ -573,17 +571,17 @@ pub fn getErrorTableSymbol(zig_object: *ZigObject, wasm_file: *Wasm, pt: Zcu.Per /// /// This creates a table that consists of pointers and length to each error name. /// The table is what is being pointed to within the runtime bodies that are generated. -fn populateErrorNameTable(zig_object: *ZigObject, wasm_file: *Wasm, tid: Zcu.PerThread.Id) !void { +fn populateErrorNameTable(zig_object: *ZigObject, wasm: *Wasm, tid: Zcu.PerThread.Id) !void { if (zig_object.error_table_symbol == .null) return; - const gpa = wasm_file.base.comp.gpa; - const atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = zig_object.error_table_symbol }).?; + const gpa = wasm.base.comp.gpa; + const atom_index = wasm.symbol_atom.get(.{ .file = .zig_object, .index = zig_object.error_table_symbol }).?; // Rather than creating a symbol for each individual error name, // we create a symbol for the entire region of error names. We then calculate // the pointers into the list using addends which are appended to the relocation. const names_sym_index = try zig_object.allocateSymbol(gpa); - const names_atom_index = try wasm_file.createAtom(names_sym_index, zig_object.index); - const names_atom = wasm_file.getAtomPtr(names_atom_index); + const names_atom_index = try wasm.createAtom(names_sym_index, .zig_object); + const names_atom = wasm.getAtomPtr(names_atom_index); names_atom.alignment = .@"1"; const sym_name = try zig_object.string_table.insert(gpa, "__zig_err_names"); const segment_name = try gpa.dupe(u8, ".rodata.__zig_err_names"); @@ -600,9 +598,9 @@ fn populateErrorNameTable(zig_object: *ZigObject, wasm_file: *Wasm, tid: Zcu.Per // Addend for each relocation to the table var addend: u32 = 0; - const pt: Zcu.PerThread = .{ .zcu = wasm_file.base.comp.zcu.?, .tid = tid }; + const pt: Zcu.PerThread = .{ .zcu = wasm.base.comp.zcu.?, .tid = tid }; const slice_ty = Type.slice_const_u8_sentinel_0; - const atom = wasm_file.getAtomPtr(atom_index); + const atom = wasm.getAtomPtr(atom_index); { // TODO: remove this unreachable entry try atom.code.appendNTimes(gpa, 0, 4); @@ -646,7 +644,7 @@ fn populateErrorNameTable(zig_object: *ZigObject, wasm_file: *Wasm, tid: Zcu.Per /// In all other cases, a data-symbol will be created instead. pub fn addOrUpdateImport( zig_object: *ZigObject, - wasm_file: *Wasm, + wasm: *Wasm, /// Name of the import name: []const u8, /// Symbol index that is external @@ -658,7 +656,7 @@ pub fn addOrUpdateImport( /// is asserted instead. type_index: ?u32, ) !void { - const gpa = wasm_file.base.comp.gpa; + const gpa = wasm.base.comp.gpa; std.debug.assert(symbol_index != .null); // For the import name, we use the decl's name, rather than the fully qualified name // Also mangle the name when the lib name is set and not equal to "C" so imports with the same @@ -682,7 +680,7 @@ pub fn addOrUpdateImport( if (type_index) |ty_index| { const gop = try zig_object.imports.getOrPut(gpa, symbol_index); - const module_name = if (lib_name) |l_name| l_name else wasm_file.host_name; + const module_name = if (lib_name) |l_name| l_name else wasm.host_name; if (!gop.found_existing) { zig_object.imported_functions_count += 1; } @@ -733,7 +731,7 @@ pub fn getGlobalSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator, name: []c /// Returns the given pointer address pub fn getNavVAddr( zig_object: *ZigObject, - wasm_file: *Wasm, + wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: link.File.RelocInfo, @@ -744,12 +742,12 @@ pub fn getNavVAddr( const nav = ip.getNav(nav_index); const target = &zcu.navFileScope(nav_index).mod.resolved_target.result; - const target_atom_index = try zig_object.getOrCreateAtomForNav(wasm_file, pt, nav_index); - const target_atom = wasm_file.getAtom(target_atom_index); + const target_atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, nav_index); + const target_atom = wasm.getAtom(target_atom_index); const target_symbol_index = @intFromEnum(target_atom.sym_index); switch (ip.indexToKey(nav.status.resolved.val)) { .@"extern" => |@"extern"| try zig_object.addOrUpdateImport( - wasm_file, + wasm, nav.name.toSlice(ip), target_atom.sym_index, @"extern".lib_name.toSlice(ip), @@ -759,11 +757,11 @@ pub fn getNavVAddr( } std.debug.assert(reloc_info.parent.atom_index != 0); - const atom_index = wasm_file.symbol_atom.get(.{ - .file = zig_object.index, + const atom_index = wasm.symbol_atom.get(.{ + .file = .zig_object, .index = @enumFromInt(reloc_info.parent.atom_index), }).?; - const atom = wasm_file.getAtomPtr(atom_index); + const atom = wasm.getAtomPtr(atom_index); const is_wasm32 = target.cpu.arch == .wasm32; if (ip.isFunctionType(ip.getNav(nav_index).typeOf(ip))) { std.debug.assert(reloc_info.addend == 0); // addend not allowed for function relocations @@ -790,22 +788,22 @@ pub fn getNavVAddr( pub fn getUavVAddr( zig_object: *ZigObject, - wasm_file: *Wasm, + wasm: *Wasm, uav: InternPool.Index, reloc_info: link.File.RelocInfo, ) !u64 { - const gpa = wasm_file.base.comp.gpa; - const target = wasm_file.base.comp.root_mod.resolved_target.result; + const gpa = wasm.base.comp.gpa; + const target = wasm.base.comp.root_mod.resolved_target.result; const atom_index = zig_object.uavs.get(uav).?; - const target_symbol_index = @intFromEnum(wasm_file.getAtom(atom_index).sym_index); + const target_symbol_index = @intFromEnum(wasm.getAtom(atom_index).sym_index); - const parent_atom_index = wasm_file.symbol_atom.get(.{ - .file = zig_object.index, + const parent_atom_index = wasm.symbol_atom.get(.{ + .file = .zig_object, .index = @enumFromInt(reloc_info.parent.atom_index), }).?; - const parent_atom = wasm_file.getAtomPtr(parent_atom_index); + const parent_atom = wasm.getAtomPtr(parent_atom_index); const is_wasm32 = target.cpu.arch == .wasm32; - const zcu = wasm_file.base.comp.zcu.?; + const zcu = wasm.base.comp.zcu.?; const ty = Type.fromInterned(zcu.intern_pool.typeOf(uav)); if (ty.zigTypeTag(zcu) == .@"fn") { std.debug.assert(reloc_info.addend == 0); // addend not allowed for function relocations @@ -832,11 +830,11 @@ pub fn getUavVAddr( pub fn deleteExport( zig_object: *ZigObject, - wasm_file: *Wasm, + wasm: *Wasm, exported: Zcu.Exported, name: InternPool.NullTerminatedString, ) void { - const zcu = wasm_file.base.comp.zcu.?; + const zcu = wasm.base.comp.zcu.?; const nav_index = switch (exported) { .nav => |nav_index| nav_index, .uav => @panic("TODO: implement Wasm linker code for exporting a constant value"), @@ -846,15 +844,15 @@ pub fn deleteExport( const sym = zig_object.symbol(sym_index); nav_info.deleteExport(sym_index); std.debug.assert(zig_object.global_syms.remove(sym.name)); - std.debug.assert(wasm_file.symbol_atom.remove(.{ .file = zig_object.index, .index = sym_index })); - zig_object.symbols_free_list.append(wasm_file.base.comp.gpa, sym_index) catch {}; + std.debug.assert(wasm.symbol_atom.remove(.{ .file = .zig_object, .index = sym_index })); + zig_object.symbols_free_list.append(wasm.base.comp.gpa, sym_index) catch {}; sym.tag = .dead; } } pub fn updateExports( zig_object: *ZigObject, - wasm_file: *Wasm, + wasm: *Wasm, pt: Zcu.PerThread, exported: Zcu.Exported, export_indices: []const u32, @@ -869,10 +867,10 @@ pub fn updateExports( }, }; const nav = ip.getNav(nav_index); - const atom_index = try zig_object.getOrCreateAtomForNav(wasm_file, pt, nav_index); + const atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, nav_index); const nav_info = zig_object.navs.getPtr(nav_index).?; - const atom = wasm_file.getAtom(atom_index); - const atom_sym = atom.symbolLoc().getSymbol(wasm_file).*; + const atom = wasm.getAtom(atom_index); + const atom_sym = wasm.symbolLocSymbol(atom.symbolLoc()).*; const gpa = zcu.gpa; log.debug("Updating exports for decl '{}'", .{nav.name.fmt(ip)}); @@ -926,17 +924,17 @@ pub fn updateExports( } log.debug(" with name '{s}' - {}", .{ export_string, sym }); try zig_object.global_syms.put(gpa, export_name, sym_index); - try wasm_file.symbol_atom.put(gpa, .{ .file = zig_object.index, .index = sym_index }, atom_index); + try wasm.symbol_atom.put(gpa, .{ .file = .zig_object, .index = sym_index }, atom_index); } } -pub fn freeNav(zig_object: *ZigObject, wasm_file: *Wasm, nav_index: InternPool.Nav.Index) void { - const gpa = wasm_file.base.comp.gpa; - const zcu = wasm_file.base.comp.zcu.?; +pub fn freeNav(zig_object: *ZigObject, wasm: *Wasm, nav_index: InternPool.Nav.Index) void { + const gpa = wasm.base.comp.gpa; + const zcu = wasm.base.comp.zcu.?; const ip = &zcu.intern_pool; const nav_info = zig_object.navs.getPtr(nav_index).?; const atom_index = nav_info.atom; - const atom = wasm_file.getAtomPtr(atom_index); + const atom = wasm.getAtomPtr(atom_index); zig_object.symbols_free_list.append(gpa, atom.sym_index) catch {}; for (nav_info.exports.items) |exp_sym_index| { const exp_sym = zig_object.symbol(exp_sym_index); @@ -947,11 +945,11 @@ pub fn freeNav(zig_object: *ZigObject, wasm_file: *Wasm, nav_index: InternPool.N std.debug.assert(zig_object.navs.remove(nav_index)); const sym = &zig_object.symbols.items[atom.sym_index]; for (atom.locals.items) |local_atom_index| { - const local_atom = wasm_file.getAtom(local_atom_index); + const local_atom = wasm.getAtom(local_atom_index); const local_symbol = &zig_object.symbols.items[local_atom.sym_index]; std.debug.assert(local_symbol.tag == .data); zig_object.symbols_free_list.append(gpa, local_atom.sym_index) catch {}; - std.debug.assert(wasm_file.symbol_atom.remove(local_atom.symbolLoc())); + std.debug.assert(wasm.symbol_atom.remove(local_atom.symbolLoc())); local_symbol.tag = .dead; // also for any local symbol const segment = &zig_object.segment_info.items[local_atom.sym_index]; gpa.free(segment.name); @@ -962,7 +960,7 @@ pub fn freeNav(zig_object: *ZigObject, wasm_file: *Wasm, nav_index: InternPool.N if (ip.indexToKey(nav_val) == .@"extern") { std.debug.assert(zig_object.imports.remove(atom.sym_index)); } - std.debug.assert(wasm_file.symbol_atom.remove(atom.symbolLoc())); + std.debug.assert(wasm.symbol_atom.remove(atom.symbolLoc())); // if (wasm.dwarf) |*dwarf| { // dwarf.freeDecl(decl_index); @@ -1014,15 +1012,15 @@ pub fn putOrGetFuncType(zig_object: *ZigObject, gpa: std.mem.Allocator, func_typ /// Generates an atom containing the global error set' size. /// This will only be generated if the symbol exists. -fn setupErrorsLen(zig_object: *ZigObject, wasm_file: *Wasm) !void { - const gpa = wasm_file.base.comp.gpa; +fn setupErrorsLen(zig_object: *ZigObject, wasm: *Wasm) !void { + const gpa = wasm.base.comp.gpa; const sym_index = zig_object.findGlobalSymbol("__zig_errors_len") orelse return; - const errors_len = 1 + wasm_file.base.comp.zcu.?.intern_pool.global_error_set.getNamesFromMainThread().len; + const errors_len = 1 + wasm.base.comp.zcu.?.intern_pool.global_error_set.getNamesFromMainThread().len; // overwrite existing atom if it already exists (maybe the error set has increased) // if not, allocate a new atom. - const atom_index = if (wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = sym_index })) |index| blk: { - const atom = wasm_file.getAtomPtr(index); + const atom_index = if (wasm.symbol_atom.get(.{ .file = .zig_object, .index = sym_index })) |index| blk: { + const atom = wasm.getAtomPtr(index); atom.prev = .null; atom.deinit(gpa); break :blk index; @@ -1036,10 +1034,10 @@ fn setupErrorsLen(zig_object: *ZigObject, wasm_file: *Wasm) !void { sym.tag = .data; const segment_name = try gpa.dupe(u8, ".rodata.__zig_errors_len"); sym.index = try zig_object.createDataSegment(gpa, segment_name, .@"2"); - break :idx try wasm_file.createAtom(sym_index, zig_object.index); + break :idx try wasm.createAtom(sym_index, .zig_object); }; - const atom = wasm_file.getAtomPtr(atom_index); + const atom = wasm.getAtomPtr(atom_index); atom.code.clearRetainingCapacity(); atom.sym_index = sym_index; atom.size = 2; @@ -1073,15 +1071,15 @@ pub fn initDebugSections(zig_object: *ZigObject) !void { /// From a given index variable, creates a new debug section. /// This initializes the index, appends a new segment, /// and finally, creates a managed `Atom`. -pub fn createDebugSectionForIndex(zig_object: *ZigObject, wasm_file: *Wasm, index: *?u32, name: []const u8) !Atom.Index { - const gpa = wasm_file.base.comp.gpa; +pub fn createDebugSectionForIndex(zig_object: *ZigObject, wasm: *Wasm, index: *?u32, name: []const u8) !Atom.Index { + const gpa = wasm.base.comp.gpa; const new_index: u32 = @intCast(zig_object.segments.items.len); index.* = new_index; try zig_object.appendDummySegment(); const sym_index = try zig_object.allocateSymbol(gpa); - const atom_index = try wasm_file.createAtom(sym_index, zig_object.index); - const atom = wasm_file.getAtomPtr(atom_index); + const atom_index = try wasm.createAtom(sym_index, .zig_object); + const atom = wasm.getAtomPtr(atom_index); zig_object.symbols.items[sym_index] = .{ .tag = .section, .name = try zig_object.string_table.put(gpa, name), @@ -1148,13 +1146,13 @@ pub fn storeDeclType(zig_object: *ZigObject, gpa: std.mem.Allocator, nav_index: /// The symbols in ZigObject are already represented by an atom as we need to store its data. /// So rather than creating a new Atom and returning its index, we use this opportunity to scan /// its relocations and create any GOT symbols or function table indexes it may require. -pub fn parseSymbolIntoAtom(zig_object: *ZigObject, wasm_file: *Wasm, index: Symbol.Index) !Atom.Index { - const gpa = wasm_file.base.comp.gpa; - const loc: Wasm.SymbolLoc = .{ .file = zig_object.index, .index = index }; - const atom_index = wasm_file.symbol_atom.get(loc).?; - const final_index = try wasm_file.getMatchingSegment(zig_object.index, index); - try wasm_file.appendAtomAtIndex(final_index, atom_index); - const atom = wasm_file.getAtom(atom_index); +pub fn parseSymbolIntoAtom(zig_object: *ZigObject, wasm: *Wasm, index: Symbol.Index) !Atom.Index { + const gpa = wasm.base.comp.gpa; + const loc: Wasm.SymbolLoc = .{ .file = .zig_object, .index = index }; + const atom_index = wasm.symbol_atom.get(loc).?; + const final_index = try wasm.getMatchingSegment(.zig_object, index); + try wasm.appendAtomAtIndex(final_index, atom_index); + const atom = wasm.getAtom(atom_index); for (atom.relocs.items) |reloc| { const reloc_index: Symbol.Index = @enumFromInt(reloc.index); switch (reloc.relocation_type) { @@ -1163,8 +1161,8 @@ pub fn parseSymbolIntoAtom(zig_object: *ZigObject, wasm_file: *Wasm, index: Symb .R_WASM_TABLE_INDEX_SLEB, .R_WASM_TABLE_INDEX_SLEB64, => { - try wasm_file.function_table.put(gpa, .{ - .file = zig_object.index, + try wasm.function_table.put(gpa, .{ + .file = .zig_object, .index = reloc_index, }, 0); }, @@ -1173,8 +1171,8 @@ pub fn parseSymbolIntoAtom(zig_object: *ZigObject, wasm_file: *Wasm, index: Symb => { const sym = zig_object.symbol(reloc_index); if (sym.tag != .global) { - try wasm_file.got_symbols.append(gpa, .{ - .file = zig_object.index, + try wasm.got_symbols.append(gpa, .{ + .file = .zig_object, .index = reloc_index, }); } @@ -1189,13 +1187,13 @@ pub fn parseSymbolIntoAtom(zig_object: *ZigObject, wasm_file: *Wasm, index: Symb /// Returns the symbol index of the new function. pub fn createFunction( zig_object: *ZigObject, - wasm_file: *Wasm, + wasm: *Wasm, symbol_name: []const u8, func_ty: std.wasm.Type, function_body: *std.ArrayList(u8), - relocations: *std.ArrayList(types.Relocation), + relocations: *std.ArrayList(Wasm.Relocation), ) !Symbol.Index { - const gpa = wasm_file.base.comp.gpa; + const gpa = wasm.base.comp.gpa; const sym_index = try zig_object.allocateSymbol(gpa); const sym = zig_object.symbol(sym_index); sym.tag = .function; @@ -1203,8 +1201,8 @@ pub fn createFunction( const type_index = try zig_object.putOrGetFuncType(gpa, func_ty); sym.index = try zig_object.appendFunction(gpa, .{ .type_index = type_index }); - const atom_index = try wasm_file.createAtom(sym_index, zig_object.index); - const atom = wasm_file.getAtomPtr(atom_index); + const atom_index = try wasm.createAtom(sym_index, .zig_object); + const atom = wasm.getAtomPtr(atom_index); atom.size = @intCast(function_body.items.len); atom.code = function_body.moveToUnmanaged(); atom.relocs = relocations.moveToUnmanaged(); @@ -1227,9 +1225,9 @@ fn appendFunction(zig_object: *ZigObject, gpa: std.mem.Allocator, func: std.wasm return index; } -pub fn flushModule(zig_object: *ZigObject, wasm_file: *Wasm, tid: Zcu.PerThread.Id) !void { - try zig_object.populateErrorNameTable(wasm_file, tid); - try zig_object.setupErrorsLen(wasm_file); +pub fn flushModule(zig_object: *ZigObject, wasm: *Wasm, tid: Zcu.PerThread.Id) !void { + try zig_object.populateErrorNameTable(wasm, tid); + try zig_object.setupErrorsLen(wasm); } const build_options = @import("build_options"); @@ -1238,12 +1236,10 @@ const codegen = @import("../../codegen.zig"); const link = @import("../../link.zig"); const log = std.log.scoped(.zig_object); const std = @import("std"); -const types = @import("types.zig"); const Air = @import("../../Air.zig"); -const Atom = @import("Atom.zig"); +const Atom = Wasm.Atom; const Dwarf = @import("../Dwarf.zig"); -const File = @import("file.zig").File; const InternPool = @import("../../InternPool.zig"); const Liveness = @import("../../Liveness.zig"); const Zcu = @import("../../Zcu.zig"); diff --git a/src/link/Wasm/file.zig b/src/link/Wasm/file.zig deleted file mode 100644 index e0ff121322..0000000000 --- a/src/link/Wasm/file.zig +++ /dev/null @@ -1,132 +0,0 @@ -pub const File = union(enum) { - zig_object: *ZigObject, - object: *Object, - - pub const Index = enum(u16) { - null = std.math.maxInt(u16), - _, - }; - - pub fn path(file: File) []const u8 { - return switch (file) { - inline else => |obj| obj.path, - }; - } - - pub fn segmentInfo(file: File) []const types.Segment { - return switch (file) { - .zig_object => |obj| obj.segment_info.items, - .object => |obj| obj.segment_info, - }; - } - - pub fn symbol(file: File, index: Symbol.Index) *Symbol { - return switch (file) { - .zig_object => |obj| &obj.symbols.items[@intFromEnum(index)], - .object => |obj| &obj.symtable[@intFromEnum(index)], - }; - } - - pub fn symbols(file: File) []const Symbol { - return switch (file) { - .zig_object => |obj| obj.symbols.items, - .object => |obj| obj.symtable, - }; - } - - pub fn symbolName(file: File, index: Symbol.Index) []const u8 { - switch (file) { - .zig_object => |obj| { - const sym = obj.symbols.items[@intFromEnum(index)]; - return obj.string_table.get(sym.name).?; - }, - .object => |obj| { - const sym = obj.symtable[@intFromEnum(index)]; - return obj.string_table.get(sym.name); - }, - } - } - - pub fn parseSymbolIntoAtom(file: File, wasm_file: *Wasm, index: Symbol.Index) !AtomIndex { - return switch (file) { - inline else => |obj| obj.parseSymbolIntoAtom(wasm_file, index), - }; - } - - /// For a given symbol index, find its corresponding import. - /// Asserts import exists. - pub fn import(file: File, symbol_index: Symbol.Index) types.Import { - return switch (file) { - .zig_object => |obj| obj.imports.get(symbol_index).?, - .object => |obj| obj.findImport(obj.symtable[@intFromEnum(symbol_index)]), - }; - } - - /// For a given offset, returns its string value. - /// Asserts string exists in the object string table. - pub fn string(file: File, offset: u32) []const u8 { - return switch (file) { - .zig_object => |obj| obj.string_table.get(offset).?, - .object => |obj| obj.string_table.get(offset), - }; - } - - pub fn importedGlobals(file: File) u32 { - return switch (file) { - inline else => |obj| obj.imported_globals_count, - }; - } - - pub fn importedFunctions(file: File) u32 { - return switch (file) { - inline else => |obj| obj.imported_functions_count, - }; - } - - pub fn importedTables(file: File) u32 { - return switch (file) { - inline else => |obj| obj.imported_tables_count, - }; - } - - pub fn function(file: File, sym_index: Symbol.Index) std.wasm.Func { - switch (file) { - .zig_object => |obj| { - const sym = obj.symbols.items[@intFromEnum(sym_index)]; - return obj.functions.items[sym.index]; - }, - .object => |obj| { - const sym = obj.symtable[@intFromEnum(sym_index)]; - return obj.functions[sym.index - obj.imported_functions_count]; - }, - } - } - - pub fn globals(file: File) []const std.wasm.Global { - return switch (file) { - .zig_object => |obj| obj.globals.items, - .object => |obj| obj.globals, - }; - } - - pub fn funcTypes(file: File) []const std.wasm.Type { - return switch (file) { - .zig_object => |obj| obj.func_types.items, - .object => |obj| obj.func_types, - }; - } - - pub const Entry = union(enum) { - zig_object: ZigObject, - object: Object, - }; -}; - -const std = @import("std"); -const types = @import("types.zig"); - -const AtomIndex = @import("Atom.zig").Index; -const Object = @import("Object.zig"); -const Symbol = @import("Symbol.zig"); -const Wasm = @import("../Wasm.zig"); -const ZigObject = @import("ZigObject.zig"); diff --git a/src/link/Wasm/types.zig b/src/link/Wasm/types.zig deleted file mode 100644 index 93b6835eae..0000000000 --- a/src/link/Wasm/types.zig +++ /dev/null @@ -1,267 +0,0 @@ -//! This file contains all constants and related to wasm's object format. - -const std = @import("std"); - -pub const Relocation = struct { - /// Represents the type of the `Relocation` - relocation_type: RelocationType, - /// Offset of the value to rewrite relative to the relevant section's contents. - /// When `offset` is zero, its position is immediately after the id and size of the section. - offset: u32, - /// The index of the symbol used. - /// When the type is `R_WASM_TYPE_INDEX_LEB`, it represents the index of the type. - index: u32, - /// Addend to add to the address. - /// This field is only non-zero for `R_WASM_MEMORY_ADDR_*`, `R_WASM_FUNCTION_OFFSET_I32` and `R_WASM_SECTION_OFFSET_I32`. - addend: i32 = 0, - - /// All possible relocation types currently existing. - /// This enum is exhaustive as the spec is WIP and new types - /// can be added which means that a generated binary will be invalid, - /// so instead we will show an error in such cases. - pub const RelocationType = enum(u8) { - R_WASM_FUNCTION_INDEX_LEB = 0, - R_WASM_TABLE_INDEX_SLEB = 1, - R_WASM_TABLE_INDEX_I32 = 2, - R_WASM_MEMORY_ADDR_LEB = 3, - R_WASM_MEMORY_ADDR_SLEB = 4, - R_WASM_MEMORY_ADDR_I32 = 5, - R_WASM_TYPE_INDEX_LEB = 6, - R_WASM_GLOBAL_INDEX_LEB = 7, - R_WASM_FUNCTION_OFFSET_I32 = 8, - R_WASM_SECTION_OFFSET_I32 = 9, - R_WASM_EVENT_INDEX_LEB = 10, - R_WASM_GLOBAL_INDEX_I32 = 13, - R_WASM_MEMORY_ADDR_LEB64 = 14, - R_WASM_MEMORY_ADDR_SLEB64 = 15, - R_WASM_MEMORY_ADDR_I64 = 16, - R_WASM_TABLE_INDEX_SLEB64 = 18, - R_WASM_TABLE_INDEX_I64 = 19, - R_WASM_TABLE_NUMBER_LEB = 20, - R_WASM_MEMORY_ADDR_TLS_SLEB = 21, - R_WASM_MEMORY_ADDR_TLS_SLEB64 = 25, - - /// Returns true for relocation types where the `addend` field is present. - pub fn addendIsPresent(self: RelocationType) bool { - return switch (self) { - .R_WASM_MEMORY_ADDR_LEB, - .R_WASM_MEMORY_ADDR_SLEB, - .R_WASM_MEMORY_ADDR_I32, - .R_WASM_MEMORY_ADDR_LEB64, - .R_WASM_MEMORY_ADDR_SLEB64, - .R_WASM_MEMORY_ADDR_I64, - .R_WASM_MEMORY_ADDR_TLS_SLEB, - .R_WASM_MEMORY_ADDR_TLS_SLEB64, - .R_WASM_FUNCTION_OFFSET_I32, - .R_WASM_SECTION_OFFSET_I32, - => true, - else => false, - }; - } - }; - - /// Verifies the relocation type of a given `Relocation` and returns - /// true when the relocation references a function call or address to a function. - pub fn isFunction(self: Relocation) bool { - return switch (self.relocation_type) { - .R_WASM_FUNCTION_INDEX_LEB, - .R_WASM_TABLE_INDEX_SLEB, - => true, - else => false, - }; - } - - pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { - _ = fmt; - _ = options; - try writer.print("{s} offset=0x{x:0>6} symbol={d}", .{ - @tagName(self.relocation_type), - self.offset, - self.index, - }); - } -}; - -/// Unlike the `Import` object defined by the wasm spec, and existing -/// in the std.wasm namespace, this construct saves the 'module name' and 'name' -/// of the import using offsets into a string table, rather than the slices itself. -/// This saves us (potentially) 24 bytes per import on 64bit machines. -pub const Import = struct { - module_name: u32, - name: u32, - kind: std.wasm.Import.Kind, -}; - -/// Unlike the `Export` object defined by the wasm spec, and existing -/// in the std.wasm namespace, this construct saves the 'name' -/// of the export using offsets into a string table, rather than the slice itself. -/// This saves us (potentially) 12 bytes per export on 64bit machines. -pub const Export = struct { - name: u32, - index: u32, - kind: std.wasm.ExternalKind, -}; - -pub const SubsectionType = enum(u8) { - WASM_SEGMENT_INFO = 5, - WASM_INIT_FUNCS = 6, - WASM_COMDAT_INFO = 7, - WASM_SYMBOL_TABLE = 8, -}; - -pub const Alignment = @import("../../InternPool.zig").Alignment; - -pub const Segment = struct { - /// Segment's name, encoded as UTF-8 bytes. - name: []const u8, - /// The required alignment of the segment, encoded as a power of 2 - alignment: Alignment, - /// Bitfield containing flags for a segment - flags: u32, - - pub fn isTLS(segment: Segment) bool { - return segment.flags & @intFromEnum(Flags.WASM_SEG_FLAG_TLS) != 0; - } - - /// Returns the name as how it will be output into the final object - /// file or binary. When `merge_segments` is true, this will return the - /// short name. i.e. ".rodata". When false, it returns the entire name instead. - pub fn outputName(segment: Segment, merge_segments: bool) []const u8 { - if (segment.isTLS()) { - return ".tdata"; - } else if (!merge_segments) { - return segment.name; - } else if (std.mem.startsWith(u8, segment.name, ".rodata.")) { - return ".rodata"; - } else if (std.mem.startsWith(u8, segment.name, ".text.")) { - return ".text"; - } else if (std.mem.startsWith(u8, segment.name, ".data.")) { - return ".data"; - } else if (std.mem.startsWith(u8, segment.name, ".bss.")) { - return ".bss"; - } - return segment.name; - } - - pub const Flags = enum(u32) { - WASM_SEG_FLAG_STRINGS = 0x1, - WASM_SEG_FLAG_TLS = 0x2, - }; -}; - -pub const InitFunc = struct { - /// Priority of the init function - priority: u32, - /// The symbol index of init function (not the function index). - symbol_index: u32, -}; - -pub const Comdat = struct { - name: []const u8, - /// Must be zero, no flags are currently defined by the tool-convention. - flags: u32, - symbols: []const ComdatSym, -}; - -pub const ComdatSym = struct { - kind: Type, - /// Index of the data segment/function/global/event/table within a WASM module. - /// The object must not be an import. - index: u32, - - pub const Type = enum(u8) { - WASM_COMDAT_DATA = 0, - WASM_COMDAT_FUNCTION = 1, - WASM_COMDAT_GLOBAL = 2, - WASM_COMDAT_EVENT = 3, - WASM_COMDAT_TABLE = 4, - WASM_COMDAT_SECTION = 5, - }; -}; - -pub const Feature = struct { - /// Provides information about the usage of the feature. - /// - '0x2b' (+): 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. - /// - '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. - prefix: Prefix, - /// Type of the feature, must be unique in the sequence of features. - tag: Tag, - - /// Unlike `std.Target.wasm.Feature` this also contains linker-features such as shared-mem - pub const Tag = enum { - atomics, - bulk_memory, - exception_handling, - extended_const, - half_precision, - multimemory, - multivalue, - mutable_globals, - nontrapping_fptoint, - reference_types, - relaxed_simd, - sign_ext, - simd128, - tail_call, - shared_mem, - - /// From a given cpu feature, returns its linker feature - pub fn fromCpuFeature(feature: std.Target.wasm.Feature) Tag { - return @as(Tag, @enumFromInt(@intFromEnum(feature))); - } - - pub fn format(tag: Tag, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void { - _ = fmt; - _ = opt; - try writer.writeAll(switch (tag) { - .atomics => "atomics", - .bulk_memory => "bulk-memory", - .exception_handling => "exception-handling", - .extended_const => "extended-const", - .half_precision => "half-precision", - .multimemory => "multimemory", - .multivalue => "multivalue", - .mutable_globals => "mutable-globals", - .nontrapping_fptoint => "nontrapping-fptoint", - .reference_types => "reference-types", - .relaxed_simd => "relaxed-simd", - .sign_ext => "sign-ext", - .simd128 => "simd128", - .tail_call => "tail-call", - .shared_mem => "shared-mem", - }); - } - }; - - pub const Prefix = enum(u8) { - used = '+', - disallowed = '-', - required = '=', - }; - - pub fn format(feature: Feature, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void { - _ = opt; - _ = fmt; - try writer.print("{c} {}", .{ feature.prefix, feature.tag }); - } -}; - -pub const known_features = std.StaticStringMap(Feature.Tag).initComptime(.{ - .{ "atomics", .atomics }, - .{ "bulk-memory", .bulk_memory }, - .{ "exception-handling", .exception_handling }, - .{ "extended-const", .extended_const }, - .{ "half-precision", .half_precision }, - .{ "multimemory", .multimemory }, - .{ "multivalue", .multivalue }, - .{ "mutable-globals", .mutable_globals }, - .{ "nontrapping-fptoint", .nontrapping_fptoint }, - .{ "reference-types", .reference_types }, - .{ "relaxed-simd", .relaxed_simd }, - .{ "sign-ext", .sign_ext }, - .{ "simd128", .simd128 }, - .{ "tail-call", .tail_call }, - .{ "shared-mem", .shared_mem }, -}); |
