| author | |
| committer | |
| log | 2ae2ac33d9ddd1fb181e08a811d97b1bf238bced |
| tree | a9b3d1f461ef443259d947f3840cb4e3da5e19b6 |
| parent | 9b6b7034c20a0089b88e58b5142c91b2d26bcef8 |
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 wasm | 90 | /// Debug information for wasm |
| 91 | dwarf: ?Dwarf = null, | 91 | dwarf: ?Dwarf = null, |
| 92 | 92 | ||
| 93 | // *debug information* // | ||
| 94 | /// Contains all bytes for the '.debug_info' section | ||
| 95 | debug_info: std.ArrayListUnmanaged(u8) = .{}, | ||
| 96 | /// Contains all bytes for the '.debug_line' section | ||
| 97 | debug_line: std.ArrayListUnmanaged(u8) = .{}, | ||
| 98 | /// Contains all bytes for the '.debug_abbrev' section | ||
| 99 | debug_abbrev: std.ArrayListUnmanaged(u8) = .{}, | ||
| 100 | |||
| 93 | // Output sections | 101 | // Output sections |
| 94 | /// Output type section | 102 | /// Output type section |
| 95 | func_types: std.ArrayListUnmanaged(wasm.Type) = .{}, | 103 | func_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 | } |
| 505 | 517 | ||
| 506 | pub fn allocateDeclIndexes(self: *Wasm, decl_index: Module.Decl.Index) !void { | 518 | pub 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 relocation | 591 | // 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 segment | 1030 | // 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 segment | 1031 | // 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 memory | 1040 | // 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 | } |
| 1321 | 1337 | ||
| 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' segments | 1340 | 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 | } |
| 1592 | 1606 | ||
| 1593 | pub fn flush(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { | 1607 | pub 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 | } |
| 2022 | 2049 | ||
| 2050 | fn 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 | |||
| 2023 | fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void { | 2070 | fn 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 | } |
| 92 | 92 | ||
| 93 | /// Returns the atom for the given `symbol_index`. | ||
| 94 | /// This can be either the `Atom` itself, or one of its locals. | ||
| 95 | pub 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` |
| 103 | pub fn symbolLoc(self: Atom) Wasm.SymbolLoc { | 94 | pub fn symbolLoc(self: Atom) Wasm.SymbolLoc { |
| 104 | return .{ .file = self.file, .index = self.sym_index }; | 95 | return .{ .file = self.file, .index = self.sym_index }; |