authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-04 21:25:33+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-09 18:51:46+02:00
log2ae2ac33d9ddd1fb181e08a811d97b1bf238bced
treea9b3d1f461ef443259d947f3840cb4e3da5e19b6
parent9b6b7034c20a0089b88e58b5142c91b2d26bcef8

wasm: Emit debug sections

This commit adds the ability to emit the following debug sections: .debug_info .debug_abbrev .debug_line .debug_str Line information and files are now being loaded correctly by browser debuggers.

3 files changed, 99 insertions(+), 27 deletions(-)

src/link/Dwarf.zig+45-11
...@@ -963,21 +963,18 @@ pub fn commitDeclState(...@@ -963,21 +963,18 @@ pub fn commitDeclState(
963 const wasm_file = file.cast(File.Wasm).?;963 const wasm_file = file.cast(File.Wasm).?;
964 const segment_index = try wasm_file.getDebugLineIndex();964 const segment_index = try wasm_file.getDebugLineIndex();
965 const segment = &wasm_file.segments.items[segment_index];965 const segment = &wasm_file.segments.items[segment_index];
966 const debug_atom = wasm_file.atoms.get(segment_index).?;966 const debug_line = &wasm_file.debug_line;
967 if (needed_size != segment.size) {967 if (needed_size != segment.size) {
968 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});968 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
969 if (needed_size > segment.size) {969 if (needed_size > segment.size) {
970 log.debug(" allocating {d} bytes for debug line information", .{needed_size - segment.size});970 log.debug(" allocating {d} bytes for 'debug line' information", .{needed_size - segment.size});
971 try debug_atom.code.resize(self.allocator, needed_size);971 try debug_line.resize(self.allocator, needed_size);
972 std.mem.set(u8, debug_atom.code.items[segment.size..], 0);972 mem.set(u8, debug_line.items[segment.size..], 0);
973 }973 }
974 debug_atom.size = needed_size;
975 segment.size = needed_size;974 segment.size = needed_size;
976 }975 }
977 // since we can tighly pack the debug lines, wasm does not require
978 // us to pad with Nops.
979 const offset = segment.offset + src_fn.off;976 const offset = segment.offset + src_fn.off;
980 std.mem.copy(u8, debug_atom.code.items[offset..], dbg_line_buffer.items);977 mem.copy(u8, debug_line.items[offset..], dbg_line_buffer.items);
981 },978 },
982 else => unreachable,979 else => unreachable,
983 }980 }
...@@ -1116,7 +1113,9 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3...@@ -1116,7 +1113,9 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3
1116 const file_pos = debug_info_sect.offset + atom.off;1113 const file_pos = debug_info_sect.offset + atom.off;
1117 try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false);1114 try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false);
1118 },1115 },
1119 .wasm => {},1116 .wasm => {
1117 log.debug(" todo: updateDeclDebugInfoAllocation for Wasm: {d}", .{atom.len});
1118 },
1120 else => unreachable,1119 else => unreachable,
1121 }1120 }
1122 // TODO Look at the free list before appending at the end.1121 // TODO Look at the free list before appending at the end.
...@@ -1241,6 +1240,23 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co...@@ -1241,6 +1240,23 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
1241 trailing_zero,1240 trailing_zero,
1242 );1241 );
1243 },1242 },
1243 .wasm => {
1244 const wasm_file = file.cast(File.Wasm).?;
1245 const segment_index = try wasm_file.getDebugInfoIndex();
1246 const segment = &wasm_file.segments.items[segment_index];
1247 const debug_info = &wasm_file.debug_info;
1248 if (needed_size != segment.size) {
1249 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
1250 if (needed_size > segment.size) {
1251 log.debug(" allocating {d} bytes for 'debug info' information", .{needed_size - segment.size});
1252 try debug_info.resize(self.allocator, needed_size);
1253 mem.set(u8, debug_info.items[segment.size..], 0);
1254 }
1255 segment.size = needed_size;
1256 }
1257 const offset = segment.offset + atom.off;
1258 mem.copy(u8, debug_info.items[offset..], dbg_info_buf);
1259 },
1244 else => unreachable,1260 else => unreachable,
1245 }1261 }
1246}1262}
...@@ -1279,8 +1295,7 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl)...@@ -1279,8 +1295,7 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl)
1279 const segment_index = wasm_file.getDebugLineIndex() catch unreachable;1295 const segment_index = wasm_file.getDebugLineIndex() catch unreachable;
1280 const segment = wasm_file.segments.items[segment_index];1296 const segment = wasm_file.segments.items[segment_index];
1281 const offset = segment.offset + decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff();1297 const offset = segment.offset + decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff();
1282 const debug_atom = wasm_file.atoms.get(segment_index).?;1298 mem.copy(u8, wasm_file.debug_line.items[offset..], &data);
1283 std.mem.copy(u8, debug_atom.code.items[offset..], &data);
1284 },1299 },
1285 else => unreachable,1300 else => unreachable,
1286 }1301 }
...@@ -1514,6 +1529,11 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void {...@@ -1514,6 +1529,11 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void {
1514 const file_pos = debug_abbrev_sect.offset + abbrev_offset;1529 const file_pos = debug_abbrev_sect.offset + abbrev_offset;
1515 try d_sym.file.pwriteAll(&abbrev_buf, file_pos);1530 try d_sym.file.pwriteAll(&abbrev_buf, file_pos);
1516 },1531 },
1532 .wasm => {
1533 const wasm_file = file.cast(File.Wasm).?;
1534 try wasm_file.debug_abbrev.resize(wasm_file.base.allocator, needed_size);
1535 mem.copy(u8, wasm_file.debug_abbrev.items, &abbrev_buf);
1536 },
1517 else => unreachable,1537 else => unreachable,
1518 }1538 }
1519}1539}
...@@ -1621,6 +1641,10 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6...@@ -1621,6 +1641,10 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6
1621 const file_pos = debug_info_sect.offset;1641 const file_pos = debug_info_sect.offset;
1622 try pwriteDbgInfoNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt, false);1642 try pwriteDbgInfoNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt, false);
1623 },1643 },
1644 .wasm => {
1645 const wasm_file = file.cast(File.Wasm).?;
1646 mem.copy(u8, wasm_file.debug_info.items, di_buf.items);
1647 },
1624 else => unreachable,1648 else => unreachable,
1625 }1649 }
1626}1650}
...@@ -2004,6 +2028,10 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void {...@@ -2004,6 +2028,10 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void {
2004 const file_pos = debug_line_sect.offset;2028 const file_pos = debug_line_sect.offset;
2005 try pwriteDbgLineNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt);2029 try pwriteDbgLineNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt);
2006 },2030 },
2031 .wasm => {
2032 const wasm_file = file.cast(File.Wasm).?;
2033 mem.copy(u8, wasm_file.debug_line.items, di_buf.items);
2034 },
2007 else => unreachable,2035 else => unreachable,
2008 }2036 }
2009}2037}
...@@ -2127,6 +2155,8 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void {...@@ -2127,6 +2155,8 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void {
2127 const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?];2155 const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?];
2128 break :blk debug_info_sect.offset;2156 break :blk debug_info_sect.offset;
2129 },2157 },
2158 // for wasm, the offset is always 0 as we write to memory first
2159 .wasm => break :blk @as(u32, 0),
2130 else => unreachable,2160 else => unreachable,
2131 }2161 }
2132 };2162 };
...@@ -2145,6 +2175,10 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void {...@@ -2145,6 +2175,10 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void {
2145 const d_sym = &macho_file.d_sym.?;2175 const d_sym = &macho_file.d_sym.?;
2146 try d_sym.file.pwriteAll(&buf, file_pos + reloc.atom.off + reloc.offset);2176 try d_sym.file.pwriteAll(&buf, file_pos + reloc.atom.off + reloc.offset);
2147 },2177 },
2178 .wasm => {
2179 const wasm_file = file.cast(File.Wasm).?;
2180 mem.copy(u8, wasm_file.debug_info.items[reloc.atom.off + reloc.offset ..], &buf);
2181 },
2148 else => unreachable,2182 else => unreachable,
2149 }2183 }
2150 }2184 }
src/link/Wasm.zig+54-7
...@@ -90,6 +90,14 @@ string_table: StringTable = .{},...@@ -90,6 +90,14 @@ string_table: StringTable = .{},
90/// Debug information for wasm90/// Debug information for wasm
91dwarf: ?Dwarf = null,91dwarf: ?Dwarf = null,
9292
93// *debug information* //
94/// Contains all bytes for the '.debug_info' section
95debug_info: std.ArrayListUnmanaged(u8) = .{},
96/// Contains all bytes for the '.debug_line' section
97debug_line: std.ArrayListUnmanaged(u8) = .{},
98/// Contains all bytes for the '.debug_abbrev' section
99debug_abbrev: std.ArrayListUnmanaged(u8) = .{},
100
93// Output sections101// Output sections
94/// Output type section102/// Output type section
95func_types: std.ArrayListUnmanaged(wasm.Type) = .{},103func_types: std.ArrayListUnmanaged(wasm.Type) = .{},
...@@ -501,6 +509,10 @@ pub fn deinit(self: *Wasm) void {...@@ -501,6 +509,10 @@ pub fn deinit(self: *Wasm) void {
501 if (self.dwarf) |*dwarf| {509 if (self.dwarf) |*dwarf| {
502 dwarf.deinit();510 dwarf.deinit();
503 }511 }
512
513 self.debug_info.deinit(gpa);
514 self.debug_line.deinit(gpa);
515 self.debug_abbrev.deinit(gpa);
504}516}
505517
506pub fn allocateDeclIndexes(self: *Wasm, decl_index: Module.Decl.Index) !void {518pub fn allocateDeclIndexes(self: *Wasm, decl_index: Module.Decl.Index) !void {
...@@ -576,7 +588,9 @@ pub fn updateFunc(self: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes...@@ -576,7 +588,9 @@ pub fn updateFunc(self: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes
576 &self.base,588 &self.base,
577 mod,589 mod,
578 decl,590 decl,
579 // Actual value will be written after relocation591 // Actual value will be written after relocation.
592 // For Wasm, this is the offset relative to the code section
593 // which isn't known until flush().
580 0,594 0,
581 code.len,595 code.len,
582 &decl_state.?,596 &decl_state.?,
...@@ -1016,10 +1030,12 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {...@@ -1016,10 +1030,12 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
1016 // segment indexes can be off by 1 due to also containing a segment1030 // segment indexes can be off by 1 due to also containing a segment
1017 // for the code section, so we must check if the existing segment1031 // for the code section, so we must check if the existing segment
1018 // is larger than that of the code section, and substract the index by 1 in such case.1032 // is larger than that of the code section, and substract the index by 1 in such case.
1019 const info_add = if (self.code_section_index) |idx| blk: {1033 var info_add = if (self.code_section_index) |idx| blk: {
1020 if (idx < index) break :blk @as(u32, 1);1034 if (idx < index) break :blk @as(u32, 1);
1021 break :blk 0;1035 break :blk 0;
1022 } else @as(u32, 0);1036 } else @as(u32, 0);
1037 if (self.debug_info_index != null) info_add += 1;
1038 if (self.debug_line_index != null) info_add += 1;
1023 symbol.index = index - info_add;1039 symbol.index = index - info_add;
1024 // segment info already exists, so free its memory1040 // segment info already exists, so free its memory
1025 self.base.allocator.free(segment_name);1041 self.base.allocator.free(segment_name);
...@@ -1320,11 +1336,8 @@ fn setupMemory(self: *Wasm) !void {...@@ -1320,11 +1336,8 @@ fn setupMemory(self: *Wasm) !void {
1320 }1336 }
13211337
1322 var offset: u32 = @intCast(u32, memory_ptr);1338 var offset: u32 = @intCast(u32, memory_ptr);
1323 for (self.segments.items) |*segment, i| {1339 for (self.data_segments.values()) |segment_index| {
1324 // skip 'code' segments1340 const segment = &self.segments.items[segment_index];
1325 if (self.code_section_index) |index| {
1326 if (index == i) continue;
1327 }
1328 memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment);1341 memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment);
1329 memory_ptr += segment.size;1342 memory_ptr += segment.size;
1330 segment.offset = offset;1343 segment.offset = offset;
...@@ -1588,6 +1601,7 @@ fn resetState(self: *Wasm) void {...@@ -1588,6 +1601,7 @@ fn resetState(self: *Wasm) void {
1588 self.symbol_atom.clearRetainingCapacity();1601 self.symbol_atom.clearRetainingCapacity();
1589 self.code_section_index = null;1602 self.code_section_index = null;
1590 self.debug_info_index = null;1603 self.debug_info_index = null;
1604 self.debug_line_index = null;
1591}1605}
15921606
1593pub fn flush(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void {1607pub fn flush(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void {
...@@ -2016,10 +2030,43 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2016,10 +2030,43 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
2016 try self.emitDataRelocations(file, arena, data_index, symbol_table);2030 try self.emitDataRelocations(file, arena, data_index, symbol_table);
2017 }2031 }
2018 } else if (!self.base.options.strip) {2032 } else if (!self.base.options.strip) {
2033 if (self.dwarf) |*dwarf| {
2034 if (self.debug_info_index != null) {
2035 _ = dwarf;
2036 try dwarf.writeDbgAbbrev(&self.base);
2037 try dwarf.writeDbgInfoHeader(&self.base, mod, 0, 0);
2038 try dwarf.writeDbgLineHeader(&self.base, mod);
2039
2040 try emitDebugSection(file, self.debug_info.items, ".debug_info");
2041 try emitDebugSection(file, self.debug_abbrev.items, ".debug_abbrev"); // TODO
2042 try emitDebugSection(file, self.debug_line.items, ".debug_line");
2043 try emitDebugSection(file, dwarf.strtab.items, ".debug_str");
2044 }
2045 }
2019 try self.emitNameSection(file, arena);2046 try self.emitNameSection(file, arena);
2020 }2047 }
2021}2048}
20222049
2050fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void {
2051 const header_offset = try reserveCustomSectionHeader(file);
2052 const writer = file.writer();
2053 try leb.writeULEB128(writer, @intCast(u32, name.len));
2054 try writer.writeAll(name);
2055
2056 try file.writevAll(&[_]std.os.iovec_const{.{
2057 .iov_base = data.ptr,
2058 .iov_len = data.len,
2059 }});
2060 const start = header_offset + 6 + name.len + getULEB128Size(@intCast(u32, name.len));
2061 log.debug("Emit debug section: '{s}' start=0x{x:0>8} end=0x{x:0>8}", .{ name, start, start + data.len });
2062
2063 try writeCustomSectionHeader(
2064 file,
2065 header_offset,
2066 @intCast(u32, (try file.getPos()) - header_offset - 6),
2067 );
2068}
2069
2023fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void {2070fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void {
2024 const Name = struct {2071 const Name = struct {
2025 index: u32,2072 index: u32,
src/link/Wasm/Atom.zig-9
...@@ -90,15 +90,6 @@ pub fn getFirst(self: *Atom) *Atom {...@@ -90,15 +90,6 @@ pub fn getFirst(self: *Atom) *Atom {
90 return tmp;90 return tmp;
91}91}
9292
93/// Returns the atom for the given `symbol_index`.
94/// This can be either the `Atom` itself, or one of its locals.
95pub fn symbolAtom(self: *Atom, symbol_index: u32) *Atom {
96 if (self.sym_index == symbol_index) return self;
97 return for (self.locals.items) |*local_atom| {
98 if (local_atom.sym_index == symbol_index) break local_atom;
99 } else unreachable; // Used a symbol index not present in this atom or its children.
100}
101
102/// Returns the location of the symbol that represents this `Atom`93/// Returns the location of the symbol that represents this `Atom`
103pub fn symbolLoc(self: Atom) Wasm.SymbolLoc {94pub fn symbolLoc(self: Atom) Wasm.SymbolLoc {
104 return .{ .file = self.file, .index = self.sym_index };95 return .{ .file = self.file, .index = self.sym_index };