mirror of
https://github.com/ziglang/zig.git
synced 2025-12-12 09:13:11 +00:00
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.
This commit is contained in:
parent
9b6b7034c2
commit
2ae2ac33d9
@ -963,21 +963,18 @@ pub fn commitDeclState(
|
||||
const wasm_file = file.cast(File.Wasm).?;
|
||||
const segment_index = try wasm_file.getDebugLineIndex();
|
||||
const segment = &wasm_file.segments.items[segment_index];
|
||||
const debug_atom = wasm_file.atoms.get(segment_index).?;
|
||||
const debug_line = &wasm_file.debug_line;
|
||||
if (needed_size != segment.size) {
|
||||
log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
|
||||
if (needed_size > segment.size) {
|
||||
log.debug(" allocating {d} bytes for debug line information", .{needed_size - segment.size});
|
||||
try debug_atom.code.resize(self.allocator, needed_size);
|
||||
std.mem.set(u8, debug_atom.code.items[segment.size..], 0);
|
||||
log.debug(" allocating {d} bytes for 'debug line' information", .{needed_size - segment.size});
|
||||
try debug_line.resize(self.allocator, needed_size);
|
||||
mem.set(u8, debug_line.items[segment.size..], 0);
|
||||
}
|
||||
debug_atom.size = needed_size;
|
||||
segment.size = needed_size;
|
||||
}
|
||||
// since we can tighly pack the debug lines, wasm does not require
|
||||
// us to pad with Nops.
|
||||
const offset = segment.offset + src_fn.off;
|
||||
std.mem.copy(u8, debug_atom.code.items[offset..], dbg_line_buffer.items);
|
||||
mem.copy(u8, debug_line.items[offset..], dbg_line_buffer.items);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
@ -1116,7 +1113,9 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3
|
||||
const file_pos = debug_info_sect.offset + atom.off;
|
||||
try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false);
|
||||
},
|
||||
.wasm => {},
|
||||
.wasm => {
|
||||
log.debug(" todo: updateDeclDebugInfoAllocation for Wasm: {d}", .{atom.len});
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
// 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
|
||||
trailing_zero,
|
||||
);
|
||||
},
|
||||
.wasm => {
|
||||
const wasm_file = file.cast(File.Wasm).?;
|
||||
const segment_index = try wasm_file.getDebugInfoIndex();
|
||||
const segment = &wasm_file.segments.items[segment_index];
|
||||
const debug_info = &wasm_file.debug_info;
|
||||
if (needed_size != segment.size) {
|
||||
log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
|
||||
if (needed_size > segment.size) {
|
||||
log.debug(" allocating {d} bytes for 'debug info' information", .{needed_size - segment.size});
|
||||
try debug_info.resize(self.allocator, needed_size);
|
||||
mem.set(u8, debug_info.items[segment.size..], 0);
|
||||
}
|
||||
segment.size = needed_size;
|
||||
}
|
||||
const offset = segment.offset + atom.off;
|
||||
mem.copy(u8, debug_info.items[offset..], dbg_info_buf);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
@ -1279,8 +1295,7 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl)
|
||||
const segment_index = wasm_file.getDebugLineIndex() catch unreachable;
|
||||
const segment = wasm_file.segments.items[segment_index];
|
||||
const offset = segment.offset + decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff();
|
||||
const debug_atom = wasm_file.atoms.get(segment_index).?;
|
||||
std.mem.copy(u8, debug_atom.code.items[offset..], &data);
|
||||
mem.copy(u8, wasm_file.debug_line.items[offset..], &data);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
@ -1514,6 +1529,11 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void {
|
||||
const file_pos = debug_abbrev_sect.offset + abbrev_offset;
|
||||
try d_sym.file.pwriteAll(&abbrev_buf, file_pos);
|
||||
},
|
||||
.wasm => {
|
||||
const wasm_file = file.cast(File.Wasm).?;
|
||||
try wasm_file.debug_abbrev.resize(wasm_file.base.allocator, needed_size);
|
||||
mem.copy(u8, wasm_file.debug_abbrev.items, &abbrev_buf);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
@ -1621,6 +1641,10 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6
|
||||
const file_pos = debug_info_sect.offset;
|
||||
try pwriteDbgInfoNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt, false);
|
||||
},
|
||||
.wasm => {
|
||||
const wasm_file = file.cast(File.Wasm).?;
|
||||
mem.copy(u8, wasm_file.debug_info.items, di_buf.items);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
@ -2004,6 +2028,10 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void {
|
||||
const file_pos = debug_line_sect.offset;
|
||||
try pwriteDbgLineNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt);
|
||||
},
|
||||
.wasm => {
|
||||
const wasm_file = file.cast(File.Wasm).?;
|
||||
mem.copy(u8, wasm_file.debug_line.items, di_buf.items);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
@ -2127,6 +2155,8 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void {
|
||||
const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?];
|
||||
break :blk debug_info_sect.offset;
|
||||
},
|
||||
// for wasm, the offset is always 0 as we write to memory first
|
||||
.wasm => break :blk @as(u32, 0),
|
||||
else => unreachable,
|
||||
}
|
||||
};
|
||||
@ -2145,6 +2175,10 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void {
|
||||
const d_sym = &macho_file.d_sym.?;
|
||||
try d_sym.file.pwriteAll(&buf, file_pos + reloc.atom.off + reloc.offset);
|
||||
},
|
||||
.wasm => {
|
||||
const wasm_file = file.cast(File.Wasm).?;
|
||||
mem.copy(u8, wasm_file.debug_info.items[reloc.atom.off + reloc.offset ..], &buf);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,6 +90,14 @@ string_table: StringTable = .{},
|
||||
/// Debug information for wasm
|
||||
dwarf: ?Dwarf = null,
|
||||
|
||||
// *debug information* //
|
||||
/// Contains all bytes for the '.debug_info' section
|
||||
debug_info: std.ArrayListUnmanaged(u8) = .{},
|
||||
/// Contains all bytes for the '.debug_line' section
|
||||
debug_line: std.ArrayListUnmanaged(u8) = .{},
|
||||
/// Contains all bytes for the '.debug_abbrev' section
|
||||
debug_abbrev: std.ArrayListUnmanaged(u8) = .{},
|
||||
|
||||
// Output sections
|
||||
/// Output type section
|
||||
func_types: std.ArrayListUnmanaged(wasm.Type) = .{},
|
||||
@ -501,6 +509,10 @@ pub fn deinit(self: *Wasm) void {
|
||||
if (self.dwarf) |*dwarf| {
|
||||
dwarf.deinit();
|
||||
}
|
||||
|
||||
self.debug_info.deinit(gpa);
|
||||
self.debug_line.deinit(gpa);
|
||||
self.debug_abbrev.deinit(gpa);
|
||||
}
|
||||
|
||||
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
|
||||
&self.base,
|
||||
mod,
|
||||
decl,
|
||||
// Actual value will be written after relocation
|
||||
// Actual value will be written after relocation.
|
||||
// For Wasm, this is the offset relative to the code section
|
||||
// which isn't known until flush().
|
||||
0,
|
||||
code.len,
|
||||
&decl_state.?,
|
||||
@ -1016,10 +1030,12 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
|
||||
// segment indexes can be off by 1 due to also containing a segment
|
||||
// for the code section, so we must check if the existing segment
|
||||
// is larger than that of the code section, and substract the index by 1 in such case.
|
||||
const info_add = if (self.code_section_index) |idx| blk: {
|
||||
var info_add = if (self.code_section_index) |idx| blk: {
|
||||
if (idx < index) break :blk @as(u32, 1);
|
||||
break :blk 0;
|
||||
} else @as(u32, 0);
|
||||
if (self.debug_info_index != null) info_add += 1;
|
||||
if (self.debug_line_index != null) info_add += 1;
|
||||
symbol.index = index - info_add;
|
||||
// segment info already exists, so free its memory
|
||||
self.base.allocator.free(segment_name);
|
||||
@ -1320,11 +1336,8 @@ fn setupMemory(self: *Wasm) !void {
|
||||
}
|
||||
|
||||
var offset: u32 = @intCast(u32, memory_ptr);
|
||||
for (self.segments.items) |*segment, i| {
|
||||
// skip 'code' segments
|
||||
if (self.code_section_index) |index| {
|
||||
if (index == i) continue;
|
||||
}
|
||||
for (self.data_segments.values()) |segment_index| {
|
||||
const segment = &self.segments.items[segment_index];
|
||||
memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment);
|
||||
memory_ptr += segment.size;
|
||||
segment.offset = offset;
|
||||
@ -1588,6 +1601,7 @@ fn resetState(self: *Wasm) void {
|
||||
self.symbol_atom.clearRetainingCapacity();
|
||||
self.code_section_index = null;
|
||||
self.debug_info_index = null;
|
||||
self.debug_line_index = null;
|
||||
}
|
||||
|
||||
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
|
||||
try self.emitDataRelocations(file, arena, data_index, symbol_table);
|
||||
}
|
||||
} else if (!self.base.options.strip) {
|
||||
if (self.dwarf) |*dwarf| {
|
||||
if (self.debug_info_index != null) {
|
||||
_ = dwarf;
|
||||
try dwarf.writeDbgAbbrev(&self.base);
|
||||
try dwarf.writeDbgInfoHeader(&self.base, mod, 0, 0);
|
||||
try dwarf.writeDbgLineHeader(&self.base, mod);
|
||||
|
||||
try emitDebugSection(file, self.debug_info.items, ".debug_info");
|
||||
try emitDebugSection(file, self.debug_abbrev.items, ".debug_abbrev"); // TODO
|
||||
try emitDebugSection(file, self.debug_line.items, ".debug_line");
|
||||
try emitDebugSection(file, dwarf.strtab.items, ".debug_str");
|
||||
}
|
||||
}
|
||||
try self.emitNameSection(file, arena);
|
||||
}
|
||||
}
|
||||
|
||||
fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void {
|
||||
const header_offset = try reserveCustomSectionHeader(file);
|
||||
const writer = file.writer();
|
||||
try leb.writeULEB128(writer, @intCast(u32, name.len));
|
||||
try writer.writeAll(name);
|
||||
|
||||
try file.writevAll(&[_]std.os.iovec_const{.{
|
||||
.iov_base = data.ptr,
|
||||
.iov_len = data.len,
|
||||
}});
|
||||
const start = header_offset + 6 + name.len + getULEB128Size(@intCast(u32, name.len));
|
||||
log.debug("Emit debug section: '{s}' start=0x{x:0>8} end=0x{x:0>8}", .{ name, start, start + data.len });
|
||||
|
||||
try writeCustomSectionHeader(
|
||||
file,
|
||||
header_offset,
|
||||
@intCast(u32, (try file.getPos()) - header_offset - 6),
|
||||
);
|
||||
}
|
||||
|
||||
fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void {
|
||||
const Name = struct {
|
||||
index: u32,
|
||||
|
||||
@ -90,15 +90,6 @@ pub fn getFirst(self: *Atom) *Atom {
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/// Returns the atom for the given `symbol_index`.
|
||||
/// This can be either the `Atom` itself, or one of its locals.
|
||||
pub fn symbolAtom(self: *Atom, symbol_index: u32) *Atom {
|
||||
if (self.sym_index == symbol_index) return self;
|
||||
return for (self.locals.items) |*local_atom| {
|
||||
if (local_atom.sym_index == symbol_index) break local_atom;
|
||||
} else unreachable; // Used a symbol index not present in this atom or its children.
|
||||
}
|
||||
|
||||
/// Returns the location of the symbol that represents this `Atom`
|
||||
pub fn symbolLoc(self: Atom) Wasm.SymbolLoc {
|
||||
return .{ .file = self.file, .index = self.sym_index };
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user