From 9a92f3d290694bfefbc7d71b5ba1823edb6c547f Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Mon, 29 Aug 2022 20:13:55 +0200 Subject: [PATCH 1/8] wasm/Object: parse debug sections into reloc data Rather than storing the name of a debug section into the structure `RelocatableData`, we use the `index` field as an offset into the debug names table. This means we do not have to store an extra 16 bytes for non-debug sections which can be massive for object files where each data symbol has its own data section. The name of a debug section can then be retrieved again when needed by using the offset and then reading until the 0-delimiter. --- src/link/Wasm.zig | 2 +- src/link/Wasm/Object.zig | 49 +++++++++++++++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 050d9287a5..a62a11a389 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -1782,7 +1782,7 @@ pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32 }); break :blk index; }, - .custom => return error.@"TODO: Custom section relocations for wasm", + .debug => return error.@"TODO: Custom section relocations for wasm", } } diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index 50827ca9fb..d07d0b39ea 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -63,16 +63,21 @@ relocatable_data: []const RelocatableData = &.{}, /// 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, custom }, + type: enum { data, code, debug }, /// 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 size: u32, - /// The index within the section itself + /// The index within the section itself, or in case of a debug section, + /// the offset within the `debug_names` table. index: u32, /// The offset within the section where the data starts offset: u32, @@ -96,7 +101,7 @@ const RelocatableData = struct { return switch (self.type) { .data => .data, .code => .function, - .custom => .section, + .debug => unreachable, // illegal, debug sections are not represented by a symbol }; } }; @@ -111,6 +116,7 @@ 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; @@ -197,6 +203,11 @@ pub fn importedCountByKind(self: *const Object, kind: std.wasm.ExternalKind) u32 } else i; } +/// From a given `RelocatableDate`, find the corresponding debug section name +pub fn getDebugName(self: *const Object, relocatable_data: RelocatableData) []const u8 { + return std.mem.sliceTo(self.debug_names[relocatable_data.index..], 0); +} + /// Checks if the object file is an MVP version. /// When that's the case, we check if there's an import table definiton with its name /// set to '__indirect_function_table". When that's also the case, @@ -328,10 +339,15 @@ fn Parser(comptime ReaderType: type) type { self.object.version = version; var relocatable_data = std.ArrayList(RelocatableData).init(gpa); + var debug_names = std.ArrayList(u8).init(gpa); - errdefer while (relocatable_data.popOrNull()) |rel_data| { - gpa.free(rel_data.data[0..rel_data.size]); - } else relocatable_data.deinit(); + errdefer { + while (relocatable_data.popOrNull()) |rel_data| { + gpa.free(rel_data.data[0..rel_data.size]); + } else relocatable_data.deinit(); + gpa.free(debug_names.items); + debug_names.deinit(); + } var section_index: u32 = 0; while (self.reader.reader().readByte()) |byte| : (section_index += 1) { @@ -352,6 +368,24 @@ fn Parser(comptime ReaderType: type) type { try self.parseRelocations(gpa); } else if (std.mem.eql(u8, name, "target_features")) { try self.parseFeatures(gpa); + } else if (std.mem.startsWith(u8, name, ".debug")) { + const debug_size = @intCast(u32, reader.context.bytes_left); + const debug_content = try gpa.alloc(u8, debug_size); + errdefer gpa.free(debug_content); + try reader.readNoEof(debug_content); + + const debug_name_index = @intCast(u32, debug_names.items.len); + try debug_names.ensureUnusedCapacity(name.len + 1); + debug_names.appendSliceAssumeCapacity(try gpa.dupe(u8, name)); + debug_names.appendAssumeCapacity(0); + try relocatable_data.append(.{ + .type = .debug, + .data = debug_content.ptr, + .size = debug_size, + .index = debug_name_index, + .offset = len - debug_size, + .section_index = section_index, + }); } else { try reader.skipBytes(reader.context.bytes_left, .{}); } @@ -517,6 +551,9 @@ fn Parser(comptime ReaderType: type) type { else => |e| return e, } self.object.relocatable_data = relocatable_data.toOwnedSlice(); + + const names = debug_names.toOwnedSlice(); + self.object.debug_names = names[0 .. names.len - 1 :0]; } /// Based on the "features" custom section, parses it into a list of From f060edb0f3e23a18b17af9b619f7f499ac8e4e7f Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 30 Aug 2022 21:47:05 +0200 Subject: [PATCH 2/8] wasm-linker: create atoms from debug sections --- src/link/Wasm.zig | 173 ++++++++++++++++++++++++++++----------- src/link/Wasm/Object.zig | 39 +++++---- 2 files changed, 146 insertions(+), 66 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index a62a11a389..ae995bd0b9 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -67,6 +67,18 @@ code_section_index: ?u32 = null, debug_info_index: ?u32 = null, /// The index of the segment representing the custom '.debug_line' section. debug_line_index: ?u32 = null, +/// The index of the segment representing the custom '.debug_loc' section. +debug_loc_index: ?u32 = null, +/// The index of the segment representing the custom '.debug_ranges' section. +debug_ranges_index: ?u32 = null, +/// The index of the segment representing the custom '.debug_pubnames' section. +debug_pubnames_index: ?u32 = null, +/// The index of the segment representing the custom '.debug_pubtypes' section. +debug_pubtypes_index: ?u32 = null, +/// The index of the segment representing the custom '.debug_pubtypes' section. +debug_str_index: ?u32 = null, +/// The index of the segment representing the custom '.debug_pubtypes' section. +debug_abbrev_index: ?u32 = null, /// The count of imported functions. This number will be appended /// to the function indexes as their index starts at the lowest non-extern function. imported_functions_count: u32 = 0, @@ -1753,7 +1765,7 @@ fn setupMemory(self: *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(self: *Wasm, object_index: u16, relocatable_index: u32) !u32 { +pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32) !?u32 { const object: Object = self.objects.items[object_index]; const relocatable_data = object.relocatable_data[relocatable_index]; const index = @intCast(u32, self.segments.items.len); @@ -1765,27 +1777,83 @@ pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32 const result = try self.data_segments.getOrPut(self.base.allocator, segment_info.outputName(merge_segment)); if (!result.found_existing) { result.value_ptr.* = index; - try self.segments.append(self.base.allocator, .{ - .alignment = 1, - .size = 0, - .offset = 0, - }); + try self.appendDummySegment(); return index; } else return result.value_ptr.*; }, .code => return self.code_section_index orelse blk: { self.code_section_index = index; - try self.segments.append(self.base.allocator, .{ - .alignment = 1, - .size = 0, - .offset = 0, - }); + try self.appendDummySegment(); break :blk index; }, - .debug => return error.@"TODO: Custom section relocations for wasm", + .debug => { + const debug_name = object.getDebugName(relocatable_data); + if (mem.eql(u8, debug_name, ".debug_info")) { + return self.debug_info_index orelse blk: { + self.debug_info_index = index; + try self.appendDummySegment(); + break :blk index; + }; + } else if (mem.eql(u8, debug_name, ".debug_line")) { + return self.debug_line_index orelse blk: { + self.debug_line_index = index; + try self.appendDummySegment(); + break :blk index; + }; + } else if (mem.eql(u8, debug_name, ".debug_loc")) { + return self.debug_loc_index orelse blk: { + self.debug_loc_index = index; + try self.appendDummySegment(); + break :blk index; + }; + } else if (mem.eql(u8, debug_name, ".debug_ranges")) { + return self.debug_line_index orelse blk: { + self.debug_ranges_index = index; + try self.appendDummySegment(); + break :blk index; + }; + } else if (mem.eql(u8, debug_name, ".debug_pubnames")) { + return self.debug_pubnames_index orelse blk: { + self.debug_pubnames_index = index; + try self.appendDummySegment(); + break :blk index; + }; + } else if (mem.eql(u8, debug_name, ".debug_pubtypes")) { + return self.debug_pubtypes_index orelse blk: { + self.debug_pubtypes_index = index; + try self.appendDummySegment(); + break :blk index; + }; + } else if (mem.eql(u8, debug_name, ".debug_abbrev")) { + return self.debug_abbrev_index orelse blk: { + self.debug_abbrev_index = index; + try self.appendDummySegment(); + break :blk index; + }; + } else if (mem.eql(u8, debug_name, ".debug_str")) { + return self.debug_str_index orelse blk: { + self.debug_str_index = index; + try self.appendDummySegment(); + break :blk index; + }; + } else { + log.warn("found unknown debug section '{s}'", .{debug_name}); + log.warn(" debug section will be skipped", .{}); + return null; + } + }, } } +/// Appends a new segment with default field values +fn appendDummySegment(self: *Wasm) !void { + try self.segments.append(self.base.allocator, .{ + .alignment = 1, + .size = 0, + .offset = 0, + }); +} + /// Returns the symbol index of the error name table. /// /// When the symbol does not yet exist, it will create a new one instead. @@ -1936,17 +2004,18 @@ fn resetState(self: *Wasm) void { for (self.segment_info.items) |*segment_info| { self.base.allocator.free(segment_info.name); } - const mod = self.base.options.module.?; - var decl_it = self.decls.keyIterator(); - while (decl_it.next()) |decl_index_ptr| { - const decl = mod.declPtr(decl_index_ptr.*); - const atom = &decl.link.wasm; - atom.next = null; - atom.prev = null; + if (self.base.options.module) |mod| { + var decl_it = self.decls.keyIterator(); + while (decl_it.next()) |decl_index_ptr| { + const decl = mod.declPtr(decl_index_ptr.*); + const atom = &decl.link.wasm; + atom.next = null; + atom.prev = null; - for (atom.locals.items) |*local_atom| { - local_atom.next = null; - local_atom.prev = null; + for (atom.locals.items) |*local_atom| { + local_atom.next = null; + local_atom.prev = null; + } } } self.functions.clearRetainingCapacity(); @@ -2036,29 +2105,34 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod defer self.resetState(); try self.setupStart(); try self.setupImports(); - const mod = self.base.options.module.?; - var decl_it = self.decls.keyIterator(); - while (decl_it.next()) |decl_index_ptr| { - const decl = mod.declPtr(decl_index_ptr.*); - if (decl.isExtern()) continue; - const atom = &decl.*.link.wasm; - if (decl.ty.zigTypeTag() == .Fn) { - try self.parseAtom(atom, .{ .function = decl.fn_link.wasm }); - } else if (decl.getVariable()) |variable| { - if (!variable.is_mutable) { - try self.parseAtom(atom, .{ .data = .read_only }); - } else if (variable.init.isUndefDeep()) { - try self.parseAtom(atom, .{ .data = .uninitialized }); + if (self.base.options.module) |mod| { + var decl_it = self.decls.keyIterator(); + while (decl_it.next()) |decl_index_ptr| { + const decl = mod.declPtr(decl_index_ptr.*); + if (decl.isExtern()) continue; + const atom = &decl.*.link.wasm; + if (decl.ty.zigTypeTag() == .Fn) { + try self.parseAtom(atom, .{ .function = decl.fn_link.wasm }); + } else if (decl.getVariable()) |variable| { + if (!variable.is_mutable) { + try self.parseAtom(atom, .{ .data = .read_only }); + } else if (variable.init.isUndefDeep()) { + try self.parseAtom(atom, .{ .data = .uninitialized }); + } else { + try self.parseAtom(atom, .{ .data = .initialized }); + } } else { - try self.parseAtom(atom, .{ .data = .initialized }); + try self.parseAtom(atom, .{ .data = .read_only }); + } + + // also parse atoms for a decl's locals + for (atom.locals.items) |*local_atom| { + try self.parseAtom(local_atom, .{ .data = .read_only }); } - } else { - try self.parseAtom(atom, .{ .data = .read_only }); } - // also parse atoms for a decl's locals - for (atom.locals.items) |*local_atom| { - try self.parseAtom(local_atom, .{ .data = .read_only }); + if (self.dwarf) |*dwarf| { + try dwarf.flushModule(&self.base, self.base.options.module.?); } } @@ -2066,9 +2140,6 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try object.parseIntoAtoms(self.base.allocator, @intCast(u16, object_index), self); } - if (self.dwarf) |*dwarf| { - try dwarf.flushModule(&self.base, self.base.options.module.?); - } try self.allocateAtoms(); try self.setupMemory(); self.mapFunctionTable(); @@ -2425,12 +2496,14 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } else if (!self.base.options.strip) { if (self.dwarf) |*dwarf| { if (self.debug_info_index != null) { - try dwarf.writeDbgAbbrev(&self.base); - // for debug info and ranges, the address is always 0, - // as locations are always offsets relative to 'code' section. - try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size); - try dwarf.writeDbgAranges(&self.base, 0, code_section_size); - try dwarf.writeDbgLineHeader(&self.base, mod); + if (self.base.options.module) |mod| { + try dwarf.writeDbgAbbrev(&self.base); + // for debug info and ranges, the address is always 0, + // as locations are always offsets relative to 'code' section. + try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size); + try dwarf.writeDbgAranges(&self.base, 0, code_section_size); + try dwarf.writeDbgLineHeader(&self.base, mod); + } try emitDebugSection(file, self.debug_info.items, ".debug_info"); try emitDebugSection(file, self.debug_aranges.items, ".debug_ranges"); diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index d07d0b39ea..bb00eeb821 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -889,12 +889,9 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin } for (self.relocatable_data) |relocatable_data, index| { - const symbols = symbol_for_segment.getPtr(.{ - .kind = relocatable_data.getSymbolKind(), - .index = @intCast(u32, relocatable_data.index), - }) orelse continue; // encountered a segment we do not create an atom for - const sym_index = symbols.pop(); - const final_index = try wasm_bin.getMatchingSegment(object_index, @intCast(u32, index)); + const final_index = (try wasm_bin.getMatchingSegment(object_index, @intCast(u32, index))) orelse { + continue; // found unknown section, so skip parsing into atom as we do not know how to handle it. + }; const atom = try gpa.create(Atom); atom.* = Atom.empty; @@ -907,7 +904,6 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin atom.file = object_index; atom.size = relocatable_data.size; atom.alignment = relocatable_data.getAlignment(self); - atom.sym_index = sym_index; const relocations: []types.Relocation = self.relocations.get(relocatable_data.section_index) orelse &.{}; for (relocations) |relocation| { @@ -929,19 +925,30 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]); - // symbols referencing the same atom will be added as alias - // or as 'parent' when they are global. - while (symbols.popOrNull()) |idx| { - const alias_symbol = self.symtable[idx]; - const symbol = self.symtable[atom.sym_index]; - if (alias_symbol.isGlobal() and symbol.isLocal()) { - atom.sym_index = idx; + if (relocatable_data.type != .debug) { + const symbols = symbol_for_segment.getPtr(.{ + .kind = relocatable_data.getSymbolKind(), + .index = @intCast(u32, relocatable_data.index), + }) orelse continue; // encountered a segment we do not create an atom for + const sym_index = symbols.pop(); + atom.sym_index = sym_index; + + // symbols referencing the same atom will be added as alias + // or as 'parent' when they are global. + while (symbols.popOrNull()) |idx| { + const alias_symbol = self.symtable[idx]; + const symbol = self.symtable[atom.sym_index]; + if (alias_symbol.isGlobal() and symbol.isLocal()) { + atom.sym_index = idx; + } } + try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom); } - try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom); const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index]; - segment.alignment = std.math.max(segment.alignment, atom.alignment); + if (relocatable_data.type == .data) { //code section and debug sections are 1-byte aligned + segment.alignment = std.math.max(segment.alignment, atom.alignment); + } if (wasm_bin.atoms.getPtr(final_index)) |last| { last.*.next = atom; From c347751338a4a1e2874207674fb47908fd601484 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 31 Aug 2022 23:03:29 +0200 Subject: [PATCH 3/8] wasm-linker: write debug sections from objects We now link relocatable debug sections with the correct section symbol and then allocate and resolve the debug atoms before writing them into the final binary. Although this does perform the relocation, the actual relocations are not done correctly yet. --- src/link/Wasm.zig | 47 +++++++++++++++++++++++++++------------ src/link/Wasm/Object.zig | 48 ++++++++++++++++++++++------------------ 2 files changed, 59 insertions(+), 36 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index ae995bd0b9..b17eeb57e9 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2495,21 +2495,39 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } } else if (!self.base.options.strip) { if (self.dwarf) |*dwarf| { - if (self.debug_info_index != null) { - if (self.base.options.module) |mod| { - try dwarf.writeDbgAbbrev(&self.base); - // for debug info and ranges, the address is always 0, - // as locations are always offsets relative to 'code' section. - try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size); - try dwarf.writeDbgAranges(&self.base, 0, code_section_size); - try dwarf.writeDbgLineHeader(&self.base, mod); - } + const mod = self.base.options.module.?; + try dwarf.writeDbgAbbrev(&self.base); + // for debug info and ranges, the address is always 0, + // as locations are always offsets relative to 'code' section. + try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size); + try dwarf.writeDbgAranges(&self.base, 0, code_section_size); + try dwarf.writeDbgLineHeader(&self.base, mod); + } - try emitDebugSection(file, self.debug_info.items, ".debug_info"); - try emitDebugSection(file, self.debug_aranges.items, ".debug_ranges"); - try emitDebugSection(file, self.debug_abbrev.items, ".debug_abbrev"); - try emitDebugSection(file, self.debug_line.items, ".debug_line"); - try emitDebugSection(file, dwarf.strtab.items, ".debug_str"); + var debug_bytes = std.ArrayList(u8).init(self.base.allocator); + defer debug_bytes.deinit(); + + const debug_sections = .{ + .{ ".debug_info", self.debug_info_index }, + .{ ".debug_pubtypes", self.debug_pubtypes_index }, + .{ ".debug_abbrev", self.debug_abbrev_index }, + .{ ".debug_line", self.debug_line_index }, + .{ ".debug_str", self.debug_str_index }, + .{ ".debug_pubnames", self.debug_pubnames_index }, + .{ ".debug_loc", self.debug_loc_index }, + .{ ".debug_ranges", self.debug_ranges_index }, + }; + + inline for (debug_sections) |item| { + if (item[1]) |index| { + var atom = self.atoms.get(index).?.getFirst(); + while (true) { + atom.resolveRelocs(self); + try debug_bytes.appendSlice(atom.code.items); + atom = atom.next orelse break; + } + try emitDebugSection(file, debug_bytes.items, item[0]); + debug_bytes.clearRetainingCapacity(); } } try self.emitNameSection(file, arena); @@ -2517,6 +2535,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void { + if (data.len == 0) return; const header_offset = try reserveCustomSectionHeader(file); const writer = file.writer(); try leb.writeULEB128(writer, @intCast(u32, name.len)); diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index bb00eeb821..fac5ce3aa8 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -77,7 +77,7 @@ const RelocatableData = struct { /// The size in bytes of the data representing the segment within the section size: u32, /// The index within the section itself, or in case of a debug section, - /// the offset within the `debug_names` table. + /// the offset within the `string_table`. index: u32, /// The offset within the section where the data starts offset: u32, @@ -101,9 +101,16 @@ const RelocatableData = struct { return switch (self.type) { .data => .data, .code => .function, - .debug => unreachable, // illegal, debug sections are not represented by a symbol + .debug => .section, }; } + + /// Returns the index within a section itself, or in case of a debug section, + /// returns the section index within the object file. + pub fn getIndex(self: RelocatableData) u32 { + if (self.type == .debug) return self.section_index; + return self.index; + } }; pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadError; @@ -205,7 +212,7 @@ pub fn importedCountByKind(self: *const Object, kind: std.wasm.ExternalKind) u32 /// From a given `RelocatableDate`, find the corresponding debug section name pub fn getDebugName(self: *const Object, relocatable_data: RelocatableData) []const u8 { - return std.mem.sliceTo(self.debug_names[relocatable_data.index..], 0); + return self.string_table.get(relocatable_data.index); } /// Checks if the object file is an MVP version. @@ -363,6 +370,7 @@ fn Parser(comptime ReaderType: type) type { if (std.mem.eql(u8, name, "linking")) { is_object_file.* = true; + self.object.relocatable_data = relocatable_data.items; // at this point no new relocatable sections will appear so we're free to store them. try self.parseMetadata(gpa, @intCast(usize, reader.context.bytes_left)); } else if (std.mem.startsWith(u8, name, "reloc")) { try self.parseRelocations(gpa); @@ -374,19 +382,16 @@ fn Parser(comptime ReaderType: type) type { errdefer gpa.free(debug_content); try reader.readNoEof(debug_content); - const debug_name_index = @intCast(u32, debug_names.items.len); - try debug_names.ensureUnusedCapacity(name.len + 1); - debug_names.appendSliceAssumeCapacity(try gpa.dupe(u8, name)); - debug_names.appendAssumeCapacity(0); try relocatable_data.append(.{ .type = .debug, .data = debug_content.ptr, .size = debug_size, - .index = debug_name_index, + .index = try self.object.string_table.put(gpa, name), .offset = len - debug_size, .section_index = section_index, }); } else { + log.info("found unknown custom section '{s}' - skipping parsing", .{name}); try reader.skipBytes(reader.context.bytes_left, .{}); } }, @@ -551,9 +556,6 @@ fn Parser(comptime ReaderType: type) type { else => |e| return e, } self.object.relocatable_data = relocatable_data.toOwnedSlice(); - - const names = debug_names.toOwnedSlice(); - self.object.debug_names = names[0 .. names.len - 1 :0]; } /// Based on the "features" custom section, parses it into a list of @@ -774,7 +776,12 @@ fn Parser(comptime ReaderType: type) type { }, .section => { symbol.index = try leb.readULEB128(u32, reader); - symbol.name = try self.object.string_table.put(gpa, @tagName(symbol.tag)); + for (self.object.relocatable_data) |data| { + if (data.section_index == symbol.index) { + symbol.name = data.index; + break; + } + } }, else => { symbol.index = try leb.readULEB128(u32, reader); @@ -864,7 +871,6 @@ fn assertEnd(reader: anytype) !void { /// Parses an object file into atoms, for code and data sections pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin: *Wasm) !void { - log.debug("Parsing data section into atoms", .{}); const Key = struct { kind: Symbol.Tag, index: u32, @@ -876,7 +882,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin for (self.symtable) |symbol, symbol_index| { switch (symbol.tag) { - .function, .data => if (!symbol.isUndefined()) { + .function, .data, .section => if (!symbol.isUndefined()) { const gop = try symbol_for_segment.getOrPut(.{ .kind = symbol.tag, .index = symbol.index }); const sym_idx = @intCast(u32, symbol_index); if (!gop.found_existing) { @@ -925,13 +931,11 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]); - if (relocatable_data.type != .debug) { - const symbols = symbol_for_segment.getPtr(.{ - .kind = relocatable_data.getSymbolKind(), - .index = @intCast(u32, relocatable_data.index), - }) orelse continue; // encountered a segment we do not create an atom for - const sym_index = symbols.pop(); - atom.sym_index = sym_index; + if (symbol_for_segment.getPtr(.{ + .kind = relocatable_data.getSymbolKind(), + .index = relocatable_data.getIndex(), + })) |symbols| { + atom.sym_index = symbols.pop(); // symbols referencing the same atom will be added as alias // or as 'parent' when they are global. @@ -957,7 +961,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin } else { try wasm_bin.atoms.putNoClobber(gpa, final_index, atom); } - log.debug("Parsed into atom: '{s}'", .{self.string_table.get(self.symtable[atom.sym_index].name)}); + log.debug("Parsed into atom: '{s}' at segment index {d}", .{ self.string_table.get(self.symtable[atom.sym_index].name), final_index }); } } From 46c932a2c9650f14ae8035d7382d825bfabdc0a5 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 1 Sep 2022 22:02:24 +0200 Subject: [PATCH 4/8] wasm-linker: perform debug relocations This correctly performs a relocation for debug sections. The result is that the wasm-linker can now correctly create a binary from object files while preserving all debug information. --- src/link/Wasm.zig | 12 ++++++++++++ src/link/Wasm/Atom.zig | 7 +++---- src/link/Wasm/Object.zig | 3 +-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index b17eeb57e9..2ff631d9ba 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -221,6 +221,18 @@ pub const SymbolLoc = struct { } return wasm_bin.string_table.get(wasm_bin.symbols.items[self.index].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 itself. + pub fn finalLoc(self: SymbolLoc, wasm_bin: *const Wasm) SymbolLoc { + if (wasm_bin.discarded.get(self)) |new_loc| { + return new_loc.finalLoc(wasm_bin); + } + return self; + } }; /// Generic string table that duplicates strings diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index 9e7f7a5a76..64efa8320f 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -145,7 +145,7 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) void { /// 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(self: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) u64 { - const target_loc: Wasm.SymbolLoc = .{ .file = self.file, .index = relocation.index }; + const target_loc = (Wasm.SymbolLoc{ .file = self.file, .index = relocation.index }).finalLoc(wasm_bin); const symbol = target_loc.getSymbol(wasm_bin).*; switch (relocation.relocation_type) { .R_WASM_FUNCTION_INDEX_LEB => return symbol.index, @@ -174,8 +174,7 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa => { std.debug.assert(symbol.tag == .data and !symbol.isUndefined()); const merge_segment = wasm_bin.base.options.output_mode != .Obj; - const target_atom_loc = wasm_bin.discarded.get(target_loc) orelse target_loc; - const target_atom = wasm_bin.symbol_atom.get(target_atom_loc).?; + const target_atom = wasm_bin.symbol_atom.get(target_loc).?; const segment_info = if (target_atom.file) |object_index| blk: { break :blk wasm_bin.objects.items[object_index].segment_info; } else wasm_bin.segment_info.items; @@ -187,6 +186,6 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa .R_WASM_EVENT_INDEX_LEB => return symbol.index, .R_WASM_SECTION_OFFSET_I32, .R_WASM_FUNCTION_OFFSET_I32, - => return relocation.offset, + => return relocation.addend orelse 0, } } diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index fac5ce3aa8..86c9cdc023 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -387,11 +387,10 @@ fn Parser(comptime ReaderType: type) type { .data = debug_content.ptr, .size = debug_size, .index = try self.object.string_table.put(gpa, name), - .offset = len - debug_size, + .offset = 0, // debug sections only contain 1 entry, so no need to calculate offset .section_index = section_index, }); } else { - log.info("found unknown custom section '{s}' - skipping parsing", .{name}); try reader.skipBytes(reader.context.bytes_left, .{}); } }, From b2718e213ed7e7cd8bcd85bdf49d7ae33c857c58 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 2 Sep 2022 21:13:59 +0200 Subject: [PATCH 5/8] wasm-linker: use Atoms for zig debug info Previously we used single arraylists for each debug section for debug information that was generated from Zig code. (e.i. `Module` is available). This information is now stored in Atoms, similarly to debug information from object files. This will allow us to link them together and resolve debug relocations. --- src/link/Dwarf.zig | 45 +++++++++++++++++++++++-------------- src/link/Wasm.zig | 55 +++++++++++++--------------------------------- 2 files changed, 44 insertions(+), 56 deletions(-) diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 3ae151491f..e610d56df4 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -861,7 +861,9 @@ pub fn commitDeclState( }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - writeDbgLineNopsBuffered(wasm_file.debug_line.items, src_fn.off, 0, &.{}, src_fn.len); + const segment_index = wasm_file.debug_line_index.?; + const debug_line = wasm_file.atoms.get(segment_index).?.code; + writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len); }, else => unreachable, } @@ -972,9 +974,9 @@ pub fn commitDeclState( }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - const segment_index = try wasm_file.getDebugLineIndex(); + const segment_index = try wasm_file.getOrSetDebugIndex(&wasm_file.debug_line_index); const segment = &wasm_file.segments.items[segment_index]; - const debug_line = &wasm_file.debug_line; + const debug_line = &wasm_file.atoms.get(segment_index).?.code; if (needed_size != segment.size) { log.debug(" needed size does not equal allocated size: {d}", .{needed_size}); if (needed_size > segment.size) { @@ -1146,10 +1148,11 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3 }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - const segment_index = try wasm_file.getDebugInfoIndex(); + const segment_index = try wasm_file.getOrSetDebugIndex(&wasm_file.debug_info_index); const segment = &wasm_file.segments.items[segment_index]; + const debug_info = &wasm_file.atoms.get(segment_index).?.code; const offset = segment.offset + atom.off; - try writeDbgInfoNopsToArrayList(gpa, &wasm_file.debug_info, offset, 0, &.{0}, atom.len, false); + try writeDbgInfoNopsToArrayList(gpa, debug_info, offset, 0, &.{0}, atom.len, false); }, else => unreachable, } @@ -1276,9 +1279,9 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - const segment_index = try wasm_file.getDebugInfoIndex(); + const segment_index = try wasm_file.getOrSetDebugIndex(&wasm_file.debug_info_index); const segment = &wasm_file.segments.items[segment_index]; - const debug_info = &wasm_file.debug_info; + const debug_info = &wasm_file.atoms.get(segment_index).?.code; if (needed_size != segment.size) { log.debug(" needed size does not equal allocated size: {d}", .{needed_size}); if (needed_size > segment.size) { @@ -1337,10 +1340,10 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl) }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - const segment_index = wasm_file.getDebugLineIndex() catch unreachable; + const segment_index = wasm_file.debug_line_index.?; const segment = wasm_file.segments.items[segment_index]; const offset = segment.offset + decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff(); - mem.copy(u8, wasm_file.debug_line.items[offset..], &data); + mem.copy(u8, wasm_file.atoms.get(segment_index).?.code.items[offset..], &data); }, else => unreachable, } @@ -1576,8 +1579,10 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - try wasm_file.debug_abbrev.resize(wasm_file.base.allocator, needed_size); - mem.copy(u8, wasm_file.debug_abbrev.items, &abbrev_buf); + const segment_index = try wasm_file.getOrSetDebugIndex(&wasm_file.debug_abbrev_index); + const debug_abbrev = &wasm_file.atoms.get(segment_index).?.code; + try debug_abbrev.resize(wasm_file.base.allocator, needed_size); + mem.copy(u8, debug_abbrev.items, &abbrev_buf); }, else => unreachable, } @@ -1687,7 +1692,9 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6 }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - try writeDbgInfoNopsToArrayList(self.allocator, &wasm_file.debug_info, 0, 0, di_buf.items, jmp_amt, false); + const segment_index = wasm_file.debug_info_index.?; + const debug_info = &wasm_file.atoms.get(segment_index).?.code; + try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false); }, else => unreachable, } @@ -2016,8 +2023,10 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void { }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - try wasm_file.debug_aranges.resize(wasm_file.base.allocator, needed_size); - mem.copy(u8, wasm_file.debug_aranges.items, di_buf.items); + const segment_index = try wasm_file.getOrSetDebugIndex(&wasm_file.debug_ranges_index); + const debug_ranges = &wasm_file.atoms.get(segment_index).?.code; + try debug_ranges.resize(wasm_file.base.allocator, needed_size); + mem.copy(u8, debug_ranges.items, di_buf.items); }, else => unreachable, } @@ -2139,7 +2148,9 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void { }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - writeDbgLineNopsBuffered(wasm_file.debug_line.items, 0, 0, di_buf.items, jmp_amt); + const segment_index = wasm_file.debug_line_index.?; + const debug_line = wasm_file.atoms.get(segment_index).?.code; + writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt); }, else => unreachable, } @@ -2287,7 +2298,9 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void { }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - mem.copy(u8, wasm_file.debug_info.items[reloc.atom.off + reloc.offset ..], &buf); + const segment_index = wasm_file.debug_info_index.?; + const debug_info = wasm_file.atoms.get(segment_index).?.code; + mem.copy(u8, debug_info.items[reloc.atom.off + reloc.offset ..], &buf); }, else => unreachable, } diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 2ff631d9ba..7204182df2 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -103,16 +103,6 @@ string_table: StringTable = .{}, /// Debug information for wasm dwarf: ?Dwarf = null, -// *debug information* // -/// Contains all bytes for the '.debug_info' section -debug_info: std.ArrayListUnmanaged(u8) = .{}, -/// Contains all bytes for the '.debug_line' section -debug_line: std.ArrayListUnmanaged(u8) = .{}, -/// Contains all bytes for the '.debug_abbrev' section -debug_abbrev: std.ArrayListUnmanaged(u8) = .{}, -/// Contains all bytes for the '.debug_ranges' section -debug_aranges: std.ArrayListUnmanaged(u8) = .{}, - // Output sections /// Output type section func_types: std.ArrayListUnmanaged(wasm.Type) = .{}, @@ -716,11 +706,6 @@ pub fn deinit(self: *Wasm) void { if (self.dwarf) |*dwarf| { dwarf.deinit(); } - - self.debug_info.deinit(gpa); - self.debug_line.deinit(gpa); - self.debug_abbrev.deinit(gpa); - self.debug_aranges.deinit(gpa); } pub fn allocateDeclIndexes(self: *Wasm, decl_index: Module.Decl.Index) !void { @@ -1983,32 +1968,22 @@ fn populateErrorNameTable(self: *Wasm) !void { try self.parseAtom(names_atom, .{ .data = .read_only }); } -pub fn getDebugInfoIndex(self: *Wasm) !u32 { - assert(self.dwarf != null); - return self.debug_info_index orelse { - self.debug_info_index = @intCast(u32, self.segments.items.len); - const segment = try self.segments.addOne(self.base.allocator); - segment.* = .{ - .size = 0, - .offset = 0, - // debug sections always have alignment '1' - .alignment = 1, - }; - return self.debug_info_index.?; - }; -} +/// From a given index variable, returns it value if set. +/// When not set, initialises a new segment, sets the index, +/// and returns it value. +/// When a new segment is initialised. It also creates an atom. +pub fn getOrSetDebugIndex(self: *Wasm, index: *?u32) !u32 { + return (index.*) orelse { + const new_index = @intCast(u32, self.segments.items.len); + index.* = new_index; + try self.appendDummySegment(); -pub fn getDebugLineIndex(self: *Wasm) !u32 { - assert(self.dwarf != null); - return self.debug_line_index orelse { - self.debug_line_index = @intCast(u32, self.segments.items.len); - const segment = try self.segments.addOne(self.base.allocator); - segment.* = .{ - .size = 0, - .offset = 0, - .alignment = 1, - }; - return self.debug_line_index.?; + const atom = try self.base.allocator.create(Atom); + atom.* = Atom.empty; + atom.alignment = 1; // debug sections are always 1-byte-aligned + try self.managed_atoms.append(self.base.allocator, atom); + try self.atoms.put(self.base.allocator, new_index, atom); + return new_index; }; } From f2c8d09c4f743f4172e0a0a6f0fd59a96e56386d Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 4 Sep 2022 21:00:44 +0200 Subject: [PATCH 6/8] wasm-linker: Mix Zig -and Object debug atoms When linking a Zig-compilation with an object file, we allow mixing the debug atoms to make sure debug information is preserved from object files. By default, we now always initialize all debug sections if the `strip` flag is unset. This also fixes relocations for debug information as previously the offset of an atom wasn't calculated, and neither was the code size itself which meant that debug lines were off and file names from other object files were missing. --- src/link/Dwarf.zig | 35 ++++++++++++++++++-------------- src/link/Wasm.zig | 46 +++++++++++++++++++++++++++--------------- src/link/Wasm/Atom.zig | 35 +++++++++++++++++++++++++++++--- 3 files changed, 82 insertions(+), 34 deletions(-) diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index e610d56df4..671425ab71 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -862,7 +862,8 @@ pub fn commitDeclState( .wasm => { const wasm_file = file.cast(File.Wasm).?; const segment_index = wasm_file.debug_line_index.?; - const debug_line = wasm_file.atoms.get(segment_index).?.code; + const atom = wasm_file.atoms.get(segment_index).?; + const debug_line = atom.getFirstZigAtom().code; writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len); }, else => unreachable, @@ -974,9 +975,10 @@ pub fn commitDeclState( }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - const segment_index = try wasm_file.getOrSetDebugIndex(&wasm_file.debug_line_index); + const segment_index = wasm_file.debug_line_index.?; const segment = &wasm_file.segments.items[segment_index]; - const debug_line = &wasm_file.atoms.get(segment_index).?.code; + const atom = wasm_file.atoms.get(segment_index).?; + const debug_line = &atom.getFirstZigAtom().code; if (needed_size != segment.size) { log.debug(" needed size does not equal allocated size: {d}", .{needed_size}); if (needed_size > segment.size) { @@ -1148,9 +1150,10 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3 }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - const segment_index = try wasm_file.getOrSetDebugIndex(&wasm_file.debug_info_index); + const segment_index = wasm_file.debug_info_index.?; const segment = &wasm_file.segments.items[segment_index]; - const debug_info = &wasm_file.atoms.get(segment_index).?.code; + const info_atom = wasm_file.atoms.get(segment_index).?; + const debug_info = &info_atom.getFirstZigAtom().code; const offset = segment.offset + atom.off; try writeDbgInfoNopsToArrayList(gpa, debug_info, offset, 0, &.{0}, atom.len, false); }, @@ -1279,9 +1282,10 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - const segment_index = try wasm_file.getOrSetDebugIndex(&wasm_file.debug_info_index); + const segment_index = wasm_file.debug_info_index.?; const segment = &wasm_file.segments.items[segment_index]; - const debug_info = &wasm_file.atoms.get(segment_index).?.code; + const info_atom = wasm_file.atoms.get(segment_index).?; + const debug_info = &info_atom.getFirstZigAtom().code; if (needed_size != segment.size) { log.debug(" needed size does not equal allocated size: {d}", .{needed_size}); if (needed_size > segment.size) { @@ -1343,7 +1347,8 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl) const segment_index = wasm_file.debug_line_index.?; const segment = wasm_file.segments.items[segment_index]; const offset = segment.offset + decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff(); - mem.copy(u8, wasm_file.atoms.get(segment_index).?.code.items[offset..], &data); + const atom = wasm_file.atoms.get(segment_index).?.getFirstZigAtom(); + mem.copy(u8, atom.code.items[offset..], &data); }, else => unreachable, } @@ -1579,8 +1584,8 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - const segment_index = try wasm_file.getOrSetDebugIndex(&wasm_file.debug_abbrev_index); - const debug_abbrev = &wasm_file.atoms.get(segment_index).?.code; + const segment_index = wasm_file.debug_abbrev_index.?; + const debug_abbrev = &wasm_file.atoms.get(segment_index).?.getFirstZigAtom().code; try debug_abbrev.resize(wasm_file.base.allocator, needed_size); mem.copy(u8, debug_abbrev.items, &abbrev_buf); }, @@ -1693,7 +1698,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6 .wasm => { const wasm_file = file.cast(File.Wasm).?; const segment_index = wasm_file.debug_info_index.?; - const debug_info = &wasm_file.atoms.get(segment_index).?.code; + const debug_info = &wasm_file.atoms.get(segment_index).?.getFirstZigAtom().code; try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false); }, else => unreachable, @@ -2023,8 +2028,8 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void { }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - const segment_index = try wasm_file.getOrSetDebugIndex(&wasm_file.debug_ranges_index); - const debug_ranges = &wasm_file.atoms.get(segment_index).?.code; + const segment_index = wasm_file.debug_ranges_index.?; + const debug_ranges = &wasm_file.atoms.get(segment_index).?.getFirstZigAtom().code; try debug_ranges.resize(wasm_file.base.allocator, needed_size); mem.copy(u8, debug_ranges.items, di_buf.items); }, @@ -2149,7 +2154,7 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void { .wasm => { const wasm_file = file.cast(File.Wasm).?; const segment_index = wasm_file.debug_line_index.?; - const debug_line = wasm_file.atoms.get(segment_index).?.code; + const debug_line = wasm_file.atoms.get(segment_index).?.getFirstZigAtom().code; writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt); }, else => unreachable, @@ -2299,7 +2304,7 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void { .wasm => { const wasm_file = file.cast(File.Wasm).?; const segment_index = wasm_file.debug_info_index.?; - const debug_info = wasm_file.atoms.get(segment_index).?.code; + const debug_info = wasm_file.atoms.get(segment_index).?.getFirstZigAtom().code; mem.copy(u8, debug_info.items[reloc.atom.off + reloc.offset ..], &buf); }, else => unreachable, diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 7204182df2..67f229ca84 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -349,6 +349,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option }; } + try wasm_bin.initDebugSections(); return wasm_bin; } @@ -377,6 +378,23 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm { return self; } +/// Initializes symbols and atoms for the debug sections +/// Initialization is only done when compiling Zig code. +/// When Zig is invoked as a linker instead, the atoms +/// and symbols come from the object files instead. +pub fn initDebugSections(self: *Wasm) !void { + if (self.dwarf == null) return; // not compiling Zig code, so no need to pre-initialize debug sections + // this will create an Atom and set the index for us. + try self.createDebugSectionForIndex(&self.debug_info_index); + try self.createDebugSectionForIndex(&self.debug_line_index); + try self.createDebugSectionForIndex(&self.debug_loc_index); + try self.createDebugSectionForIndex(&self.debug_abbrev_index); + try self.createDebugSectionForIndex(&self.debug_ranges_index); + try self.createDebugSectionForIndex(&self.debug_str_index); + try self.createDebugSectionForIndex(&self.debug_pubnames_index); + try self.createDebugSectionForIndex(&self.debug_pubtypes_index); +} + fn parseInputFiles(self: *Wasm, files: []const []const u8) !void { for (files) |path| { if (try self.parseObjectFile(path)) continue; @@ -1968,23 +1986,19 @@ fn populateErrorNameTable(self: *Wasm) !void { try self.parseAtom(names_atom, .{ .data = .read_only }); } -/// From a given index variable, returns it value if set. -/// When not set, initialises a new segment, sets the index, -/// and returns it value. -/// When a new segment is initialised. It also creates an atom. -pub fn getOrSetDebugIndex(self: *Wasm, index: *?u32) !u32 { - return (index.*) orelse { - const new_index = @intCast(u32, self.segments.items.len); - index.* = new_index; - try self.appendDummySegment(); +/// 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(self: *Wasm, index: *?u32) !void { + const new_index = @intCast(u32, self.segments.items.len); + index.* = new_index; + try self.appendDummySegment(); - const atom = try self.base.allocator.create(Atom); - atom.* = Atom.empty; - atom.alignment = 1; // debug sections are always 1-byte-aligned - try self.managed_atoms.append(self.base.allocator, atom); - try self.atoms.put(self.base.allocator, new_index, atom); - return new_index; - }; + const atom = try self.base.allocator.create(Atom); + atom.* = Atom.empty; + atom.alignment = 1; // debug sections are always 1-byte-aligned + try self.managed_atoms.append(self.base.allocator, atom); + try self.atoms.put(self.base.allocator, new_index, atom); } fn resetState(self: *Wasm) void { diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index 64efa8320f..440ebea6f4 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -90,6 +90,19 @@ pub fn getFirst(self: *Atom) *Atom { return tmp; } +/// Unlike `getFirst` this returns the first `*Atom` that was +/// produced from Zig code, rather than an object file. +/// This is useful for debug sections where we want to extend +/// the bytes, and don't want to overwrite existing Atoms. +pub fn getFirstZigAtom(self: *Atom) *Atom { + if (self.file == null) return self; + var tmp = self; + return while (tmp.prev) |prev| { + if (prev.file == null) break prev; + tmp = prev; + } else unreachable; // must allocate an Atom first! +} + /// Returns the location of the symbol that represents this `Atom` pub fn symbolLoc(self: Atom) Wasm.SymbolLoc { return .{ .file = self.file, .index = self.sym_index }; @@ -184,8 +197,24 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa return target_atom.offset + segment.offset + (relocation.addend orelse 0); }, .R_WASM_EVENT_INDEX_LEB => return symbol.index, - .R_WASM_SECTION_OFFSET_I32, - .R_WASM_FUNCTION_OFFSET_I32, - => return relocation.addend orelse 0, + .R_WASM_SECTION_OFFSET_I32 => { + const target_atom = wasm_bin.symbol_atom.get(target_loc).?; + return target_atom.offset + (relocation.addend orelse 0); + }, + .R_WASM_FUNCTION_OFFSET_I32 => { + const target_atom = wasm_bin.symbol_atom.get(target_loc).?; + var atom = target_atom.getFirst(); + var offset: u32 = 0; + // TODO: Calculate this during atom allocation, rather than + // this linear calculation. For now it's done here as atoms + // are being sorted after atom allocation, as functions aren't + // merged until later. + while (true) { + offset += 5; // each atom uses 5 bytes to store its body's size + if (atom == target_atom) break; + atom = atom.next.?; + } + return target_atom.offset + offset + (relocation.addend orelse 0); + }, } } From 971327d6e0a2cdcfd1a7695d1dea86dbbebd730e Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Mon, 5 Sep 2022 21:35:44 +0200 Subject: [PATCH 7/8] wasm: fix memory leak --- src/arch/wasm/CodeGen.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index b9637bf8e3..a72ae6a423 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -666,6 +666,10 @@ pub fn deinit(self: *Self) void { self.locals.deinit(self.gpa); self.mir_instructions.deinit(self.gpa); self.mir_extra.deinit(self.gpa); + self.free_locals_i32.deinit(self.gpa); + self.free_locals_i64.deinit(self.gpa); + self.free_locals_f32.deinit(self.gpa); + self.free_locals_f64.deinit(self.gpa); self.* = undefined; } From a8d137d05ae36870d4e896cf5e37b591d9fa219c Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 6 Sep 2022 20:21:33 +0200 Subject: [PATCH 8/8] wasm-linker: support incremental debug info Although the wasm-linker previously already supported debug information in incremental-mode, this was no longer working as-is with the addition of supporting object-file-parsed debug information. This commit implements the Zig-created debug information structure from scratch which is a lot more robust and also allows being linked with debug information from other object files. --- src/link/Dwarf.zig | 73 +++++++----------- src/link/Wasm.zig | 156 ++++++++++++++++++++++++++++----------- src/link/Wasm/Atom.zig | 2 +- src/link/Wasm/Object.zig | 8 +- 4 files changed, 141 insertions(+), 98 deletions(-) diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 671425ab71..474c822ae6 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -861,9 +861,7 @@ pub fn commitDeclState( }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - const segment_index = wasm_file.debug_line_index.?; - const atom = wasm_file.atoms.get(segment_index).?; - const debug_line = atom.getFirstZigAtom().code; + const debug_line = wasm_file.debug_line_atom.?.code; writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len); }, else => unreachable, @@ -975,24 +973,21 @@ pub fn commitDeclState( }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - const segment_index = wasm_file.debug_line_index.?; - const segment = &wasm_file.segments.items[segment_index]; - const atom = wasm_file.atoms.get(segment_index).?; - const debug_line = &atom.getFirstZigAtom().code; - if (needed_size != segment.size) { + const atom = wasm_file.debug_line_atom.?; + const debug_line = &atom.code; + const segment_size = debug_line.items.len; + if (needed_size != segment_size) { log.debug(" needed size does not equal allocated size: {d}", .{needed_size}); - if (needed_size > segment.size) { - log.debug(" allocating {d} bytes for 'debug line' information", .{needed_size - segment.size}); + if (needed_size > segment_size) { + log.debug(" allocating {d} bytes for 'debug line' information", .{needed_size - segment_size}); try debug_line.resize(self.allocator, needed_size); - mem.set(u8, debug_line.items[segment.size..], 0); + mem.set(u8, debug_line.items[segment_size..], 0); } - segment.size = needed_size; debug_line.items.len = needed_size; } - const offset = segment.offset + src_fn.off; writeDbgLineNopsBuffered( debug_line.items, - offset, + src_fn.off, prev_padding_size, dbg_line_buffer.items, next_padding_size, @@ -1150,12 +1145,8 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3 }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - const segment_index = wasm_file.debug_info_index.?; - const segment = &wasm_file.segments.items[segment_index]; - const info_atom = wasm_file.atoms.get(segment_index).?; - const debug_info = &info_atom.getFirstZigAtom().code; - const offset = segment.offset + atom.off; - try writeDbgInfoNopsToArrayList(gpa, debug_info, offset, 0, &.{0}, atom.len, false); + const debug_info = &wasm_file.debug_info_atom.?.code; + try writeDbgInfoNopsToArrayList(gpa, debug_info, atom.off, 0, &.{0}, atom.len, false); }, else => unreachable, } @@ -1282,28 +1273,25 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - const segment_index = wasm_file.debug_info_index.?; - const segment = &wasm_file.segments.items[segment_index]; - const info_atom = wasm_file.atoms.get(segment_index).?; - const debug_info = &info_atom.getFirstZigAtom().code; - if (needed_size != segment.size) { + const info_atom = wasm_file.debug_info_atom.?; + const debug_info = &info_atom.code; + const segment_size = debug_info.items.len; + if (needed_size != segment_size) { log.debug(" needed size does not equal allocated size: {d}", .{needed_size}); - if (needed_size > segment.size) { - log.debug(" allocating {d} bytes for 'debug info' information", .{needed_size - segment.size}); + if (needed_size > segment_size) { + log.debug(" allocating {d} bytes for 'debug info' information", .{needed_size - segment_size}); try debug_info.resize(self.allocator, needed_size); - mem.set(u8, debug_info.items[segment.size..], 0); + mem.set(u8, debug_info.items[segment_size..], 0); } - segment.size = needed_size; debug_info.items.len = needed_size; } - const offset = segment.offset + atom.off; log.debug(" writeDbgInfoNopsToArrayList debug_info_len={d} offset={d} content_len={d} next_padding_size={d}", .{ - debug_info.items.len, offset, dbg_info_buf.len, next_padding_size, + debug_info.items.len, atom.off, dbg_info_buf.len, next_padding_size, }); try writeDbgInfoNopsToArrayList( gpa, debug_info, - offset, + atom.off, prev_padding_size, dbg_info_buf, next_padding_size, @@ -1344,10 +1332,8 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl) }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - const segment_index = wasm_file.debug_line_index.?; - const segment = wasm_file.segments.items[segment_index]; - const offset = segment.offset + decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff(); - const atom = wasm_file.atoms.get(segment_index).?.getFirstZigAtom(); + const offset = decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff(); + const atom = wasm_file.debug_line_atom.?; mem.copy(u8, atom.code.items[offset..], &data); }, else => unreachable, @@ -1584,8 +1570,7 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - const segment_index = wasm_file.debug_abbrev_index.?; - const debug_abbrev = &wasm_file.atoms.get(segment_index).?.getFirstZigAtom().code; + const debug_abbrev = &wasm_file.debug_abbrev_atom.?.code; try debug_abbrev.resize(wasm_file.base.allocator, needed_size); mem.copy(u8, debug_abbrev.items, &abbrev_buf); }, @@ -1697,8 +1682,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6 }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - const segment_index = wasm_file.debug_info_index.?; - const debug_info = &wasm_file.atoms.get(segment_index).?.getFirstZigAtom().code; + const debug_info = &wasm_file.debug_info_atom.?.code; try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false); }, else => unreachable, @@ -2028,8 +2012,7 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void { }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - const segment_index = wasm_file.debug_ranges_index.?; - const debug_ranges = &wasm_file.atoms.get(segment_index).?.getFirstZigAtom().code; + const debug_ranges = &wasm_file.debug_ranges_atom.?.code; try debug_ranges.resize(wasm_file.base.allocator, needed_size); mem.copy(u8, debug_ranges.items, di_buf.items); }, @@ -2153,8 +2136,7 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void { }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - const segment_index = wasm_file.debug_line_index.?; - const debug_line = wasm_file.atoms.get(segment_index).?.getFirstZigAtom().code; + const debug_line = wasm_file.debug_line_atom.?.code; writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt); }, else => unreachable, @@ -2303,8 +2285,7 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void { }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - const segment_index = wasm_file.debug_info_index.?; - const debug_info = wasm_file.atoms.get(segment_index).?.getFirstZigAtom().code; + const debug_info = wasm_file.debug_info_atom.?.code; mem.copy(u8, debug_info.items[reloc.atom.off + reloc.offset ..], &buf); }, else => unreachable, diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 67f229ca84..0c5f0e810f 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -95,9 +95,10 @@ imports: std.AutoHashMapUnmanaged(SymbolLoc, types.Import) = .{}, segments: std.ArrayListUnmanaged(Segment) = .{}, /// Maps a data segment key (such as .rodata) to the index into `segments`. data_segments: std.StringArrayHashMapUnmanaged(u32) = .{}, -/// A list of `types.Segment` which provide meta data -/// about a data symbol such as its name -segment_info: std.ArrayListUnmanaged(types.Segment) = .{}, +/// A table of `types.Segment` 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) = .{}, /// Deduplicated string table for strings used by symbols, imports and exports. string_table: StringTable = .{}, /// Debug information for wasm @@ -158,6 +159,19 @@ export_names: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{}, /// The actual table is populated during `flush`. error_table_symbol: ?u32 = null, +// Debug section atoms. These are only set when the current compilation +// unit contains Zig code. The lifetime of these atoms are extended +// until the end of the compiler's lifetime. Meaning they're not freed +// during `flush()` in incremental-mode. +debug_info_atom: ?*Atom = null, +debug_line_atom: ?*Atom = null, +debug_loc_atom: ?*Atom = null, +debug_ranges_atom: ?*Atom = null, +debug_abbrev_atom: ?*Atom = null, +debug_str_atom: ?*Atom = null, +debug_pubnames_atom: ?*Atom = null, +debug_pubtypes_atom: ?*Atom = null, + pub const Segment = struct { alignment: u32, size: u32, @@ -384,15 +398,16 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm { /// and symbols come from the object files instead. pub fn initDebugSections(self: *Wasm) !void { if (self.dwarf == null) return; // not compiling Zig code, so no need to pre-initialize debug sections + assert(self.debug_info_index == null); // this will create an Atom and set the index for us. - try self.createDebugSectionForIndex(&self.debug_info_index); - try self.createDebugSectionForIndex(&self.debug_line_index); - try self.createDebugSectionForIndex(&self.debug_loc_index); - try self.createDebugSectionForIndex(&self.debug_abbrev_index); - try self.createDebugSectionForIndex(&self.debug_ranges_index); - try self.createDebugSectionForIndex(&self.debug_str_index); - try self.createDebugSectionForIndex(&self.debug_pubnames_index); - try self.createDebugSectionForIndex(&self.debug_pubtypes_index); + self.debug_info_atom = try self.createDebugSectionForIndex(&self.debug_info_index, ".debug_info"); + self.debug_line_atom = try self.createDebugSectionForIndex(&self.debug_line_index, ".debug_line"); + self.debug_loc_atom = try self.createDebugSectionForIndex(&self.debug_loc_index, ".debug_loc"); + self.debug_abbrev_atom = try self.createDebugSectionForIndex(&self.debug_abbrev_index, ".debug_abbrev"); + self.debug_ranges_atom = try self.createDebugSectionForIndex(&self.debug_ranges_index, ".debug_ranges"); + self.debug_str_atom = try self.createDebugSectionForIndex(&self.debug_str_index, ".debug_str"); + self.debug_pubnames_atom = try self.createDebugSectionForIndex(&self.debug_pubnames_index, ".debug_pubnames"); + self.debug_pubtypes_atom = try self.createDebugSectionForIndex(&self.debug_pubtypes_index, ".debug_pubtypes"); } fn parseInputFiles(self: *Wasm, files: []const []const u8) !void { @@ -676,7 +691,7 @@ pub fn deinit(self: *Wasm) void { for (self.func_types.items) |*func_type| { func_type.deinit(gpa); } - for (self.segment_info.items) |segment_info| { + for (self.segment_info.values()) |segment_info| { gpa.free(segment_info.name); } for (self.objects.items) |*object| { @@ -1364,16 +1379,7 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { const index = gop.value_ptr.*; self.segments.items[index].size += atom.size; - // segment indexes can be off by 1 due to also containing a segment - // for the code section, so we must check if the existing segment - // is larger than that of the code section, and substract the index by 1 in such case. - var info_add = if (self.code_section_index) |idx| blk: { - if (idx < index) break :blk @as(u32, 1); - break :blk 0; - } else @as(u32, 0); - if (self.debug_info_index != null) info_add += 1; - if (self.debug_line_index != null) info_add += 1; - symbol.index = index - info_add; + symbol.index = @intCast(u32, self.segment_info.getIndex(index).?); // segment info already exists, so free its memory self.base.allocator.free(segment_name); break :result index; @@ -1386,8 +1392,8 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { }); gop.value_ptr.* = index; - const info_index = @intCast(u32, self.segment_info.items.len); - try self.segment_info.append(self.base.allocator, segment_info); + const info_index = @intCast(u32, self.segment_info.count()); + try self.segment_info.put(self.base.allocator, index, segment_info); symbol.index = info_index; break :result index; } @@ -1397,18 +1403,54 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { const segment: *Segment = &self.segments.items[final_index]; segment.alignment = std.math.max(segment.alignment, atom.alignment); - if (self.atoms.getPtr(final_index)) |last| { + try self.appendAtomAtIndex(final_index, atom); +} + +/// From a given index, append the given `Atom` at the back of the linked list. +/// Simply inserts it into the map of atoms when it doesn't exist yet. +pub fn appendAtomAtIndex(self: *Wasm, index: u32, atom: *Atom) !void { + if (self.atoms.getPtr(index)) |last| { last.*.next = atom; atom.prev = last.*; last.* = atom; } else { - try self.atoms.putNoClobber(self.base.allocator, final_index, atom); + try self.atoms.putNoClobber(self.base.allocator, index, atom); } } +/// Allocates debug atoms into their respective debug sections +/// to merge them with maybe-existing debug atoms from object files. +fn allocateDebugAtoms(self: *Wasm) !void { + if (self.dwarf == null) return; + + const allocAtom = struct { + fn f(bin: *Wasm, maybe_index: *?u32, atom: *Atom) !void { + const index = maybe_index.* orelse idx: { + const index = @intCast(u32, bin.segments.items.len); + try bin.appendDummySegment(); + maybe_index.* = index; + break :idx index; + }; + atom.size = @intCast(u32, atom.code.items.len); + bin.symbols.items[atom.sym_index].index = index; + try bin.appendAtomAtIndex(index, atom); + } + }.f; + + try allocAtom(self, &self.debug_info_index, self.debug_info_atom.?); + try allocAtom(self, &self.debug_line_index, self.debug_line_atom.?); + try allocAtom(self, &self.debug_loc_index, self.debug_loc_atom.?); + try allocAtom(self, &self.debug_str_index, self.debug_str_atom.?); + try allocAtom(self, &self.debug_ranges_index, self.debug_ranges_atom.?); + try allocAtom(self, &self.debug_abbrev_index, self.debug_abbrev_atom.?); + try allocAtom(self, &self.debug_pubnames_index, self.debug_pubnames_atom.?); + try allocAtom(self, &self.debug_pubtypes_index, self.debug_pubtypes_atom.?); +} + fn allocateAtoms(self: *Wasm) !void { // first sort the data segments try sortDataSegments(self); + try allocateDebugAtoms(self); var it = self.atoms.iterator(); while (it.next()) |entry| { @@ -1426,7 +1468,7 @@ fn allocateAtoms(self: *Wasm) !void { atom.size, }); offset += atom.size; - self.symbol_atom.putAssumeCapacity(atom.symbolLoc(), atom); // Update atom pointers + try self.symbol_atom.put(self.base.allocator, atom.symbolLoc(), atom); // Update atom pointers atom = atom.next orelse break; } segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment); @@ -1989,20 +2031,35 @@ fn populateErrorNameTable(self: *Wasm) !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(self: *Wasm, index: *?u32) !void { +pub fn createDebugSectionForIndex(self: *Wasm, index: *?u32, name: []const u8) !*Atom { const new_index = @intCast(u32, self.segments.items.len); index.* = new_index; try self.appendDummySegment(); + // _ = index; + + const sym_index = self.symbols_free_list.popOrNull() orelse idx: { + const tmp_index = @intCast(u32, self.symbols.items.len); + _ = try self.symbols.addOne(self.base.allocator); + break :idx tmp_index; + }; + self.symbols.items[sym_index] = .{ + .tag = .section, + .name = try self.string_table.put(self.base.allocator, name), + .index = 0, + .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), + }; const atom = try self.base.allocator.create(Atom); atom.* = Atom.empty; atom.alignment = 1; // debug sections are always 1-byte-aligned + atom.sym_index = sym_index; try self.managed_atoms.append(self.base.allocator, atom); - try self.atoms.put(self.base.allocator, new_index, atom); + try self.symbol_atom.put(self.base.allocator, atom.symbolLoc(), atom); + return atom; } fn resetState(self: *Wasm) void { - for (self.segment_info.items) |*segment_info| { + for (self.segment_info.values()) |segment_info| { self.base.allocator.free(segment_info.name); } if (self.base.options.module) |mod| { @@ -2029,6 +2086,12 @@ fn resetState(self: *Wasm) void { self.code_section_index = null; self.debug_info_index = null; self.debug_line_index = null; + self.debug_loc_index = null; + self.debug_str_index = null; + self.debug_ranges_index = null; + self.debug_abbrev_index = null; + self.debug_pubnames_index = null; + self.debug_pubtypes_index = null; } pub fn flush(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { @@ -2508,26 +2571,31 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod var debug_bytes = std.ArrayList(u8).init(self.base.allocator); defer debug_bytes.deinit(); - const debug_sections = .{ - .{ ".debug_info", self.debug_info_index }, - .{ ".debug_pubtypes", self.debug_pubtypes_index }, - .{ ".debug_abbrev", self.debug_abbrev_index }, - .{ ".debug_line", self.debug_line_index }, - .{ ".debug_str", self.debug_str_index }, - .{ ".debug_pubnames", self.debug_pubnames_index }, - .{ ".debug_loc", self.debug_loc_index }, - .{ ".debug_ranges", self.debug_ranges_index }, + const DebugSection = struct { + name: []const u8, + index: ?u32, }; - inline for (debug_sections) |item| { - if (item[1]) |index| { + const debug_sections: []const DebugSection = &.{ + .{ .name = ".debug_info", .index = self.debug_info_index }, + .{ .name = ".debug_pubtypes", .index = self.debug_pubtypes_index }, + .{ .name = ".debug_abbrev", .index = self.debug_abbrev_index }, + .{ .name = ".debug_line", .index = self.debug_line_index }, + .{ .name = ".debug_str", .index = self.debug_str_index }, + .{ .name = ".debug_pubnames", .index = self.debug_pubnames_index }, + .{ .name = ".debug_loc", .index = self.debug_loc_index }, + .{ .name = ".debug_ranges", .index = self.debug_ranges_index }, + }; + + for (debug_sections) |item| { + if (item.index) |index| { var atom = self.atoms.get(index).?.getFirst(); while (true) { atom.resolveRelocs(self); try debug_bytes.appendSlice(atom.code.items); atom = atom.next orelse break; } - try emitDebugSection(file, debug_bytes.items, item[0]); + try emitDebugSection(file, debug_bytes.items, item.name); debug_bytes.clearRetainingCapacity(); } } @@ -3242,8 +3310,8 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { var payload = std.ArrayList(u8).init(arena); const writer = payload.writer(); try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SEGMENT_INFO)); - try leb.writeULEB128(writer, @intCast(u32, self.segment_info.items.len)); - for (self.segment_info.items) |segment_info| { + try leb.writeULEB128(writer, @intCast(u32, self.segment_info.count())); + for (self.segment_info.values()) |segment_info| { log.debug("Emit segment: {s} align({d}) flags({b})", .{ segment_info.name, @ctz(segment_info.alignment), diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index 440ebea6f4..3e288fa018 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -190,7 +190,7 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa const target_atom = wasm_bin.symbol_atom.get(target_loc).?; const segment_info = if (target_atom.file) |object_index| blk: { break :blk wasm_bin.objects.items[object_index].segment_info; - } else wasm_bin.segment_info.items; + } else wasm_bin.segment_info.values(); const segment_name = segment_info[symbol.index].outputName(merge_segment); const segment_index = wasm_bin.data_segments.get(segment_name).?; const segment = wasm_bin.segments.items[segment_index]; diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index 86c9cdc023..42c3b8a1a0 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -953,13 +953,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin segment.alignment = std.math.max(segment.alignment, atom.alignment); } - if (wasm_bin.atoms.getPtr(final_index)) |last| { - last.*.next = atom; - atom.prev = last.*; - last.* = atom; - } else { - try wasm_bin.atoms.putNoClobber(gpa, final_index, atom); - } + try wasm_bin.appendAtomAtIndex(final_index, atom); log.debug("Parsed into atom: '{s}' at segment index {d}", .{ self.string_table.get(self.symtable[atom.sym_index].name), final_index }); } }