From c347751338a4a1e2874207674fb47908fd601484 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 31 Aug 2022 23:03:29 +0200 Subject: [PATCH] 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 ae995bd0b99e48e2d17c41a45a36b8527b99fdc5..b17eeb57e973d65a75cb8656e4065b4ba4c19382 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); + } + + 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, 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"); + 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 bb00eeb82141afe4576453e257e79795816fa70d..fac5ce3aa8735d45f45b40fee14f9f787bd78b8e 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 }); } } -- 2.54.0