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
24952495 }
24962496 } else if (!self.base.options.strip) {
24972497 if (self.dwarf) |*dwarf| {
2498 if (self.debug_info_index != null) {
2499 if (self.base.options.module) |mod| {
2500 try dwarf.writeDbgAbbrev(&self.base);
2501 // for debug info and ranges, the address is always 0,
2502 // as locations are always offsets relative to 'code' section.
2503 try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size);
2504 try dwarf.writeDbgAranges(&self.base, 0, code_section_size);
2505 try dwarf.writeDbgLineHeader(&self.base, mod);
2506 }
2498 const mod = self.base.options.module.?;
2499 try dwarf.writeDbgAbbrev(&self.base);
2500 // for debug info and ranges, the address is always 0,
2501 // as locations are always offsets relative to 'code' section.
2502 try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size);
2503 try dwarf.writeDbgAranges(&self.base, 0, code_section_size);
2504 try dwarf.writeDbgLineHeader(&self.base, mod);
2505 }
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");
2509 try emitDebugSection(file, self.debug_aranges.items, ".debug_ranges");
2510 try emitDebugSection(file, self.debug_abbrev.items, ".debug_abbrev");
2511 try emitDebugSection(file, self.debug_line.items, ".debug_line");
2512 try emitDebugSection(file, dwarf.strtab.items, ".debug_str");
2521 inline for (debug_sections) |item| {
2522 if (item[1]) |index| {
2523 var atom = self.atoms.get(index).?.getFirst();
2524 while (true) {
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();
25132531 }
25142532 }
25152533 try self.emitNameSection(file, arena);
......@@ -2517,6 +2535,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
25172535}
25182536
25192537fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void {
2538 if (data.len == 0) return;
25202539 const header_offset = try reserveCustomSectionHeader(file);
25212540 const writer = file.writer();
25222541 try leb.writeULEB128(writer, @intCast(u32, name.len));
src/link/Wasm/Object.zig+26-22
......@@ -77,7 +77,7 @@ const RelocatableData = struct {
7777 /// The size in bytes of the data representing the segment within the section
7878 size: u32,
7979 /// 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`.
8181 index: u32,
8282 /// The offset within the section where the data starts
8383 offset: u32,
......@@ -101,9 +101,16 @@ const RelocatableData = struct {
101101 return switch (self.type) {
102102 .data => .data,
103103 .code => .function,
104 .debug => unreachable, // illegal, debug sections are not represented by a symbol
104 .debug => .section,
105105 };
106106 }
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 }
107114};
108115
109116pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadError;
......@@ -205,7 +212,7 @@ pub fn importedCountByKind(self: *const Object, kind: std.wasm.ExternalKind) u32
205212
206213/// From a given `RelocatableDate`, find the corresponding debug section name
207214pub 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);
209216}
210217
211218/// Checks if the object file is an MVP version.
......@@ -363,6 +370,7 @@ fn Parser(comptime ReaderType: type) type {
363370
364371 if (std.mem.eql(u8, name, "linking")) {
365372 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.
366374 try self.parseMetadata(gpa, @intCast(usize, reader.context.bytes_left));
367375 } else if (std.mem.startsWith(u8, name, "reloc")) {
368376 try self.parseRelocations(gpa);
......@@ -374,19 +382,16 @@ fn Parser(comptime ReaderType: type) type {
374382 errdefer gpa.free(debug_content);
375383 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);
381385 try relocatable_data.append(.{
382386 .type = .debug,
383387 .data = debug_content.ptr,
384388 .size = debug_size,
385 .index = debug_name_index,
389 .index = try self.object.string_table.put(gpa, name),
386390 .offset = len - debug_size,
387391 .section_index = section_index,
388392 });
389393 } else {
394 log.info("found unknown custom section '{s}' - skipping parsing", .{name});
390395 try reader.skipBytes(reader.context.bytes_left, .{});
391396 }
392397 },
......@@ -551,9 +556,6 @@ fn Parser(comptime ReaderType: type) type {
551556 else => |e| return e,
552557 }
553558 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];
557559 }
558560
559561 /// Based on the "features" custom section, parses it into a list of
......@@ -774,7 +776,12 @@ fn Parser(comptime ReaderType: type) type {
774776 },
775777 .section => {
776778 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 }
778785 },
779786 else => {
780787 symbol.index = try leb.readULEB128(u32, reader);
......@@ -864,7 +871,6 @@ fn assertEnd(reader: anytype) !void {
864871
865872/// Parses an object file into atoms, for code and data sections
866873pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin: *Wasm) !void {
867 log.debug("Parsing data section into atoms", .{});
868874 const Key = struct {
869875 kind: Symbol.Tag,
870876 index: u32,
......@@ -876,7 +882,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
876882
877883 for (self.symtable) |symbol, symbol_index| {
878884 switch (symbol.tag) {
879 .function, .data => if (!symbol.isUndefined()) {
885 .function, .data, .section => if (!symbol.isUndefined()) {
880886 const gop = try symbol_for_segment.getOrPut(.{ .kind = symbol.tag, .index = symbol.index });
881887 const sym_idx = @intCast(u32, symbol_index);
882888 if (!gop.found_existing) {
......@@ -925,13 +931,11 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
925931
926932 try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]);
927933
928 if (relocatable_data.type != .debug) {
929 const symbols = symbol_for_segment.getPtr(.{
930 .kind = relocatable_data.getSymbolKind(),
931 .index = @intCast(u32, relocatable_data.index),
932 }) orelse continue; // encountered a segment we do not create an atom for
933 const sym_index = symbols.pop();
934 atom.sym_index = sym_index;
934 if (symbol_for_segment.getPtr(.{
935 .kind = relocatable_data.getSymbolKind(),
936 .index = relocatable_data.getIndex(),
937 })) |symbols| {
938 atom.sym_index = symbols.pop();
935939
936940 // symbols referencing the same atom will be added as alias
937941 // or as 'parent' when they are global.
......@@ -957,7 +961,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
957961 } else {
958962 try wasm_bin.atoms.putNoClobber(gpa, final_index, atom);
959963 }
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 });
961965 }
962966}
963967