Commit c347751338

Luuk de Gram <luuk@degram.dev>
2022-08-31 23:03:29
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.
1 parent f060edb
Changed files (2)
src
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 });
     }
 }
 
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 },
+        };
 
-                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");
+        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));