diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 91896dea02..81e73e6ecf 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -1309,6 +1309,31 @@ pub fn deinit(wasm: *Wasm) void { archive.deinit(gpa); } + // For decls and anon decls we free the memory of its atoms. + // The memory of atoms parsed from object files is managed by + // the object file itself, and therefore we can skip those. + { + var it = wasm.decls.valueIterator(); + while (it.next()) |atom_index_ptr| { + const atom = wasm.getAtomPtr(atom_index_ptr.*); + for (atom.locals.items) |local_index| { + const local_atom = wasm.getAtomPtr(local_index); + local_atom.deinit(gpa); + } + atom.deinit(gpa); + } + } + { + for (wasm.anon_decls.values()) |atom_index| { + const atom = wasm.getAtomPtr(atom_index); + for (atom.locals.items) |local_index| { + const local_atom = wasm.getAtomPtr(local_index); + local_atom.deinit(gpa); + } + atom.deinit(gpa); + } + } + wasm.decls.deinit(gpa); wasm.anon_decls.deinit(gpa); wasm.atom_types.deinit(gpa); @@ -1321,9 +1346,6 @@ pub fn deinit(wasm: *Wasm) void { wasm.symbol_atom.deinit(gpa); wasm.export_names.deinit(gpa); wasm.atoms.deinit(gpa); - for (wasm.managed_atoms.items) |*managed_atom| { - managed_atom.deinit(wasm); - } wasm.managed_atoms.deinit(gpa); wasm.segments.deinit(gpa); wasm.data_segments.deinit(gpa); @@ -1342,6 +1364,10 @@ pub fn deinit(wasm: *Wasm) void { wasm.exports.deinit(gpa); wasm.string_table.deinit(gpa); + for (wasm.synthetic_functions.items) |atom_index| { + const atom = wasm.getAtomPtr(atom_index); + atom.deinit(gpa); + } wasm.synthetic_functions.deinit(gpa); if (wasm.dwarf) |*dwarf| { @@ -2406,7 +2432,7 @@ fn setupErrorsLen(wasm: *Wasm) !void { prev_atom.next = atom.next; atom.prev = null; } - atom.deinit(wasm); + atom.deinit(wasm.base.allocator); break :blk index; } else new_atom: { const atom_index: Atom.Index = @intCast(wasm.managed_atoms.items.len); @@ -2509,6 +2535,7 @@ fn createSyntheticFunction( .next = null, .prev = null, .code = function_body.moveToUnmanaged(), + .original_offset = 0, }; try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom_index); try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index); @@ -2545,6 +2572,7 @@ pub fn createFunction( .prev = null, .code = function_body.moveToUnmanaged(), .relocs = relocations.moveToUnmanaged(), + .original_offset = 0, }; const symbol = loc.getSymbol(wasm); symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); // ensure function does not get exported @@ -3016,14 +3044,14 @@ 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, object_index: u16, relocatable_index: u32) !?u32 { +pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u32 { const object: Object = wasm.objects.items[object_index]; - const relocatable_data = object.relocatable_data[relocatable_index]; + const symbol = object.symtable[symbol_index]; const index = @as(u32, @intCast(wasm.segments.items.len)); - switch (relocatable_data.type) { + switch (symbol.tag) { .data => { - const segment_info = object.segment_info[relocatable_data.index]; + const segment_info = object.segment_info[symbol.index]; const merge_segment = wasm.base.options.output_mode != .Obj; const result = try wasm.data_segments.getOrPut(wasm.base.allocator, segment_info.outputName(merge_segment)); if (!result.found_existing) { @@ -3041,67 +3069,67 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, relocatable_index: u32 return index; } else return result.value_ptr.*; }, - .code => return wasm.code_section_index orelse blk: { + .function => return wasm.code_section_index orelse blk: { wasm.code_section_index = index; try wasm.appendDummySegment(); break :blk index; }, - .debug => { - const debug_name = object.getDebugName(relocatable_data); - if (mem.eql(u8, debug_name, ".debug_info")) { + .section => { + const section_name = object.string_table.get(symbol.name); + if (mem.eql(u8, section_name, ".debug_info")) { return wasm.debug_info_index orelse blk: { wasm.debug_info_index = index; try wasm.appendDummySegment(); break :blk index; }; - } else if (mem.eql(u8, debug_name, ".debug_line")) { + } else if (mem.eql(u8, section_name, ".debug_line")) { return wasm.debug_line_index orelse blk: { wasm.debug_line_index = index; try wasm.appendDummySegment(); break :blk index; }; - } else if (mem.eql(u8, debug_name, ".debug_loc")) { + } else if (mem.eql(u8, section_name, ".debug_loc")) { return wasm.debug_loc_index orelse blk: { wasm.debug_loc_index = index; try wasm.appendDummySegment(); break :blk index; }; - } else if (mem.eql(u8, debug_name, ".debug_ranges")) { + } else if (mem.eql(u8, section_name, ".debug_ranges")) { return wasm.debug_line_index orelse blk: { wasm.debug_ranges_index = index; try wasm.appendDummySegment(); break :blk index; }; - } else if (mem.eql(u8, debug_name, ".debug_pubnames")) { + } else if (mem.eql(u8, section_name, ".debug_pubnames")) { return wasm.debug_pubnames_index orelse blk: { wasm.debug_pubnames_index = index; try wasm.appendDummySegment(); break :blk index; }; - } else if (mem.eql(u8, debug_name, ".debug_pubtypes")) { + } else if (mem.eql(u8, section_name, ".debug_pubtypes")) { return wasm.debug_pubtypes_index orelse blk: { wasm.debug_pubtypes_index = index; try wasm.appendDummySegment(); break :blk index; }; - } else if (mem.eql(u8, debug_name, ".debug_abbrev")) { + } else if (mem.eql(u8, section_name, ".debug_abbrev")) { return wasm.debug_abbrev_index orelse blk: { wasm.debug_abbrev_index = index; try wasm.appendDummySegment(); break :blk index; }; - } else if (mem.eql(u8, debug_name, ".debug_str")) { + } else if (mem.eql(u8, section_name, ".debug_str")) { return wasm.debug_str_index orelse blk: { wasm.debug_str_index = index; try wasm.appendDummySegment(); break :blk index; }; } else { - log.warn("found unknown debug section '{s}'", .{debug_name}); - log.warn(" debug section will be skipped", .{}); - return null; + log.warn("found unknown section '{s}'", .{section_name}); + return error.UnexpectedValue; } }, + else => unreachable, } } @@ -3468,11 +3496,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l try wasm.setupInitFunctions(); try wasm.setupStart(); - for (wasm.objects.items, 0..) |*object, object_index| { - try object.parseIntoAtoms(gpa, @as(u16, @intCast(object_index)), wasm); - } - - wasm.markReferences(); + try wasm.markReferences(); try wasm.setupImports(); try wasm.allocateAtoms(); try wasm.setupMemory(); @@ -3558,7 +3582,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try wasm.setupInitFunctions(); try wasm.setupErrorsLen(); try wasm.setupStart(); - wasm.markReferences(); + try wasm.markReferences(); try wasm.setupImports(); if (wasm.base.options.module) |mod| { var decl_it = wasm.decls.iterator(); @@ -3615,10 +3639,6 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } } - for (wasm.objects.items, 0..) |*object, object_index| { - try object.parseIntoAtoms(wasm.base.allocator, @as(u16, @intCast(object_index)), wasm); - } - try wasm.allocateAtoms(); try wasm.setupMemory(); wasm.allocateVirtualAddresses(); @@ -3885,18 +3905,15 @@ fn writeToFile( var atom_index = wasm.atoms.get(code_index).?; // The code section must be sorted in line with the function order. - var sorted_atoms = try std.ArrayList(*Atom).initCapacity(wasm.base.allocator, wasm.functions.count()); + var sorted_atoms = try std.ArrayList(*const Atom).initCapacity(wasm.base.allocator, wasm.functions.count()); defer sorted_atoms.deinit(); while (true) { - var atom = wasm.getAtomPtr(atom_index); - if (wasm.resolved_symbols.contains(atom.symbolLoc())) { - if (!is_obj) { - atom.resolveRelocs(wasm); - } - sorted_atoms.appendAssumeCapacity(atom); + const atom = wasm.getAtomPtr(atom_index); + if (!is_obj) { + atom.resolveRelocs(wasm); } - // atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break; + sorted_atoms.appendAssumeCapacity(atom); // found more code atoms than functions atom_index = atom.prev orelse break; } @@ -3908,7 +3925,7 @@ fn writeToFile( } }.sort; - mem.sort(*Atom, sorted_atoms.items, wasm, atom_sort_fn); + mem.sort(*const Atom, sorted_atoms.items, wasm, atom_sort_fn); for (sorted_atoms.items) |sorted_atom| { try leb.writeULEB128(binary_writer, sorted_atom.size); @@ -5060,20 +5077,20 @@ pub fn storeDeclType(wasm: *Wasm, decl_index: InternPool.DeclIndex, func_type: s /// Verifies all resolved symbols and checks whether itself needs to be marked alive, /// as well as any of its references. -fn markReferences(wasm: *Wasm) void { +fn markReferences(wasm: *Wasm) !void { const tracy = trace(@src()); defer tracy.end(); for (wasm.resolved_symbols.keys()) |sym_loc| { const sym = sym_loc.getSymbol(wasm); if (sym.isExported(wasm.base.options.rdynamic) or sym.isNoStrip()) { - wasm.mark(sym_loc); + try wasm.mark(sym_loc); } } } /// 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 { +fn mark(wasm: *Wasm, loc: SymbolLoc) !void { const symbol = loc.getSymbol(wasm); if (symbol.isAlive()) { // Symbol is already marked alive, including its references. @@ -5082,13 +5099,20 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) void { return; } symbol.mark(); + if (symbol.isUndefined()) { + // undefined symbols do not have an associated `Atom` and therefore also + // do not contain relocations. + return; + } - if (wasm.symbol_atom.get(loc)) |atom_index| { - const atom = wasm.getAtom(atom_index); - const relocations: []const types.Relocation = atom.relocs.items; - for (relocations) |reloc| { - const target_loc: SymbolLoc = .{ .index = reloc.index, .file = loc.file }; - wasm.mark(target_loc.finalLoc(wasm)); - } + const file = loc.file orelse return; // Marking synthetic and Zig symbols is done seperately + const object = &wasm.objects.items[file]; + const atom_index = try Object.parseSymbolIntoAtom(object, file, loc.index, wasm); + + const atom = wasm.getAtom(atom_index); + const relocations: []const types.Relocation = atom.relocs.items; + for (relocations) |reloc| { + const target_loc: SymbolLoc = .{ .index = reloc.index, .file = file }; + try wasm.mark(target_loc.finalLoc(wasm)); } } diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index 60727b6af1..b20e8628ba 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -23,6 +23,10 @@ alignment: Wasm.Alignment, /// Offset into the section where the atom lives, this already accounts /// for alignment. offset: u32, +/// The original offset within the object file. This value is substracted from +/// relocation offsets to determine where in the `data` to rewrite the value +original_offset: u32, + /// Represents the index of the file this atom was generated from. /// This is 'null' when the atom was generated by a Decl from Zig code. file: ?u16, @@ -50,11 +54,11 @@ pub const empty: Atom = .{ .prev = null, .size = 0, .sym_index = 0, + .original_offset = 0, }; /// Frees all resources owned by this `Atom`. -pub fn deinit(atom: *Atom, wasm: *Wasm) void { - const gpa = wasm.base.allocator; +pub fn deinit(atom: *Atom, gpa: std.mem.Allocator) void { atom.relocs.deinit(gpa); atom.code.deinit(gpa); atom.locals.deinit(gpa); @@ -114,10 +118,10 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void { .R_WASM_GLOBAL_INDEX_I32, .R_WASM_MEMORY_ADDR_I32, .R_WASM_SECTION_OFFSET_I32, - => std.mem.writeInt(u32, atom.code.items[reloc.offset..][0..4], @as(u32, @intCast(value)), .little), + => std.mem.writeInt(u32, atom.code.items[reloc.offset - atom.original_offset ..][0..4], @as(u32, @intCast(value)), .little), .R_WASM_TABLE_INDEX_I64, .R_WASM_MEMORY_ADDR_I64, - => std.mem.writeInt(u64, atom.code.items[reloc.offset..][0..8], value, .little), + => 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, @@ -127,12 +131,12 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void { .R_WASM_TABLE_NUMBER_LEB, .R_WASM_TYPE_INDEX_LEB, .R_WASM_MEMORY_ADDR_TLS_SLEB, - => leb.writeUnsignedFixed(5, atom.code.items[reloc.offset..][0..5], @as(u32, @intCast(value))), + => leb.writeUnsignedFixed(5, atom.code.items[reloc.offset - atom.original_offset ..][0..5], @as(u32, @intCast(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..][0..10], value), + => leb.writeUnsignedFixed(10, atom.code.items[reloc.offset - atom.original_offset ..][0..10], value), } } } @@ -150,7 +154,7 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa .R_WASM_TABLE_INDEX_I64, .R_WASM_TABLE_INDEX_SLEB, .R_WASM_TABLE_INDEX_SLEB64, - => return wasm_bin.function_table.get(target_loc) orelse 0, + => return wasm_bin.function_table.get(.{ .file = atom.file, .index = relocation.index }) orelse 0, .R_WASM_TYPE_INDEX_LEB => { const file_index = atom.file orelse { return relocation.index; diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index e7c118e48e..858d52f836 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -59,20 +59,16 @@ init_funcs: []const types.InitFunc = &.{}, comdat_info: []const types.Comdat = &.{}, /// Represents non-synthetic sections that can essentially be mem-cpy'd into place /// after performing relocations. -relocatable_data: []const RelocatableData = &.{}, +relocatable_data: std.AutoHashMapUnmanaged(RelocatableData.Tag, []RelocatableData) = .{}, /// String table for all strings required by the object file, such as symbol names, /// import name, module name and export names. Each string will be deduplicated /// and returns an offset into the table. string_table: Wasm.StringTable = .{}, -/// All the names of each debug section found in the current object file. -/// Each name is terminated by a null-terminator. The name can be found, -/// from the `index` offset within the `RelocatableData`. -debug_names: [:0]const u8, /// Represents a single item within a section (depending on its `type`) const RelocatableData = struct { /// The type of the relocatable data - type: enum { data, code, debug }, + type: Tag, /// Pointer to the data of the segment, where its length is written to `size` data: [*]u8, /// The size in bytes of the data representing the segment within the section @@ -85,6 +81,8 @@ const RelocatableData = struct { /// Represents the index of the section it belongs to section_index: u32, + const Tag = enum { data, code, custom }; + /// Returns the alignment of the segment, by retrieving it from the segment /// meta data of the given object file. /// NOTE: Alignment is encoded as a power of 2, so we shift the symbol's @@ -99,14 +97,14 @@ const RelocatableData = struct { return switch (relocatable_data.type) { .data => .data, .code => .function, - .debug => .section, + .custom => .section, }; } - /// Returns the index within a section itrelocatable_data, or in case of a debug section, + /// Returns the index within a section, or in case of a custom section, /// returns the section index within the object file. pub fn getIndex(relocatable_data: RelocatableData) u32 { - if (relocatable_data.type == .debug) return relocatable_data.section_index; + if (relocatable_data.type == .custom) return relocatable_data.section_index; return relocatable_data.index; } }; @@ -121,7 +119,6 @@ pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_siz var object: Object = .{ .file = file, .name = try gpa.dupe(u8, name), - .debug_names = &.{}, }; var is_object_file: bool = false; @@ -182,10 +179,16 @@ pub fn deinit(object: *Object, gpa: Allocator) void { gpa.free(info.name); } gpa.free(object.segment_info); - for (object.relocatable_data) |rel_data| { - gpa.free(rel_data.data[0..rel_data.size]); + { + var it = object.relocatable_data.valueIterator(); + while (it.next()) |relocatable_data| { + for (relocatable_data.*) |rel_data| { + gpa.free(rel_data.data[0..rel_data.size]); + } + gpa.free(relocatable_data.*); + } } - gpa.free(object.relocatable_data); + object.relocatable_data.deinit(gpa); object.string_table.deinit(gpa); gpa.free(object.name); object.* = undefined; @@ -345,23 +348,7 @@ fn Parser(comptime ReaderType: type) type { errdefer parser.object.deinit(gpa); try parser.verifyMagicBytes(); const version = try parser.reader.reader().readInt(u32, .little); - parser.object.version = version; - var relocatable_data = std.ArrayList(RelocatableData).init(gpa); - var debug_names = std.ArrayList(u8).init(gpa); - - errdefer { - // only free the inner contents of relocatable_data if we didn't - // assign it to the object yet. - if (parser.object.relocatable_data.len == 0) { - for (relocatable_data.items) |rel_data| { - gpa.free(rel_data.data[0..rel_data.size]); - } - relocatable_data.deinit(); - } - gpa.free(debug_names.items); - debug_names.deinit(); - } var section_index: u32 = 0; while (parser.reader.reader().readByte()) |byte| : (section_index += 1) { @@ -377,26 +364,34 @@ fn Parser(comptime ReaderType: type) type { if (std.mem.eql(u8, name, "linking")) { is_object_file.* = true; - parser.object.relocatable_data = relocatable_data.items; // at this point no new relocatable sections will appear so we're free to store them. try parser.parseMetadata(gpa, @as(usize, @intCast(reader.context.bytes_left))); } else if (std.mem.startsWith(u8, name, "reloc")) { try parser.parseRelocations(gpa); } else if (std.mem.eql(u8, name, "target_features")) { try parser.parseFeatures(gpa); } else if (std.mem.startsWith(u8, name, ".debug")) { + const gop = try parser.object.relocatable_data.getOrPut(gpa, .custom); + var relocatable_data: std.ArrayListUnmanaged(RelocatableData) = .{}; + defer relocatable_data.deinit(gpa); + if (!gop.found_existing) { + gop.value_ptr.* = &.{}; + } else { + relocatable_data = std.ArrayListUnmanaged(RelocatableData).fromOwnedSlice(gop.value_ptr.*); + } const debug_size = @as(u32, @intCast(reader.context.bytes_left)); const debug_content = try gpa.alloc(u8, debug_size); errdefer gpa.free(debug_content); try reader.readNoEof(debug_content); - try relocatable_data.append(.{ - .type = .debug, + try relocatable_data.append(gpa, .{ + .type = .custom, .data = debug_content.ptr, .size = debug_size, .index = try parser.object.string_table.put(gpa, name), .offset = 0, // debug sections only contain 1 entry, so no need to calculate offset .section_index = section_index, }); + gop.value_ptr.* = try relocatable_data.toOwnedSlice(gpa); } else { try reader.skipBytes(reader.context.bytes_left, .{}); } @@ -515,26 +510,32 @@ fn Parser(comptime ReaderType: type) type { const start = reader.context.bytes_left; var index: u32 = 0; const count = try readLeb(u32, reader); + const imported_function_count = parser.object.importedCountByKind(.function); + var relocatable_data = try std.ArrayList(RelocatableData).initCapacity(gpa, count); + defer relocatable_data.deinit(); while (index < count) : (index += 1) { const code_len = try readLeb(u32, reader); const offset = @as(u32, @intCast(start - reader.context.bytes_left)); const data = try gpa.alloc(u8, code_len); errdefer gpa.free(data); try reader.readNoEof(data); - try relocatable_data.append(.{ + relocatable_data.appendAssumeCapacity(.{ .type = .code, .data = data.ptr, .size = code_len, - .index = parser.object.importedCountByKind(.function) + index, + .index = imported_function_count + index, .offset = offset, .section_index = section_index, }); } + try parser.object.relocatable_data.put(gpa, .code, try relocatable_data.toOwnedSlice()); }, .data => { const start = reader.context.bytes_left; var index: u32 = 0; const count = try readLeb(u32, reader); + var relocatable_data = try std.ArrayList(RelocatableData).initCapacity(gpa, count); + defer relocatable_data.deinit(); while (index < count) : (index += 1) { const flags = try readLeb(u32, reader); const data_offset = try readInit(reader); @@ -545,7 +546,7 @@ fn Parser(comptime ReaderType: type) type { const data = try gpa.alloc(u8, data_len); errdefer gpa.free(data); try reader.readNoEof(data); - try relocatable_data.append(.{ + relocatable_data.appendAssumeCapacity(.{ .type = .data, .data = data.ptr, .size = data_len, @@ -554,6 +555,7 @@ fn Parser(comptime ReaderType: type) type { .section_index = section_index, }); } + try parser.object.relocatable_data.put(gpa, .data, try relocatable_data.toOwnedSlice()); }, else => try parser.reader.reader().skipBytes(len, .{}), } @@ -561,7 +563,6 @@ fn Parser(comptime ReaderType: type) type { error.EndOfStream => {}, // finished parsing the file else => |e| return e, } - parser.object.relocatable_data = try relocatable_data.toOwnedSlice(); } /// Based on the "features" custom section, parses it into a list of @@ -789,7 +790,8 @@ fn Parser(comptime ReaderType: type) type { }, .section => { symbol.index = try leb.readULEB128(u32, reader); - for (parser.object.relocatable_data) |data| { + const section_data = parser.object.relocatable_data.get(.custom).?; + for (section_data) |data| { if (data.section_index == symbol.index) { symbol.name = data.index; break; @@ -798,22 +800,15 @@ fn Parser(comptime ReaderType: type) type { }, else => { symbol.index = try leb.readULEB128(u32, reader); - var maybe_import: ?types.Import = null; - const is_undefined = symbol.isUndefined(); - if (is_undefined) { - maybe_import = parser.object.findImport(symbol.tag.externalType(), symbol.index); - } const explicit_name = symbol.hasFlag(.WASM_SYM_EXPLICIT_NAME); - if (!(is_undefined and !explicit_name)) { + symbol.name = if (!is_undefined or (is_undefined and explicit_name)) name: { const name_len = try leb.readULEB128(u32, reader); const name = try gpa.alloc(u8, name_len); defer gpa.free(name); try reader.readNoEof(name); - symbol.name = try parser.object.string_table.put(gpa, name); - } else { - symbol.name = maybe_import.?.name; - } + break :name try parser.object.string_table.put(gpa, name); + } else parser.object.findImport(symbol.tag.externalType(), symbol.index).name; }, } return symbol; @@ -887,110 +882,95 @@ fn assertEnd(reader: anytype) !void { } /// Parses an object file into atoms, for code and data sections -pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_bin: *Wasm) !void { - const Key = struct { - kind: Symbol.Tag, - index: u32, +pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32, wasm: *Wasm) !Atom.Index { + const symbol = &object.symtable[symbol_index]; + const relocatable_data: RelocatableData = switch (symbol.tag) { + .function => object.relocatable_data.get(.code).?[symbol.index - object.importedCountByKind(.function)], + .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, }; - var symbol_for_segment = std.AutoArrayHashMap(Key, std.ArrayList(u32)).init(gpa); - defer for (symbol_for_segment.values()) |*list| { - list.deinit(); - } else symbol_for_segment.deinit(); + const final_index = try wasm.getMatchingSegment(object_index, symbol_index); + const atom_index = @as(Atom.Index, @intCast(wasm.managed_atoms.items.len)); + const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); + atom.* = Atom.empty; + try wasm.appendAtomAtIndex(final_index, atom_index); - for (object.symtable, 0..) |symbol, symbol_index| { - switch (symbol.tag) { - .function, .data, .section => if (!symbol.isUndefined()) { - const gop = try symbol_for_segment.getOrPut(.{ .kind = symbol.tag, .index = symbol.index }); - const sym_idx = @as(u32, @intCast(symbol_index)); - if (!gop.found_existing) { - gop.value_ptr.* = std.ArrayList(u32).init(gpa); - } - try gop.value_ptr.*.append(sym_idx); - }, - else => continue, + atom.sym_index = symbol_index; + atom.file = object_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; + try wasm.symbol_atom.putNoClobber(wasm.base.allocator, atom.symbolLoc(), atom_index); + 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(wasm.base.allocator, .{ + .file = object_index, + .index = 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( + wasm.base.allocator, + .{ .file = object_index, .index = reloc.index }, + ); + } + }, + else => {}, + } } } - for (object.relocatable_data, 0..) |relocatable_data, index| { - const final_index = (try wasm_bin.getMatchingSegment(object_index, @as(u32, @intCast(index)))) orelse { - continue; // found unknown section, so skip parsing into atom as we do not know how to handle it. - }; + return atom_index; +} - const atom_index: Atom.Index = @intCast(wasm_bin.managed_atoms.items.len); - const atom = try wasm_bin.managed_atoms.addOne(gpa); - atom.* = Atom.empty; - atom.file = object_index; - atom.size = relocatable_data.size; - atom.alignment = relocatable_data.getAlignment(object); - - const relocations: []types.Relocation = object.relocations.get(relocatable_data.section_index) orelse &.{}; - for (relocations) |relocation| { - if (isInbetween(relocatable_data.offset, atom.size, relocation.offset)) { - // set the offset relative to the offset of the segment itobject, - // rather than within the entire section. - var reloc = relocation; - reloc.offset -= relocatable_data.offset; - try atom.relocs.append(gpa, reloc); - - switch (relocation.relocation_type) { - .R_WASM_TABLE_INDEX_I32, - .R_WASM_TABLE_INDEX_I64, - .R_WASM_TABLE_INDEX_SLEB, - .R_WASM_TABLE_INDEX_SLEB64, - => { - try wasm_bin.function_table.put(gpa, .{ - .file = object_index, - .index = relocation.index, - }, 0); - }, - .R_WASM_GLOBAL_INDEX_I32, - .R_WASM_GLOBAL_INDEX_LEB, - => { - const sym = object.symtable[relocation.index]; - if (sym.tag != .global) { - try wasm_bin.got_symbols.append( - wasm_bin.base.allocator, - .{ .file = object_index, .index = relocation.index }, - ); - } - }, - else => {}, - } - } +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; } - - try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]); - - if (symbol_for_segment.getPtr(.{ - .kind = relocatable_data.getSymbolKind(), - .index = relocatable_data.getIndex(), - })) |symbols| { - atom.sym_index = symbols.pop(); - try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom_index); - - // symbols referencing the same atom will be added as alias - // or as 'parent' when they are global. - while (symbols.popOrNull()) |idx| { - try wasm_bin.symbol_atom.putNoClobber(gpa, .{ .file = atom.file, .index = idx }, atom_index); - const alias_symbol = object.symtable[idx]; - if (alias_symbol.isGlobal()) { - atom.sym_index = idx; - } - } - } - - const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index]; - if (relocatable_data.type == .data) { //code section and debug sections are 1-byte aligned - segment.alignment = segment.alignment.max(atom.alignment); - } - - try wasm_bin.appendAtomAtIndex(final_index, atom_index); - log.debug("Parsed into atom: '{s}' at segment index {d}", .{ object.string_table.get(object.symtable[atom.sym_index].name), final_index }); } + return min; } -/// Verifies if a given value is in between a minimum -and maximum value. -/// The maxmimum value is calculated using the length, both start and end are inclusive. -inline fn isInbetween(min: u32, length: u32, value: u32) bool { - return value >= min and value <= min + length; +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; }