aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/link/MachO.zig270
-rw-r--r--src/link/MachO/DebugSymbols.zig355
2 files changed, 342 insertions, 283 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
index 4cb0b016bc..2be7cb5199 100644
--- a/src/link/MachO.zig
+++ b/src/link/MachO.zig
@@ -11,8 +11,6 @@ const codegen = @import("../codegen.zig");
const aarch64 = @import("../codegen/aarch64.zig");
const math = std.math;
const mem = std.mem;
-const DW = std.dwarf;
-const leb = std.leb;
const trace = @import("../tracy.zig").trace;
const build_options = @import("build_options");
@@ -1119,128 +1117,30 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
var code_buffer = std.ArrayList(u8).init(self.base.allocator);
defer code_buffer.deinit();
- var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator);
- defer dbg_line_buffer.deinit();
-
- var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator);
- defer dbg_info_buffer.deinit();
-
- var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{};
+ var debug_buffers = if (self.d_sym) |*ds| try ds.initDeclDebugBuffers(self.base.allocator, module, decl) else null;
defer {
- var it = dbg_info_type_relocs.iterator();
- while (it.next()) |entry| {
- entry.value.relocs.deinit(self.base.allocator);
+ if (debug_buffers) |*dbg| {
+ dbg.dbg_line_buffer.deinit();
+ dbg.dbg_info_buffer.deinit();
+ var it = dbg.dbg_info_type_relocs.iterator();
+ while (it.next()) |entry| {
+ entry.value.relocs.deinit(self.base.allocator);
+ }
+ dbg.dbg_info_type_relocs.deinit(self.base.allocator);
}
- dbg_info_type_relocs.deinit(self.base.allocator);
}
const typed_value = decl.typed_value.most_recent.typed_value;
- const is_fn: bool = switch (typed_value.ty.zigTypeTag()) {
- .Fn => true,
- else => false,
- };
- if (is_fn) {
- const zir_dumps = if (std.builtin.is_test) &[0][]const u8{} else build_options.zir_dumps;
- if (zir_dumps.len != 0) {
- for (zir_dumps) |fn_name| {
- if (mem.eql(u8, mem.spanZ(decl.name), fn_name)) {
- std.debug.print("\n{}\n", .{decl.name});
- typed_value.val.cast(Value.Payload.Function).?.func.dump(module.*);
- }
- }
- }
-
- // For functions we need to add a prologue to the debug line program.
- try dbg_line_buffer.ensureCapacity(26);
-
- const line_off: u28 = blk: {
- if (decl.scope.cast(Module.Scope.Container)) |container_scope| {
- const tree = container_scope.file_scope.contents.tree;
- const file_ast_decls = tree.root_node.decls();
- // TODO Look into improving the performance here by adding a token-index-to-line
- // lookup table. Currently this involves scanning over the source code for newlines.
- const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
- const block = fn_proto.getBodyNode().?.castTag(.Block).?;
- const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
- break :blk @intCast(u28, line_delta);
- } else if (decl.scope.cast(Module.Scope.ZIRModule)) |zir_module| {
- const byte_off = zir_module.contents.module.decls[decl.src_index].inst.src;
- const line_delta = std.zig.lineDelta(zir_module.source.bytes, 0, byte_off);
- break :blk @intCast(u28, line_delta);
- } else {
- unreachable;
- }
- };
-
- dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
- DW.LNS_extended_op,
- @sizeOf(u64) + 1,
- DW.LNE_set_address,
- });
- // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`.
- assert(DebugSymbols.dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len);
- dbg_line_buffer.items.len += @sizeOf(u64);
-
- dbg_line_buffer.appendAssumeCapacity(DW.LNS_advance_line);
- // This is the "relocatable" relative line offset from the previous function's end curly
- // to this function's begin curly.
- assert(DebugSymbols.getRelocDbgLineOff() == dbg_line_buffer.items.len);
- // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
- leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off);
-
- dbg_line_buffer.appendAssumeCapacity(DW.LNS_set_file);
- assert(DebugSymbols.getRelocDbgFileIndex() == dbg_line_buffer.items.len);
- // Once we support more than one source file, this will have the ability to be more
- // than one possible value.
- const file_index = 1;
- leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index);
-
- // Emit a line for the begin curly with prologue_end=false. The codegen will
- // do the work of setting prologue_end=true and epilogue_begin=true.
- dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy);
-
- // .debug_info subprogram
- const decl_name_with_null = decl.name[0 .. mem.lenZ(decl.name) + 1];
- try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 27 + decl_name_with_null.len);
-
- const fn_ret_type = typed_value.ty.fnReturnType();
- const fn_ret_has_bits = fn_ret_type.hasCodeGenBits();
- if (fn_ret_has_bits) {
- dbg_info_buffer.appendAssumeCapacity(DebugSymbols.abbrev_subprogram);
- } else {
- dbg_info_buffer.appendAssumeCapacity(DebugSymbols.abbrev_subprogram_retvoid);
- }
- // These get overwritten after generating the machine code. These values are
- // "relocations" and have to be in this fixed place so that functions can be
- // moved in virtual address space.
- assert(DebugSymbols.dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len);
- dbg_info_buffer.items.len += @sizeOf(u64); // DW.AT_low_pc, DW.FORM_addr
- assert(DebugSymbols.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len);
- dbg_info_buffer.items.len += 4; // DW.AT_high_pc, DW.FORM_data4
- if (fn_ret_has_bits) {
- const gop = try dbg_info_type_relocs.getOrPut(self.base.allocator, fn_ret_type);
- if (!gop.found_existing) {
- gop.entry.value = .{
- .off = undefined,
- .relocs = .{},
- };
- }
- try gop.entry.value.relocs.append(self.base.allocator, @intCast(u32, dbg_info_buffer.items.len));
- dbg_info_buffer.items.len += 4; // DW.AT_type, DW.FORM_ref4
- }
- dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT_name, DW.FORM_string
- mem.writeIntLittle(u32, dbg_info_buffer.addManyAsArrayAssumeCapacity(4), line_off + 1); // DW.AT_decl_line, DW.FORM_data4
- dbg_info_buffer.appendAssumeCapacity(file_index); // DW.AT_decl_file, DW.FORM_data1
- } else {
- // TODO implement .debug_info for global variables
- }
- const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .{
- .dwarf = .{
- .dbg_line = &dbg_line_buffer,
- .dbg_info = &dbg_info_buffer,
- .dbg_info_type_relocs = &dbg_info_type_relocs,
- },
- });
+ const res = if (debug_buffers) |*dbg|
+ try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .{
+ .dwarf = .{
+ .dbg_line = &dbg.dbg_line_buffer,
+ .dbg_info = &dbg.dbg_info_buffer,
+ .dbg_info_type_relocs = &dbg.dbg_info_type_relocs,
+ },
+ })
+ else
+ try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .none);
const code = switch (res) {
.externally_managed => |x| x,
@@ -1328,132 +1228,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
const file_offset = text_section.offset + section_offset;
try self.base.file.?.pwriteAll(code, file_offset);
- const text_block = &decl.link.macho;
- // If the Decl is a function, we need to update the __debug_line program.
- if (is_fn) {
- // Perform the relocations based on vaddr.
- {
- const ptr = dbg_line_buffer.items[DebugSymbols.dbg_line_vaddr_reloc_index..][0..8];
- mem.writeIntLittle(u64, ptr, symbol.n_value);
- }
- {
- const ptr = dbg_info_buffer.items[DebugSymbols.dbg_info_low_pc_reloc_index..][0..8];
- mem.writeIntLittle(u64, ptr, symbol.n_value);
- }
- {
- const ptr = dbg_info_buffer.items[DebugSymbols.getRelocDbgInfoSubprogramHighPC()..][0..4];
- mem.writeIntLittle(u32, ptr, @intCast(u32, text_block.size));
- }
-
- try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });
-
- // Now we have the full contents and may allocate a region to store it.
-
- // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for
- // `TextBlock` and the .debug_info. If you are editing this logic, you
- // probably need to edit that logic too.
-
- const dwarf_segment = &self.d_sym.?.load_commands.items[self.d_sym.?.dwarf_segment_cmd_index.?].Segment;
- const debug_line_sect = &dwarf_segment.sections.items[self.d_sym.?.debug_line_section_index.?];
- const src_fn = &decl.fn_link.macho;
- src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
- if (self.d_sym.?.dbg_line_fn_last) |last| {
- if (src_fn.next) |next| {
- // Update existing function - non-last item.
- if (src_fn.off + src_fn.len + DebugSymbols.min_nop_size > next.off) {
- // It grew too big, so we move it to a new location.
- if (src_fn.prev) |prev| {
- _ = self.d_sym.?.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
- prev.next = src_fn.next;
- }
- next.prev = src_fn.prev;
- src_fn.next = null;
- // Populate where it used to be with NOPs.
- const file_pos = debug_line_sect.offset + src_fn.off;
- try self.d_sym.?.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos);
- // TODO Look at the free list before appending at the end.
- src_fn.prev = last;
- last.next = src_fn;
- self.d_sym.?.dbg_line_fn_last = src_fn;
-
- src_fn.off = last.off + (last.len * alloc_num / alloc_den);
- }
- } else if (src_fn.prev == null) {
- // Append new function.
- // TODO Look at the free list before appending at the end.
- src_fn.prev = last;
- last.next = src_fn;
- self.d_sym.?.dbg_line_fn_last = src_fn;
-
- src_fn.off = last.off + (last.len * alloc_num / alloc_den);
- }
- } else {
- // This is the first function of the Line Number Program.
- self.d_sym.?.dbg_line_fn_first = src_fn;
- self.d_sym.?.dbg_line_fn_last = src_fn;
-
- src_fn.off = self.d_sym.?.dbgLineNeededHeaderBytes(module) * alloc_num / alloc_den;
- }
-
- const last_src_fn = self.d_sym.?.dbg_line_fn_last.?;
- const needed_size = last_src_fn.off + last_src_fn.len;
- if (needed_size != debug_line_sect.size) {
- if (needed_size > dwarf_segment.allocatedSize(debug_line_sect.offset)) {
- const new_offset = dwarf_segment.findFreeSpace(needed_size, 1, null);
- const existing_size = last_src_fn.off;
-
- log.debug("moving __debug_line section: {} bytes from 0x{x} to 0x{x}", .{
- existing_size,
- debug_line_sect.offset,
- new_offset,
- });
-
- const amt = try self.d_sym.?.file.copyRangeAll(debug_line_sect.offset, self.d_sym.?.file, new_offset, existing_size);
- if (amt != existing_size) return error.InputOutput;
- debug_line_sect.offset = @intCast(u32, new_offset);
- debug_line_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
- }
- debug_line_sect.size = needed_size;
- self.d_sym.?.load_commands_dirty = true; // TODO look into making only the one section dirty
- self.d_sym.?.debug_line_header_dirty = true;
- }
- const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0;
- const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;
-
- // We only have support for one compilation unit so far, so the offsets are directly
- // from the .debug_line section.
- const file_pos = debug_line_sect.offset + src_fn.off;
- try self.d_sym.?.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos);
-
- // .debug_info - End the TAG_subprogram children.
- try dbg_info_buffer.append(0);
- }
-
- // Now we emit the .debug_info types of the Decl. These will count towards the size of
- // the buffer, so we have to do it before computing the offset, and we can't perform the actual
- // relocations yet.
- var it = dbg_info_type_relocs.iterator();
- while (it.next()) |entry| {
- entry.value.off = @intCast(u32, dbg_info_buffer.items.len);
- try self.d_sym.?.addDbgInfoType(entry.key, &dbg_info_buffer, self.base.options.target);
- }
-
- try self.d_sym.?.updateDeclDebugInfoAllocation(self.base.allocator, text_block, @intCast(u32, dbg_info_buffer.items.len));
-
- // Now that we have the offset assigned we can finally perform type relocations.
- it = dbg_info_type_relocs.iterator();
- while (it.next()) |entry| {
- for (entry.value.relocs.items) |off| {
- mem.writeIntLittle(
- u32,
- dbg_info_buffer.items[off..][0..4],
- text_block.dbg_info_off + entry.value.off,
- );
- }
+ if (debug_buffers) |*db| {
+ try self.d_sym.?.commitDeclDebugInfo(
+ self.base.allocator,
+ module,
+ decl,
+ db,
+ self.base.options.target,
+ );
}
- try self.d_sym.?.writeDeclDebugInfo(text_block, dbg_info_buffer.items);
-
// Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
try self.updateDeclExports(module, decl, decl_exports);
diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig
index a6ac2dfde6..90f8cf9a00 100644
--- a/src/link/MachO/DebugSymbols.zig
+++ b/src/link/MachO/DebugSymbols.zig
@@ -10,6 +10,7 @@ const DW = std.dwarf;
const leb = std.leb;
const Allocator = mem.Allocator;
+const build_options = @import("build_options");
const trace = @import("../../tracy.zig").trace;
const Module = @import("../../Module.zig");
const Type = @import("../../type.zig").Type;
@@ -87,21 +88,21 @@ debug_aranges_section_dirty: bool = false,
debug_info_header_dirty: bool = false,
debug_line_header_dirty: bool = false,
-pub const abbrev_compile_unit = 1;
-pub const abbrev_subprogram = 2;
-pub const abbrev_subprogram_retvoid = 3;
-pub const abbrev_base_type = 4;
-pub const abbrev_pad1 = 5;
-pub const abbrev_parameter = 6;
+const abbrev_compile_unit = 1;
+const abbrev_subprogram = 2;
+const abbrev_subprogram_retvoid = 3;
+const abbrev_base_type = 4;
+const abbrev_pad1 = 5;
+const abbrev_parameter = 6;
/// The reloc offset for the virtual address of a function in its Line Number Program.
/// Size is a virtual address integer.
-pub const dbg_line_vaddr_reloc_index = 3;
+const dbg_line_vaddr_reloc_index = 3;
/// The reloc offset for the virtual address of a function in its .debug_info TAG_subprogram.
/// Size is a virtual address integer.
-pub const dbg_info_low_pc_reloc_index = 1;
+const dbg_info_low_pc_reloc_index = 1;
-pub const min_nop_size = 2;
+const min_nop_size = 2;
/// You must call this function *after* `MachO.populateMissingMetadata()`
/// has been called to get a viable debug symbols output.
@@ -888,8 +889,304 @@ fn writeStringTable(self: *DebugSymbols) !void {
self.string_table_dirty = false;
}
+pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const Module.Decl) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const container_scope = decl.scope.cast(Module.Scope.Container).?;
+ const tree = container_scope.file_scope.contents.tree;
+ const file_ast_decls = tree.root_node.decls();
+ // TODO Look into improving the performance here by adding a token-index-to-line
+ // lookup table. Currently this involves scanning over the source code for newlines.
+ const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
+ const block = fn_proto.getBodyNode().?.castTag(.Block).?;
+ const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
+ const casted_line_off = @intCast(u28, line_delta);
+
+ const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
+ const shdr = &dwarf_segment.sections.items[self.debug_line_section_index.?];
+ const file_pos = shdr.offset + decl.fn_link.macho.off + getRelocDbgLineOff();
+ var data: [4]u8 = undefined;
+ leb.writeUnsignedFixed(4, &data, casted_line_off);
+ try self.file.pwriteAll(&data, file_pos);
+}
+
+pub const DeclDebugBuffers = struct {
+ dbg_line_buffer: std.ArrayList(u8),
+ dbg_info_buffer: std.ArrayList(u8),
+ dbg_info_type_relocs: link.File.DbgInfoTypeRelocsTable,
+};
+
+/// Caller owns the returned memory.
+pub fn initDeclDebugBuffers(
+ self: *DebugSymbols,
+ allocator: *Allocator,
+ module: *Module,
+ decl: *Module.Decl,
+) !DeclDebugBuffers {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ var dbg_line_buffer = std.ArrayList(u8).init(allocator);
+ var dbg_info_buffer = std.ArrayList(u8).init(allocator);
+ var dbg_info_type_relocs: link.File.DbgInfoTypeRelocsTable = .{};
+
+ const typed_value = decl.typed_value.most_recent.typed_value;
+ switch (typed_value.ty.zigTypeTag()) {
+ .Fn => {
+ const zir_dumps = if (std.builtin.is_test) &[0][]const u8{} else build_options.zir_dumps;
+ if (zir_dumps.len != 0) {
+ for (zir_dumps) |fn_name| {
+ if (mem.eql(u8, mem.spanZ(decl.name), fn_name)) {
+ std.debug.print("\n{}\n", .{decl.name});
+ typed_value.val.cast(Value.Payload.Function).?.func.dump(module.*);
+ }
+ }
+ }
+
+ // For functions we need to add a prologue to the debug line program.
+ try dbg_line_buffer.ensureCapacity(26);
+
+ const line_off: u28 = blk: {
+ if (decl.scope.cast(Module.Scope.Container)) |container_scope| {
+ const tree = container_scope.file_scope.contents.tree;
+ const file_ast_decls = tree.root_node.decls();
+ // TODO Look into improving the performance here by adding a token-index-to-line
+ // lookup table. Currently this involves scanning over the source code for newlines.
+ const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
+ const block = fn_proto.getBodyNode().?.castTag(.Block).?;
+ const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
+ break :blk @intCast(u28, line_delta);
+ } else if (decl.scope.cast(Module.Scope.ZIRModule)) |zir_module| {
+ const byte_off = zir_module.contents.module.decls[decl.src_index].inst.src;
+ const line_delta = std.zig.lineDelta(zir_module.source.bytes, 0, byte_off);
+ break :blk @intCast(u28, line_delta);
+ } else {
+ unreachable;
+ }
+ };
+
+ dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
+ DW.LNS_extended_op,
+ @sizeOf(u64) + 1,
+ DW.LNE_set_address,
+ });
+ // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`.
+ assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len);
+ dbg_line_buffer.items.len += @sizeOf(u64);
+
+ dbg_line_buffer.appendAssumeCapacity(DW.LNS_advance_line);
+ // This is the "relocatable" relative line offset from the previous function's end curly
+ // to this function's begin curly.
+ assert(getRelocDbgLineOff() == dbg_line_buffer.items.len);
+ // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
+ leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off);
+
+ dbg_line_buffer.appendAssumeCapacity(DW.LNS_set_file);
+ assert(getRelocDbgFileIndex() == dbg_line_buffer.items.len);
+ // Once we support more than one source file, this will have the ability to be more
+ // than one possible value.
+ const file_index = 1;
+ leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index);
+
+ // Emit a line for the begin curly with prologue_end=false. The codegen will
+ // do the work of setting prologue_end=true and epilogue_begin=true.
+ dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy);
+
+ // .debug_info subprogram
+ const decl_name_with_null = decl.name[0 .. mem.lenZ(decl.name) + 1];
+ try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 27 + decl_name_with_null.len);
+
+ const fn_ret_type = typed_value.ty.fnReturnType();
+ const fn_ret_has_bits = fn_ret_type.hasCodeGenBits();
+ if (fn_ret_has_bits) {
+ dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram);
+ } else {
+ dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid);
+ }
+ // These get overwritten after generating the machine code. These values are
+ // "relocations" and have to be in this fixed place so that functions can be
+ // moved in virtual address space.
+ assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len);
+ dbg_info_buffer.items.len += @sizeOf(u64); // DW.AT_low_pc, DW.FORM_addr
+ assert(getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len);
+ dbg_info_buffer.items.len += 4; // DW.AT_high_pc, DW.FORM_data4
+ if (fn_ret_has_bits) {
+ const gop = try dbg_info_type_relocs.getOrPut(allocator, fn_ret_type);
+ if (!gop.found_existing) {
+ gop.entry.value = .{
+ .off = undefined,
+ .relocs = .{},
+ };
+ }
+ try gop.entry.value.relocs.append(allocator, @intCast(u32, dbg_info_buffer.items.len));
+ dbg_info_buffer.items.len += 4; // DW.AT_type, DW.FORM_ref4
+ }
+ dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT_name, DW.FORM_string
+ mem.writeIntLittle(u32, dbg_info_buffer.addManyAsArrayAssumeCapacity(4), line_off + 1); // DW.AT_decl_line, DW.FORM_data4
+ dbg_info_buffer.appendAssumeCapacity(file_index); // DW.AT_decl_file, DW.FORM_data1
+ },
+ else => {
+ // TODO implement .debug_info for global variables
+ },
+ }
+
+ return DeclDebugBuffers{
+ .dbg_info_buffer = dbg_info_buffer,
+ .dbg_line_buffer = dbg_line_buffer,
+ .dbg_info_type_relocs = dbg_info_type_relocs,
+ };
+}
+
+pub fn commitDeclDebugInfo(
+ self: *DebugSymbols,
+ allocator: *Allocator,
+ module: *Module,
+ decl: *Module.Decl,
+ debug_buffers: *DeclDebugBuffers,
+ target: std.Target,
+) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ var dbg_line_buffer = &debug_buffers.dbg_line_buffer;
+ var dbg_info_buffer = &debug_buffers.dbg_info_buffer;
+ var dbg_info_type_relocs = &debug_buffers.dbg_info_type_relocs;
+
+ const symbol = self.base.local_symbols.items[decl.link.macho.local_sym_index];
+ const text_block = &decl.link.macho;
+ // If the Decl is a function, we need to update the __debug_line program.
+ const typed_value = decl.typed_value.most_recent.typed_value;
+ switch (typed_value.ty.zigTypeTag()) {
+ .Fn => {
+ // Perform the relocations based on vaddr.
+ {
+ const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8];
+ mem.writeIntLittle(u64, ptr, symbol.n_value);
+ }
+ {
+ const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8];
+ mem.writeIntLittle(u64, ptr, symbol.n_value);
+ }
+ {
+ const ptr = dbg_info_buffer.items[getRelocDbgInfoSubprogramHighPC()..][0..4];
+ mem.writeIntLittle(u32, ptr, @intCast(u32, text_block.size));
+ }
+
+ try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });
+
+ // Now we have the full contents and may allocate a region to store it.
+
+ // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for
+ // `TextBlock` and the .debug_info. If you are editing this logic, you
+ // probably need to edit that logic too.
+
+ const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
+ const debug_line_sect = &dwarf_segment.sections.items[self.debug_line_section_index.?];
+ const src_fn = &decl.fn_link.macho;
+ src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
+ if (self.dbg_line_fn_last) |last| {
+ if (src_fn.next) |next| {
+ // Update existing function - non-last item.
+ if (src_fn.off + src_fn.len + min_nop_size > next.off) {
+ // It grew too big, so we move it to a new location.
+ if (src_fn.prev) |prev| {
+ _ = self.dbg_line_fn_free_list.put(allocator, prev, {}) catch {};
+ prev.next = src_fn.next;
+ }
+ next.prev = src_fn.prev;
+ src_fn.next = null;
+ // Populate where it used to be with NOPs.
+ const file_pos = debug_line_sect.offset + src_fn.off;
+ try self.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos);
+ // TODO Look at the free list before appending at the end.
+ src_fn.prev = last;
+ last.next = src_fn;
+ self.dbg_line_fn_last = src_fn;
+
+ src_fn.off = last.off + (last.len * alloc_num / alloc_den);
+ }
+ } else if (src_fn.prev == null) {
+ // Append new function.
+ // TODO Look at the free list before appending at the end.
+ src_fn.prev = last;
+ last.next = src_fn;
+ self.dbg_line_fn_last = src_fn;
+
+ src_fn.off = last.off + (last.len * alloc_num / alloc_den);
+ }
+ } else {
+ // This is the first function of the Line Number Program.
+ self.dbg_line_fn_first = src_fn;
+ self.dbg_line_fn_last = src_fn;
+
+ src_fn.off = self.dbgLineNeededHeaderBytes(module) * alloc_num / alloc_den;
+ }
+
+ const last_src_fn = self.dbg_line_fn_last.?;
+ const needed_size = last_src_fn.off + last_src_fn.len;
+ if (needed_size != debug_line_sect.size) {
+ if (needed_size > dwarf_segment.allocatedSize(debug_line_sect.offset)) {
+ const new_offset = dwarf_segment.findFreeSpace(needed_size, 1, null);
+ const existing_size = last_src_fn.off;
+
+ log.debug("moving __debug_line section: {} bytes from 0x{x} to 0x{x}", .{
+ existing_size,
+ debug_line_sect.offset,
+ new_offset,
+ });
+
+ const amt = try self.file.copyRangeAll(debug_line_sect.offset, self.file, new_offset, existing_size);
+ if (amt != existing_size) return error.InputOutput;
+ debug_line_sect.offset = @intCast(u32, new_offset);
+ debug_line_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
+ }
+ debug_line_sect.size = needed_size;
+ self.load_commands_dirty = true; // TODO look into making only the one section dirty
+ self.debug_line_header_dirty = true;
+ }
+ const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0;
+ const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;
+
+ // We only have support for one compilation unit so far, so the offsets are directly
+ // from the .debug_line section.
+ const file_pos = debug_line_sect.offset + src_fn.off;
+ try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos);
+
+ // .debug_info - End the TAG_subprogram children.
+ try dbg_info_buffer.append(0);
+ },
+ else => {},
+ }
+
+ // Now we emit the .debug_info types of the Decl. These will count towards the size of
+ // the buffer, so we have to do it before computing the offset, and we can't perform the actual
+ // relocations yet.
+ var it = dbg_info_type_relocs.iterator();
+ while (it.next()) |entry| {
+ entry.value.off = @intCast(u32, dbg_info_buffer.items.len);
+ try self.addDbgInfoType(entry.key, dbg_info_buffer, target);
+ }
+
+ try self.updateDeclDebugInfoAllocation(allocator, text_block, @intCast(u32, dbg_info_buffer.items.len));
+
+ // Now that we have the offset assigned we can finally perform type relocations.
+ it = dbg_info_type_relocs.iterator();
+ while (it.next()) |entry| {
+ for (entry.value.relocs.items) |off| {
+ mem.writeIntLittle(
+ u32,
+ dbg_info_buffer.items[off..][0..4],
+ text_block.dbg_info_off + entry.value.off,
+ );
+ }
+ }
+
+ try self.writeDeclDebugInfo(text_block, dbg_info_buffer.items);
+}
+
/// Asserts the type has codegen bits.
-pub fn addDbgInfoType(
+fn addDbgInfoType(
self: *DebugSymbols,
ty: Type,
dbg_info_buffer: *std.ArrayList(u8),
@@ -931,7 +1228,7 @@ pub fn addDbgInfoType(
}
}
-pub fn updateDeclDebugInfoAllocation(
+fn updateDeclDebugInfoAllocation(
self: *DebugSymbols,
allocator: *Allocator,
text_block: *TextBlock,
@@ -986,7 +1283,7 @@ pub fn updateDeclDebugInfoAllocation(
}
}
-pub fn writeDeclDebugInfo(self: *DebugSymbols, text_block: *TextBlock, dbg_info_buf: []const u8) !void {
+fn writeDeclDebugInfo(self: *DebugSymbols, text_block: *TextBlock, dbg_info_buf: []const u8) !void {
const tracy = trace(@src());
defer tracy.end();
@@ -1057,19 +1354,19 @@ fn makeDebugString(self: *DebugSymbols, allocator: *Allocator, bytes: []const u8
/// The reloc offset for the line offset of a function from the previous function's line.
/// It's a fixed-size 4-byte ULEB128.
-pub fn getRelocDbgLineOff() usize {
+fn getRelocDbgLineOff() usize {
return dbg_line_vaddr_reloc_index + @sizeOf(u64) + 1;
}
-pub fn getRelocDbgFileIndex() usize {
+fn getRelocDbgFileIndex() usize {
return getRelocDbgLineOff() + 5;
}
-pub fn getRelocDbgInfoSubprogramHighPC() u32 {
+fn getRelocDbgInfoSubprogramHighPC() u32 {
return dbg_info_low_pc_reloc_index + @sizeOf(u64);
}
-pub fn dbgLineNeededHeaderBytes(self: DebugSymbols, module: *Module) u32 {
+fn dbgLineNeededHeaderBytes(self: DebugSymbols, module: *Module) u32 {
const directory_entry_format_count = 1;
const file_name_entry_format_count = 1;
const directory_count = 1;
@@ -1092,7 +1389,7 @@ fn dbgInfoNeededHeaderBytes(self: DebugSymbols) u32 {
/// are less than 126,976 bytes (if this limit is ever reached, this function can be
/// improved to make more than one pwritev call, or the limit can be raised by a fixed
/// amount by increasing the length of `vecs`).
-pub fn pwriteDbgLineNops(
+fn pwriteDbgLineNops(
self: *DebugSymbols,
prev_padding_size: usize,
buf: []const u8,
@@ -1170,7 +1467,7 @@ pub fn pwriteDbgLineNops(
/// Writes to the file a buffer, prefixed and suffixed by the specified number of
/// bytes of padding.
-pub fn pwriteDbgInfoNops(
+fn pwriteDbgInfoNops(
self: *DebugSymbols,
prev_padding_size: usize,
buf: []const u8,
@@ -1239,25 +1536,3 @@ pub fn pwriteDbgInfoNops(
try self.file.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
}
-
-pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const Module.Decl) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const container_scope = decl.scope.cast(Module.Scope.Container).?;
- const tree = container_scope.file_scope.contents.tree;
- const file_ast_decls = tree.root_node.decls();
- // TODO Look into improving the performance here by adding a token-index-to-line
- // lookup table. Currently this involves scanning over the source code for newlines.
- const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
- const block = fn_proto.getBodyNode().?.castTag(.Block).?;
- const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
- const casted_line_off = @intCast(u28, line_delta);
-
- const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
- const shdr = &dwarf_segment.sections.items[self.debug_line_section_index.?];
- const file_pos = shdr.offset + decl.fn_link.macho.off + getRelocDbgLineOff();
- var data: [4]u8 = undefined;
- leb.writeUnsignedFixed(4, &data, casted_line_off);
- try self.file.pwriteAll(&data, file_pos);
-}