authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-08-31 23:03:29+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-09-07 18:53:16+02:00
logc347751338a4a1e2874207674fb47908fd601484
treedbbbd62873cd8a659919556d1043ec6e7f0ef271
parentf060edb0f3e23a18b17af9b619f7f499ac8e4e7f
signaturelock-open Commit is signed but in an unrecognized format.

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.

2 files changed, 59 insertions(+), 36 deletions(-)

src/link/Wasm.zig+33-14
...@@ -2495,21 +2495,39 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2495,21 +2495,39 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
2495 }2495 }
2496 } else if (!self.base.options.strip) {2496 } else if (!self.base.options.strip) {
2497 if (self.dwarf) |*dwarf| {2497 if (self.dwarf) |*dwarf| {
2498 if (self.debug_info_index != null) {2498 const mod = self.base.options.module.?;
2499 if (self.base.options.module) |mod| {2499 try dwarf.writeDbgAbbrev(&self.base);
2500 try dwarf.writeDbgAbbrev(&self.base);2500 // for debug info and ranges, the address is always 0,
2501 // for debug info and ranges, the address is always 0,2501 // as locations are always offsets relative to 'code' section.
2502 // as locations are always offsets relative to 'code' section.2502 try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size);
2503 try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size);2503 try dwarf.writeDbgAranges(&self.base, 0, code_section_size);
2504 try dwarf.writeDbgAranges(&self.base, 0, code_section_size);2504 try dwarf.writeDbgLineHeader(&self.base, mod);
2505 try dwarf.writeDbgLineHeader(&self.base, mod);2505 }
2506 }2506
2507 var debug_bytes = std.ArrayList(u8).init(self.base.allocator);
2508 defer debug_bytes.deinit();
2509
2510 const debug_sections = .{
2511 .{ ".debug_info", self.debug_info_index },
2512 .{ ".debug_pubtypes", self.debug_pubtypes_index },
2513 .{ ".debug_abbrev", self.debug_abbrev_index },
2514 .{ ".debug_line", self.debug_line_index },
2515 .{ ".debug_str", self.debug_str_index },
2516 .{ ".debug_pubnames", self.debug_pubnames_index },
2517 .{ ".debug_loc", self.debug_loc_index },
2518 .{ ".debug_ranges", self.debug_ranges_index },
2519 };
25072520
2508 try emitDebugSection(file, self.debug_info.items, ".debug_info");2521 inline for (debug_sections) |item| {
2509 try emitDebugSection(file, self.debug_aranges.items, ".debug_ranges");2522 if (item[1]) |index| {
2510 try emitDebugSection(file, self.debug_abbrev.items, ".debug_abbrev");2523 var atom = self.atoms.get(index).?.getFirst();
2511 try emitDebugSection(file, self.debug_line.items, ".debug_line");2524 while (true) {
2512 try emitDebugSection(file, dwarf.strtab.items, ".debug_str");2525 atom.resolveRelocs(self);
2526 try debug_bytes.appendSlice(atom.code.items);
2527 atom = atom.next orelse break;
2528 }
2529 try emitDebugSection(file, debug_bytes.items, item[0]);
2530 debug_bytes.clearRetainingCapacity();
2513 }2531 }
2514 }2532 }
2515 try self.emitNameSection(file, arena);2533 try self.emitNameSection(file, arena);
...@@ -2517,6 +2535,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2517,6 +2535,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
2517}2535}
25182536
2519fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void {2537fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void {
2538 if (data.len == 0) return;
2520 const header_offset = try reserveCustomSectionHeader(file);2539 const header_offset = try reserveCustomSectionHeader(file);
2521 const writer = file.writer();2540 const writer = file.writer();
2522 try leb.writeULEB128(writer, @intCast(u32, name.len));2541 try leb.writeULEB128(writer, @intCast(u32, name.len));
src/link/Wasm/Object.zig+26-22
...@@ -77,7 +77,7 @@ const RelocatableData = struct {...@@ -77,7 +77,7 @@ const RelocatableData = struct {
77 /// The size in bytes of the data representing the segment within the section77 /// The size in bytes of the data representing the segment within the section
78 size: u32,78 size: u32,
79 /// The index within the section itself, or in case of a debug section,79 /// The index within the section itself, or in case of a debug section,
80 /// the offset within the `debug_names` table.80 /// the offset within the `string_table`.
81 index: u32,81 index: u32,
82 /// The offset within the section where the data starts82 /// The offset within the section where the data starts
83 offset: u32,83 offset: u32,
...@@ -101,9 +101,16 @@ const RelocatableData = struct {...@@ -101,9 +101,16 @@ const RelocatableData = struct {
101 return switch (self.type) {101 return switch (self.type) {
102 .data => .data,102 .data => .data,
103 .code => .function,103 .code => .function,
104 .debug => unreachable, // illegal, debug sections are not represented by a symbol104 .debug => .section,
105 };105 };
106 }106 }
107
108 /// Returns the index within a section itself, or in case of a debug section,
109 /// returns the section index within the object file.
110 pub fn getIndex(self: RelocatableData) u32 {
111 if (self.type == .debug) return self.section_index;
112 return self.index;
113 }
107};114};
108115
109pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadError;116pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadError;
...@@ -205,7 +212,7 @@ pub fn importedCountByKind(self: *const Object, kind: std.wasm.ExternalKind) u32...@@ -205,7 +212,7 @@ pub fn importedCountByKind(self: *const Object, kind: std.wasm.ExternalKind) u32
205212
206/// From a given `RelocatableDate`, find the corresponding debug section name213/// From a given `RelocatableDate`, find the corresponding debug section name
207pub fn getDebugName(self: *const Object, relocatable_data: RelocatableData) []const u8 {214pub fn getDebugName(self: *const Object, relocatable_data: RelocatableData) []const u8 {
208 return std.mem.sliceTo(self.debug_names[relocatable_data.index..], 0);215 return self.string_table.get(relocatable_data.index);
209}216}
210217
211/// Checks if the object file is an MVP version.218/// Checks if the object file is an MVP version.
...@@ -363,6 +370,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -363,6 +370,7 @@ fn Parser(comptime ReaderType: type) type {
363370
364 if (std.mem.eql(u8, name, "linking")) {371 if (std.mem.eql(u8, name, "linking")) {
365 is_object_file.* = true;372 is_object_file.* = true;
373 self.object.relocatable_data = relocatable_data.items; // at this point no new relocatable sections will appear so we're free to store them.
366 try self.parseMetadata(gpa, @intCast(usize, reader.context.bytes_left));374 try self.parseMetadata(gpa, @intCast(usize, reader.context.bytes_left));
367 } else if (std.mem.startsWith(u8, name, "reloc")) {375 } else if (std.mem.startsWith(u8, name, "reloc")) {
368 try self.parseRelocations(gpa);376 try self.parseRelocations(gpa);
...@@ -374,19 +382,16 @@ fn Parser(comptime ReaderType: type) type {...@@ -374,19 +382,16 @@ fn Parser(comptime ReaderType: type) type {
374 errdefer gpa.free(debug_content);382 errdefer gpa.free(debug_content);
375 try reader.readNoEof(debug_content);383 try reader.readNoEof(debug_content);
376384
377 const debug_name_index = @intCast(u32, debug_names.items.len);
378 try debug_names.ensureUnusedCapacity(name.len + 1);
379 debug_names.appendSliceAssumeCapacity(try gpa.dupe(u8, name));
380 debug_names.appendAssumeCapacity(0);
381 try relocatable_data.append(.{385 try relocatable_data.append(.{
382 .type = .debug,386 .type = .debug,
383 .data = debug_content.ptr,387 .data = debug_content.ptr,
384 .size = debug_size,388 .size = debug_size,
385 .index = debug_name_index,389 .index = try self.object.string_table.put(gpa, name),
386 .offset = len - debug_size,390 .offset = len - debug_size,
387 .section_index = section_index,391 .section_index = section_index,
388 });392 });
389 } else {393 } else {
394 log.info("found unknown custom section '{s}' - skipping parsing", .{name});
390 try reader.skipBytes(reader.context.bytes_left, .{});395 try reader.skipBytes(reader.context.bytes_left, .{});
391 }396 }
392 },397 },
...@@ -551,9 +556,6 @@ fn Parser(comptime ReaderType: type) type {...@@ -551,9 +556,6 @@ fn Parser(comptime ReaderType: type) type {
551 else => |e| return e,556 else => |e| return e,
552 }557 }
553 self.object.relocatable_data = relocatable_data.toOwnedSlice();558 self.object.relocatable_data = relocatable_data.toOwnedSlice();
554
555 const names = debug_names.toOwnedSlice();
556 self.object.debug_names = names[0 .. names.len - 1 :0];
557 }559 }
558560
559 /// Based on the "features" custom section, parses it into a list of561 /// Based on the "features" custom section, parses it into a list of
...@@ -774,7 +776,12 @@ fn Parser(comptime ReaderType: type) type {...@@ -774,7 +776,12 @@ fn Parser(comptime ReaderType: type) type {
774 },776 },
775 .section => {777 .section => {
776 symbol.index = try leb.readULEB128(u32, reader);778 symbol.index = try leb.readULEB128(u32, reader);
777 symbol.name = try self.object.string_table.put(gpa, @tagName(symbol.tag));779 for (self.object.relocatable_data) |data| {
780 if (data.section_index == symbol.index) {
781 symbol.name = data.index;
782 break;
783 }
784 }
778 },785 },
779 else => {786 else => {
780 symbol.index = try leb.readULEB128(u32, reader);787 symbol.index = try leb.readULEB128(u32, reader);
...@@ -864,7 +871,6 @@ fn assertEnd(reader: anytype) !void {...@@ -864,7 +871,6 @@ fn assertEnd(reader: anytype) !void {
864871
865/// Parses an object file into atoms, for code and data sections872/// Parses an object file into atoms, for code and data sections
866pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin: *Wasm) !void {873pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin: *Wasm) !void {
867 log.debug("Parsing data section into atoms", .{});
868 const Key = struct {874 const Key = struct {
869 kind: Symbol.Tag,875 kind: Symbol.Tag,
870 index: u32,876 index: u32,
...@@ -876,7 +882,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin...@@ -876,7 +882,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
876882
877 for (self.symtable) |symbol, symbol_index| {883 for (self.symtable) |symbol, symbol_index| {
878 switch (symbol.tag) {884 switch (symbol.tag) {
879 .function, .data => if (!symbol.isUndefined()) {885 .function, .data, .section => if (!symbol.isUndefined()) {
880 const gop = try symbol_for_segment.getOrPut(.{ .kind = symbol.tag, .index = symbol.index });886 const gop = try symbol_for_segment.getOrPut(.{ .kind = symbol.tag, .index = symbol.index });
881 const sym_idx = @intCast(u32, symbol_index);887 const sym_idx = @intCast(u32, symbol_index);
882 if (!gop.found_existing) {888 if (!gop.found_existing) {
...@@ -925,13 +931,11 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin...@@ -925,13 +931,11 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
925931
926 try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]);932 try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]);
927933
928 if (relocatable_data.type != .debug) {934 if (symbol_for_segment.getPtr(.{
929 const symbols = symbol_for_segment.getPtr(.{935 .kind = relocatable_data.getSymbolKind(),
930 .kind = relocatable_data.getSymbolKind(),936 .index = relocatable_data.getIndex(),
931 .index = @intCast(u32, relocatable_data.index),937 })) |symbols| {
932 }) orelse continue; // encountered a segment we do not create an atom for938 atom.sym_index = symbols.pop();
933 const sym_index = symbols.pop();
934 atom.sym_index = sym_index;
935939
936 // symbols referencing the same atom will be added as alias940 // symbols referencing the same atom will be added as alias
937 // or as 'parent' when they are global.941 // or as 'parent' when they are global.
...@@ -957,7 +961,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin...@@ -957,7 +961,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
957 } else {961 } else {
958 try wasm_bin.atoms.putNoClobber(gpa, final_index, atom);962 try wasm_bin.atoms.putNoClobber(gpa, final_index, atom);
959 }963 }
960 log.debug("Parsed into atom: '{s}'", .{self.string_table.get(self.symtable[atom.sym_index].name)});964 log.debug("Parsed into atom: '{s}' at segment index {d}", .{ self.string_table.get(self.symtable[atom.sym_index].name), final_index });
961 }965 }
962}966}
963967