authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-30 22:18:21+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-31 10:19:04+01:00
logea4ff34e13d9768eedcc9add608be2366d6ad030
treef7b031444e312b77407eaa46bcd7f32b3c9ebf7b
parentd189614647d6c4195081e40f08f92e15db153592

macho: put all DWARF-related logic into DebugSymbols


2 files changed, 342 insertions(+), 283 deletions(-)

src/link/MachO.zig+27-243
......@@ -11,8 +11,6 @@ const codegen = @import("../codegen.zig");
1111const aarch64 = @import("../codegen/aarch64.zig");
1212const math = std.math;
1313const mem = std.mem;
14const DW = std.dwarf;
15const leb = std.leb;
1614
1715const trace = @import("../tracy.zig").trace;
1816const build_options = @import("build_options");
......@@ -1119,128 +1117,30 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
11191117 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
11201118 defer code_buffer.deinit();
11211119
1122 var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator);
1123 defer dbg_line_buffer.deinit();
1124
1125 var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator);
1126 defer dbg_info_buffer.deinit();
1127
1128 var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{};
1120 var debug_buffers = if (self.d_sym) |*ds| try ds.initDeclDebugBuffers(self.base.allocator, module, decl) else null;
11291121 defer {
1130 var it = dbg_info_type_relocs.iterator();
1131 while (it.next()) |entry| {
1132 entry.value.relocs.deinit(self.base.allocator);
1122 if (debug_buffers) |*dbg| {
1123 dbg.dbg_line_buffer.deinit();
1124 dbg.dbg_info_buffer.deinit();
1125 var it = dbg.dbg_info_type_relocs.iterator();
1126 while (it.next()) |entry| {
1127 entry.value.relocs.deinit(self.base.allocator);
1128 }
1129 dbg.dbg_info_type_relocs.deinit(self.base.allocator);
11331130 }
1134 dbg_info_type_relocs.deinit(self.base.allocator);
11351131 }
11361132
11371133 const typed_value = decl.typed_value.most_recent.typed_value;
1138 const is_fn: bool = switch (typed_value.ty.zigTypeTag()) {
1139 .Fn => true,
1140 else => false,
1141 };
1142 if (is_fn) {
1143 const zir_dumps = if (std.builtin.is_test) &[0][]const u8{} else build_options.zir_dumps;
1144 if (zir_dumps.len != 0) {
1145 for (zir_dumps) |fn_name| {
1146 if (mem.eql(u8, mem.spanZ(decl.name), fn_name)) {
1147 std.debug.print("\n{}\n", .{decl.name});
1148 typed_value.val.cast(Value.Payload.Function).?.func.dump(module.*);
1149 }
1150 }
1151 }
1152
1153 // For functions we need to add a prologue to the debug line program.
1154 try dbg_line_buffer.ensureCapacity(26);
1155
1156 const line_off: u28 = blk: {
1157 if (decl.scope.cast(Module.Scope.Container)) |container_scope| {
1158 const tree = container_scope.file_scope.contents.tree;
1159 const file_ast_decls = tree.root_node.decls();
1160 // TODO Look into improving the performance here by adding a token-index-to-line
1161 // lookup table. Currently this involves scanning over the source code for newlines.
1162 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
1163 const block = fn_proto.getBodyNode().?.castTag(.Block).?;
1164 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
1165 break :blk @intCast(u28, line_delta);
1166 } else if (decl.scope.cast(Module.Scope.ZIRModule)) |zir_module| {
1167 const byte_off = zir_module.contents.module.decls[decl.src_index].inst.src;
1168 const line_delta = std.zig.lineDelta(zir_module.source.bytes, 0, byte_off);
1169 break :blk @intCast(u28, line_delta);
1170 } else {
1171 unreachable;
1172 }
1173 };
1174
1175 dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
1176 DW.LNS_extended_op,
1177 @sizeOf(u64) + 1,
1178 DW.LNE_set_address,
1179 });
1180 // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`.
1181 assert(DebugSymbols.dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len);
1182 dbg_line_buffer.items.len += @sizeOf(u64);
1183
1184 dbg_line_buffer.appendAssumeCapacity(DW.LNS_advance_line);
1185 // This is the "relocatable" relative line offset from the previous function's end curly
1186 // to this function's begin curly.
1187 assert(DebugSymbols.getRelocDbgLineOff() == dbg_line_buffer.items.len);
1188 // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
1189 leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off);
1190
1191 dbg_line_buffer.appendAssumeCapacity(DW.LNS_set_file);
1192 assert(DebugSymbols.getRelocDbgFileIndex() == dbg_line_buffer.items.len);
1193 // Once we support more than one source file, this will have the ability to be more
1194 // than one possible value.
1195 const file_index = 1;
1196 leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index);
1197
1198 // Emit a line for the begin curly with prologue_end=false. The codegen will
1199 // do the work of setting prologue_end=true and epilogue_begin=true.
1200 dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy);
1201
1202 // .debug_info subprogram
1203 const decl_name_with_null = decl.name[0 .. mem.lenZ(decl.name) + 1];
1204 try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 27 + decl_name_with_null.len);
1205
1206 const fn_ret_type = typed_value.ty.fnReturnType();
1207 const fn_ret_has_bits = fn_ret_type.hasCodeGenBits();
1208 if (fn_ret_has_bits) {
1209 dbg_info_buffer.appendAssumeCapacity(DebugSymbols.abbrev_subprogram);
1210 } else {
1211 dbg_info_buffer.appendAssumeCapacity(DebugSymbols.abbrev_subprogram_retvoid);
1212 }
1213 // These get overwritten after generating the machine code. These values are
1214 // "relocations" and have to be in this fixed place so that functions can be
1215 // moved in virtual address space.
1216 assert(DebugSymbols.dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len);
1217 dbg_info_buffer.items.len += @sizeOf(u64); // DW.AT_low_pc, DW.FORM_addr
1218 assert(DebugSymbols.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len);
1219 dbg_info_buffer.items.len += 4; // DW.AT_high_pc, DW.FORM_data4
1220 if (fn_ret_has_bits) {
1221 const gop = try dbg_info_type_relocs.getOrPut(self.base.allocator, fn_ret_type);
1222 if (!gop.found_existing) {
1223 gop.entry.value = .{
1224 .off = undefined,
1225 .relocs = .{},
1226 };
1227 }
1228 try gop.entry.value.relocs.append(self.base.allocator, @intCast(u32, dbg_info_buffer.items.len));
1229 dbg_info_buffer.items.len += 4; // DW.AT_type, DW.FORM_ref4
1230 }
1231 dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT_name, DW.FORM_string
1232 mem.writeIntLittle(u32, dbg_info_buffer.addManyAsArrayAssumeCapacity(4), line_off + 1); // DW.AT_decl_line, DW.FORM_data4
1233 dbg_info_buffer.appendAssumeCapacity(file_index); // DW.AT_decl_file, DW.FORM_data1
1234 } else {
1235 // TODO implement .debug_info for global variables
1236 }
1237 const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .{
1238 .dwarf = .{
1239 .dbg_line = &dbg_line_buffer,
1240 .dbg_info = &dbg_info_buffer,
1241 .dbg_info_type_relocs = &dbg_info_type_relocs,
1242 },
1243 });
1134 const res = if (debug_buffers) |*dbg|
1135 try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .{
1136 .dwarf = .{
1137 .dbg_line = &dbg.dbg_line_buffer,
1138 .dbg_info = &dbg.dbg_info_buffer,
1139 .dbg_info_type_relocs = &dbg.dbg_info_type_relocs,
1140 },
1141 })
1142 else
1143 try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .none);
12441144
12451145 const code = switch (res) {
12461146 .externally_managed => |x| x,
......@@ -1328,132 +1228,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
13281228 const file_offset = text_section.offset + section_offset;
13291229 try self.base.file.?.pwriteAll(code, file_offset);
13301230
1331 const text_block = &decl.link.macho;
1332 // If the Decl is a function, we need to update the __debug_line program.
1333 if (is_fn) {
1334 // Perform the relocations based on vaddr.
1335 {
1336 const ptr = dbg_line_buffer.items[DebugSymbols.dbg_line_vaddr_reloc_index..][0..8];
1337 mem.writeIntLittle(u64, ptr, symbol.n_value);
1338 }
1339 {
1340 const ptr = dbg_info_buffer.items[DebugSymbols.dbg_info_low_pc_reloc_index..][0..8];
1341 mem.writeIntLittle(u64, ptr, symbol.n_value);
1342 }
1343 {
1344 const ptr = dbg_info_buffer.items[DebugSymbols.getRelocDbgInfoSubprogramHighPC()..][0..4];
1345 mem.writeIntLittle(u32, ptr, @intCast(u32, text_block.size));
1346 }
1347
1348 try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });
1349
1350 // Now we have the full contents and may allocate a region to store it.
1351
1352 // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for
1353 // `TextBlock` and the .debug_info. If you are editing this logic, you
1354 // probably need to edit that logic too.
1355
1356 const dwarf_segment = &self.d_sym.?.load_commands.items[self.d_sym.?.dwarf_segment_cmd_index.?].Segment;
1357 const debug_line_sect = &dwarf_segment.sections.items[self.d_sym.?.debug_line_section_index.?];
1358 const src_fn = &decl.fn_link.macho;
1359 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
1360 if (self.d_sym.?.dbg_line_fn_last) |last| {
1361 if (src_fn.next) |next| {
1362 // Update existing function - non-last item.
1363 if (src_fn.off + src_fn.len + DebugSymbols.min_nop_size > next.off) {
1364 // It grew too big, so we move it to a new location.
1365 if (src_fn.prev) |prev| {
1366 _ = self.d_sym.?.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
1367 prev.next = src_fn.next;
1368 }
1369 next.prev = src_fn.prev;
1370 src_fn.next = null;
1371 // Populate where it used to be with NOPs.
1372 const file_pos = debug_line_sect.offset + src_fn.off;
1373 try self.d_sym.?.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos);
1374 // TODO Look at the free list before appending at the end.
1375 src_fn.prev = last;
1376 last.next = src_fn;
1377 self.d_sym.?.dbg_line_fn_last = src_fn;
1378
1379 src_fn.off = last.off + (last.len * alloc_num / alloc_den);
1380 }
1381 } else if (src_fn.prev == null) {
1382 // Append new function.
1383 // TODO Look at the free list before appending at the end.
1384 src_fn.prev = last;
1385 last.next = src_fn;
1386 self.d_sym.?.dbg_line_fn_last = src_fn;
1387
1388 src_fn.off = last.off + (last.len * alloc_num / alloc_den);
1389 }
1390 } else {
1391 // This is the first function of the Line Number Program.
1392 self.d_sym.?.dbg_line_fn_first = src_fn;
1393 self.d_sym.?.dbg_line_fn_last = src_fn;
1394
1395 src_fn.off = self.d_sym.?.dbgLineNeededHeaderBytes(module) * alloc_num / alloc_den;
1396 }
1397
1398 const last_src_fn = self.d_sym.?.dbg_line_fn_last.?;
1399 const needed_size = last_src_fn.off + last_src_fn.len;
1400 if (needed_size != debug_line_sect.size) {
1401 if (needed_size > dwarf_segment.allocatedSize(debug_line_sect.offset)) {
1402 const new_offset = dwarf_segment.findFreeSpace(needed_size, 1, null);
1403 const existing_size = last_src_fn.off;
1404
1405 log.debug("moving __debug_line section: {} bytes from 0x{x} to 0x{x}", .{
1406 existing_size,
1407 debug_line_sect.offset,
1408 new_offset,
1409 });
1410
1411 const amt = try self.d_sym.?.file.copyRangeAll(debug_line_sect.offset, self.d_sym.?.file, new_offset, existing_size);
1412 if (amt != existing_size) return error.InputOutput;
1413 debug_line_sect.offset = @intCast(u32, new_offset);
1414 debug_line_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
1415 }
1416 debug_line_sect.size = needed_size;
1417 self.d_sym.?.load_commands_dirty = true; // TODO look into making only the one section dirty
1418 self.d_sym.?.debug_line_header_dirty = true;
1419 }
1420 const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0;
1421 const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;
1422
1423 // We only have support for one compilation unit so far, so the offsets are directly
1424 // from the .debug_line section.
1425 const file_pos = debug_line_sect.offset + src_fn.off;
1426 try self.d_sym.?.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos);
1427
1428 // .debug_info - End the TAG_subprogram children.
1429 try dbg_info_buffer.append(0);
1430 }
1431
1432 // Now we emit the .debug_info types of the Decl. These will count towards the size of
1433 // the buffer, so we have to do it before computing the offset, and we can't perform the actual
1434 // relocations yet.
1435 var it = dbg_info_type_relocs.iterator();
1436 while (it.next()) |entry| {
1437 entry.value.off = @intCast(u32, dbg_info_buffer.items.len);
1438 try self.d_sym.?.addDbgInfoType(entry.key, &dbg_info_buffer, self.base.options.target);
1439 }
1440
1441 try self.d_sym.?.updateDeclDebugInfoAllocation(self.base.allocator, text_block, @intCast(u32, dbg_info_buffer.items.len));
1442
1443 // Now that we have the offset assigned we can finally perform type relocations.
1444 it = dbg_info_type_relocs.iterator();
1445 while (it.next()) |entry| {
1446 for (entry.value.relocs.items) |off| {
1447 mem.writeIntLittle(
1448 u32,
1449 dbg_info_buffer.items[off..][0..4],
1450 text_block.dbg_info_off + entry.value.off,
1451 );
1452 }
1231 if (debug_buffers) |*db| {
1232 try self.d_sym.?.commitDeclDebugInfo(
1233 self.base.allocator,
1234 module,
1235 decl,
1236 db,
1237 self.base.options.target,
1238 );
14531239 }
14541240
1455 try self.d_sym.?.writeDeclDebugInfo(text_block, dbg_info_buffer.items);
1456
14571241 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
14581242 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
14591243 try self.updateDeclExports(module, decl, decl_exports);
src/link/MachO/DebugSymbols.zig+315-40
......@@ -10,6 +10,7 @@ const DW = std.dwarf;
1010const leb = std.leb;
1111const Allocator = mem.Allocator;
1212
13const build_options = @import("build_options");
1314const trace = @import("../../tracy.zig").trace;
1415const Module = @import("../../Module.zig");
1516const Type = @import("../../type.zig").Type;
......@@ -87,21 +88,21 @@ debug_aranges_section_dirty: bool = false,
8788debug_info_header_dirty: bool = false,
8889debug_line_header_dirty: bool = false,
8990
90pub const abbrev_compile_unit = 1;
91pub const abbrev_subprogram = 2;
92pub const abbrev_subprogram_retvoid = 3;
93pub const abbrev_base_type = 4;
94pub const abbrev_pad1 = 5;
95pub const abbrev_parameter = 6;
91const abbrev_compile_unit = 1;
92const abbrev_subprogram = 2;
93const abbrev_subprogram_retvoid = 3;
94const abbrev_base_type = 4;
95const abbrev_pad1 = 5;
96const abbrev_parameter = 6;
9697
9798/// The reloc offset for the virtual address of a function in its Line Number Program.
9899/// Size is a virtual address integer.
99pub const dbg_line_vaddr_reloc_index = 3;
100const dbg_line_vaddr_reloc_index = 3;
100101/// The reloc offset for the virtual address of a function in its .debug_info TAG_subprogram.
101102/// Size is a virtual address integer.
102pub const dbg_info_low_pc_reloc_index = 1;
103const dbg_info_low_pc_reloc_index = 1;
103104
104pub const min_nop_size = 2;
105const min_nop_size = 2;
105106
106107/// You must call this function *after* `MachO.populateMissingMetadata()`
107108/// has been called to get a viable debug symbols output.
......@@ -888,8 +889,304 @@ fn writeStringTable(self: *DebugSymbols) !void {
888889 self.string_table_dirty = false;
889890}
890891
892pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const Module.Decl) !void {
893 const tracy = trace(@src());
894 defer tracy.end();
895
896 const container_scope = decl.scope.cast(Module.Scope.Container).?;
897 const tree = container_scope.file_scope.contents.tree;
898 const file_ast_decls = tree.root_node.decls();
899 // TODO Look into improving the performance here by adding a token-index-to-line
900 // lookup table. Currently this involves scanning over the source code for newlines.
901 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
902 const block = fn_proto.getBodyNode().?.castTag(.Block).?;
903 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
904 const casted_line_off = @intCast(u28, line_delta);
905
906 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
907 const shdr = &dwarf_segment.sections.items[self.debug_line_section_index.?];
908 const file_pos = shdr.offset + decl.fn_link.macho.off + getRelocDbgLineOff();
909 var data: [4]u8 = undefined;
910 leb.writeUnsignedFixed(4, &data, casted_line_off);
911 try self.file.pwriteAll(&data, file_pos);
912}
913
914pub const DeclDebugBuffers = struct {
915 dbg_line_buffer: std.ArrayList(u8),
916 dbg_info_buffer: std.ArrayList(u8),
917 dbg_info_type_relocs: link.File.DbgInfoTypeRelocsTable,
918};
919
920/// Caller owns the returned memory.
921pub fn initDeclDebugBuffers(
922 self: *DebugSymbols,
923 allocator: *Allocator,
924 module: *Module,
925 decl: *Module.Decl,
926) !DeclDebugBuffers {
927 const tracy = trace(@src());
928 defer tracy.end();
929
930 var dbg_line_buffer = std.ArrayList(u8).init(allocator);
931 var dbg_info_buffer = std.ArrayList(u8).init(allocator);
932 var dbg_info_type_relocs: link.File.DbgInfoTypeRelocsTable = .{};
933
934 const typed_value = decl.typed_value.most_recent.typed_value;
935 switch (typed_value.ty.zigTypeTag()) {
936 .Fn => {
937 const zir_dumps = if (std.builtin.is_test) &[0][]const u8{} else build_options.zir_dumps;
938 if (zir_dumps.len != 0) {
939 for (zir_dumps) |fn_name| {
940 if (mem.eql(u8, mem.spanZ(decl.name), fn_name)) {
941 std.debug.print("\n{}\n", .{decl.name});
942 typed_value.val.cast(Value.Payload.Function).?.func.dump(module.*);
943 }
944 }
945 }
946
947 // For functions we need to add a prologue to the debug line program.
948 try dbg_line_buffer.ensureCapacity(26);
949
950 const line_off: u28 = blk: {
951 if (decl.scope.cast(Module.Scope.Container)) |container_scope| {
952 const tree = container_scope.file_scope.contents.tree;
953 const file_ast_decls = tree.root_node.decls();
954 // TODO Look into improving the performance here by adding a token-index-to-line
955 // lookup table. Currently this involves scanning over the source code for newlines.
956 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
957 const block = fn_proto.getBodyNode().?.castTag(.Block).?;
958 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
959 break :blk @intCast(u28, line_delta);
960 } else if (decl.scope.cast(Module.Scope.ZIRModule)) |zir_module| {
961 const byte_off = zir_module.contents.module.decls[decl.src_index].inst.src;
962 const line_delta = std.zig.lineDelta(zir_module.source.bytes, 0, byte_off);
963 break :blk @intCast(u28, line_delta);
964 } else {
965 unreachable;
966 }
967 };
968
969 dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
970 DW.LNS_extended_op,
971 @sizeOf(u64) + 1,
972 DW.LNE_set_address,
973 });
974 // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`.
975 assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len);
976 dbg_line_buffer.items.len += @sizeOf(u64);
977
978 dbg_line_buffer.appendAssumeCapacity(DW.LNS_advance_line);
979 // This is the "relocatable" relative line offset from the previous function's end curly
980 // to this function's begin curly.
981 assert(getRelocDbgLineOff() == dbg_line_buffer.items.len);
982 // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
983 leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off);
984
985 dbg_line_buffer.appendAssumeCapacity(DW.LNS_set_file);
986 assert(getRelocDbgFileIndex() == dbg_line_buffer.items.len);
987 // Once we support more than one source file, this will have the ability to be more
988 // than one possible value.
989 const file_index = 1;
990 leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index);
991
992 // Emit a line for the begin curly with prologue_end=false. The codegen will
993 // do the work of setting prologue_end=true and epilogue_begin=true.
994 dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy);
995
996 // .debug_info subprogram
997 const decl_name_with_null = decl.name[0 .. mem.lenZ(decl.name) + 1];
998 try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 27 + decl_name_with_null.len);
999
1000 const fn_ret_type = typed_value.ty.fnReturnType();
1001 const fn_ret_has_bits = fn_ret_type.hasCodeGenBits();
1002 if (fn_ret_has_bits) {
1003 dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram);
1004 } else {
1005 dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid);
1006 }
1007 // These get overwritten after generating the machine code. These values are
1008 // "relocations" and have to be in this fixed place so that functions can be
1009 // moved in virtual address space.
1010 assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len);
1011 dbg_info_buffer.items.len += @sizeOf(u64); // DW.AT_low_pc, DW.FORM_addr
1012 assert(getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len);
1013 dbg_info_buffer.items.len += 4; // DW.AT_high_pc, DW.FORM_data4
1014 if (fn_ret_has_bits) {
1015 const gop = try dbg_info_type_relocs.getOrPut(allocator, fn_ret_type);
1016 if (!gop.found_existing) {
1017 gop.entry.value = .{
1018 .off = undefined,
1019 .relocs = .{},
1020 };
1021 }
1022 try gop.entry.value.relocs.append(allocator, @intCast(u32, dbg_info_buffer.items.len));
1023 dbg_info_buffer.items.len += 4; // DW.AT_type, DW.FORM_ref4
1024 }
1025 dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT_name, DW.FORM_string
1026 mem.writeIntLittle(u32, dbg_info_buffer.addManyAsArrayAssumeCapacity(4), line_off + 1); // DW.AT_decl_line, DW.FORM_data4
1027 dbg_info_buffer.appendAssumeCapacity(file_index); // DW.AT_decl_file, DW.FORM_data1
1028 },
1029 else => {
1030 // TODO implement .debug_info for global variables
1031 },
1032 }
1033
1034 return DeclDebugBuffers{
1035 .dbg_info_buffer = dbg_info_buffer,
1036 .dbg_line_buffer = dbg_line_buffer,
1037 .dbg_info_type_relocs = dbg_info_type_relocs,
1038 };
1039}
1040
1041pub fn commitDeclDebugInfo(
1042 self: *DebugSymbols,
1043 allocator: *Allocator,
1044 module: *Module,
1045 decl: *Module.Decl,
1046 debug_buffers: *DeclDebugBuffers,
1047 target: std.Target,
1048) !void {
1049 const tracy = trace(@src());
1050 defer tracy.end();
1051
1052 var dbg_line_buffer = &debug_buffers.dbg_line_buffer;
1053 var dbg_info_buffer = &debug_buffers.dbg_info_buffer;
1054 var dbg_info_type_relocs = &debug_buffers.dbg_info_type_relocs;
1055
1056 const symbol = self.base.local_symbols.items[decl.link.macho.local_sym_index];
1057 const text_block = &decl.link.macho;
1058 // If the Decl is a function, we need to update the __debug_line program.
1059 const typed_value = decl.typed_value.most_recent.typed_value;
1060 switch (typed_value.ty.zigTypeTag()) {
1061 .Fn => {
1062 // Perform the relocations based on vaddr.
1063 {
1064 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8];
1065 mem.writeIntLittle(u64, ptr, symbol.n_value);
1066 }
1067 {
1068 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8];
1069 mem.writeIntLittle(u64, ptr, symbol.n_value);
1070 }
1071 {
1072 const ptr = dbg_info_buffer.items[getRelocDbgInfoSubprogramHighPC()..][0..4];
1073 mem.writeIntLittle(u32, ptr, @intCast(u32, text_block.size));
1074 }
1075
1076 try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });
1077
1078 // Now we have the full contents and may allocate a region to store it.
1079
1080 // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for
1081 // `TextBlock` and the .debug_info. If you are editing this logic, you
1082 // probably need to edit that logic too.
1083
1084 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
1085 const debug_line_sect = &dwarf_segment.sections.items[self.debug_line_section_index.?];
1086 const src_fn = &decl.fn_link.macho;
1087 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
1088 if (self.dbg_line_fn_last) |last| {
1089 if (src_fn.next) |next| {
1090 // Update existing function - non-last item.
1091 if (src_fn.off + src_fn.len + min_nop_size > next.off) {
1092 // It grew too big, so we move it to a new location.
1093 if (src_fn.prev) |prev| {
1094 _ = self.dbg_line_fn_free_list.put(allocator, prev, {}) catch {};
1095 prev.next = src_fn.next;
1096 }
1097 next.prev = src_fn.prev;
1098 src_fn.next = null;
1099 // Populate where it used to be with NOPs.
1100 const file_pos = debug_line_sect.offset + src_fn.off;
1101 try self.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos);
1102 // TODO Look at the free list before appending at the end.
1103 src_fn.prev = last;
1104 last.next = src_fn;
1105 self.dbg_line_fn_last = src_fn;
1106
1107 src_fn.off = last.off + (last.len * alloc_num / alloc_den);
1108 }
1109 } else if (src_fn.prev == null) {
1110 // Append new function.
1111 // TODO Look at the free list before appending at the end.
1112 src_fn.prev = last;
1113 last.next = src_fn;
1114 self.dbg_line_fn_last = src_fn;
1115
1116 src_fn.off = last.off + (last.len * alloc_num / alloc_den);
1117 }
1118 } else {
1119 // This is the first function of the Line Number Program.
1120 self.dbg_line_fn_first = src_fn;
1121 self.dbg_line_fn_last = src_fn;
1122
1123 src_fn.off = self.dbgLineNeededHeaderBytes(module) * alloc_num / alloc_den;
1124 }
1125
1126 const last_src_fn = self.dbg_line_fn_last.?;
1127 const needed_size = last_src_fn.off + last_src_fn.len;
1128 if (needed_size != debug_line_sect.size) {
1129 if (needed_size > dwarf_segment.allocatedSize(debug_line_sect.offset)) {
1130 const new_offset = dwarf_segment.findFreeSpace(needed_size, 1, null);
1131 const existing_size = last_src_fn.off;
1132
1133 log.debug("moving __debug_line section: {} bytes from 0x{x} to 0x{x}", .{
1134 existing_size,
1135 debug_line_sect.offset,
1136 new_offset,
1137 });
1138
1139 const amt = try self.file.copyRangeAll(debug_line_sect.offset, self.file, new_offset, existing_size);
1140 if (amt != existing_size) return error.InputOutput;
1141 debug_line_sect.offset = @intCast(u32, new_offset);
1142 debug_line_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
1143 }
1144 debug_line_sect.size = needed_size;
1145 self.load_commands_dirty = true; // TODO look into making only the one section dirty
1146 self.debug_line_header_dirty = true;
1147 }
1148 const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0;
1149 const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;
1150
1151 // We only have support for one compilation unit so far, so the offsets are directly
1152 // from the .debug_line section.
1153 const file_pos = debug_line_sect.offset + src_fn.off;
1154 try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos);
1155
1156 // .debug_info - End the TAG_subprogram children.
1157 try dbg_info_buffer.append(0);
1158 },
1159 else => {},
1160 }
1161
1162 // Now we emit the .debug_info types of the Decl. These will count towards the size of
1163 // the buffer, so we have to do it before computing the offset, and we can't perform the actual
1164 // relocations yet.
1165 var it = dbg_info_type_relocs.iterator();
1166 while (it.next()) |entry| {
1167 entry.value.off = @intCast(u32, dbg_info_buffer.items.len);
1168 try self.addDbgInfoType(entry.key, dbg_info_buffer, target);
1169 }
1170
1171 try self.updateDeclDebugInfoAllocation(allocator, text_block, @intCast(u32, dbg_info_buffer.items.len));
1172
1173 // Now that we have the offset assigned we can finally perform type relocations.
1174 it = dbg_info_type_relocs.iterator();
1175 while (it.next()) |entry| {
1176 for (entry.value.relocs.items) |off| {
1177 mem.writeIntLittle(
1178 u32,
1179 dbg_info_buffer.items[off..][0..4],
1180 text_block.dbg_info_off + entry.value.off,
1181 );
1182 }
1183 }
1184
1185 try self.writeDeclDebugInfo(text_block, dbg_info_buffer.items);
1186}
1187
8911188/// Asserts the type has codegen bits.
892pub fn addDbgInfoType(
1189fn addDbgInfoType(
8931190 self: *DebugSymbols,
8941191 ty: Type,
8951192 dbg_info_buffer: *std.ArrayList(u8),
......@@ -931,7 +1228,7 @@ pub fn addDbgInfoType(
9311228 }
9321229}
9331230
934pub fn updateDeclDebugInfoAllocation(
1231fn updateDeclDebugInfoAllocation(
9351232 self: *DebugSymbols,
9361233 allocator: *Allocator,
9371234 text_block: *TextBlock,
......@@ -986,7 +1283,7 @@ pub fn updateDeclDebugInfoAllocation(
9861283 }
9871284}
9881285
989pub fn writeDeclDebugInfo(self: *DebugSymbols, text_block: *TextBlock, dbg_info_buf: []const u8) !void {
1286fn writeDeclDebugInfo(self: *DebugSymbols, text_block: *TextBlock, dbg_info_buf: []const u8) !void {
9901287 const tracy = trace(@src());
9911288 defer tracy.end();
9921289
......@@ -1057,19 +1354,19 @@ fn makeDebugString(self: *DebugSymbols, allocator: *Allocator, bytes: []const u8
10571354
10581355/// The reloc offset for the line offset of a function from the previous function's line.
10591356/// It's a fixed-size 4-byte ULEB128.
1060pub fn getRelocDbgLineOff() usize {
1357fn getRelocDbgLineOff() usize {
10611358 return dbg_line_vaddr_reloc_index + @sizeOf(u64) + 1;
10621359}
10631360
1064pub fn getRelocDbgFileIndex() usize {
1361fn getRelocDbgFileIndex() usize {
10651362 return getRelocDbgLineOff() + 5;
10661363}
10671364
1068pub fn getRelocDbgInfoSubprogramHighPC() u32 {
1365fn getRelocDbgInfoSubprogramHighPC() u32 {
10691366 return dbg_info_low_pc_reloc_index + @sizeOf(u64);
10701367}
10711368
1072pub fn dbgLineNeededHeaderBytes(self: DebugSymbols, module: *Module) u32 {
1369fn dbgLineNeededHeaderBytes(self: DebugSymbols, module: *Module) u32 {
10731370 const directory_entry_format_count = 1;
10741371 const file_name_entry_format_count = 1;
10751372 const directory_count = 1;
......@@ -1092,7 +1389,7 @@ fn dbgInfoNeededHeaderBytes(self: DebugSymbols) u32 {
10921389/// are less than 126,976 bytes (if this limit is ever reached, this function can be
10931390/// improved to make more than one pwritev call, or the limit can be raised by a fixed
10941391/// amount by increasing the length of `vecs`).
1095pub fn pwriteDbgLineNops(
1392fn pwriteDbgLineNops(
10961393 self: *DebugSymbols,
10971394 prev_padding_size: usize,
10981395 buf: []const u8,
......@@ -1170,7 +1467,7 @@ pub fn pwriteDbgLineNops(
11701467
11711468/// Writes to the file a buffer, prefixed and suffixed by the specified number of
11721469/// bytes of padding.
1173pub fn pwriteDbgInfoNops(
1470fn pwriteDbgInfoNops(
11741471 self: *DebugSymbols,
11751472 prev_padding_size: usize,
11761473 buf: []const u8,
......@@ -1239,25 +1536,3 @@ pub fn pwriteDbgInfoNops(
12391536
12401537 try self.file.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
12411538}
1242
1243pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const Module.Decl) !void {
1244 const tracy = trace(@src());
1245 defer tracy.end();
1246
1247 const container_scope = decl.scope.cast(Module.Scope.Container).?;
1248 const tree = container_scope.file_scope.contents.tree;
1249 const file_ast_decls = tree.root_node.decls();
1250 // TODO Look into improving the performance here by adding a token-index-to-line
1251 // lookup table. Currently this involves scanning over the source code for newlines.
1252 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
1253 const block = fn_proto.getBodyNode().?.castTag(.Block).?;
1254 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
1255 const casted_line_off = @intCast(u28, line_delta);
1256
1257 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
1258 const shdr = &dwarf_segment.sections.items[self.debug_line_section_index.?];
1259 const file_pos = shdr.offset + decl.fn_link.macho.off + getRelocDbgLineOff();
1260 var data: [4]u8 = undefined;
1261 leb.writeUnsignedFixed(4, &data, casted_line_off);
1262 try self.file.pwriteAll(&data, file_pos);
1263}